diff --git a/src/Ratchet/Component/Server/FlashPolicyComponent.php b/src/Ratchet/Component/Server/FlashPolicyComponent.php index b31e813..8510fac 100644 --- a/src/Ratchet/Component/Server/FlashPolicyComponent.php +++ b/src/Ratchet/Component/Server/FlashPolicyComponent.php @@ -4,6 +4,8 @@ use Ratchet\Component\MessageComponentInterface; use Ratchet\Resource\ConnectionInterface; use Ratchet\Resource\Connection; use Ratchet\Resource\Command\CommandInterface; +use Ratchet\Resource\Command\Action\SendMessage; +use Ratchet\Resource\Command\Action\CloseConnection; /** * An app to go on a server stack to pass a policy file to a Flash socket @@ -11,41 +13,102 @@ use Ratchet\Resource\Command\CommandInterface; * Be sure to run your server instance on port 843 * By default this lets accepts everything, make sure you tighten the rules up for production * @final - * @todo This just gets dumped with a whole xml file - I will make a nice API to implement this (eventually) - * @todo Move this into Ratchet when the above todo is complete + * @link http://www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html + * @link http://learn.adobe.com/wiki/download/attachments/64389123/CrossDomain_PolicyFile_Specification.pdf?version=1 + * @link view-source:http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd */ class FlashPolicyComponent implements MessageComponentInterface { - protected $_policy = ''; - protected $_access = array(); + /** + * Contains the root policy node + * @var string + */ + protected $_policy = ''; + + /** + * Stores an array of allowed domains and their ports + * @var array + */ + protected $_access = array(); + + /** + * @var string + */ protected $_siteControl = ''; - protected $_cache = ''; + /** + * @var string + */ + protected $_cache = ''; + + /** + * @var string + */ protected $_cacheValid = false; /** - * @{inheritdoc} + * Add a domain to an allowed access list. + * + * @param string Specifies a requesting domain to be granted access. Both named domains and IP + * addresses are acceptable values. Subdomains are considered different domains. A wildcard (*) can + * be used to match all domains when used alone, or multiple domains (subdomains) when used as a + * prefix for an explicit, second-level domain name separated with a dot (.) + * @param string A comma-separated list of ports or range of ports that a socket connection + * is allowed to connect to. A range of ports is specified through a dash (-) between two port numbers. + * Ranges can be used with individual ports when separated with a comma. A single wildcard (*) can + * be used to allow all ports. + * @param bool + * @return FlashPolicyComponent */ - public function onOpen(ConnectionInterface $conn) { - $conn->PolicyRequest = ''; + public function addAllowedAccess($domain, $ports = '*', $secure = false) { + if (!$this->validateDomain($domain)) { + throw new \UnexpectedValueException('Invalid domain'); + } + + if (!$this->validatePorts($ports)) { + throw new \UnexpectedValueException('Invalid Port'); + } + + $this->_access[] = array($domain, $ports, (boolean)$secure); + $this->_cacheValid = false; + + return $this; } /** - * @{inheritdoc} + * site-control defines the meta-policy for the current domain. A meta-policy specifies acceptable + * domain policy files other than the master policy file located in the target domain's root and named + * crossdomain.xml. + * + * @param string + * @return FlashPolicyComponent + */ + public function setSiteControl($permittedCrossDomainPolicies = 'all') { + if (!$this->validateSiteControl($permittedCrossDomainPolicies)) { + throw new \UnexpectedValueException('Invalid site control set'); + } + + $this->_siteControl = $permittedCrossDomainPolicies; + $this->_cacheValid = false; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function onOpen(ConnectionInterface $conn) { + } + + /** + * {@inheritdoc} */ public function onMessage(ConnectionInterface $from, $msg) { - if (!$this->_cacheValid) { $this->_cache = $this->renderPolicy()->asXML(); $this->_cacheValid = true; } - $from->PolicyRequest .= $msg; - if (strlen($from->_cache) < 20) { - return; - } - - $cmd = new SendMessage($from); $cmd->setMessage($this->_cache . "\0"); @@ -53,141 +116,76 @@ class FlashPolicyComponent implements MessageComponentInterface { } /** - * @{inheritdoc} + * {@inheritdoc} */ public function onClose(ConnectionInterface $conn) { } /** - * @{inheritdoc} + * {@inheritdoc} */ public function onError(ConnectionInterface $conn, \Exception $e) { return new CloseConnection($conn); } /** - * setSiteControl function. + * Builds the crossdomain file based on the template policy * - * @access public - * @param string $permittedCrossDomainPolicies (default: 'all') - * @return bool - */ - public function setSiteControl($permittedCrossDomainPolicies = 'all') { - if (!$this->validateSiteControl($permittedCrossDomainPolicies)) { - throw new \UnexpectedValueException('Invalid site control set'); - return false; - } - $this->_siteControl = $permittedCrossDomainPolicies; - return true; - } - - /** - * renderPolicy function. - * - * @access public * @return SimpleXMLElement */ public function renderPolicy() { - $policy = new \SimpleXMLElement($this->_policy); - $siteControl = $policy->addChild('site-control'); if ($this->_siteControl == '') { - throw new \UnexpectedValueException('Where\'s my site control?'); + $this->setSiteControl(); } + $siteControl->addAttribute('permitted-cross-domain-policies', $this->_siteControl); - if (empty($this->_access)) { - throw new \UnexpectedValueException('Missing site access'); + throw new \UnexpectedValueException('You must add a domain through addAllowedAccess()'); } - foreach ($this->_access as $access) { + foreach ($this->_access as $access) { $tmp = $policy->addChild('allow-access-from'); $tmp->addAttribute('domain', $access[0]); $tmp->addAttribute('to-ports', $access[1]); - $tmp->addAttribute('secure', ($access[2] == true) ? 'true' : 'false'); + $tmp->addAttribute('secure', ($access[2] === true) ? 'true' : 'false'); } return $policy; - } /** - * addAllowedAccess function. + * Make sure the proper site control was passed * - * @access public - * @param string $domain - * @param string $ports (default: '*') - * @param bool $secure (default: false) + * @param string * @return bool */ - public function addAllowedAccess($domain, $ports = '*', $secure = false) { - - if (!$this->validateDomain($domain)) { - throw new \UnexpectedValueException('Invalid domain'); - return false; - } - if (!$this->validatePorts($ports)) { - throw new \UnexpectedValueException('Invalid Port'); - return false; - } - - - $this->_access[] = array($domain, $ports, $secure); - $this->_cacheValid = false; - - return true; - } - - /** - * validateSiteControl function. - * - * @access public - * @param mixed $permittedCrossDomainPolicies - * @return void - */ public function validateSiteControl($permittedCrossDomainPolicies) { - - //'by-content-type' and 'by-ftp-filename' not available for sockets + //'by-content-type' and 'by-ftp-filename' are not available for sockets return (bool)in_array($permittedCrossDomainPolicies, array('none', 'master-only', 'all')); } /** - * validateDomain function. + * Validate for proper domains (wildcards allowed) * - * @access public - * @param string $domain + * @param string * @return bool */ public function validateDomain($domain) { - return (bool)preg_match("/^((http(s)?:\/\/)?([a-z0-9-_]+\.|\*\.)*([a-z0-9-_\.]+)|\*)$/i", $domain); } /** - * validatePorts function. + * Make sure valid ports were passed * - * @access public - * @param string $port + * @param string * @return bool */ public function validatePorts($port) { - return (bool)preg_match('/^(\*|(\d+[,-]?)*\d+)$/', $port); } - - /** - * validateSecure function. - * - * @access public - * @param bool $secure - * @return bool - */ - public function validateSecure($secure) { - - return is_bool($secure); - } } \ No newline at end of file diff --git a/tests/Ratchet/Tests/Component/Server/FlashPolicyComponentTest.php b/tests/Ratchet/Tests/Component/Server/FlashPolicyComponentTest.php index 6382711..cdce9a7 100644 --- a/tests/Ratchet/Tests/Component/Server/FlashPolicyComponentTest.php +++ b/tests/Ratchet/Tests/Component/Server/FlashPolicyComponentTest.php @@ -13,7 +13,6 @@ class FlashPolicyComponentTest extends \PHPUnit_Framework_TestCase { $this->_policy = new FlashPolicyComponent(); } - public function testPolicyRender() { $this->_policy->setSiteControl('all'); $this->_policy->addAllowedAccess('example.com', '*'); @@ -25,12 +24,6 @@ class FlashPolicyComponentTest extends \PHPUnit_Framework_TestCase { $this->setExpectedException('UnexpectedValueException'); $this->_policy->renderPolicy(); } - - public function testAnotherInvalidPolicyReader() { - $this->setExpectedException('UnexpectedValueException'); - $this->_policy->addAllowedAccess('dev.example.com', '*'); - $this->_policy->renderPolicy(); - } public function testInvalidDomainPolicyReader() { $this->setExpectedException('UnexpectedValueException'); @@ -38,8 +31,7 @@ class FlashPolicyComponentTest extends \PHPUnit_Framework_TestCase { $this->_policy->addAllowedAccess('dev.example.*', '*'); $this->_policy->renderPolicy(); } - - + /** * @dataProvider siteControl */ @@ -62,7 +54,6 @@ class FlashPolicyComponentTest extends \PHPUnit_Framework_TestCase { ); } - /** * @dataProvider URI */ @@ -92,7 +83,6 @@ class FlashPolicyComponentTest extends \PHPUnit_Framework_TestCase { ); } - /** * @dataProvider ports */ @@ -118,25 +108,4 @@ class FlashPolicyComponentTest extends \PHPUnit_Framework_TestCase { , array(false, '838*') ); } - - /** - * @dataProvider bools - */ - public function testSecureValidation($accept, $bool) { - $this->assertEquals($accept, $this->_policy->validateSecure($bool)); - } - - public static function bools() { - return array( - array(true, true) - , array(true, false) - , array(false, 1) - , array(false, 0) - , array(false, 'false') - , array(false, 'on') - , array(false, 'yes') - , array(false, '--') - , array(false, '!') - ); - } } \ No newline at end of file