Initial commit with Symfony 2.1+Vendors

Signed-off-by: Gergely POLONKAI (W00d5t0ck) <polesz@w00d5t0ck.info>
This commit is contained in:
Polonkai Gergely
2012-07-01 09:52:20 +02:00
commit 082a0130c2
5381 changed files with 416709 additions and 0 deletions

View File

@@ -0,0 +1,59 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Annotation;
use JMS\SecurityExtraBundle\Exception\InvalidArgumentException;
/**
* Annotation for expression-based access control.
*
* @Annotation
* @Target("METHOD")
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
final class PreAuthorize
{
/**
* @Required
* @var string
*/
public $expr;
public function __construct()
{
if (0 === func_num_args()) {
return;
}
$values = func_get_arg(0);
if (isset($values['value'])) {
$values['expr'] = $values['value'];
}
if (!isset($values['expr'])) {
throw new InvalidArgumentException('The "expr" attribute must be set for annotation @PreAuthorize.');
}
if (!is_string($values['expr'])) {
throw new InvalidArgumentException(sprintf('The "expr" attribute of annotation @PreAuthorize must be a string, but got "%s".', gettype($values['expr'])));
}
$this->expr = $values['expr'];
}
}

View File

@@ -0,0 +1,42 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Annotation;
use JMS\SecurityExtraBundle\Exception\InvalidArgumentException;
/**
* @Annotation
* @Target("METHOD")
*/
final class RunAs
{
public $roles;
public function __construct(array $values)
{
if (isset($values['value'])) {
$values['roles'] = $values['value'];
}
if (!isset($values['roles'])) {
throw new InvalidArgumentException('"roles" must be defined for RunAs annotation.');
}
$this->roles = array_map('trim', explode(',', $values['roles']));
}
}

View File

@@ -0,0 +1,81 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Annotation;
/**
* This must be declared on classes which inherit from classes that have
* requested method invocation securing capabilities.
*
* It indicates to the analyzer that the developer is aware of these security
* restrictions, and has applied them to the root class in an appropriate
* fashion.
*
* We cannot do this automatically without properly analyzing the control flow,
* and in some cases it is not possible at all. See the following example:
*
* <code>
* // child class
* public function editComment($commentId)
* {
* // retrieve comment from database
* $comment = $this->entityManager->find($commentId);
*
* return parent::editComment($comment);
* }
*
* // base class which is inherited from
* /**
* * @SecureParam(name="comment", permissions="EDIT")
* *\/
* public function editComment(Comment $comment)
* {
* // do some supposedly secure action
* }
* <code>
*
* The above example can be rewritten so that we can apply security checks
* automatically:
*
* <code>
* // child class
* public function editComment($commentId)
* {
* // retrieve comment from database
* $comment = $this->entityManager->find($commentId);
*
* return $this->doEditComment($comment);
* }
*
* // base class which is inherited from
* /**
* * @SecureParam(name="comment", permissions="EDIT")
* *\/
* protected function doEditComment(Comment $comment)
* {
* // do some secure action
* }
* </code>
*
* @Annotation
* @Target("METHOD")
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
final class SatisfiesParentSecurityPolicy
{
}

View File

@@ -0,0 +1,45 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Annotation;
use JMS\SecurityExtraBundle\Exception\InvalidArgumentException;
/**
* Represents a @Secure annotation.
*
* @Annotation
* @Target("METHOD")
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
final class Secure
{
public $roles;
public function __construct(array $values)
{
if (isset($values['value'])) {
$values['roles'] = $values['value'];
}
if (!isset($values['roles'])) {
throw new InvalidArgumentException('You must define a "roles" attribute for each Secure annotation.');
}
$this->roles = array_map('trim', explode(',', $values['roles']));
}
}

View File

@@ -0,0 +1,48 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Annotation;
use JMS\SecurityExtraBundle\Exception\InvalidArgumentException;
/**
* Represents a @SecureParam annotation.
*
* @Annotation
* @Target("METHOD")
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
final class SecureParam
{
public $name;
public $permissions;
public function __construct(array $values)
{
if (!isset($values['name'])) {
throw new InvalidArgumentException('You must define a "name" attribute for each SecureParam annotation.');
}
if (!isset($values['permissions'])) {
throw new InvalidArgumentException('You must define a "permissions" attribute for each SecureParam annotation.');
}
$this->name = $values['name'];
$this->permissions = array_map('trim', explode(',', $values['permissions']));
}
}

View File

@@ -0,0 +1,45 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Annotation;
use JMS\SecurityExtraBundle\Exception\InvalidArgumentException;
/**
* Represents a @SecureReturn annotation.
*
* @Annotation
* @Target("METHOD")
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
final class SecureReturn
{
public $permissions;
public function __construct(array $values)
{
if (isset($values['value'])) {
$values['permissions'] = $values['value'];
}
if (!isset($values['permissions'])) {
throw new InvalidArgumentException('You must define a "permissions" attribute for each SecureReturn annotation.');
}
$this->permissions = array_map('trim', explode(',', $values['permissions']));
}
}

View File

@@ -0,0 +1,38 @@
This document details all changes from JMSSecurityExtraBundle 1.0.x to 1.1:
- The configuration option "secure_controllers" has been removed. This setting is
now automatically enabled, but it requires the JMSDiExtraBundle.
- The dependencies of this bundle have changed:
* The metadata library 1.1 version is now required instead of the 1.0 version
(if you are using the Standard Edition, just change the "version=origin/1.0.x"
line from your deps file to "version=1.1.0").
* The JMSAopBundle is now required. For installation instructions, please see
https://github.com/schmittjoh/JMSAopBundle
* The JMSDiExtraBundle is now required if you want to secure your non-service
controllers (if you only have service controllers, you don't need it). For
installation instructions, see https://github.com/schmittjoh/JMSDiExtraBundle
- The attribute "IS_IDDQD" has been renamed to "ROLE_IDDQD"
- A powerful expression-based authorization language has been added which works
in combination with the existing voting system. Since it is much more powerful
than the built-in voters, and also much faster, you are highly encouraged to
migrate your existing authorization rules to expressions, and eventually disable
the built-in voters entirely. Some examples for how to convert simple attributes
to their equivalent expressions are listed below:
* IS_AUTHENTICATED_ANONYMOUSLY -> "permitAll"
* IS_AUTHENTICATED_REMEMBERED -> "isAuthenticated()"
* IS_AUTHENTICATED_FULLY -> "isFullyAuthenticated()"
* ROLE_FOO -> "hasRole('ROLE_FOO')"
- The ability to configure method access control (e.g. for controller actions)
in the DI configuration has been added. Note that for non-service controllers
the JMSDiExtraBundle is required.
- The "is_expr_granted" Twig function has been added if you want to check an
expression from a Twig template.

View File

@@ -0,0 +1,90 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\DependencyInjection;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Bundle\SecurityBundle\DependencyInjection\MainConfiguration as BaseConfiguration;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AbstractFactory;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
/**
* Enhances the access_control section configuration.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class AccessControlConfiguration implements ConfigurationInterface
{
/**
* Generates the configuration tree builder.
*
* @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder
*/
public function getConfigTreeBuilder()
{
$tb = new TreeBuilder();
$rootNode = $tb->root('security');
$rootNode
->ignoreExtraKeys()
->fixXmlConfig('rule', 'access_control')
->children()
->arrayNode('access_control')
->cannotBeOverwritten()
->prototype('array')
->fixXmlConfig('role')
->validate()
->always(function($v) {
if (!empty($v['roles']) && isset($v['access'])) {
throw new \Exception('"roles", and "access" cannot be set at the same time.');
}
if (empty($v['roles'])) {
unset($v['roles']);
}
return $v;
})
->end()
->children()
->scalarNode('requires_channel')->defaultNull()->end()
->scalarNode('path')->defaultNull()->end()
->scalarNode('host')->defaultNull()->end()
->scalarNode('ip')->defaultNull()->end()
->arrayNode('methods')
->beforeNormalization()->ifString()->then(function($v) { return preg_split('/\s*,\s*/', $v); })->end()
->prototype('scalar')->end()
->end()
->arrayNode('roles')
->beforeNormalization()->ifString()->then(function($v) { return preg_split('/\s*,\s*/', $v); })->end()
->prototype('scalar')->end()
->end()
->scalarNode('access')->end()
->end()
->end()
->end()
->end()
;
return $tb;
}
}

View File

@@ -0,0 +1,52 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* Collects after invocation providers.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class AddAfterInvocationProvidersPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('security.access.after_invocation_manager')) {
return;
}
$providers = array();
foreach (array_keys($container->findTaggedServiceIds('security.after_invocation.provider')) as $id) {
if ('security.access.after_invocation.acl_provider' === $id && !$container->has('security.acl.provider')) {
continue;
}
$providers[] = new Reference($id);
}
$container
->getDefinition('security.access.after_invocation_manager')
->setArguments(array($providers))
;
}
}

View File

@@ -0,0 +1,66 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\DependencyInjection\Compiler;
use JMS\SecurityExtraBundle\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
class AddExpressionCompilersPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('security.expressions.compiler')) {
return;
}
$compilerDef = $container->getDefinition('security.expressions.compiler');
foreach ($container->findTaggedServiceIds('security.expressions.function_compiler')
as $id => $attr) {
$compilerDef->addMethodCall('addFunctionCompiler', array(new Reference($id)));
}
foreach ($container->findTaggedServiceIds('security.expressions.type_compiler')
as $id => $attr) {
$compilerDef->addMethodCall('addTypeCompiler', array(new Reference($id)));
}
$serviceMap = $parameterMap = array();
foreach ($container->findTaggedServiceIds('security.expressions.variable') as $id => $attributes) {
foreach ($attributes as $attr) {
if (!isset($attr['variable']) || (!isset($attr['service']) && !isset($attr['parameter']))) {
throw new RuntimeException(sprintf('"variable", and either "service" or "parameter" must be given for tag "security.expressions.variable" for service id "%s".', $id));
}
if (isset($attr['service'])) {
$serviceMap[$attr['variable']] = $attr['service'];
$container
->findDefinition($attr['service'])
->setPublic(true)
;
} else {
$parameterMap[$attr['variable']] = $attr['parameter'];
}
}
}
$container->getDefinition('security.expressions.variable_compiler')
->addMethodCall('setMaps', array($serviceMap, $parameterMap));
}
}

View File

@@ -0,0 +1,43 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* Collects secured services.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class CollectSecuredServicesPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$securedClasses = array();
foreach ($container->findTaggedServiceIds('security.secure_service') as $id => $attr) {
$securedClasses[] = $container->getDefinition($id)->getClass();
}
$container
->getDefinition('security.access.pointcut')
->addMethodCall('setSecuredClasses', array($securedClasses))
;
}
}

View File

@@ -0,0 +1,46 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
class DisableVotersPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if ($container->getParameter('security.role_voter.disabled')) {
$container->removeDefinition('security.access.role_hierarchy_voter');
$container->removeDefinition('security.access.simple_role_voter');
}
if ($container->getParameter('security.authenticated_voter.disabled')) {
$container->removeDefinition('security.access.authenticated_voter');
}
if ($container->hasDefinition('security.acl.voter.basic_permissions')) {
if ($container->getParameter('security.acl_voter.disabled')) {
$container->removeDefinition('security.acl.voter.basic_permissions');
} else {
$container->getDefinition('security.acl.voter.basic_permissions')
->setClass('JMS\SecurityExtraBundle\Security\Acl\Voter\AclVoter');
}
}
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace JMS\SecurityExtraBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
class IntegrationPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasAlias('security.acl.provider')
&& !$container->hasDefinition('security.acl.provider')) {
$container->removeDefinition('security.acl.permission_evaluator');
}
if ($container->hasDefinition('security.role_hierarchy')) {
$container->getDefinition('security.role_hierarchy')
->setPublic(true);
}
}
}

View File

@@ -0,0 +1,64 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$tb = new TreeBuilder();
$tb
->root('jms_security_extra')
->validate()
->always(function($v) {
if ($v['method_access_control'] && !$v['expressions']) {
throw new \Exception('You need to enable expressions if you want to configure method access via the DI config.');
}
return $v;
})
->end()
->children()
->booleanNode('secure_all_services')->defaultFalse()->end()
->booleanNode('enable_iddqd_attribute')->defaultFalse()->end()
->scalarNode('cache_dir')->cannotBeEmpty()->defaultValue('%kernel.cache_dir%/jms_security')->end()
->booleanNode('expressions')->defaultFalse()->end()
->arrayNode('voters')
->addDefaultsIfNotSet()
->canBeUnset()
->children()
->booleanNode('disable_authenticated')->defaultFalse()->end()
->booleanNode('disable_role')->defaultFalse()->end()
->booleanNode('disable_acl')->defaultFalse()->end()
->end()
->end()
->arrayNode('method_access_control')
->useAttributeAsKey('pattern')
->prototype('scalar')->isRequired()->cannotBeEmpty()->end()
->end()
->end()
->end()
;
return $tb;
}
}

View File

