Author: bshaffer Date: 2010-02-07 18:56:57 +0100 (Sun, 07 Feb 2010) New Revision: 27650
Added: plugins/csSecurityTaskExtraPlugin/LICENSE plugins/csSecurityTaskExtraPlugin/README plugins/csSecurityTaskExtraPlugin/lib/ plugins/csSecurityTaskExtraPlugin/lib/task/ plugins/csSecurityTaskExtraPlugin/lib/task/sfAppGroupSecurityTask.class.php plugins/csSecurityTaskExtraPlugin/lib/task/sfAppRouteSecurityTask.class.php plugins/csSecurityTaskExtraPlugin/lib/task/sfAppSecurityTask.class.php plugins/csSecurityTaskExtraPlugin/lib/task/sfAppUserSecurityTask.class.php plugins/csSecurityTaskExtraPlugin/lib/task/sfBaseSecurityTaskExtraTask.class.php Log: initial commit for sfBaseSecurityTaskExtraPlugin Added: plugins/csSecurityTaskExtraPlugin/LICENSE =================================================================== --- plugins/csSecurityTaskExtraPlugin/LICENSE (rev 0) +++ plugins/csSecurityTaskExtraPlugin/LICENSE 2010-02-07 17:56:57 UTC (rev 27650) @@ -0,0 +1,7 @@ +Copyright (c) 2010 Brent Shaffer + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Property changes on: plugins/csSecurityTaskExtraPlugin/LICENSE ___________________________________________________________________ Added: svn:executable + * Added: plugins/csSecurityTaskExtraPlugin/README =================================================================== --- plugins/csSecurityTaskExtraPlugin/README (rev 0) +++ plugins/csSecurityTaskExtraPlugin/README 2010-02-07 17:56:57 UTC (rev 27650) @@ -0,0 +1,52 @@ +csSecurityTaskExtraPlugin +========================= + +The `csSecurityTaskExtraPlugin` provide some new tasks to the symfony command line tool + + * app:security application : Assess security coverage in your application + * app:route-security application : Assess security coverage of routes in your application + * app:group-security application [group] : Assess security coverage for groups in your application + * app:user-security application [user] : Assess security coverage for users in your application + +Examples +-------- + +The app:security task outputs a readable list of security.yml specifications in your application + + $ ./symfony app:security frontend + + + +The app:route-security task compares your security.ymls to all the routes in your application + + $ ./symfony app:route-security frontend + + + +You can also list who has access to with actions specified in security.yml with the group-security task. + + $ ./symfony app:group-security frontend + + + +Pass the name of an sfGuardGroup object as the second argument to narrow down your output + + $ ./symfony app:group-security frontend author + + + +List users who has access with the user-security task. + + $ ./symfony app:user-security frontend + + + +Pass the username or id of an sfGuardUser object as the second argument to narrow down your output + + $ ./symfony app:group-security frontend andyadministrator + OR + $ ./symfony app:group-security frontend 3 + + + +Please send all comments or questions to [Brent Shaffer](http://symplist.net/contact) \ No newline at end of file Property changes on: plugins/csSecurityTaskExtraPlugin/README ___________________________________________________________________ Added: svn:executable + * Added: plugins/csSecurityTaskExtraPlugin/lib/task/sfAppGroupSecurityTask.class.php =================================================================== --- plugins/csSecurityTaskExtraPlugin/lib/task/sfAppGroupSecurityTask.class.php (rev 0) +++ plugins/csSecurityTaskExtraPlugin/lib/task/sfAppGroupSecurityTask.class.php 2010-02-07 17:56:57 UTC (rev 27650) @@ -0,0 +1,108 @@ +<?php + +class sfAppGroupSecurityTask extends sfBaseSecurityTaskExtraTask +{ + protected function configure() + { + $this->addArguments(array( + new sfCommandArgument('application', sfCommandArgument::REQUIRED, 'The application'), + new sfCommandArgument('group', sfCommandArgument::OPTIONAL, 'The name of a group to check'), + )); + + $this->addOptions(array( + new sfCommandOption('env', null, sfCommandOption::PARAMETER_REQUIRED, 'The environment', 'prod'), + )); + + $this->aliases = array('group-security'); + $this->namespace = 'app'; + $this->name = 'group-security'; + $this->briefDescription = 'Assess security coverage for groups in your application'; + $this->detailedDescription = <<<EOF +The [app:group-security|INFO] displays the group security coverage for a given application: + + [./symfony app:group-security frontend|INFO] + +Pass the name of an sfGuardGroup object as the second argument to retrieve information about a specific group + + [./symfony app:group-security frontend administrator|INFO] +EOF; + } + + protected function execute($arguments = array(), $options = array()) + { + $databaseManager = new sfDatabaseManager($this->configuration); + $connection = $databaseManager->getDatabase('doctrine')->getConnection(); + + $maxGroup = 5; + $maxModule = 6; + $maxAction = 6; + $maxHasAccess = 10; + + if ($arguments['group']) + { + $groups = Doctrine::getTable('sfGuardGroup')->createQuery()->where('name = ?', $arguments['group'])->execute(); + } + else + { + $groups = Doctrine::getTable('sfGuardGroup')->findAll(); + } + + if (!$groups->count()) + { + throw new InvalidArgumentException($arguments['group'] ? "Group $arguments[group] not found":"No groups found in database"); + } + + $security = $this->getSecurityArray(); + + $groupAccess = array(); + foreach ($groups as $group) + { + $name = $group['name']; + if (strlen($name) > $maxGroup) + { + $maxGroup = strlen($name); + } + + $permissions = $group->getPermissions()->toKeyValueArray('id', 'name'); + + $groupAccess[$name] = $security; + foreach ($groupAccess[$name] as $i => $item) + { + if (!$item['is_secure']) + { + unset($groupAccess[$name][$i]); + continue; + } + $hasAccess = $this->testCredentials($permissions, $item['credentials']); + $groupAccess[$name][$i]['has_access'] = $hasAccess; + $groupAccess[$name][$i]['has_access_string'] = $hasAccess ? $this->formatter->format('yes', 'INFO') : $this->formatter->format('no', 'COMMENT'); + if (strlen($item['module']) > $maxModule) + { + $maxModule = strlen($item['module']); + } + + if (strlen($item['action']) > $maxAction) + { + $maxAction = strlen($item['action']); + } + } + } + + $formatRow1 = '%-'.($maxGroup + 11).'s %-'.$maxModule.'s %-'.($maxAction).'s %-'.($maxHasAccess + 11).'s %s'; + $formatRow2 = '%-'.($maxGroup + 11).'s %-'.$maxModule.'s %-'.($maxAction).'s %-'.($maxHasAccess + 9).'s %s'; + $formatHeader = '%-'.($maxGroup + 9).'s %-'.($maxModule + 9).'s %-'.($maxAction + 9).'s %-'.($maxHasAccess + 9).'s %s'; + + $this->log(sprintf($formatHeader, $this->formatter->format('Group', 'COMMENT'), $this->formatter->format('Module', 'COMMENT'), $this->formatter->format('Action', 'COMMENT'), $this->formatter->format('Has Access', 'COMMENT'), $this->formatter->format('Credentials', 'COMMENT'))); + + foreach ($groupAccess as $group => $security) + { + $count = 0; + $this->logSection('app', sprintf('Current security for application "%s" and group "%s"', $arguments['application'], $group)); + foreach ($security as $i => $item) + { + $this->log(sprintf($item['has_access'] ? $formatRow1:$formatRow2, $this->formatter->format($count == 0 ? $group : '',$this->labelFormat), $item['module'], $item['action'], $item['has_access_string'], $item['credential_string'])); + $count++; + } + } + } +} \ No newline at end of file Added: plugins/csSecurityTaskExtraPlugin/lib/task/sfAppRouteSecurityTask.class.php =================================================================== --- plugins/csSecurityTaskExtraPlugin/lib/task/sfAppRouteSecurityTask.class.php (rev 0) +++ plugins/csSecurityTaskExtraPlugin/lib/task/sfAppRouteSecurityTask.class.php 2010-02-07 17:56:57 UTC (rev 27650) @@ -0,0 +1,91 @@ +<?php + +class sfAppRouteSecurityTask extends sfBaseSecurityTaskExtraTask +{ + protected function configure() + { + $this->addArguments(array( + new sfCommandArgument('application', sfCommandArgument::REQUIRED, 'The application'), + )); + + $this->addOptions(array( + new sfCommandOption('env', null, sfCommandOption::PARAMETER_REQUIRED, 'The environment', 'prod'), + )); + + $this->aliases = array('route-security'); + $this->namespace = 'app'; + $this->name = 'route-security'; + $this->briefDescription = 'Assess security coverage of routes in your application'; + $this->detailedDescription = <<<EOF +The [app:route-security|INFO] displays the security coverage for a given application: + + [./symfony app:route-security frontend|INFO] +EOF; + } + /** + * @see sfTask + */ + protected function execute($arguments = array(), $options = array()) + { + $this->routes = $this->getRouting()->getRoutes(); + + $this->security = $this->getSecurityByModuleAction(); + + $this->logSection('app', sprintf('Current route security for application "%s"', $arguments['application'])); + + $maxName = 4; + $maxIsSecure = 9; + + $routeSecurity = array(); + + foreach ($this->routes as $name => $route) + { + $defaults = $route->getDefaults(); + + if (!isset($defaults['module']) || !isset($defaults['action'])) + { + // Can't determine security + continue; + } + + $module = $defaults['module']; + $action = $defaults['action']; + + if (isset($this->security[$module])) + { + if (isset($this->security[$module][$action])) + { + $routeSecurity[$name] = $this->security[$module][$action]; + } + elseif(isset($this->security[$module]['all'])) + { + $routeSecurity[$name] = $this->security[$module]['all']; + } + else + { + $routeSecurity[$name] = $this->appSecurity; + } + } + else + { + $routeSecurity[$name] = $this->appSecurity; + } + + if (strlen($name) > $maxName) + { + $maxName = strlen($name); + } + } + $formatRow1 = '%-'.$maxName.'s %-'.($maxIsSecure + 11).'s %s'; + $formatRow2 = '%-'.$maxName.'s %-'.($maxIsSecure + 9).'s %s'; + $formatHeader = '%-'.($maxName + 9).'s %-'.($maxIsSecure + 9).'s %s'; + + // displays the generated routes + $this->log(sprintf($formatHeader, $this->formatter->format('Name', 'COMMENT'), $this->formatter->format('Is Secure', 'COMMENT'), $this->formatter->format('Credentials', 'COMMENT'))); + foreach ($routeSecurity as $name => $policy) + { + $this->log(sprintf($policy['is_secure'] ? $formatRow1 : $formatRow2, $name, $policy['is_secure_string'], $policy['credential_string'])); + } + } + +} \ No newline at end of file Added: plugins/csSecurityTaskExtraPlugin/lib/task/sfAppSecurityTask.class.php =================================================================== --- plugins/csSecurityTaskExtraPlugin/lib/task/sfAppSecurityTask.class.php (rev 0) +++ plugins/csSecurityTaskExtraPlugin/lib/task/sfAppSecurityTask.class.php 2010-02-07 17:56:57 UTC (rev 27650) @@ -0,0 +1,59 @@ +<?php + +class sfAppSecurityTask extends sfBaseSecurityTaskExtraTask +{ + protected function configure() + { + $this->addArguments(array( + new sfCommandArgument('application', sfCommandArgument::REQUIRED, 'The application'), + )); + + $this->addOptions(array( + new sfCommandOption('env', null, sfCommandOption::PARAMETER_REQUIRED, 'The environment', 'prod'), + )); + + $this->namespace = 'app'; + $this->name = 'security'; + $this->briefDescription = 'Assess security coverage in your application'; + $this->detailedDescription = <<<EOF +Assess security.yml in your application +EOF; + } + + protected function execute($arguments = array(), $options = array()) + { + $maxModule = 6; + $maxAction = 6; + $maxIsSecure = 9; + + $security = $this->getSecurityArray(); + + foreach ($security as $item) + { + if (strlen($item['module']) > $maxModule) + { + $maxModule = strlen($item['module']); + } + + if (strlen($item['action']) > $maxAction) + { + $maxAction = strlen($item['action']); + } + + if (strlen($item['is_secure']) > $maxIsSecure) + { + $maxIsSecure = strlen($item['is_secure']); + } + } + + $formatRow1 = '%-'.$maxModule.'s %-'.$maxAction.'s %-'.($maxIsSecure + 11).'s %s'; + $formatRow2 = '%-'.$maxModule.'s %-'.$maxAction.'s %-'.($maxIsSecure + 9).'s %s'; + $formatHeader = '%-'.($maxModule + 9).'s %-'.($maxAction + 9).'s %-'.($maxIsSecure + 9).'s %s'; + + $this->log(sprintf($formatHeader, $this->formatter->format('Module', 'COMMENT'), $this->formatter->format('Action', 'COMMENT'), $this->formatter->format('Is Secure', 'COMMENT'), $this->formatter->format('Credentials', 'COMMENT'))); + foreach ($security as $item) + { + $this->log(sprintf($item['is_secure'] ? $formatRow1:$formatRow2, $item['module'], $item['action'], $item['is_secure']?$this->formatter->format('on', 'INFO') : $this->formatter->format('off', 'COMMENT'), $item['credential_string'])); + } + } +} \ No newline at end of file Property changes on: plugins/csSecurityTaskExtraPlugin/lib/task/sfAppSecurityTask.class.php ___________________________________________________________________ Added: svn:executable + * Added: plugins/csSecurityTaskExtraPlugin/lib/task/sfAppUserSecurityTask.class.php =================================================================== --- plugins/csSecurityTaskExtraPlugin/lib/task/sfAppUserSecurityTask.class.php (rev 0) +++ plugins/csSecurityTaskExtraPlugin/lib/task/sfAppUserSecurityTask.class.php 2010-02-07 17:56:57 UTC (rev 27650) @@ -0,0 +1,110 @@ +<?php + +class sfAppUserSecurityTask extends sfBaseSecurityTaskExtraTask +{ + protected function configure() + { + $this->addArguments(array( + new sfCommandArgument('application', sfCommandArgument::REQUIRED, 'The application'), + new sfCommandArgument('user', sfCommandArgument::OPTIONAL, 'The id or username of a user to check'), + )); + + $this->addOptions(array( + new sfCommandOption('env', null, sfCommandOption::PARAMETER_REQUIRED, 'The environment', 'prod'), + )); + + $this->aliases = array('user-security'); + $this->namespace = 'app'; + $this->name = 'user-security'; + $this->briefDescription = 'Assess security coverage for users in your application'; + $this->detailedDescription = <<<EOF +The [app:user-security|INFO] displays the user security coverage for a given application: + + [./symfony app:user-security frontend|INFO] + +Pass the id or username of an sfGuardUser object as the second argument to retrieve information about a specific user + + [./symfony app:user-security frontend johndoe|INFO] +EOF; + } + + protected function execute($arguments = array(), $options = array()) + { + $databaseManager = new sfDatabaseManager($this->configuration); + $connection = $databaseManager->getDatabase('doctrine')->getConnection(); + + $maxUsername = 8; + $maxModule = 6; + $maxAction = 6; + $maxHasAccess = 10; + + if ($arguments['user']) + { + $column = (int) $arguments['user'] ? 'id' : 'username'; + $users = Doctrine::getTable('sfGuardUser')->createQuery()->where($column.' = ?', $arguments['user'])->execute(); + } + else + { + $users = Doctrine::getTable('sfGuardUser')->findAll(); + } + + if (!$users->count()) + { + throw new InvalidArgumentException($arguments['users'] ? "User $arguments[user] not found":"No users found in database"); + } + + $this->logSection('app', sprintf('Current user security for application "%s"', $arguments['application']) . ($arguments['user'] ? sprintf(' and user "%s"', $arguments['user']) : '')); + + $security = $this->getSecurityArray(); + + $userAccess = array(); + foreach ($users as $user) + { + $name = $user['username']; + if (strlen($name) > $maxUsername) + { + $maxUsername = strlen($name); + } + + $permissions = $user->getAllPermissionNames(); + + $userAccess[$name] = $security; + foreach ($userAccess[$name] as $i => $item) + { + if (!$item['is_secure']) + { + unset($userAccess[$name][$i]); + continue; + } + $hasAccess = $user['is_super_admin'] || $this->testCredentials($permissions, $item['credentials']); + $userAccess[$name][$i]['has_access'] = $hasAccess; + $userAccess[$name][$i]['has_access_string'] = $hasAccess ? $this->formatter->format('yes', 'INFO') : $this->formatter->format('no', 'COMMENT'); + + if (strlen($item['module']) > $maxModule) + { + $maxModule = strlen($item['module']); + } + + if (strlen($item['action']) > $maxAction) + { + $maxAction = strlen($item['action']); + } + } + } + + $formatRow1 = '%-'.($maxUsername + 11).'s %-'.$maxModule.'s %-'.($maxAction).'s %-'.($maxHasAccess + 11).'s %s'; + $formatRow2 = '%-'.($maxUsername + 11).'s %-'.$maxModule.'s %-'.($maxAction).'s %-'.($maxHasAccess + 9).'s %s'; + $formatHeader = '%-'.($maxUsername + 9).'s %-'.($maxModule + 9).'s %-'.($maxAction + 9).'s %-'.($maxHasAccess + 9).'s %s'; + + $this->log(sprintf($formatHeader, $this->formatter->format('Username', 'COMMENT'), $this->formatter->format('Module', 'COMMENT'), $this->formatter->format('Action', 'COMMENT'), $this->formatter->format('Has Access', 'COMMENT'), $this->formatter->format('Credentials', 'COMMENT'))); + foreach ($userAccess as $name => $security) + { + $count = 0; + foreach ($security as $i => $item) + { + $this->log(sprintf($item['has_access'] ? $formatRow1:$formatRow2, $this->formatter->format($count == 0 ? $name:'', $this->labelFormat), $item['module'], $item['action'], $item['has_access_string'], $item['credential_string'])); + $count++; + } + } + } +} \ No newline at end of file Added: plugins/csSecurityTaskExtraPlugin/lib/task/sfBaseSecurityTaskExtraTask.class.php =================================================================== --- plugins/csSecurityTaskExtraPlugin/lib/task/sfBaseSecurityTaskExtraTask.class.php (rev 0) +++ plugins/csSecurityTaskExtraPlugin/lib/task/sfBaseSecurityTaskExtraTask.class.php 2010-02-07 17:56:57 UTC (rev 27650) @@ -0,0 +1,145 @@ +<?php + +abstract class sfBaseSecurityTaskExtraTask extends sfBaseTask +{ + protected $labelFormat = array('fg' => 'white', 'bold' => true); + public function getSecurityArray($options = array()) + { + $appSecurityPath = sfConfig::get('sf_app_config_dir').'/security.yml'; + + $appSecurity = file_exists($appSecurityPath) ? sfYaml::load($appSecurityPath) : array(); + + $this->appSecurity = isset($appSecurity['default']) ? $appSecurity['default'] : array('default' => array('is_secure' => false)); + + $this->appSecurity = array( + 'module' => 'global', + 'action' => 'default', + 'is_secure' => $this->appSecurity['is_secure'], + 'credentials' => isset($this->appSecurity['credentials']) ? $this->appSecurity['credentials'] : array(), + 'credential_string' => isset($this->appSecurity['credentials']) ? $this->formatCredentials($this->appSecurity['credentials']) : $this->formatter->format('none', 'COMMENT')); + + $this->appSecurity['is_secure_string'] = $this->appSecurity['is_secure'] ? $this->formatter->format('yes', 'INFO') : $this->formatter->format('no', 'COMMENT'); + + $files = glob(sfConfig::get('sf_app_dir').'/modules/*/config/security.yml'); + $security = array('global-defaut' => $this->appSecurity); + + foreach ($files as $policy_file) { + ereg("^".sfConfig::get('sf_app_dir')."/modules/([^/]+)/", $policy_file, $matches); + + $policy = sfYaml::load($policy_file); + + foreach ($policy as $action => $actionOptions) + { + $params = array('module' => $matches[1], + 'action' => $action, + 'is_secure' => isset($actionOptions['is_secure']) ? $actionOptions['is_secure'] : null, + 'credentials' => isset($actionOptions['credentials']) ? $actionOptions['credentials'] : null); + + $security[$matches[1].'-'.$action] = $params; + } + } + + // Clean + foreach ($security as $key => $policy) + { + $allKey = $policy['module'].'-all'; + + if ($policy['is_secure'] === null) + { + $policy['is_secure'] = $key != $allKey && isset($security[$allKey]) && $security[$allKey]['is_secure'] !== null ? $security[$allKey]['is_secure'] : $this->appSecurity['is_secure']; + } + + if($policy['credentials'] === null) + { + $policy['credentials'] = $key != $allKey && isset($security[$allKey]) && $security[$allKey]['credentials'] !== null ? $security[$allKey]['credentials'] : $this->appSecurity['credentials']; + } + + if (!$policy['is_secure']) + { + $policy['credentials'] = array(); + } + + $policy['is_secure_string'] = $policy['is_secure'] ? $this->formatter->format('yes', 'INFO') : $this->formatter->format('no', 'COMMENT'); + + $policy['credential_string'] = $policy['credentials'] ? $this->formatCredentials($policy['credentials']) : $this->formatter->format('none', 'COMMENT'); + $security[$key] = $policy; + } + + return $security; + } + + protected function getSecurityByModuleAction() + { + $security = array(); + foreach ($this->getSecurityArray() as $policy) + { + if(!isset($security[$policy['module']])) + { + $security[$policy['module']] = array(); + } + $security[$policy['module']][$policy['action']] = $policy; + } + + return $security; + } + + protected function testCredentials($setCredentials, $checkCredentials) + { + $this->credentials = $setCredentials; + return $this->hasCredential($checkCredentials); + } + + protected function formatCredentials($credentials, $and = true) + { + $formattedCredentials = array(); + if (is_array($credentials)) + { + foreach ($credentials as $credential) + { + $formattedString = is_array($credential) ? $this->formatCredentials($credential, !$and) : $credential; + if (is_array($credential) && count($credentials) > 1) + { + $formattedString = $this->formatter->format("(", 'COMMENT').$formattedString.$this->formatter->format(")", 'COMMENT'); + } + $formattedCredentials[] = $formattedString; + } + return implode($this->formatter->format($and ? ' AND ' : ' OR ', 'INFO'), $formattedCredentials); + } + + return $credentials; + } + + protected function hasCredential($credentials, $useAnd = true) + { + if (!is_array($credentials)) + { + return in_array($credentials, $this->credentials); + } + + // now we assume that $credentials is an array + $test = false; + + foreach ($credentials as $credential) + { + // recursively check the credential with a switched AND/OR mode + $test = $this->hasCredential($credential, $useAnd ? false : true); + + if ($useAnd) + { + $test = $test ? false : true; + } + + if ($test) // either passed one in OR mode or failed one in AND mode + { + break; // the matter is settled + } + } + + if ($useAnd) // in AND mode we succeed if $test is false + { + $test = $test ? false : true; + } + + return $test; + } +} \ No newline at end of file -- You received this message because you are subscribed to the Google Groups "symfony SVN" group. To post to this group, send email to [email protected]. To unsubscribe from this group, send email to [email protected]. For more options, visit this group at http://groups.google.com/group/symfony-svn?hl=en.
