Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
4038697
Added support for DI-configurable features similar to annotations
cblegare Sep 23, 2013
6a8abde
Fixed test conflict
cblegare Sep 23, 2013
c445d15
fixed doc
cblegare Sep 23, 2013
a0092a2
Fixed indentation of switch/case statements, fixed pluralization norm…
cblegare Sep 24, 2013
e08bef4
Fixed ContextErrorExcepton when no configuration was present.
cblegare Sep 24, 2013
2a38b4d
Fixed bad last commit
cblegare Sep 24, 2013
3e7120b
Moved pre-processing configs to a private method.
cblegare Oct 1, 2013
967a962
Cleaned left behing unsused assert methods in tests
cblegare Oct 1, 2013
0d6f042
Corrected typo in variable name
cblegare Oct 1, 2013
443f53d
Removed AbstractDriver to lessen impact on codebase
cblegare Oct 1, 2013
0aa6b7c
Added more strict configuration validation
cblegare Oct 1, 2013
42138e5
Added more strict configuration validation
cblegare Oct 1, 2013
6cc8511
Added a more complete gitignore file
cblegare Jan 6, 2014
83f47fc
Merge branch 'master' of github.com:abstrus/JMSSecurityExtraBundle
cblegare Jan 6, 2014
d1cd7b1
Merge branch 'master' of https://github.com/schmittjoh/JMSSecurityExt…
cblegare Jan 6, 2014
0ce45d1
Fixing PR#123
Mar 21, 2013
f13a4c1
adds lock file
schmittjoh Jun 8, 2013
4022e33
some constraints changes
schmittjoh Jun 9, 2013
ded85f5
Fixed branch alias
Jun 9, 2013
c022926
fix some file mode bug 755->644
bronze1man Aug 6, 2013
4be8fe0
Added support for DI-configurable features similar to annotations
cblegare Sep 23, 2013
6629a67
Fixed test conflict
cblegare Sep 23, 2013
86a3578
fixed doc
cblegare Sep 23, 2013
c77655b
Fixed indentation of switch/case statements, fixed pluralization norm…
cblegare Sep 24, 2013
aea1378
Fixed ContextErrorExcepton when no configuration was present.
cblegare Sep 24, 2013
9fde144
Fixed bad last commit
cblegare Sep 24, 2013
e7eebc0
Moved pre-processing configs to a private method.
cblegare Oct 1, 2013
aea3c1e
Cleaned left behing unsused assert methods in tests
cblegare Oct 1, 2013
8537653
Corrected typo in variable name
cblegare Oct 1, 2013
f51d6e8
Added more strict configuration validation
cblegare Oct 1, 2013
5cdb9ae
Added a more complete gitignore file
cblegare Jan 6, 2014
3513555
Added more strict configuration validation
cblegare Oct 1, 2013
ee86ded
Restoring abstract driver
cblegare Jan 6, 2014
ab50517
Merging
cblegare Jan 6, 2014
c5dfa57
Removed AbstractDriver to lessen impact on codebase
cblegare Jan 6, 2014
5fa5d73
Merge branch 'PR-configdriver' of github.com:abstrus/JMSSecurityExtra…
cblegare Jan 6, 2014
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 57 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,59 @@
phpunit.xml
###
# .gitignore
###

###
# Common pollution
###
*~
*.DS_Store
*.settings
*Thumb.db

###
# Temporary, cache, backup and buffer files
###
*.tmp
*.swp
*.lock
~*
.*.sw[a-z]
.Trashes
.sass-cache
*.swp
composer.lock

###
# Development environement common files
###
*nbproject
*.project
*.idea
*atlassian-ide-plugin.xml
*catalog.xml
*.sublime*
.Spotlight-V100
Thumb.db
Vagrantfile
.vagrant*
nbproject/

###
# Generated files and directories
###
coverage/
vendor/
cache/
log/
app/cache/*
app/logs/*
clover.xml
web/bundles/nelmioapidoc

###
# Excluded executables
###
bin/
composer.phar


46 changes: 44 additions & 2 deletions DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,51 @@ public function getConfigTreeBuilder()
->end()
->end()
->arrayNode('method_access_control')
->beforeNormalization()
->always(function ($node) {
/** This is a backward compatibility layer */
if (is_array($node)) {
foreach ($node as $key => $value) {
if (is_string($value)) {
$node[$key] = array(
'pre_authorize' => $value
);
}
}
}