@@ -0,0 +1,100 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\Reference;
use JMS\SecurityExtraBundle\Exception\RuntimeException;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Definition\Processor;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* JMSSecurityExtraExtension.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class JMSSecurityExtraExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$bundles = $container->getParameter('kernel.bundles');
if (!isset($bundles['JMSAopBundle'])) {
throw new RuntimeException('The JMSSecurityExtraBundle requires the JMSAopBundle, please make sure to enable it in your AppKernel.');
}
$config = $this->processConfiguration(new Configuration(), $configs);
$loader = new XmlFileLoader($container, new FileLocator(array(__DIR__.'/../Resources/config/')));
$loader->load('services.xml');
$container->setParameter('security.access.secure_all_services', $config['secure_all_services']);
$cacheDir = $container->getParameterBag()->resolveValue($config['cache_dir']);
if (!is_dir($cacheDir)) {
if (false === @mkdir($cacheDir, 0777, true)) {
throw new RuntimeException(sprintf('Could not create cache directory "%s".', $cacheDir));
}
}
$container->setParameter('security.extra.cache_dir', $cacheDir);
if ($config['expressions']) {
$loader->load('security_expressions.xml');
if (!is_dir($cacheDir.'/expressions')) {
if (false === @mkdir($cacheDir.'/expressions', 0777, true)) {
throw new RuntimeException(sprintf('Could not create cache directory "%s".', $cacheDir.'/expressions'));
}
}
$container->getDefinition('security.expressions.voter')
->addMethodCall('setCacheDir', array($cacheDir.'/expressions'));
}
$disableAllVoters = !isset($config['voters']);
$container->setParameter('security.authenticated_voter.disabled',
$disableAllVoters || $config['voters']['disable_authenticated']);
$container->setParameter('security.role_voter.disabled',
$disableAllVoters || $config['voters']['disable_role']);
$container->setParameter('security.acl_voter.disabled',
$disableAllVoters || $config['voters']['disable_acl']);
if ($config['enable_iddqd_attribute']) {
$container
->getDefinition('security.extra.iddqd_voter')
->addTag('security.voter')
;
// FIXME: Also add an iddqd after invocation provider
}
if ($config['method_access_control']) {
$driverDef = $container->getDefinition('security.extra.driver_chain');
$args = $driverDef->getArguments();
array_unshift($args[0], new Reference('security.extra.config_driver'));
$driverDef->setArguments($args);
$container->setParameter('security.access.method_access_control',
$config['method_access_control']);
}
}
}

View File

@@ -0,0 +1,130 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\DependencyInjection;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension as BaseSecurityExtension;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\Config\FileLocator;
/**
* Enhances the access_control section of the SecurityBundle.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class SecurityExtension extends Extension
{
private $extension;
public function __construct(BaseSecurityExtension $extension)
{
$this->extension = $extension;
}
public function getAlias()
{
return $this->extension->getAlias();
}
public function getNamespace()
{
return $this->extension->getNamespace();
}
public function getXsdValidationBasePath()
{
return $this->extension->getXsdValidationBasePath();
}
public function getClassesToCompile()
{
return array_merge(parent::getClassesToCompile(), $this->extension->getClassesToCompile());
}
public function load(array $configs, ContainerBuilder $container)
{
$parentConfigs = array();
foreach ($configs as $config) {
if (isset($config['rule'])) {
unset($config['rule']);
}
if (isset($config['access_control'])) {
unset($config['access_control']);
}
$parentConfigs[] = $config;
}
$this->extension->load($parentConfigs, $container);
$config = $this->processConfiguration(new AccessControlConfiguration(), $configs);
$this->createAuthorization($config, $container);
}
public function __call($method, array $args)
{
return call_user_func_array(array($this->extension, $method), $args);
}
private function createAuthorization($config, ContainerBuilder $container)
{
if (!$config['access_control']) {
return;
}
$this->addClassesToCompile(array(
'Symfony\\Component\\Security\\Http\\AccessMap',
));
foreach ($config['access_control'] as $access) {
$matcher = $this->invokeParent('createRequestMatcher', array(
$container,
$access['path'],
$access['host'],
count($access['methods']) === 0 ? null : $access['methods'],
$access['ip']
));
if (isset($access['roles'])) {
$attributes = $access['roles'];
} else {
$def = new DefinitionDecorator('security.expressions.expression');
$def->addArgument($access['access']);
$container->setDefinition($exprId = 'security.expressions.expression.'.sha1($access['access']), $def);
$attributes = array(new Reference($exprId));
}
$container->getDefinition('security.access_map')
->addMethodCall('add', array($matcher, $attributes, $access['requires_channel']));
}
}
private function invokeParent($method, array $args = array())
{
$ref = new \ReflectionMethod($this->extension, $method);
$ref->setAccessible(true);
return $ref->invokeArgs($this->extension, $args);
}
}

View File

@@ -0,0 +1,28 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Exception;
/**
* Base exception for the SecurityExtraBundle.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
interface Exception
{
}

View File

@@ -0,0 +1,28 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Exception;
/**
* InvalidArgumentException for the SecurityExtraBundle.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class InvalidArgumentException extends \InvalidArgumentException implements Exception
{
}

View File

@@ -0,0 +1,28 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Exception;
/**
* RuntimeException for the SecurityExtraBundle.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class RuntimeException extends \RuntimeException implements Exception
{
}

View File

@@ -0,0 +1,59 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle;
use JMS\SecurityExtraBundle\DependencyInjection\SecurityExtension;
use JMS\SecurityExtraBundle\DependencyInjection\Compiler\IntegrationPass;
use JMS\SecurityExtraBundle\DependencyInjection\Compiler\DisableVotersPass;
use JMS\SecurityExtraBundle\DependencyInjection\Compiler\AddExpressionCompilersPass;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use JMS\SecurityExtraBundle\DependencyInjection\Compiler\AddAfterInvocationProvidersPass;
use JMS\SecurityExtraBundle\DependencyInjection\Compiler\CollectSecuredServicesPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
/**
* Registers our custom compiler pass.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class JMSSecurityExtraBundle extends Bundle
{
const VERSION = '1.1.0';
public function build(ContainerBuilder $container)
{
if (!$container->hasExtension('security')) {
throw new \LogicException('The JMSSecurityExtraBundle must be registered after the SecurityBundle in your AppKernel.php.');
}
$container->registerExtension(new SecurityExtension($container->getExtension('security')));
$passConfig = $container->getCompilerPassConfig();
// needs to run before voter collection
$passes = $passConfig->getBeforeOptimizationPasses();
array_unshift($passes, new DisableVotersPass());
$passConfig->setBeforeOptimizationPasses($passes);
$passConfig->addPass(new AddAfterInvocationProvidersPass());
$passConfig->addPass(new CollectSecuredServicesPass());
$passConfig->addPass(new AddExpressionCompilersPass());
$passConfig->addPass(new IntegrationPass());
}
}

View File

@@ -0,0 +1,93 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Metadata;
use JMS\SecurityExtraBundle\Exception\RuntimeException;
use JMS\SecurityExtraBundle\Exception\InvalidArgumentException;
use Metadata\MethodMetadata;
use Metadata\MergeableInterface;
use Metadata\MergeableClassMetadata;
/**
* Contains class metadata information
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class ClassMetadata extends MergeableClassMetadata
{
public function addMethodMetadata(MethodMetadata $metadata)
{
if ($this->reflection->isFinal()) {
throw new RuntimeException(sprintf('Class "%s" is declared final, and cannot be secured.', $reflection->name));
}
if ($metadata->reflection->isStatic()) {
throw new RuntimeException(sprintf('Method "%s::%s" is declared static and cannot be secured.', $metadata->reflection->class, $metadata->reflection->name));
}
if ($metadata->reflection->isFinal()) {
throw new RuntimeException(sprintf('Method "%s::%s" is declared final and cannot be secured.', $metadata->reflection->class, $metadata->reflection->name));
}
parent::addMethodMetadata($metadata);
}
public function merge(MergeableInterface $metadata)
{
if (!$metadata instanceof ClassMetadata) {
throw new InvalidArgumentException('$metadata must be an instance of ClassMetadata.');
}
foreach ($this->methodMetadata as $name => $methodMetadata) {
// check if metadata was declared on an interface
if (!$metadata->reflection->hasMethod($name)) {
continue;
}
if ($metadata->reflection->getMethod($name)->getDeclaringClass()->name
!== $methodMetadata->class) {
if (!isset($metadata->methodMetadata[$name])) {
if ($methodMetadata->reflection->isAbstract()) {
continue;
}
throw new RuntimeException(sprintf(
'You have overridden a secured method "%s::%s" in "%s". '
.'Please copy over the applicable security metadata, and '
.'also add @SatisfiesParentSecurityPolicy.',
$methodMetadata->reflection->class,
$name,
$metadata->reflection->name
));
}
if (!$metadata->methodMetadata[$name]->satisfiesParentSecurityPolicy) {
throw new RuntimeException(sprintf('Unresolved security metadata conflict for method "%s::%s" in "%s". Please copy the respective annotations, and add @SatisfiesParentSecurityPolicy to the child method.', $metadata->reflection->name, $name, $methodMetadata->reflection->getDeclaringClass()->getFilename()));
}
}
}
parent::merge($metadata);
}
public function isProxyRequired()
{
return !empty($this->methodMetadata);
}
}

View File

@@ -0,0 +1,107 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Metadata\Driver;
use JMS\SecurityExtraBundle\Exception\InvalidArgumentException;
use Doctrine\Common\Annotations\Reader;
use JMS\SecurityExtraBundle\Annotation\PreAuthorize;
use JMS\SecurityExtraBundle\Annotation\RunAs;
use JMS\SecurityExtraBundle\Annotation\SatisfiesParentSecurityPolicy;
use JMS\SecurityExtraBundle\Annotation\Secure;
use JMS\SecurityExtraBundle\Annotation\SecureParam;
use JMS\SecurityExtraBundle\Annotation\SecureReturn;
use JMS\SecurityExtraBundle\Metadata\ClassMetadata;
use JMS\SecurityExtraBundle\Metadata\MethodMetadata;
use Metadata\Driver\DriverInterface;
use \ReflectionClass;
use \ReflectionMethod;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Expression;
/**
* Loads security annotations and converts them to metadata
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class AnnotationDriver implements DriverInterface
{
private $reader;
public function __construct(Reader $reader)
{
$this->reader = $reader;
}
public function loadMetadataForClass(ReflectionClass $reflection)
{
$metadata = new ClassMetadata($reflection->getName());
foreach ($reflection->getMethods(ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED) as $method) {
// check if the method was defined on this class
if ($method->getDeclaringClass()->getName() !== $reflection->getName()) {
continue;
}
$annotations = $this->reader->getMethodAnnotations($method);
if ($annotations && null !== $methodMetadata = $this->convertMethodAnnotations($method, $annotations)) {
$metadata->addMethodMetadata($methodMetadata);
}
}
return $metadata;
}
private function convertMethodAnnotations(\ReflectionMethod $method, array $annotations)
{
$parameters = array();
foreach ($method->getParameters() as $index => $parameter) {
$parameters[$parameter->getName()] = $index;
}
$methodMetadata = new MethodMetadata($method->getDeclaringClass()->getName(), $method->getName());
$hasSecurityMetadata = false;
foreach ($annotations as $annotation) {
if ($annotation instanceof Secure) {
$methodMetadata->roles = $annotation->roles;
$hasSecurityMetadata = true;
} else if ($annotation instanceof PreAuthorize) {
$methodMetadata->roles = array(new Expression($annotation->expr));
$hasSecurityMetadata = true;
} else if ($annotation instanceof SecureParam) {
if (!isset($parameters[$annotation->name])) {
throw new InvalidArgumentException(sprintf('The parameter "%s" does not exist for method "%s".', $annotation->name, $method->getName()));
}
$methodMetadata->addParamPermissions($parameters[$annotation->name], $annotation->permissions);
$hasSecurityMetadata = true;
} else if ($annotation instanceof SecureReturn) {
$methodMetadata->returnPermissions = $annotation->permissions;
$hasSecurityMetadata = true;
} else if ($annotation instanceof SatisfiesParentSecurityPolicy) {
$methodMetadata->satisfiesParentSecurityPolicy = true;
$hasSecurityMetadata = true;
} else if ($annotation instanceof RunAs) {
$methodMetadata->runAsRoles = $annotation->roles;
$hasSecurityMetadata = true;
}
}
return $hasSecurityMetadata ? $methodMetadata : null;
}
}

View File

@@ -0,0 +1,100 @@
<?php
namespace JMS\SecurityExtraBundle\Metadata\Driver;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Expression;
use JMS\SecurityExtraBundle\Metadata\MethodMetadata;
use Symfony\Component\HttpKernel\Kernel;
use JMS\SecurityExtraBundle\Metadata\ClassMetadata;
use Metadata\Driver\DriverInterface;
/**
* Uses Symfony2 DI configuration for metadata.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class ConfigDriver implements DriverInterface
{
private $bundles;
private $config;
public function __construct(array $bundles, array $config)
{
uasort($bundles, function($a, $b) {
return strlen($b) - strlen($a);
});
foreach ($bundles as $name => $namespace) {
$bundles[$name] = substr($namespace, 0, strrpos($namespace, '\\'));
}
$this->bundles = $bundles;
$this->config = $config;
}
public function loadMetadataForClass(\ReflectionClass $class)
{
$metadata = new ClassMetadata($class->name);
foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $method) {
if ($method->getDeclaringClass()->name !== $class->name) {
continue;
}
$expression = null;
if (null !== $notation = $this->getControllerNotation($method)) {
$expression = $this->getExpressionForSignature($notation);
}
if (null === $expression && null === $expression =
$this->getExpressionForSignature($method->class.'::'.$method->name)) {
continue;
}
$methodMetadata = new MethodMetadata($method->class, $method->name);
$methodMetadata->roles = array(new Expression($expression));
$metadata->addMethodMetadata($methodMetadata);
}
if (!$metadata->methodMetadata) {
return null;
}
return $metadata;
}
private function getExpressionForSignature($signature)
{
foreach ($this->config as $pattern => $expr) {
if (!preg_match('#'.$pattern.'#i', $signature)) {
continue;
}
return $expr;
}
return null;
}
// TODO: Is it feasible to reverse-engineer the notation for service controllers?
private function getControllerNotation(\ReflectionMethod $method)
{
$signature = $method->class.'::'.$method->name;
// check if class is a controller
if (0 === preg_match('#\\\\Controller\\\\([^\\\\]+)Controller::(.+)Action$#', $signature, $match)) {
return null;
}
foreach ($this->bundles as $name => $namespace) {
if (0 !== strpos($method->class, $namespace)) {
continue;
}
// controller notation (AcmeBundle:Foo:foo)
return $name.':'.$match[1].':'.$match[2];
}
return null;
}
}

View File

@@ -0,0 +1,84 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Metadata;
use Metadata\MethodMetadata as BaseMethodMetadata;
/**
* Contains method metadata information
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class MethodMetadata extends BaseMethodMetadata
{
public $roles = array();
public $paramPermissions = array();
public $returnPermissions = array();
public $runAsRoles = array();
public $satisfiesParentSecurityPolicy = false;
/**
* Adds a parameter restriction
*
* @param integer $index 0-based
* @param array $permissions
*/
public function addParamPermissions($index, array $permissions)
{
$this->paramPermissions[$index] = $permissions;
}
public function isDeclaredOnInterface()
{
foreach ($this->reflection->getDeclaringClass()->getInterfaces() as $interface) {
if ($interface->hasMethod($this->name)) {
return true;
}
}
return false;
}
/**
* This allows to merge in metadata from an interface
*
* @param MethodMetadata $method
* @return void
*/
public function merge(MethodMetadata $method)
{
if (!$this->roles) {
$this->roles = $method->roles;
}
if (!$this->returnPermissions) {
$this->returnPermissions = $method->returnPermissions;
}
if (!$this->runAsRoles) {
$this->runAsRoles = $method->runAsRoles;
}
foreach ($method->paramPermissions as $index => $permissions) {
if (!isset($this->paramPermissions[$index])) {
$this->paramPermissions[$index] = $permissions;
}
}
}
}

View File

@@ -0,0 +1,8 @@
For documentation, see:
Resources/doc
For license, see:
Resources/meta/LICENSE

View File

@@ -0,0 +1,84 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="security.acl.permission_evaluator.class">JMS\SecurityExtraBundle\Security\Acl\Expression\PermissionEvaluator</parameter>
<parameter key="security.acl.has_permission_compiler.class">JMS\SecurityExtraBundle\Security\Acl\Expression\HasPermissionFunctionCompiler</parameter>
<parameter key="security.expressions.voter.class">JMS\SecurityExtraBundle\Security\Authorization\Expression\LazyLoadingExpressionVoter</parameter>
<parameter key="security.expressions.handler.class">JMS\SecurityExtraBundle\Security\Authorization\Expression\ContainerAwareExpressionHandler</parameter>
<parameter key="security.expressions.compiler.class">JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler</parameter>
<parameter key="security.expressions.expression.class">JMS\SecurityExtraBundle\Security\Authorization\Expression\Expression</parameter>
<parameter key="security.expressions.variable_compiler.class">JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\ContainerAwareVariableCompiler</parameter>
<parameter key="security.expressions.parameter_compiler.class">JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\ParameterExpressionCompiler</parameter>
<parameter key="security.extra.config_driver.class">JMS\SecurityExtraBundle\Metadata\Driver\ConfigDriver</parameter>
<parameter key="security.extra.twig_extension.class">JMS\SecurityExtraBundle\Twig\SecurityExtension</parameter>
</parameters>
<services>
<service id="security.extra.twig_extension" class="%security.extra.twig_extension.class%" public="false">
<argument type="service" id="security.context" />
<tag name="twig.extension" alias="jms_security_extra" />
</service>
<service id="security.extra.config_driver" class="%security.extra.config_driver.class%" public="false">
<argument>%kernel.bundles%</argument>
<argument>%security.access.method_access_control%</argument>
</service>
<service id="security.expressions.parameter_compiler" class="%security.expressions.parameter_compiler.class%" public="false">
<tag name="security.expressions.type_compiler" />
</service>
<service id="security.expressions.variable_compiler" class="%security.expressions.variable_compiler.class%" public="false">
<tag name="security.expressions.type_compiler" />
<!-- Some variables for built-in compilers for lazy-loading -->
<tag name="security.expressions.variable" variable="trust_resolver"
service="security.authentication.trust_resolver" />
<tag name="security.expressions.variable" variable="role_hierarchy"
service="security.role_hierarchy" />
</service>
<service id="security.expressions.voter" class="%security.expressions.voter.class%" public="false">
<argument type="service" id="security.expressions.handler" />
<call method="setLazyCompiler">
<argument type="service" id="service_container" />
<argument>security.expressions.compiler</argument>
</call>
<tag name="security.voter" priority="230" />
</service>
<service id="security.expressions.handler" class="%security.expressions.handler.class%" public="false">
<argument type="service" id="service_container" />
</service>
<service id="security.expressions.compiler" class="%security.expressions.compiler.class%" />
<service id="security.expressions.expression" class="%security.expressions.expression.class%" public="false" abstract="true" />
<!-- Expression-Based ACL -->
<service id="security.acl.permission_evaluator" class="%security.acl.permission_evaluator.class%" public="false">
<tag name="monolog.logger" channel="security" />
<argument type="service" id="security.acl.provider" />
<argument type="service" id="security.acl.object_identity_retrieval_strategy" />
<argument type="service" id="security.acl.security_identity_retrieval_strategy" />
<argument type="service" id="security.acl.permission.map" />
<argument type="service" id="logger" on-invalid="null" />
</service>
<service id="security.acl.has_permission_compiler" class="%security.acl.has_permission_compiler.class%" public="false">
<tag name="security.expressions.function_compiler" />
<tag name="security.expressions.variable" variable="permission_evaluator" service="security.acl.permission_evaluator"/>
</service>
</services>
</container>

View File

@@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="security.secured_services" type="collection"></parameter>
<parameter key="security.access.method_interceptor.class">JMS\SecurityExtraBundle\Security\Authorization\Interception\MethodSecurityInterceptor</parameter>
<parameter key="security.access.method_access_control" type="collection" />
<parameter key="security.access.run_as_manager.class">JMS\SecurityExtraBundle\Security\Authorization\RunAsManager</parameter>
<parameter key="security.authentication.provider.run_as.class">JMS\SecurityExtraBundle\Security\Authentication\Provider\RunAsAuthenticationProvider</parameter>
<parameter key="security.run_as.key">RunAsToken</parameter>
<parameter key="security.run_as.role_prefix">ROLE_</parameter>
<parameter key="security.access.after_invocation_manager.class">JMS\SecurityExtraBundle\Security\Authorization\AfterInvocation\AfterInvocationManager</parameter>
<parameter key="security.access.after_invocation.acl_provider.class">JMS\SecurityExtraBundle\Security\Authorization\AfterInvocation\AclAfterInvocationProvider</parameter>
<parameter key="security.access.iddqd_voter.class">JMS\SecurityExtraBundle\Security\Authorization\Voter\IddqdVoter</parameter>
<parameter key="security.extra.metadata_factory.class">Metadata\MetadataFactory</parameter>
<parameter key="security.extra.lazy_loading_driver.class">Metadata\Driver\LazyLoadingDriver</parameter>
<parameter key="security.extra.driver_chain.class">Metadata\Driver\DriverChain</parameter>
<parameter key="security.extra.annotation_driver.class">JMS\SecurityExtraBundle\Metadata\Driver\AnnotationDriver</parameter>
<parameter key="security.extra.file_cache.class">Metadata\Cache\FileCache</parameter>
</parameters>
<services>
<service id="security.access.run_as_manager" class="%security.access.run_as_manager.class%" public="false">
<argument>%security.run_as.key%</argument>
<argument>%security.run_as.role_prefix%</argument>
</service>
<service id="security.access.method_interceptor" class="%security.access.method_interceptor.class%">
<argument type="service" id="security.context" />
<argument type="service" id="security.authentication.manager" />
<argument type="service" id="security.access.decision_manager" />
<argument type="service" id="security.access.after_invocation_manager" />
<argument type="service" id="security.access.run_as_manager" />
<argument type="service" id="security.extra.metadata_factory" />
<argument type="service" id="logger" on-invalid="null" />
</service>
<service id="security.access.pointcut" class="JMS\SecurityExtraBundle\Security\Authorization\Interception\SecurityPointcut">
<argument type="service" id="security.extra.metadata_factory" />
<argument>%security.access.secure_all_services%</argument>
<argument>%security.access.method_access_control%</argument>
<tag name="jms_aop.pointcut" interceptor="security.access.method_interceptor" />
</service>
<service id="security.access.after_invocation_manager" class="%security.access.after_invocation_manager.class%" public="false">
<argument type="collection"></argument>
</service>
<service id="security.access.after_invocation.acl_provider" class="%security.access.after_invocation.acl_provider.class%" public="false">
<argument type="service" id="security.acl.provider" />
<argument type="service" id="security.acl.object_identity_retrieval_strategy" />
<argument type="service" id="security.acl.security_identity_retrieval_strategy" />
<argument type="service" id="security.acl.permission.map" />
<tag name="security.after_invocation.provider" />
</service>
<service id="security.extra.iddqd_voter" class="%security.access.iddqd_voter.class%" public="false">
</service>
<service id="security.extra.driver_chain" class="%security.extra.driver_chain.class%" public="false">
<argument type="collection">
<argument type="service" id="security.extra.annotation_driver" />
</argument>
</service>
<service id="security.extra.metadata_driver" alias="security.extra.driver_chain"></service>
<service id="security.extra.lazy_loading_driver" class="%security.extra.lazy_loading_driver.class%" public="false">
<argument type="service" id="service_container" />
<argument>security.extra.metadata_driver</argument>
</service>
<service id="security.extra.annotation_driver" class="%security.extra.annotation_driver.class%" public="false">
<argument type="service" id="annotation_reader" />
</service>
<service id="security.extra.file_cache" class="%security.extra.file_cache.class%" public="false">
<argument>%security.extra.cache_dir%</argument>
<argument>%kernel.debug%</argument>
</service>
<service id="security.extra.metadata_factory" class="%security.extra.metadata_factory.class%" public="false">
<argument type="service" id="security.extra.lazy_loading_driver" />
<argument type="service" id="security.extra.file_cache" />
<call method="setIncludeInterfaces">
<argument>true</argument>
</call>
</service>
</services>
</container>

View File