return $node;
})
->end()
->useAttributeAsKey('pattern')
->prototype('scalar')->isRequired()->cannotBeEmpty()->end()
->end()
->prototype('array')
->children()
->scalarNode('pattern')->end()
->scalarNode('pre_authorize')->cannotBeEmpty()->end()
->arrayNode('secure')
->fixXmlConfig('role')
->children()
->arrayNode('roles')->prototype('scalar')->end()
->end()
->arrayNode('secure_param')
->fixXmlConfig('permission')
->children()
->scalarNode('name')->end()
->arrayNode('permissions')->prototype('scalar')->end()
->end()
->arrayNode('secure_return')
->fixXmlConfig('permission')
->children()
->arrayNode('permissions')->prototype('scalar')->end()
->end()
->arrayNode('run_as')
->fixXmlConfig('role')
->children()
->arrayNode('roles')->prototype('scalar')->end()
->end()
->booleanNode('satisfies_parent_security_policy')->end()
->end()
->end()
->arrayNode('util')
->addDefaultsIfNotSet()
->children()
Expand Down
4 changes: 2 additions & 2 deletions Metadata/ClassMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

use JMS\SecurityExtraBundle\Exception\RuntimeException;
use JMS\SecurityExtraBundle\Exception\InvalidArgumentException;
use Metadata\MethodMetadata;
use Metadata\MethodMetadata as PlainMethodMetadata;
use Metadata\MergeableInterface;
use Metadata\MergeableClassMetadata;