@@ -0,0 +1,352 @@
========
Overview
========
This bundle enhances the Symfony2 Security Component by adding several new features.
Features:
- powerful expression-based authorization language
- method security authorization
- authorization configuration via annotations
Installation
------------
Add the following to your ``deps`` file::
[JMSSecurityExtraBundle]
git=https://github.com/schmittjoh/JMSSecurityExtraBundle.git
target=/bundles/JMS/SecurityExtraBundle
; Dependencies:
;--------------
[metadata]
git=https://github.com/schmittjoh/metadata.git
version=1.1.0 ; <- make sure to get 1.1, not 1.0
; see https://github.com/schmittjoh/JMSAopBundle/blob/master/Resources/doc/index.rst
[JMSAopBundle]
git=https://github.com/schmittjoh/JMSAopBundle.git
target=/bundles/JMS/AopBundle
[cg-library]
git=https://github.com/schmittjoh/cg-library.git
; This dependency is optional (you need it if you are using non-service controllers):
; see https://github.com/schmittjoh/JMSDiExtraBundle/blob/master/Resources/doc/index.rst
[JMSDiExtraBundle]
git=https://github.com/schmittjoh/JMSDiExtraBundle.git
target=/bundles/JMS/DiExtraBundle
Then register the bundle with your kernel::
// in AppKernel::registerBundles()
$bundles = array(
// ...
new JMS\AopBundle\JMSAopBundle(),
new JMS\SecurityExtraBundle\JMSSecurityExtraBundle(),
new JMS\DiExtraBundle\JMSDiExtraBundle($this),
// ...
);
Make sure that you also register the namespaces with the autoloader::
// app/autoload.php
$loader->registerNamespaces(array(
// ...
'JMS' => __DIR__.'/../vendor/bundles',
'Metadata' => __DIR__.'/../vendor/metadata/src',
'CG' => __DIR__.'/../vendor/cg-library/src',
// ...
));
Configuration
-------------
Below, you find the default configuration::
# app/config/config.yml
jms_security_extra:
# Whether you want to secure all services (true), or only secure specific
# services (false); see also below
secure_all_services: false
# Enabling this setting will add an additional special attribute "IS_IDDQD".
# Anybody with this attribute will effectively bypass all security checks.
enable_iddqd_attribute: false
# Enables expression language
expressions: false
# Allows you to disable some, or all built-in voters
voters:
disable_authenticated: false
disable_role: false
disable_acl: false
# Allows you to specify access control rules for specific methods, such
# as controller actions
method_access_control: { }
Expression-based Authorization Language
---------------------------------------
The expression language is a very powerful alternative to the simple attributes
of the security voting system. They allow to perform complex access decision
checks, and because they are compiled down to raw PHP, they are much faster than
the built-in voters. Also they are lazy-loading by nature, so you will also
save some resources for example by not having to initialize the entire ACL system
on each request.
Programmatic Usage
~~~~~~~~~~~~~~~~~~
You can execute expressions programmatically by using the ``isGranted`` method
of the SecurityContext. Some examples::
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Expression;
$securityContext->isGranted(array(new Expression('hasRole("A")')));
$securityContext->isGranted(array(new Expression('hasRole("A") or (hasRole("B") and hasRole("C"))')));
$securityContext->isGranted(array(new Expression('hasPermission(object, "VIEW")'), $object));
$securityContext->isGranted(array(new Expression('token.getUsername() == "Johannes"')));
Twig Usage
~~~~~~~~~~
You can check expressions from Twig templates using the ``is_expr_granted``
function. Some examples::
is_expr_granted("hasRole('FOO')")
is_expr_granted("hasPermission(object, 'VIEW')", object)
Usage in Access Control
~~~~~~~~~~~~~~~~~~~~~~~
You can also use expressions in the ``access_control``::
security:
access_control:
- { path: ^/foo, access: "hasRole('FOO') and hasRole('BAR')" }
Annotation-based Usage
~~~~~~~~~~~~~~~~~~~~~~
see @PreAuthorize in the annotation reference
Reference
~~~~~~~~~
+-----------------------------------+--------------------------------------------+
| Expression | Description |
+===================================+============================================+
| hasRole('ROLE') | Checks whether the token has a certain |
| | role. |
+-----------------------------------+--------------------------------------------+
| hasAnyRole('ROLE1', 'ROLE2', ...) | Checks whether the token has any of the |
| | given roles. |
+-----------------------------------+--------------------------------------------+
| isAnonymous() | Checks whether the token is anonymous. |
+-----------------------------------+--------------------------------------------+
| isRememberMe() | Checks whether the token is remember me. |
+-----------------------------------+--------------------------------------------+
| isFullyAuthenticated() | Checks whether the token is fully |
| | authenticated. |
+-----------------------------------+--------------------------------------------+
| isAuthenticated() | Checks whether the token is not anonymous. |
+-----------------------------------+--------------------------------------------+
| hasPermission(*var*, 'PERMISSION')| Checks whether the token has the given |
| | permission for the given object (requires |
| | the ACL system). |
+-----------------------------------+--------------------------------------------+
| token | Variable that refers to the token |
| | which is currently in the security context.|
+-----------------------------------+--------------------------------------------+
| user | Variable that refers to the user |
| | which is currently in the security context.|
+-----------------------------------+--------------------------------------------+
| object | Variable that refers to the object for |
| | which access is being requested. |
+-----------------------------------+--------------------------------------------+
| #*paramName* | Any identifier prefixed with # refers to |
| | a parameter of the same name that is passed|
| | to the method where the expression is used.|
+-----------------------------------+--------------------------------------------+
| and / && | Binary "and" operator |
+-----------------------------------+--------------------------------------------+
| or / || | Binary "or" operator |
+-----------------------------------+--------------------------------------------+
| == | Binary "is equal" operator |
+-----------------------------------+--------------------------------------------+
| not / ! | Negation operator |
+-----------------------------------+--------------------------------------------+
Method Security Authorization
-----------------------------
Generally, you can secure all public, or protected methods which are non-static,
and non-final. Private methods cannot be secured. You can also add metadata for
abstract methods, or interfaces which will then be applied to their concrete
implementations automatically.
Access Control via DI configuration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can specify access control **expressions** in the DI configuration::
# config.yml
jms_security_extra:
method_access_control:
':loginAction$': 'isAnonymous()'
'AcmeFooBundle:.*:deleteAction': 'hasRole("ROLE_ADMIN")'
'^MyNamespace\MyService::foo$': 'hasPermission(#user, "VIEW")'
The pattern is a case-sensitive regular expression which is matched against two notations.
The first match is being used.
First, your pattern is matched against the notation for non-service controllers.
This obviously is only done if your class is actually a controller, e.g.
``AcmeFooBundle:Add:new`` for a controller named ``AddController`` and a method
named ``newAction`` in a sub-namespace ``Controller`` in a bundle named ``AcmeFooBundle``.
Last, your pattern is matched against the concatenation of the class name, and
the method name that is being called, e.g. ``My\Fully\Qualified\ClassName::myMethodName``.
**Note:** If you would like to secure non-service controllers, the
``JMSDiExtraBundle`` must be installed.
Access Control via Annotations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you like to secure a service with annotations, you need to enable annotation
configuration for this service::
<service id="foo" class="Bar">
<tag name="security.secure_service"/>
</service>
In case, you like to configure all services via annotations, you can also set
``secure_all_services`` to true. Then, you do not need to add a tag for each
service.
Annotations
-----------
@PreAuthorize
~~~~~~~~~~~~~
This annotation lets you define an expression (see the expression language
paragraph) which is executed prior to invoking a method::
<?php
use JMS\SecurityExtraBundle\Annotation\PreAuthorize;
class MyService
{
/** @PreAuthorize("hasRole('A') or (hasRole('B') and hasRole('C'))") */
public function secureMethod()
{
// ...
}
}
@Secure
~~~~~~~
This annotation lets you define who is allowed to invoke a method::
<?php
use JMS\SecurityExtraBundle\Annotation\Secure;
class MyService
{
/**
* @Secure(roles="ROLE_USER, ROLE_FOO, ROLE_ADMIN")
*/
public function secureMethod()
{
// ...
}
}
@SecureParam
~~~~~~~~~~~~
This annotation lets you define restrictions for parameters which are passed to
the method. This is only useful if the parameters are domain objects::
<?php
use JMS\SecurityExtraBundle\Annotation\SecureParam;
class MyService
{
/**
* @SecureParam(name="comment", permissions="EDIT, DELETE")
* @SecureParam(name="post", permissions="OWNER")
*/
public function secureMethod($comment, $post)
{
// ...
}
}
@SecureReturn
~~~~~~~~~~~~~
This annotation lets you define restrictions for the value which is returned by
the method. This is also only useful if the returned value is a domain object::
<?php
use JMS\SecurityExtraBundle\Annotation\SecureReturn;
class MyService
{
/**
* @SecureReturn(permissions="VIEW")
*/
public function secureMethod()
{
// ...
return $domainObject;
}
}
@RunAs
~~~~~~
This annotation lets you specifiy roles which are added only for the duration
of the method invocation. These roles will not be taken into consideration
for before, or after invocation access decisions.
This is typically used to implement a two-tier service layer where you have
public and private services, and private services are only to be invoked
through a specific public service::
<?php
use JMS\SecurityExtraBundle\Annotation\Secure;
use JMS\SecurityExtraBundle\Annotation\RunAs;
class MyPrivateService
{
/**
* @Secure(roles="ROLE_PRIVATE_SERVICE")
*/
public function aMethodOnlyToBeInvokedThroughASpecificChannel()
{
// ...
}
}
class MyPublicService
{
protected $myPrivateService;
/**
* @Secure(roles="ROLE_USER")
* @RunAs(roles="ROLE_PRIVATE_SERVICE")
*/
public function canBeInvokedFromOtherServices()
{
return $this->myPrivateService->aMethodOnlyToBeInvokedThroughASpecificChannel();
}
}
@SatisfiesParentSecurityPolicy
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This must be defined on a method that overrides a method which has security metadata.
It is there to ensure that you are aware the security of the overridden method cannot
be enforced anymore, and that you must copy over all annotations if you want to keep
them.

View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,63 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Acl\Expression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ConstantExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\VariableExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\FunctionExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\Func\FunctionCompilerInterface;
class HasPermissionFunctionCompiler implements FunctionCompilerInterface
{
public function getName()
{
return 'hasPermission';
}
public function compilePreconditions(ExpressionCompiler $compiler, FunctionExpression $function)
{
$compiler->verifyItem('token', 'Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
}
public function compile(ExpressionCompiler $compiler, FunctionExpression $function)
{
$compiler
->compileInternal(new VariableExpression('permission_evaluator'))
->write('->hasPermission(')
->compileInternal(new VariableExpression('token'))
->write(', ')
->compileInternal($function->args[0])
->write(', ')
;
if ($function->args[1] instanceof ConstantExpression) {
$compiler->write(var_export(strtoupper($function->args[1]->value), true).')');
return;
}
$compiler
->write('strtoupper(')
->compileInternal($function->args[1])
->write('))')
;
}
}

View File

@@ -0,0 +1,122 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Acl\Expression;
use Symfony\Component\Security\Acl\Exception\NoAceFoundException;
use Symfony\Component\Security\Acl\Exception\AclNotFoundException;
use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
use Symfony\Component\Security\Acl\Permission\PermissionMapInterface;
use Symfony\Component\Security\Acl\Model\AclProviderInterface;
use Symfony\Component\Security\Acl\Model\SecurityIdentityRetrievalStrategyInterface;
use Symfony\Component\Security\Acl\Model\ObjectIdentityRetrievalStrategyInterface;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
class PermissionEvaluator
{
private $aclProvider;
private $oidRetrievalStrategy;
private $sidRetrievalStrategy;
private $permissionMap;
private $allowIfObjectIdentityUnavailable;
private $logger;
public function __construct(AclProviderInterface $aclProvider,
ObjectIdentityRetrievalStrategyInterface $oidRetrievalStrategy,
SecurityIdentityRetrievalStrategyInterface $sidRetrievalStrategy,
PermissionMapInterface $permissionMap,
$allowIfObjectIdentityUnavailable = true,
LoggerInterface $logger = null)
{
$this->aclProvider = $aclProvider;
$this->oidRetrievalStrategy = $oidRetrievalStrategy;
$this->sidRetrievalStrategy = $sidRetrievalStrategy;
$this->permissionMap = $permissionMap;
$this->allowIfObjectIdentityUnavailable = $allowIfObjectIdentityUnavailable;
$this->logger = $logger;
}
public function hasPermission(TokenInterface $token, $object, $permission)
{
if (null === $masks = $this->permissionMap->getMasks($permission, $object)) {
return false;
}
if (null === $object) {
if (null !== $this->logger) {
$this->logger->debug(sprintf('Object identity unavailable. Voting to %s', $this->allowIfObjectIdentityUnavailable? 'grant access' : 'abstain'));
}
return $this->allowIfObjectIdentityUnavailable ? true : false;
} else if ($object instanceof FieldVote) {
$field = $object->getField();
$object = $object->getDomainObject();
} else {
$field = null;
}
if ($object instanceof ObjectIdentityInterface) {
$oid = $object;
} else if (null === $oid = $this->oidRetrievalStrategy->getObjectIdentity($object)) {
if (null !== $this->logger) {
$this->logger->debug(sprintf('Object identity unavailable. Voting to %s', $this->allowIfObjectIdentityUnavailable? 'grant access' : 'abstain'));
}
return $this->allowIfObjectIdentityUnavailable ? true : false;
}
$sids = $this->sidRetrievalStrategy->getSecurityIdentities($token);
try {
$acl = $this->aclProvider->findAcl($oid, $sids);
if (null === $field && $acl->isGranted($masks, $sids, false)) {
if (null !== $this->logger) {
$this->logger->debug('ACL found, permission granted. Voting to grant access');
}
return true;
} else if (null !== $field && $acl->isFieldGranted($field, $masks, $sids, false)) {
if (null !== $this->logger) {
$this->logger->debug('ACL found, permission granted. Voting to grant access');
}
return true;
}
if (null !== $this->logger) {
$this->logger->debug('ACL found, insufficient permissions. Voting to deny access.');
}
return false;
} catch (AclNotFoundException $noAcl) {
if (null !== $this->logger) {
$this->logger->debug('No ACL found for the object identity. Voting to deny access.');
}
return false;
} catch (NoAceFoundException $noAce) {
if (null !== $this->logger) {
$this->logger->debug('ACL found, no ACE applicable. Voting to deny access.');
}
return false;
}
}
}

View File

@@ -0,0 +1,136 @@
<?php
namespace JMS\SecurityExtraBundle\Security\Acl\Voter;
use Symfony\Component\Security\Acl\Voter\FieldVote;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
use Symfony\Component\Security\Acl\Exception\NoAceFoundException;
use Symfony\Component\Security\Acl\Exception\AclNotFoundException;
use Symfony\Component\Security\Acl\Model\AclProviderInterface;
use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
use Symfony\Component\Security\Acl\Permission\PermissionMapInterface;
use Symfony\Component\Security\Acl\Model\SecurityIdentityRetrievalStrategyInterface;
use Symfony\Component\Security\Acl\Model\ObjectIdentityRetrievalStrategyInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
/**
* This voter can be used as a base class for implementing your own permissions.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class AclVoter implements VoterInterface
{
private $aclProvider;
private $permissionMap;
private $objectIdentityRetrievalStrategy;
private $securityIdentityRetrievalStrategy;
private $allowIfObjectIdentityUnavailable;
private $logger;
public function __construct(AclProviderInterface $aclProvider, ObjectIdentityRetrievalStrategyInterface $oidRetrievalStrategy, SecurityIdentityRetrievalStrategyInterface $sidRetrievalStrategy, PermissionMapInterface $permissionMap, LoggerInterface $logger = null, $allowIfObjectIdentityUnavailable = true)
{
$this->aclProvider = $aclProvider;
$this->permissionMap = $permissionMap;
$this->objectIdentityRetrievalStrategy = $oidRetrievalStrategy;
$this->securityIdentityRetrievalStrategy = $sidRetrievalStrategy;
$this->logger = $logger;
$this->allowIfObjectIdentityUnavailable = $allowIfObjectIdentityUnavailable;
}
public function supportsAttribute($attribute)
{
return $this->permissionMap->contains($attribute);
}
public function vote(TokenInterface $token, $object, array $attributes)
{
foreach ($attributes as $attribute) {
if (null === $masks = $this->permissionMap->getMasks((string) $attribute, $object)) {
continue;
}
if (null === $object) {
if (null !== $this->logger) {
$this->logger->debug(sprintf('Object identity unavailable. Voting to %s', $this->allowIfObjectIdentityUnavailable? 'grant access' : 'abstain'));
}
return $this->allowIfObjectIdentityUnavailable ? self::ACCESS_GRANTED : self::ACCESS_ABSTAIN;
} else if ($object instanceof FieldVote) {
$field = $object->getField();
$object = $object->getDomainObject();
} else {
$field = null;
}
if ($object instanceof ObjectIdentityInterface) {
$oid = $object;
} else if (null === $oid = $this->objectIdentityRetrievalStrategy->getObjectIdentity($object)) {
if (null !== $this->logger) {
$this->logger->debug(sprintf('Object identity unavailable. Voting to %s', $this->allowIfObjectIdentityUnavailable? 'grant access' : 'abstain'));
}
return $this->allowIfObjectIdentityUnavailable ? self::ACCESS_GRANTED : self::ACCESS_ABSTAIN;
}
if (!$this->supportsClass($oid->getType())) {
return self::ACCESS_ABSTAIN;
}
$sids = $this->securityIdentityRetrievalStrategy->getSecurityIdentities($token);
try {
$acl = $this->aclProvider->findAcl($oid, $sids);
if (null === $field && $acl->isGranted($masks, $sids, false)) {
if (null !== $this->logger) {
$this->logger->debug('ACL found, permission granted. Voting to grant access');
}
return self::ACCESS_GRANTED;
} else if (null !== $field && $acl->isFieldGranted($field, $masks, $sids, false)) {
if (null !== $this->logger) {
$this->logger->debug('ACL found, permission granted. Voting to grant access');
}
return self::ACCESS_GRANTED;
}
if (null !== $this->logger) {
$this->logger->debug('ACL found, insufficient permissions. Voting to deny access.');
}
return self::ACCESS_DENIED;
} catch (AclNotFoundException $noAcl) {
if (null !== $this->logger) {
$this->logger->debug('No ACL found for the object identity. Voting to deny access.');
}
return self::ACCESS_DENIED;
} catch (NoAceFoundException $noAce) {
if (null !== $this->logger) {
$this->logger->debug('ACL found, no ACE applicable. Voting to deny access.');
}
return self::ACCESS_DENIED;
}
}
// no attribute was supported
return self::ACCESS_ABSTAIN;
}
/**
* You can override this method when writing a voter for a specific domain
* class.
*
* @param string $class The class name
*
* @return Boolean
*/
public function supportsClass($class)
{
return true;
}
}

View File

@@ -0,0 +1,57 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authentication\Provider;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use JMS\SecurityExtraBundle\Security\Authentication\Token\RunAsUserToken;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
/**
* Class which authenticates RunAsTokens.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class RunAsAuthenticationProvider implements AuthenticationProviderInterface
{
private $key;
public function __construct($key)
{
$this->key = $key;
}
public function authenticate(TokenInterface $token)
{
if (!$this->supports($token)) {
return null;
}
if ($token->getKey() === $this->key) {
return $token;
} else {
throw new BadCredentialsException('The keys do not match.');
}
}
public function supports(TokenInterface $token)
{
return $token instanceof RunAsUserToken;
}
}

View File

@@ -0,0 +1,85 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authentication\Token;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;
/**
* This token is automatically generated by the RunAsManager when an invocation
* is supposed to be run with a different Token.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class RunAsUserToken extends AbstractToken
{
private $originalToken;
private $key;
private $credentials;
public function __construct($key, $user, $credentials, array $roles, TokenInterface $originalToken)
{
parent::__construct($roles);
$this->originalToken = $originalToken;
$this->credentials = $credentials;
$this->key = $key;
$this->setUser($user);
$this->setAuthenticated(true);
}
public function getKey()
{
return $this->key;
}
public function getOriginalToken()
{
return $this->originalToken;
}
public function getCredentials()
{
return $this->credentials;
}
public function eraseCredentials()
{
parent::eraseCredentials();
$this->credentials = null;
}
public function serialize()
{
return serialize(array(
$this->originalToken,
$this->key,
$this->credentials,
parent::serialize(),
));
}
public function unserialize($str)
{
list($this->originalToken, $this->key, $this->credentials, $parentStr) = unserialize($str);
parent::unserialize($parentStr);
}
}

View File

@@ -0,0 +1,111 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\AfterInvocation;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
use Symfony\Component\Security\Acl\Exception\AclNotFoundException;
use Symfony\Component\Security\Acl\Exception\NoAceFoundException;
use Symfony\Component\Security\Acl\Model\SecurityIdentityRetrievalStrategyInterface;
use Symfony\Component\Security\Acl\Model\ObjectIdentityRetrievalStrategyInterface;
use Symfony\Component\Security\Acl\Model\AclProviderInterface;
use Symfony\Component\Security\Acl\Permission\PermissionMapInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
/**
* This after invocation provider filters returned objects based on ACLs.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class AclAfterInvocationProvider implements AfterInvocationProviderInterface
{
private $aclProvider;
private $oidRetrievalStrategy;
private $sidRetrievalStrategy;
private $permissionMap;
private $logger;
public function __construct(AclProviderInterface $aclProvider, ObjectIdentityRetrievalStrategyInterface $oidRetrievalStrategy, SecurityIdentityRetrievalStrategyInterface $sidRetrievalStrategy, PermissionMapInterface $permissionMap, LoggerInterface $logger = null)
{
$this->aclProvider = $aclProvider;
$this->oidRetrievalStrategy = $oidRetrievalStrategy;
$this->sidRetrievalStrategy = $sidRetrievalStrategy;
$this->permissionMap = $permissionMap;
$this->logger = $logger;
}
public function decide(TokenInterface $token, $secureObject, array $attributes, $returnedObject)
{
if (null === $returnedObject) {
if (null !== $this->logger) {
$this->logger->debug('Returned object was null, skipping security check.');
}
return null;
}
foreach ($attributes as $attribute) {
if (!$this->supportsAttribute($attribute)) {
continue;
}
if (null === $oid = $this->oidRetrievalStrategy->getObjectIdentity($returnedObject)) {
if (null !== $this->logger) {
$this->logger->debug('Returned object was no domain object, skipping security check.');
}
return $returnedObject;
}
$sids = $this->sidRetrievalStrategy->getSecurityIdentities($token);
try {
$acl = $this->aclProvider->findAcl($oid, $sids);
if ($acl->isGranted($this->permissionMap->getMasks($attribute, $returnedObject), $sids, false)) {
return $returnedObject;
}
if (null !== $this->logger) {
$this->logger->debug('Token has been denied access for returned object.');
}
} catch (AclNotFoundException $noAcl) {
throw new AccessDeniedException('No applicable ACL found for domain object.');
} catch (NoAceFoundException $noAce) {
if (null !== $this->logger) {
$this->logger->debug('No applicable ACE found for the given Token, denying access.');
}
}
throw new AccessDeniedException('ACL has denied access for attribute: '.$attribute);
}
// no attribute was supported
return $returnedObject;
}
public function supportsAttribute($attribute)
{
return $this->permissionMap->contains($attribute);
}
public function supportsClass($className)
{
return true;
}
}

View File

@@ -0,0 +1,77 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\AfterInvocation;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
/**
* This is the pendant to the AccessDecisionManager which is used to make
* access decisions after a method has been executed.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class AfterInvocationManager implements AfterInvocationManagerInterface
{
private $providers;
public function __construct(array $providers)
{
$this->providers = $providers;
}
/**
* {@inheritDoc}
*/
public function decide(TokenInterface $token, $secureInvocation, array $attributes, $returnedObject)
{
foreach ($this->providers as $provider) {
$returnedObject = $provider->decide($token, $secureInvocation, $attributes, $returnedObject);
}
return $returnedObject;
}
/**
* {@inheritDoc}
*/
public function supportsAttribute($attribute)
{
foreach ($this->providers as $provider) {
if (true === $provider->supportsAttribute($attribute)) {
return true;
}
}
return false;
}
/**
* {@inheritDoc}
*/
public function supportsClass($className)
{
foreach ($this->providers as $provider) {
if (true === $provider->supportsClass($className)) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,56 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\AfterInvocation;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
/**
* AfterInvocationManagerInterface
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
interface AfterInvocationManagerInterface
{
/**
* Makes an access decision after the invocation of a method
*
* @param TokenInterface $token
* @param object $secureObject
* @param array $attributes
* @param mixed $returnedValue the value that was returned by the method invocation
* @return mixed the filter return value
*/
function decide(TokenInterface $token, $secureObject, array $attributes, $returnedValue);
/**
* Determines whether the given attribute is supported
*
* @param string $attribute
* @return Boolean
*/
function supportsAttribute($attribute);
/**
* Determines whether the given class is supported
*
* @param string $className the class of the secure object
* @return Boolean
*/
function supportsClass($className);
}

View File

@@ -0,0 +1,33 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\AfterInvocation;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
/**
* AfterInvocationProviderInterface
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
interface AfterInvocationProviderInterface
{
function decide(TokenInterface $token, $secureObject, array $attributes, $returnedObject);
function supportsAttribute($attribute);
function supportsClass($className);
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast;
class AndExpression implements ExpressionInterface
{
public $left;
public $right;
public function __construct(ExpressionInterface $left, ExpressionInterface $right)
{
$this->left = $left;
$this->right = $right;
}
}

View File

@@ -0,0 +1,29 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast;
class ArrayExpression implements ExpressionInterface
{
public $elements;
public function __construct(array $elements)
{
$this->elements = $elements;
}
}

View File

@@ -0,0 +1,29 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast;
class ConstantExpression implements ExpressionInterface
{
public $value;
public function __construct($value)
{
$this->value = $value;
}
}

View File

@@ -0,0 +1,23 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast;
interface ExpressionInterface
{
}

View File

@@ -0,0 +1,32 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast;
class FunctionExpression implements ExpressionInterface
{
/** READ-ONLY */
public $name;
public $args;
public function __construct($name, array $args)
{
$this->name = $name;
$this->args = $args;
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast;
class GetItemExpression
{
public $array;
public $key;
public function __construct(ExpressionInterface $array, ExpressionInterface $key)
{
$this->array = $array;
$this->key = $key;
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast;
class GetPropertyExpression implements ExpressionInterface
{
public $object;
public $name;
public function __construct(ExpressionInterface $obj, $name)
{
$this->object = $obj;
$this->name = $name;
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast;
class IsEqualExpression implements ExpressionInterface
{
public $left;
public $right;
public function __construct(ExpressionInterface $left, ExpressionInterface $right)
{
$this->left = $left;
$this->right = $right;
}
}

View File

@@ -0,0 +1,33 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast;
class MethodCallExpression implements ExpressionInterface
{
public $object;
public $method;
public $args;
public function __construct(ExpressionInterface $obj, $method, array $args)
{
$this->object = $obj;
$this->method = $method;
$this->args = $args;
}
}

View File

@@ -0,0 +1,29 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast;
class NotExpression implements ExpressionInterface
{
public $expr;
public function __construct(ExpressionInterface $expr)
{
$this->expr = $expr;
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast;
class OrExpression implements ExpressionInterface
{
public $left;
public $right;
public function __construct(ExpressionInterface $left, ExpressionInterface $right)
{
$this->left = $left;
$this->right = $right;
}
}

View File

@@ -0,0 +1,29 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast;
class ParameterExpression implements ExpressionInterface
{
public $name;
public function __construct($name)
{
$this->name = $name;
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast;
class VariableExpression implements ExpressionInterface
{
public $name;
public $allowNull;
public function __construct($name, $allowNull = false)
{
$this->name = $name;
$this->allowNull = $allowNull;
}
}

View File

@@ -0,0 +1,32 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler;
class AndExpressionCompiler extends BinaryExprCompiler
{
public function getType()
{
return 'JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\AndExpression';
}
protected function getOperator()
{
return '&&';
}
}

View File

@@ -0,0 +1,51 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ExpressionInterface;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler;
/**
* Base Compiler for Binary Operators.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
abstract class BinaryExprCompiler implements TypeCompilerInterface
{
public function compilePreconditions(ExpressionCompiler $compiler, ExpressionInterface $expr)
{
$compiler
->compilePreconditions($expr->left)
->compilePreconditions($expr->right)
;
}
public function compile(ExpressionCompiler $compiler, ExpressionInterface $expr)
{
$compiler
->write("(")
->compileInternal($expr->left)
->write(") ".$this->getOperator()." (")
->compileInternal($expr->right)
->write(")")
;
}
abstract protected function getOperator();
}

View File

@@ -0,0 +1,60 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler;
use Symfony\Component\DependencyInjection\ContainerInterface;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ExpressionInterface;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\VariableExpressionCompiler;
class ContainerAwareVariableCompiler extends VariableExpressionCompiler
{
private $serviceMap = array();
private $parameterMap = array();
public function setMaps(array $serviceMap, array $parameterMap)
{
$this->serviceMap = $serviceMap;
$this->parameterMap = $parameterMap;
}
public function compile(ExpressionCompiler $compiler, ExpressionInterface $expr)
{
if (isset($this->serviceMap[$expr->name])) {
$compiler->write("\$context['container']->get('{$this->serviceMap[$expr->name]}'");
if ($expr->allowNull) {
$compiler->write(", ".ContainerInterface::NULL_ON_INVALID_REFERENCE);
}
$compiler->write(")");
return;
}
if (isset($this->parameterMap[$expr->name])) {
$compiler->write("\$context['container']->getParameter('{$this->parameterMap[$expr->name]}')");
return;
}
parent::compile($compiler, $expr);
}
}

View File

@@ -0,0 +1,35 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\Func;
use JMS\SecurityExtraBundle\Exception\InvalidArgumentException;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\FunctionExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler;
abstract class AuthenticationTrustFunctionCompiler implements FunctionCompilerInterface
{
public function compilePreconditions(ExpressionCompiler $compiler, FunctionExpression $function)
{
if (!empty($function->args)) {
throw new InvalidArgumentException(sprintf('The '.$this->getName().'() function does not accept any arguments, but got "%s".', var_export($function->args, true)));
}
$compiler->verifyItem('token', 'Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
}
}

View File

@@ -0,0 +1,29 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\Func;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\FunctionExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler;
interface FunctionCompilerInterface
{
function getName();
function compilePreconditions(ExpressionCompiler $compiler, FunctionExpression $function);
function compile(ExpressionCompiler $compiler, FunctionExpression $function);
}

View File

@@ -0,0 +1,63 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\Func;
use JMS\SecurityExtraBundle\Exception\RuntimeException;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\FunctionExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler;
class HasAnyRoleFunctionCompiler implements FunctionCompilerInterface
{
private $rolesExpr;
public function getName()
{
return 'hasAnyRole';
}
public function compilePreconditions(ExpressionCompiler $compiler, FunctionExpression $function)
{
if (0 === count($function->args)) {
throw new RuntimeException('The function hasAnyRole() expects at least one argument, but got none.');
}
$this->rolesExpr = $compiler->getRolesExpr();
}
public function compile(ExpressionCompiler $compiler, FunctionExpression $function)
{
$compiler->write("(");
$first = true;
foreach ($function->args as $arg) {
if (!$first) {
$compiler->write(" || ");
}
$first = false;
$compiler
->write("isset({$this->rolesExpr}[")
->compileInternal($arg)
->write("])")
;
}
$compiler->write(")");
}
}

View File

@@ -0,0 +1,51 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\Func;
use JMS\SecurityExtraBundle\Exception\RuntimeException;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\FunctionExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler;
class HasRoleFunctionCompiler implements FunctionCompilerInterface
{
private $rolesExpr;
public function getName()
{
return 'hasRole';
}
public function compilePreconditions(ExpressionCompiler $compiler, FunctionExpression $function)
{
if (1 !== count($function->args)) {
throw new RuntimeException(sprintf('The hasRole() function expects exactly one argument, but got "%s".', var_export($function->args, true)));
}
$this->rolesExpr = $compiler->getRolesExpr();
}
public function compile(ExpressionCompiler $compiler, FunctionExpression $function)
{
$compiler
->write("isset({$this->rolesExpr}[")
->compileInternal($function->args[0])
->write("])")
;
}
}

View File

@@ -0,0 +1,39 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\Func;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\VariableExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\FunctionExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler;
class IsAnonymousFunctionCompiler extends AuthenticationTrustFunctionCompiler
{
public function getName()
{
return 'isAnonymous';
}
public function compile(ExpressionCompiler $compiler, FunctionExpression $function)
{
$compiler
->compileInternal(new VariableExpression('trust_resolver'))
->write("->isAnonymous(\$context['token'])");
}
}

View File

@@ -0,0 +1,41 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\Func;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\VariableExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\FunctionExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler;
class IsAuthenticatedFunctionCompiler extends AuthenticationTrustFunctionCompiler
{
public function getName()
{
return 'isAuthenticated';
}
public function compile(ExpressionCompiler $compiler, FunctionExpression $function)
{
$compiler
->write("!")
->compileInternal(new VariableExpression('trust_resolver'))
->write("->isAnonymous(\$context['token'])")
;
}
}

View File

@@ -0,0 +1,39 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\Func;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\VariableExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\FunctionExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler;
class IsFullyAuthenticatedFunctionCompiler extends AuthenticationTrustFunctionCompiler
{
public function getName()
{
return 'isFullyAuthenticated';
}
public function compile(ExpressionCompiler $compiler, FunctionExpression $function)
{
$compiler
->compileInternal(new VariableExpression('trust_resolver'))
->write("->isFullFledged(\$context['token'])");
}
}

View File

@@ -0,0 +1,39 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\Func;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\VariableExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\FunctionExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler;
class IsRememberMeFunctionCompiler extends AuthenticationTrustFunctionCompiler
{
public function getName()
{
return 'isRememberMe';
}
public function compile(ExpressionCompiler $compiler, FunctionExpression $function)
{
$compiler
->compileInternal(new VariableExpression('trust_resolver'))
->write("->isRememberMe(\$context['token'])");
}
}

View File

@@ -0,0 +1,32 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler;
class IsEqualExpressionCompiler extends BinaryExprCompiler
{
public function getType()
{
return 'JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\IsEqualExpression';
}
protected function getOperator()
{
return '===';
}
}

View File

@@ -0,0 +1,44 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ExpressionInterface;
class NotExpressionCompiler implements TypeCompilerInterface
{
public function getType()
{
return 'JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\NotExpression';
}
public function compilePreconditions(ExpressionCompiler $compiler, ExpressionInterface $expr)
{
$compiler->compilePreconditions($expr->expr);
}
public function compile(ExpressionCompiler $compiler, ExpressionInterface $expr)
{
$compiler
->write('!(')
->compileInternal($expr->expr)
->write(')')
;
}
}

View File

@@ -0,0 +1,32 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler;
class OrExpressionCompiler extends BinaryExprCompiler
{
public function getType()
{
return 'JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\OrExpression';
}
protected function getOperator()
{
return '||';
}
}

View File

@@ -0,0 +1,80 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\VariableExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ExpressionInterface;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\TypeCompilerInterface;
class ParameterExpressionCompiler implements TypeCompilerInterface
{
public function getType()
{
return 'JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ParameterExpression';
}
public function compilePreconditions(ExpressionCompiler $compiler, ExpressionInterface $parameter)
{
$compiler->verifyItem('object', 'CG\Proxy\MethodInvocation');
if (!isset($compiler->attributes['parameter_mapping_name'])) {
$this->addParameterMapping($compiler);
}
$compiler
->writeln("if (!isset(\${$compiler->attributes['parameter_mapping_name']}['{$parameter->name}'])) {")
->indent()
->write("throw new RuntimeException(sprintf('There is no parameter with name \"{$parameter->name}\" for method \"%s\".', ")
->compileInternal(new VariableExpression('object'))
->writeln("));")
->outdent()
->write("}\n\n")
;
}
public function compile(ExpressionCompiler $compiler, ExpressionInterface $parameter)
{
$compiler
->compileInternal(new VariableExpression('object'))
->write("->arguments[")
->write("\${$compiler->attributes['parameter_mapping_name']}")
->write("['{$parameter->name}']]")
;
}
private function addParameterMapping(ExpressionCompiler $compiler)
{
$name = $compiler->nextName();
$indexName = $compiler->nextName();
$paramName = $compiler->nextName();
$compiler
->setAttribute('parameter_mapping_name', $name)
->writeln("\$$name = array();")
->write("foreach (")
->compileInternal(new VariableExpression('object'))
->writeln("->reflection->getParameters() as \$$indexName => \$$paramName) {")
->indent()
->writeln("\${$name}[\${$paramName}->name] = \$$indexName;")
->outdent()
->writeln("}\n")
;
}
}

View File

@@ -0,0 +1,29 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ExpressionInterface;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler;
interface TypeCompilerInterface
{
function getType();
function compilePreconditions(ExpressionCompiler $compiler, ExpressionInterface $expr);
function compile(ExpressionCompiler $compiler, ExpressionInterface $expr);
}

View File

@@ -0,0 +1,74 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\VariableExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ExpressionInterface;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler;
class VariableExpressionCompiler implements TypeCompilerInterface
{
public function getType()
{
return 'JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\VariableExpression';
}
public function compilePreconditions(ExpressionCompiler $compiler, ExpressionInterface $expr)
{
if ('user' === $expr->name) {
$compiler
->setAttribute('user_var_name', $name = $compiler->nextName())
->write("\$$name = ")
->compileInternal(new VariableExpression('token'))
->write("->getUser();\n\n")
;
}
}
public function compile(ExpressionCompiler $compiler, ExpressionInterface $expr)
{
if ('permitAll' === $expr->name) {
$compiler->write('true');
return;
}
if ('denyAll' === $expr->name) {
$compiler->write('false');
return;
}
if ('user' === $expr->name) {
$compiler->write("\${$compiler->attributes['user_var_name']}");
return;
}
if ($expr->allowNull) {
$compiler->write("(isset(\$context['{$expr->name}']) ? ");
}
$compiler->write("\$context['{$expr->name}']");
if ($expr->allowNull) {
$compiler->write(" : null)");
}
}
}

View File

@@ -0,0 +1,47 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionHandlerInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Lazy-loading container aware expression handler.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class ContainerAwareExpressionHandler implements ExpressionHandlerInterface
{
private $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function createContext(TokenInterface $token, $object)
{
return array(
'container' => $this->container,
'token' => $token,
'object' => $object,
);
}
}

View File

@@ -0,0 +1,51 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression;
use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
class DefaultExpressionHandler implements ExpressionHandlerInterface
{
private $trustResolver;
private $roleHierarchy;
public function __construct(AuthenticationTrustResolverInterface $trustResolver,
RoleHierarchyInterface $roleHierarchy = null)
{
$this->trustResolver = $trustResolver;
$this->roleHierarchy = $roleHierarchy;
}
public function createContext(TokenInterface $token, $object)
{
$context = array(
'token' => $token,
'object' => $object,
'trust_resolver' => $this->trustResolver,
);
if (null !== $this->roleHierarchy) {
$context['role_hierarchy'] = $this->roleHierarchy;
}
return $context;
}
}

View File

@@ -0,0 +1,40 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression;
final class Expression
{
/** READ-ONLY */
public $expression;
public function __construct($expression)
{
$this->expression = $expression;
}
public function getHashCode()
{
return sha1($this->expression);
}
public function __toString()
{
return 'EXPRESSION('.$this->expression.')';
}
}

View File

@@ -0,0 +1,395 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression;
use JMS\SecurityExtraBundle\Exception\RuntimeException;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\TypeCompilerInterface;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\IsEqualExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\Func\FunctionCompilerInterface;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ExpressionInterface;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\VariableExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\OrExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\MethodCallExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\GetPropertyExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\GetItemExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\FunctionExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ConstantExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ArrayExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\AndExpression;
class ExpressionCompiler
{
public $attributes = array();
private $indentationLevel = 0;
private $indentationSpaces = 4;
private $nameCount = 0;
private $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
private $charCount = 52;
private $reservedNames = array('context' => true);
private $itemExists = array();
private $itemType = array();
private $rolesName;
private $code;
private $parser;
private $typeCompilers;
private $functionCompilers;
public function __construct()
{
$this->addTypeCompiler(new Compiler\AndExpressionCompiler());
$this->addTypeCompiler(new Compiler\IsEqualExpressionCompiler());
$this->addTypeCompiler(new Compiler\OrExpressionCompiler());
$this->addTypeCompiler(new Compiler\VariableExpressionCompiler());
$this->addTypeCompiler(new Compiler\NotExpressionCompiler());
$this->functionCompilers = array(
'isAnonymous' => new Compiler\Func\IsAnonymousFunctionCompiler(),
'isAuthenticated' => new Compiler\Func\IsAuthenticatedFunctionCompiler(),
'isRememberMe' => new Compiler\Func\IsRememberMeFunctionCompiler(),
'isFullyAuthenticated' => new Compiler\Func\IsFullyAuthenticatedFunctionCompiler(),
'hasRole' => new Compiler\Func\HasRoleFunctionCompiler(),
'hasAnyRole' => new Compiler\Func\HasAnyRoleFunctionCompiler(),
);
}
public function setAttribute($name, $value)
{
$this->attributes[$name] = $value;
return $this;
}
public function addTypeCompiler(TypeCompilerInterface $compiler)
{
$this->typeCompilers[$compiler->getType()] = $compiler;
}
public function addFunctionCompiler(FunctionCompilerInterface $compiler)
{
$this->functionCompilers[$compiler->getName()] = $compiler;
}
public function compileExpression(Expression $expr)
{
return $this->compile($this->getParser()->parse($expr->expression),
$expr->expression);
}
public function compile(ExpressionInterface $expr, $raw = null)
{
$this->nameCount = 0;
$this->code = '';
$this->itemExists = $this->itemType = $this->attributes = array();
$this->rolesName = null;
if ($raw) {
$this->writeln('// Expression: '.$raw);
}
$this
->writeln('return function(array $context) {')
->indent()
->compilePreconditions($expr)
->write('return ')
->compileInternal($expr)
->writeln(';')
->outdent()
->writeln('};')
;
return $this->code;
}
public function indent()
{
$this->indentationLevel += 1;
return $this;
}
public function outdent()
{
$this->indentationLevel -= 1;
if ($this->indentationLevel < 0) {
throw new RuntimeException('The identation level cannot be less than zero.');
}
return $this;
}
public function writeln($content)
{
$this->write($content."\n");
return $this;
}
public function getRolesExpr()
{
if (null !== $this->rolesName) {
return '$'.$this->rolesName;
}
$this->verifyItem('token', 'Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
$this->rolesName = $rolesName = $this->nextName();
$hierarchyName = $this->nextName();
$tmpName = $this->nextName();
$this
->writeln("\$$rolesName = \$context['token']->getRoles();")
->write("if (null !== \$$hierarchyName = ")
->compileInternal(new VariableExpression('role_hierarchy', true))
->writeln(") {")
->indent()
->writeln("\$$rolesName = \${$hierarchyName}->getReachableRoles(\$$rolesName);")
->outdent()
->write("}\n\n")
->writeln("\$$tmpName = array();")
->writeln("foreach (\$$rolesName as \$role) {")
->indent()
->writeln("\${$tmpName}[\$role->getRole()] = true;")
->outdent()
->writeln("}")
->write("\$$rolesName = \$$tmpName;\n\n")
;
return '$'.$rolesName;
}
public function verifyItem($key, $expectedType = null)
{
if (!isset($this->itemExists[$key])) {
$this->itemExists[$key] = true;
$this
->writeln("if (!isset(\$context['$key'])) {")
->indent()
->writeln("throw new RuntimeException('The context contains no item with key \"$key\".');")
->outdent()
->write("}\n\n")
;
}
if (null !== $expectedType) {
if (isset($this->itemType[$key])) {
if ($this->itemType[$key] !== $expectedType) {
throw new RuntimeException(sprintf('Cannot verify that item "%s" is of type "%s" because it is already expected to be of type "%s".',
$key, $expectedType, $this->itemType[$key]
));
}
return $this;
}
$this
->writeln("if (!\$context['$key'] instanceof $expectedType) {")
->indent()
->writeln("throw new RuntimeException(sprintf('The item \"$key\" is expected to be of type \"$expectedType\", but got \"%s\".', get_class(\$context['$key'])));")
->outdent()
->write("}\n\n")
;
}
return $this;
}
public function write($content)
{
$lines = explode("\n", $content);
for ($i=0,$c=count($lines); $i<$c; $i++) {
if ($this->indentationLevel > 0
&& !empty($lines[$i])
&& (empty($this->code) || "\n" === substr($this->code, -1))) {
$this->code .= str_repeat(' ', $this->indentationLevel * $this->indentationSpaces);
}
$this->code .= $lines[$i];
if ($i+1 < $c) {
$this->code .= "\n";
}
}
return $this;
}
public function nextName()
{
while (true) {
$name = '';
$i = $this->nameCount;
$name .= $this->chars[$i % $this->charCount];
$i = intval($i / $this->charCount);
while ($i > 0) {
$i -= 1;
$name .= $this->chars[$i % $this->charCount];
$i = intval($i / $this->charCount);
}
$this->nameCount += 1;
// check that the name is not reserved
if (isset($this->reservedNames[$name])) {
continue;
}
return $name;
}
}
public function compilePreconditions(ExpressionInterface $expr)
{
if ($typeCompiler = $this->findTypeCompiler(get_class($expr))) {
$typeCompiler->compilePreconditions($this, $expr);
return $this;
}
if ($expr instanceof FunctionExpression) {
$this->getFunctionCompiler($expr->name)->compilePreconditions($this, $expr);
foreach ($expr->args as $arg) {
$this->compilePreconditions($arg);
}
return $this;
}
if ($expr instanceof VariableExpression) {
$this->getVariableCompiler($expr->name)->compilePreconditions($this, $expr);
return $this;
}
if ($expr instanceof MethodCallExpression) {
$this->compilePreconditions($expr->object);
foreach ($expr->args as $arg) {
$this->compilePreconditions($arg);
}
return $this;
}
if ($expr instanceof GetPropertyExpression) {
$this->compilePreconditions($expr->object);
return $this;
}
return $this;
}
public function compileInternal(ExpressionInterface $expr)
{
if ($typeCompiler = $this->findTypeCompiler(get_class($expr))) {
$typeCompiler->compile($this, $expr);
return $this;
}
if ($expr instanceof ArrayExpression) {
$this->code .= 'array(';
foreach ($expr->elements as $key => $value) {
$this->code .= var_export($key, true).' => ';
$this->compileInternal($value);
$this->code .= ',';
}
$this->code .= ')';
return $this;
}
if ($expr instanceof ConstantExpression) {
$this->code .= var_export($expr->value, true);
return $this;
}
if ($expr instanceof FunctionExpression) {
$this->getFunctionCompiler($expr->name)->compile($this, $expr);
return $this;
}
if ($expr instanceof GetItemExpression) {
$this->compileInternal($expr->array);
$this->code .= '['.$expr->key.']';
return $this;
}
if ($expr instanceof GetPropertyExpression) {
$this->compileInternal($expr->object);
$this->code .= '->'.$expr->name;
return $this;
}
if ($expr instanceof MethodCallExpression) {
$this->compileInternal($expr->object);
$this->code .= '->'.$expr->method.'(';
$first = true;
foreach ($expr->args as $arg) {
if (!$first) {
$this->code .= ', ';
}
$first = false;
$this->compileInternal($arg);
}
$this->code .= ')';
return $this;
}
throw new RuntimeException(sprintf('Unknown expression "%s".', get_class($expr)));
}
public function getFunctionCompiler($name)
{
if (!isset($this->functionCompilers[$name])) {
throw new RuntimeException(sprintf('There is no compiler for function "%s".', $name));
}
return $this->functionCompilers[$name];
}
private function findTypeCompiler($type)
{
return isset($this->typeCompilers[$type]) ? $this->typeCompilers[$type] : null;
}
private function getParser()
{
if (null !== $this->parser) {
return $this->parser;
}
return $this->parser = new ExpressionParser();
}
}

View File

@@ -0,0 +1,26 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
interface ExpressionHandlerInterface
{
function createContext(TokenInterface $token, $object);
}

View File

@@ -0,0 +1,134 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression;
use JMS\SecurityExtraBundle\Exception\InvalidArgumentException;
final class ExpressionLexer
{
public $token;
public $lookahead;
private $tokens;
private $pointer;
const T_STRING = 1;
const T_IDENTIFIER = 2;
const T_NONE = 3;
const T_COMMA = 4;
const T_OPEN_PARENTHESIS = 5;
const T_CLOSE_PARENTHESIS = 6;
const T_AND = 7;
const T_OR = 8;
const T_PARAMETER = 9;
const T_OBJECT_OPERATOR = 10;
const T_OPEN_BRACKET = 11;
const T_CLOSE_BRACKET = 12;
const T_OPEN_BRACE = 13;
const T_CLOSE_BRACE = 14;
const T_COLON = 15;
const T_IS_EQUAL = 16;
const T_NOT = 17;
public static function getLiteral($type)
{
static $constants;
if (null === $constants) {
$ref = new \ReflectionClass(get_called_class());
$constants = $ref->getConstants();
}
if (false === $literal = array_search($type, $constants, true)) {
throw new InvalidArgumentException(sprintf('There is no token of value "%s".', $type));
}
return $literal;
}
public function initialize($input)
{
static $pattern = '/(#?[a-z][a-z0-9]*|\'(?:[^\']|(?<=\\\\)\')*\'|"(?:[^"]|(?<=\\\\)")*"|&&|\|\||==)|\s+|(.)/i';
$parts = preg_split($pattern, $input, -1, PREG_SPLIT_OFFSET_CAPTURE
| PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
$tokens = array();
foreach ($parts as $part) {
list($value, $position) = $part;
$type = self::T_NONE;
if ("'" === $value[0] || '"' === $value[0]) {
$type = self::T_STRING;
$value = substr($value, 1, -1);
} else if (',' === $value) {
$type = self::T_COMMA;
} else if ('(' === $value) {
$type = self::T_OPEN_PARENTHESIS;
} else if (')' === $value) {
$type = self::T_CLOSE_PARENTHESIS;
} else if ('[' === $value) {
$type = self::T_OPEN_BRACKET;
} else if (']' === $value) {
$type = self::T_CLOSE_BRACKET;
} else if ('{' === $value) {
$type = self::T_OPEN_BRACE;
} else if ('}' === $value) {
$type = self::T_CLOSE_BRACE;
} else if ('&&' === $value || 'and' === strtolower($value)) {
$type = self::T_AND;
} else if ('||' === $value || 'or' === strtolower($value)) {
$type = self::T_OR;
} else if ('!' === $value || 'not' === strtolower($value)) {
$type = self::T_NOT;
} else if (':' === $value) {
$type = self::T_COLON;
} else if ('.' === $value) {
$type = self::T_OBJECT_OPERATOR;
} else if ('==' === $value) {
$type = self::T_IS_EQUAL;
} else if ('#' === $value[0]) {
$type = self::T_PARAMETER;
$value = substr($value, 1);
} else if (ctype_alpha($value)) {
$type = self::T_IDENTIFIER;
}
$tokens[] = array(
'type' => $type,
'value' => $value,
'position' => $position,
);
}
$this->tokens = $tokens;
$this->pointer = -1;
$this->next();
}
public function next()
{
$this->pointer += 1;
$this->token = $this->lookahead;
$this->lookahead = isset($this->tokens[$this->pointer]) ?
$this->tokens[$this->pointer] : null;
return $this->lookahead !== null;
}
}

View File

@@ -0,0 +1,290 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\NotExpression;
use JMS\SecurityExtraBundle\Exception\RuntimeException;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\IsEqualExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ParameterExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\VariableExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ConstantExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\OrExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\AndExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ArrayExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\GetItemExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\GetPropertyExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\MethodCallExpression;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ExpressionInterface;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\FunctionExpression;
final class ExpressionParser
{
const PRECEDENCE_OR = 10;
const PRECEDENCE_AND = 15;
const PRECEDENCE_IS_EQUAL = 20;
const PRECEDENCE_NOT = 30;
private $lexer;
public function __construct()
{
$this->lexer = new ExpressionLexer();
}
public function parse($str)
{
$this->lexer->initialize($str);
return $this->Expression();
}
private function Expression($precedence = 0)
{
$expr = $this->Primary();
while (true) {
if (ExpressionLexer::T_AND === $this->lexer->lookahead['type']
&& $precedence <= self::PRECEDENCE_AND) {
$this->lexer->next();
$expr = new AndExpression($expr, $this->Expression(
self::PRECEDENCE_AND + 1));
continue;
}
if (ExpressionLexer::T_OR === $this->lexer->lookahead['type']
&& $precedence <= self::PRECEDENCE_OR) {
$this->lexer->next();
$expr = new OrExpression($expr, $this->Expression(
self::PRECEDENCE_OR + 1));
continue;
}
if (ExpressionLexer::T_IS_EQUAL === $this->lexer->lookahead['type']
&& $precedence <= self::PRECEDENCE_IS_EQUAL) {
$this->lexer->next();
$expr = new IsEqualExpression($expr, $this->Expression(
self::PRECEDENCE_IS_EQUAL + 1));
continue;
}
break;
}
return $expr;
}
private function Primary()
{
if (ExpressionLexer::T_NOT === $this->lexer->lookahead['type']) {
$this->lexer->next();
$expr = new NotExpression($this->Expression(self::PRECEDENCE_NOT));
return $this->Suffix($expr);
}
if (ExpressionLexer::T_OPEN_PARENTHESIS === $this->lexer->lookahead['type']) {
$this->lexer->next();
$expr = $this->Expression();
$this->match(ExpressionLexer::T_CLOSE_PARENTHESIS);
return $this->Suffix($expr);
}
if (ExpressionLexer::T_STRING === $this->lexer->lookahead['type']) {
return new ConstantExpression($this->match(ExpressionLexer::T_STRING));
}
if (ExpressionLexer::T_OPEN_BRACE === $this->lexer->lookahead['type']) {
return $this->Suffix($this->MapExpr());
}
if (ExpressionLexer::T_OPEN_BRACKET === $this->lexer->lookahead['type']) {
return $this->Suffix($this->ListExpr());
}
if (ExpressionLexer::T_IDENTIFIER === $this->lexer->lookahead['type']) {
$name = $this->match(ExpressionLexer::T_IDENTIFIER);
if (ExpressionLexer::T_OPEN_PARENTHESIS === $this->lexer->lookahead['type']) {
$args = $this->Arguments();
return $this->Suffix(new FunctionExpression($name, $args));
}
return $this->Suffix(new VariableExpression($name));
}
if (ExpressionLexer::T_PARAMETER === $this->lexer->lookahead['type']) {
return $this->Suffix(new ParameterExpression($this->match(ExpressionLexer::T_PARAMETER)));
}
$this->error('primary expression');
}
private function ListExpr()
{
$this->match(ExpressionLexer::T_OPEN_BRACKET);
$elements = array();
while (ExpressionLexer::T_CLOSE_BRACKET !== $this->lexer->lookahead['type']) {
$elements[] = $this->Expression();
if (ExpressionLexer::T_COMMA !== $this->lexer->lookahead['type']) {
break;
}
$this->lexer->next();
}
$this->match(ExpressionLexer::T_CLOSE_BRACKET);
return new ArrayExpression($elements);
}
private function MapExpr()
{
$this->match(ExpressionLexer::T_OPEN_BRACE);
$entries = array();
while (ExpressionLexer::T_CLOSE_BRACE !== $this->lexer->lookahead['type']) {
$key = $this->match(ExpressionLexer::T_STRING);
$this->match(ExpressionLexer::T_COLON);
$entries[$key] = $this->Expression();
if (ExpressionLexer::T_COMMA !== $this->lexer->lookahead['type']) {
break;
}
$this->lexer->next();
}
$this->match(ExpressionLexer::T_CLOSE_BRACE);
return new ArrayExpression($entries);
}
private function Suffix(ExpressionInterface $expr)
{
while (true) {
if (ExpressionLexer::T_OBJECT_OPERATOR === $this->lexer->lookahead['type']) {
$this->lexer->next();
$name = $this->match(ExpressionLexer::T_IDENTIFIER);
if (ExpressionLexer::T_OPEN_PARENTHESIS === $this->lexer->lookahead['type']) {
$args = $this->Arguments();
$expr = new MethodCallExpression($expr, $name, $args);
continue;
}
$expr = new GetPropertyExpression($expr, $name);
continue;
}
if (ExpressionLexer::T_OPEN_BRACKET === $this->lexer->lookahead['type']) {
$this->lexer->next();
$key = $this->Expression();
$this->match(ExpressionLexer::T_CLOSE_BRACKET);
$expr = new GetItemExpression($expr, $key);
continue;
}
break;
}
return $expr;
}
private function FunctionCall()
{
$name = $this->match(ExpressionLexer::T_IDENTIFIER);
$args = $this->Arguments();
return new FunctionExpression($name, $args);
}
private function Arguments()
{
$this->match(ExpressionLexer::T_OPEN_PARENTHESIS);
$args = array();
while (ExpressionLexer::T_CLOSE_PARENTHESIS !== $this->lexer->lookahead['type']) {
$args[] = $this->Expression();
if (ExpressionLexer::T_COMMA !== $this->lexer->lookahead['type']) {
break;
}
$this->match(ExpressionLexer::T_COMMA);
}
$this->match(ExpressionLexer::T_CLOSE_PARENTHESIS);
return $args;
}
private function Value()
{
return $this->matchAny(array(ExpressionLexer::T_STRING));
}
private function matchAny(array $types)
{
if (null !== $this->lexer->lookahead) {
foreach ($types as $type) {
if ($type === $this->lexer->lookahead['type']) {
$this->lexer->next();
return $this->lexer->token['value'];
}
}
}
$this->error(sprintf('one of these tokens "%s"',
implode('", "', array_map(array('JMS\SecurityExtraBundle\Security\Authorization\Expression\Lexer', 'getLiteral'), $types))
));
}
private function match($type)
{
if (null === $this->lexer->lookahead
|| $type !== $this->lexer->lookahead['type']) {
$this->error(sprintf('token "%s"', ExpressionLexer::getLiteral($type)));
}
$this->lexer->next();
return $this->lexer->token['value'];
}
private function error($expected)
{
$actual = null === $this->lexer->lookahead ? 'end of file'
: sprintf('token "%s" with value "%s" at position %d',
ExpressionLexer::getLiteral($this->lexer->lookahead['type']),
$this->lexer->lookahead['value'],
$this->lexer->lookahead['position']);
throw new RuntimeException(sprintf('Expected %s, but got %s.', $expected, $actual));
}
}

View File

@@ -0,0 +1,114 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression;
use JMS\SecurityExtraBundle\Exception\RuntimeException;
use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
/**
* Expression-based voter.
*
* This voter allows to use complex access expression in a high-performance
* way. This is the preferred voter for any non-simple access checks.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class ExpressionVoter implements VoterInterface
{
private $evaluators = array();
private $compiler;
private $cacheDir;
private $expressionHandler;
public function __construct(ExpressionHandlerInterface $expressionHandler) {
$this->expressionHandler = $expressionHandler;
}
public function setCacheDir($cacheDir)
{
$this->cacheDir = $cacheDir;
}
public function setCompiler(ExpressionCompiler $compiler)
{
$this->compiler = $compiler;
}
public function vote(TokenInterface $token, $object, array $attributes)
{
$result = VoterInterface::ACCESS_ABSTAIN;
foreach ($attributes as $attribute) {
if (!$attribute instanceof Expression) {
continue;
}
$result = VoterInterface::ACCESS_DENIED;
if (!isset($this->evaluators[$attribute->expression])) {
$this->evaluators[$attribute->expression] =
$this->createEvaluator($attribute);
}
if (call_user_func($this->evaluators[$attribute->expression],
$this->expressionHandler->createContext($token, $object))) {
return VoterInterface::ACCESS_GRANTED;
}
}
return $result;
}
public function supportsAttribute($attribute)
{
return $attribute instanceof Expression;
}
public function supportsClass($class)
{
return true;
}
protected function getCompiler()
{
if (null === $this->compiler) {
throw new RuntimeException('A compiler must be set.');
}
return $this->compiler;
}
private function createEvaluator(Expression $expr)
{
if ($this->cacheDir) {
if (is_file($file = $this->cacheDir.'/'.sha1($expr->expression).'.php')) {
return require $file;
}
$source = $this->getCompiler()->compileExpression($expr);
file_put_contents($file, "<?php\n".$source);
return require $file;
}
return eval($this->getCompiler()->compileExpression($expr));
}
}

View File

@@ -0,0 +1,39 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Expression;
use Symfony\Component\DependencyInjection\ContainerInterface;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionVoter;
class LazyLoadingExpressionVoter extends ExpressionVoter
{
private $container;
private $compilerId;
public function setLazyCompiler(ContainerInterface $container, $id)
{
$this->container = $container;
$this->compilerId = $id;
}
protected function getCompiler()
{
return $this->container->get($this->compilerId);
}
}

View File

@@ -0,0 +1,150 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Interception;
use JMS\SecurityExtraBundle\Exception\RuntimeException;
use CG\Core\ClassUtils;
use CG\Proxy\MethodInterceptorInterface;
use CG\Proxy\MethodInvocation;
use JMS\SecurityExtraBundle\Metadata\MethodMetadata;
use JMS\SecurityExtraBundle\Security\Authentication\Token\RunAsUserToken;
use JMS\SecurityExtraBundle\Security\Authorization\AfterInvocation\AfterInvocationManagerInterface;
use JMS\SecurityExtraBundle\Security\Authorization\RunAsManagerInterface;
use Metadata\MetadataFactoryInterface;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException;
/**
* All invocations of secure methods will go through this class.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class MethodSecurityInterceptor implements MethodInterceptorInterface
{
private $alwaysAuthenticate;
private $securityContext;
private $metadataFactory;
private $authenticationManager;
private $accessDecisionManager;
private $afterInvocationManager;
private $runAsManager;
private $logger;
public function __construct(SecurityContext $securityContext, AuthenticationManagerInterface $authenticationManager, AccessDecisionManagerInterface $accessDecisionManager,
AfterInvocationManagerInterface $afterInvocationManager, RunAsManagerInterface $runAsManager, MetadataFactoryInterface $metadataFactory, LoggerInterface $logger = null)
{
$this->alwaysAuthenticate = false;
$this->securityContext = $securityContext;
$this->metadataFactory = $metadataFactory;
$this->authenticationManager = $authenticationManager;
$this->accessDecisionManager = $accessDecisionManager;
$this->afterInvocationManager = $afterInvocationManager;
$this->runAsManager = $runAsManager;
$this->logger = $logger;
}
public function setAlwaysAuthenticate($boolean)
{
$this->alwaysAuthenticate = !!$boolean;
}
public function intercept(MethodInvocation $method)
{
$metadata = $this->metadataFactory->getMetadataForClass($method->reflection->class);
// no security metadata, proceed
if (empty($metadata) || !isset($metadata->methodMetadata[$method->reflection->name])) {
return $method->proceed();
}
$metadata = $metadata->methodMetadata[$method->reflection->name];
if (null === $token = $this->securityContext->getToken()) {
throw new AuthenticationCredentialsNotFoundException(
'The security context was not populated with a Token.'
);
}
if ($this->alwaysAuthenticate || !$token->isAuthenticated()) {
$token = $this->authenticationManager->authenticate($token);
$this->securityContext->setToken($token);
}
if (!empty($metadata->roles) && false === $this->accessDecisionManager->decide($token, $metadata->roles, $method)) {
throw new AccessDeniedException('Token does not have the required roles.');
}
if (!empty($metadata->paramPermissions)) {
foreach ($method->arguments as $index => $argument) {
if (null !== $argument && isset($metadata->paramPermissions[$index]) && false === $this->accessDecisionManager->decide($token, $metadata->paramPermissions[$index], $argument)) {
throw new AccessDeniedException(sprintf('Token does not have the required permissions for method "%s::%s".', $method->reflection->class, $method->reflection->name));
}
}
}
$runAsToken = null;
if (!empty($metadata->runAsRoles)) {
$runAsToken = $this->runAsManager->buildRunAs($token, $method, $metadata->runAsRoles);
if (null !== $this->logger) {
$this->logger->debug('Populating security context with RunAsToken');
}
if (null === $runAsToken) {
throw new RuntimeException('RunAsManager must not return null from buildRunAs().');
}
$this->securityContext->setToken($runAsToken);
}
try {
$returnValue = $method->proceed();
if (null !== $runAsToken) {
$this->restoreOriginalToken($runAsToken);
}
if (empty($metadata->returnPermissions)) {
return $returnValue;
}
return $this->afterInvocationManager->decide($this->securityContext->getToken(), $method, $metadata->returnPermissions, $returnValue);
} catch (\Exception $failed) {
if (null !== $runAsToken) {
$this->restoreOriginalToken($runAsToken);
}
throw $failed;
}
}
private function restoreOriginalToken(RunAsUserToken $runAsToken)
{
if (null !== $this->logger) {
$this->logger->debug('Populating security context with original Token.');
}
$this->securityContext->setToken($runAsToken->getOriginalToken());
}
}

View File

@@ -0,0 +1,92 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Interception;
use CG\Core\ClassUtils;
use Metadata\MetadataFactoryInterface;
use JMS\AopBundle\Aop\PointcutInterface;
class SecurityPointcut implements PointcutInterface
{
private $metadataFactory;
private $secureAllServices;
private $securedClasses = array();
private $patterns;
public function __construct(MetadataFactoryInterface $metadataFactory, $secureAllServices = false, array $patterns = array())
{
$this->metadataFactory = $metadataFactory;
$this->secureAllServices = $secureAllServices;
$this->patterns = $patterns;
}
public function setSecuredClasses(array $classes)
{
$this->securedClasses = $classes;
}
public function matchesClass(\ReflectionClass $class)
{
if ($this->secureAllServices) {
return true;
}
if ('Controller' === substr(ClassUtils::getUserClass($class->name), -10)) {
return true;
}
foreach ($this->patterns as $pattern => $expr) {
// if not for all patterns the class is specified, then we need to scan all
// classes to catch all methods
if (false === $pos = strpos($pattern, '::')) {
// controller notation is already checked by JMSDiExtraBundle,
// we can safely ignore these patterns here
if (2 === substr_count($pattern, ':')) {
continue;
}
return true;
}
if (0 < preg_match('#'.substr($pattern, 0, $pos).'$#', $class->name)) {
return true;
}
}
foreach ($this->securedClasses as $securedClass) {
if ($class->name === $securedClass || $class->isSubclassOf($securedClass)) {
return true;
}
}
return false;
}
public function matchesMethod(\ReflectionMethod $method)
{
$userClass = ClassUtils::getUserClass($method->class);
$metadata = $this->metadataFactory->getMetadataForClass($userClass);
if (null === $metadata) {
return false;
}
return isset($metadata->methodMetadata[$method->name]);
}
}

View File

@@ -0,0 +1,79 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization;
use JMS\SecurityExtraBundle\Security\Authentication\Token\RunAsUserToken;
use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
/**
* The RunAsManager creates throw-away Tokens which are temporarily injected into
* the security context for the duration of the invocation of a specific method.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class RunAsManager implements RunAsManagerInterface
{
private $key;
private $rolePrefix;
public function __construct($key, $rolePrefix = 'ROLE_')
{
$this->key = $key;
$this->rolePrefix = $rolePrefix;
}
/**
* {@inheritDoc}
*/
public function buildRunAs(TokenInterface $token, $secureObject, array $attributes)
{
$roles = array();
foreach ($attributes as $attribute)
{
if ($this->supportsAttribute($attribute)) {
$roles[] = new Role($attribute);
}
}
if (0 === count($roles)) {
return null;
}
$roles = array_merge($roles, $token->getRoles());
return new RunAsUserToken($this->key, $token->getUser(), $token->getCredentials(), $roles, $token);
}
/**
* {@inheritDoc}
*/
public function supportsAttribute($attribute)
{
return !empty($attribute) && 0 === strpos($attribute, $this->rolePrefix);
}
/**
* {@inheritDoc}
*/
public function supportsClass($className)
{
return true;
}
}

View File

@@ -0,0 +1,57 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
/**
* RunAsManagerInterface
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
interface RunAsManagerInterface
{
/**
* Creates a temporary RunAsToken.
*
* The returned Token must have a complementing AuthenticationProvider implementation.
*
* @param TokenInterface $token the original Token
* @param object $secureObject the secure object which caused this call
* @param array $attributes an array of attributes to apply to the built token
* @return TokenInterface
*/
function buildRunAs(TokenInterface $token, $secureObject, array $attributes);
/**
* Whether this RunAsManager supports the given attribute
*
* @param string $attribute
* @return Boolean
*/
function supportsAttribute($attribute);
/**
* Whether this RunAsManager supports the given class.
*
* @param string $className The class of the secure object which requests RunAs capabilities
* @return Boolean
*/
function supportsClass($className);
}

View File

@@ -0,0 +1,61 @@
<?php
/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace JMS\SecurityExtraBundle\Security\Authorization\Voter;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
/**
* This voter adds a special role "ROLE_IDDQD" which effectively bypasses any,
* and all security checks.
*
* Most of the time, you will want to use this rule in combination with a
* @RunAs annotation to disable security checks for the invocation of a
* specific method.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class IddqdVoter implements VoterInterface
{
public function vote(TokenInterface $token, $object, array $attributes)
{
return $this->isIddqd($token) ? VoterInterface::ACCESS_GRANTED : VoterInterface::ACCESS_ABSTAIN;
}
protected function isIddqd(TokenInterface $token)
{
foreach ($token->getRoles() as $role) {
if ('ROLE_IDDQD' === $role->getRole()) {
return true;
}
}
return false;
}
public function supportsAttribute($attribute)
{
return true;
}
public function supportsClass($class)
{
return true;
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace JMS\SecurityExtraBundle\Twig;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Expression;
use Symfony\Component\Security\Core\SecurityContextInterface;
class SecurityExtension extends \Twig_Extension
{
private $context;
public function __construct(SecurityContextInterface $context)
{
$this->context = $context;
}
public function getFunctions()
{
return array(
'is_expr_granted' => new \Twig_Function_Method($this, 'isExprGranted', array(
'is_safe' => true,
)),
);
}
public function isExprGranted($expr, $object = null)
{
return $this->context->isGranted(array(new Expression($expr)), $object);
}
public function getName()
{
return 'jms_security_extra';
}
}

View File

@@ -0,0 +1,25 @@
{
"name": "jms/security-extra-bundle",
"description": "Enhances the Symfony2 Security Component by adding several new features",
"keywords": ["annotations","authorization"],
"type": "symfony-bundle",
"license": "Apache",
"authors": [
{
"name": "Johannes M. Schmitt",
"email": "schmittjoh@gmail.com"
}
],
"require": {
"symfony/framework-bundle": "2.*",
"jms/metadata": "1.1.*",
"jms/aop-bundle": "1.0.*"
},
"recommend": {
"jms/di-extra-bundle": "1.0.*"
},
"autoload": {
"psr-0": { "JMS\\SecurityExtraBundle": "" }
},
"target-dir": "JMS/SecurityExtraBundle"
}