Expand All @@ -31,7 +31,7 @@
*/
class ClassMetadata extends MergeableClassMetadata
{
public function addMethodMetadata(MethodMetadata $metadata)
public function addMethodMetadata(PlainMethodMetadata $metadata)
{
if ($this->reflection->isFinal()) {
throw new RuntimeException(sprintf('Class "%s" is declared final, and cannot be secured.', $reflection->name));
Expand Down
2 changes: 1 addition & 1 deletion Metadata/Driver/AnnotationDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,4 @@ private function convertMethodAnnotations(\ReflectionMethod $method, array $anno

return $hasSecurityMetadata ? $methodMetadata : null;
}
}
}
154 changes: 131 additions & 23 deletions Metadata/Driver/ConfigDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,62 +17,137 @@ class ConfigDriver implements DriverInterface
private $bundles;
private $config;

/**
* @param array $bundles A list of used bundles indexed by name.
* @param array $config Metadata configuration
*/
public function __construct(array $bundles, array $config)
{
uasort($bundles, function($a, $b) {
return strlen($b) - strlen($a);
uasort($bundles, function($operandA, $operandB) {
return strlen($operandB) - strlen($operandA);
});

foreach ($bundles as $name => $namespace) {
$bundles[$name] = substr($namespace, 0, strrpos($namespace, '\\'));
}

$this->bundles = $bundles;
$this->config = $config;
$this->setConfigs($config);
}

/**
* {@inheritDoc}
*/
public function loadMetadataForClass(\ReflectionClass $class)
{
$metadata = new ClassMetadata($class->name);

foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $method) {
$methods = $class->getMethods(
\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED
);

foreach ($methods as $method) {
if ($method->getDeclaringClass()->name !== $class->name) {
continue;
}

$expression = null;
if (null !== $notation = $this->getControllerNotation($method)) {
$expression = $this->getExpressionForSignature($notation);
}
$methodMetadataConfig = $this->getMethodScopeMetadata($method);

if (null === $expression && null === $expression =
$this->getExpressionForSignature($method->class.'::'.$method->name)) {
continue;
$methodMetadata = $this->fromMetadataConfig($method, $methodMetadataConfig);

if ($methodMetadata) {
$metadata->addMethodMetadata($methodMetadata);
}
}

$methodMetadata = new MethodMetadata($method->class, $method->name);
$methodMetadata->roles = array(new Expression($expression));
$metadata->addMethodMetadata($methodMetadata);
return $this->metadataPostTreatment($metadata);
}

protected function getMethodScopeMetadata(\ReflectionMethod $method)
{
$configurationFound = null;

if (null !== $notation = $this->getControllerNotation($method)) {
$configurationFound = $this->getConfigForSignature($notation);
}

if (!$metadata->methodMetadata) {
return null;
if (null === $configurationFound) {
$configurationFound = $this->getConfigForSignature($method->class.'::'.$method->name);
}

return $metadata;
return $configurationFound ? $configurationFound : array();
}

protected function fromMetadataConfig(\ReflectionMethod $method, array $configs)
{
$parameters = array();
foreach ($method->getParameters() as $index => $parameter) {
$parameters[$parameter->getName()] = $index;
}

$methodMetadata = new MethodMetadata($method->class, $method->name);

$hasSecurityMetadata = false;

foreach ($configs as $name => $config) {
switch ($name) {
case "pre_authorize":
$methodMetadata->roles = array(new Expression($config));
$hasSecurityMetadata = true;
break;
case "secure":
$methodMetadata->roles = $config['roles'];
$hasSecurityMetadata = true;
break;
case "secure_param":
$this->assertParamExistsForMethod($parameters, $config['name'], $method->name);
$methodMetadata->addParamPermissions(
$parameters[$config['name']], $config['permissions']
);
$hasSecurityMetadata = true;
break;
case "secure_return":
$methodMetadata->returnPermissions = $config['permissions'];
$hasSecurityMetadata = true;
break;
case "run_as":
$methodMetadata->runAsRoles = $config['roles'];
$hasSecurityMetadata = true;
break;
case "satisfies_parent_security_policy":
$methodMetadata->satisfiesParentSecurityPolicy = true;
$hasSecurityMetadata = true;
break;
}
}

return $hasSecurityMetadata ? $methodMetadata : null;
}

private function getExpressionForSignature($signature)
protected function getConfigForSignature($signature)
{
foreach ($this->config as $pattern => $expr) {
$configurationFound = null;

foreach ($this->config as $pattern => $config) {
if (!preg_match('#'.$pattern.'#i', $signature)) {
continue;
}

return $expr;
$configurationFound = $config;

break;
}

return null;
return $configurationFound;
}

protected function metadataPostTreatment(ClassMetadata $metadata)
{
if (!$metadata->methodMetadata) {
$metadata = null;
}

return $metadata;
}

// TODO: Is it feasible to reverse-engineer the notation for service controllers?
Expand All @@ -81,7 +156,13 @@ 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)) {
$matched = preg_match(
'#\\\\Controller\\\\([^\\\\]+)Controller::(.+)Action$#',
$signature,
$match
);

if (!$matched) {
return null;
}

Expand All @@ -96,4 +177,31 @@ private function getControllerNotation(\ReflectionMethod $method)

return null;
}
}

private function assertParamExistsForMethod(array $params, $name, $method)
{
if (!isset($params[$name])) {
throw new \InvalidArgumentException(
sprintf(
'The parameter "%s" does not exist for method "%s".',
$name,
$method
)
);
}
}

private function setConfigs(array $config)
{
$this->config = array();
foreach ($config as $key => $value) {
if (is_string($value)) {
$this->config[$key] = array(
'pre_authorize' => $value
);
} else {
$this->config[$key] = $value;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,4 @@ public function getName()
{
return $this->functionName;
}
}
}
9 changes: 7 additions & 2 deletions Tests/DependencyInjection/JMSSecurityExtraExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,12 @@ public function testConfigLoadWithMethodAccessControl()
)
)), $container = $this->getContainer());

$this->assertEquals(array(':login$' => 'hasRole("FOO")'),
$this->assertEquals(
array(
':login$' => array(
'pre_authorize' => 'hasRole("FOO")',
)
),
$container->getParameter('security.access.method_access_control'));
}

Expand All @@ -71,7 +76,7 @@ public function testConfigLoadThrowsExceptionWhenMethodAccessControlWithoutExpre
{
$this->extension->load(array(array(
'expressions' => false,
'method_access_control' => array('foo' => 'bar'),
'method_access_control' => array('foo' => 'bar', 'FOO' => 'BAR'),
)), $this->getContainer());
}

Expand Down
Loading