Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 4401f34

Browse files
author
Thomas Rabaix
committedMay 22, 2011
Start integrating the security component, small tweak
1 parent 278c0dc commit 4401f34

File tree

9 files changed

+327
-29
lines changed

9 files changed

+327
-29
lines changed
 

‎Builder/ORM/FormContractor.php

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,21 @@ class FormContractor implements FormContractorInterface
3838
* @var array
3939
*/
4040
protected $formTypes = array(
41-
'string' => 'text',
42-
'text' => 'textarea',
43-
'boolean' => 'checkbox',
44-
'checkbox' => 'checkbox',
45-
'integer' => 'integer',
46-
'tinyint' => 'integer',
47-
'smallint' => 'integer',
48-
'mediumint' => 'integer',
49-
'bigint' => 'integer',
50-
'decimal' => 'number',
51-
'datetime' => 'datetime',
52-
'date' => 'date',
53-
'choice' => 'choice',
54-
'array' => 'collection',
55-
'country' => 'country',
41+
'string' => array('text', array()),
42+
'text' => array('textarea', array()),
43+
'boolean' => array('checkbox', array()),
44+
'checkbox' => array('checkbox', array()),
45+
'integer' => array('integer', array()),
46+
'tinyint' => array('integer', array()),
47+
'smallint' => array('integer', array()),
48+
'mediumint' => array('integer', array()),
49+
'bigint' => array('integer', array()),
50+
'decimal' => array('number', array()),
51+
'datetime' => array('datetime', array()),
52+
'date' => array('date', array()),
53+
'choice' => array('choice', array()),
54+
'array' => array('collection', array()),
55+
'country' => array('country', array()),
5656
);
5757

5858
public function __construct(FormFactoryInterface $formFactory)
@@ -231,10 +231,11 @@ public function addField(FormBuilder $formBuilder, FieldDescriptionInterface $fi
231231
break;
232232

233233
default:
234+
list($type, $default_options) = $this->getFormTypeName($fieldDescription);
234235
$formBuilder->add(
235236
$fieldDescription->getFieldName(),
236-
$this->getFormTypeName($fieldDescription),
237-
$fieldDescription->getOption('form_field_options', array())
237+
$type,
238+
array_merge($default_options, $fieldDescription->getOption('form_field_options', array()))
238239
);
239240
}
240241
}

‎Command/DumpActionRolesCommand.php

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Sonata package.
5+
*
6+
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Sonata\AdminBundle\Command;
13+
14+
use Symfony\Bundle\FrameworkBundle\Command\Command;
15+
use Symfony\Component\Console\Input\InputArgument;
16+
use Symfony\Component\Console\Input\InputOption;
17+
use Symfony\Component\Console\Input\InputInterface;
18+
use Symfony\Component\Console\Output\OutputInterface;
19+
use Symfony\Component\Console\Output\Output;
20+
21+
use Symfony\Component\Routing\RouteCollection;
22+
use Symfony\Component\Routing\Route;
23+
use Symfony\Component\Config\Resource\FileResource;
24+
25+
class DumpActionRolesCommand extends Command
26+
{
27+
28+
public function configure()
29+
{
30+
$this->setName('sonata:admin:dump-action-roles');
31+
$this->setDescription('');
32+
$this->addOption('format', null, InputOption::VALUE_OPTIONAL, 'define the output format (default: yaml)', 'yaml');
33+
$this->addOption('prefix', null, InputOption::VALUE_OPTIONAL, 'define the admin route prefix (default: /admin)', '/admin');
34+
}
35+
36+
public function execute(InputInterface $input, OutputInterface $output)
37+
{
38+
$infos = array();
39+
foreach ($this->getAdminRoutesCollection($input->getOption('prefix'))->all() as $route) {
40+
$compiledRoute = $route->compile();
41+
42+
$regex = str_replace(array("\n", ' '), '', $compiledRoute->getRegex());
43+
if ($pos = strpos($regex, '/$')) {
44+
$regex = substr($regex, 0, $pos).'/?$'.substr($regex, $pos + 2);
45+
}
46+
47+
$defaults = $route->getDefaults();
48+
49+
$controllerInfos = explode(':', $defaults['_controller']);
50+
51+
$group = strtoupper(sprintf('ROLE_%s', str_replace(array('.','|'), '_', strtoupper($defaults['_sonata_admin']))));
52+
if (!isset($infos[$group])) {
53+
$infos[$group] = array();
54+
}
55+
56+
$name = strtoupper(sprintf('ROLE_%s_%s',
57+
str_replace(array('.','|'), '_', strtoupper($defaults['_sonata_admin'])),
58+
$controllerInfos[2]
59+
));
60+
61+
$infos[$group][] = array(
62+
'path' => substr($regex, 1, -2),
63+
'roles' => $name
64+
);
65+
}
66+
67+
$this->dumpYaml($output, $infos);
68+
}
69+
70+
public function dumpYaml(OutputInterface $output, array $infos)
71+
{
72+
73+
$output->writeln('sonata_admin:');
74+
$output->writeln(' access_control:');
75+
foreach ($infos as $groups) {
76+
foreach ($groups as $group) {
77+
$output->writeln(sprintf(' - { path: %s, roles: [%s], methods: null }', $group['path'], $group['roles']));
78+
}
79+
}
80+
81+
$output->writeln('');
82+
$output->writeln(' role_hierarchy:');
83+
84+
$superAdmin = array();
85+
foreach ($infos as $groupName => $groups) {
86+
$roles = array();
87+
foreach ($groups as $group) {
88+
$roles[] = $group['roles'];
89+
}
90+
$output->writeln(sprintf(' %s: [%s] ', $groupName, implode(', ', $roles)));
91+
92+
$superAdmin[] = $groupName;
93+
}
94+
95+
$output->writeln(sprintf(' ROLE_SONATA_ADMIN_ROOT: [%s] ', implode(', ', $superAdmin)));
96+
}
97+
98+
public function getAdminRoutesCollection($prefix)
99+
{
100+
$pool = $this->container->get('sonata.admin.pool');
101+
$collection = new RouteCollection;
102+
103+
foreach ($pool->getAdminServiceIds() as $id) {
104+
105+
$admin = $pool->getInstance($id);
106+
107+
foreach ($admin->getRoutes()->getElements() as $code => $route) {
108+
$collection->add($route->getDefault('_sonata_name'), $route);
109+
}
110+
111+
$reflection = new \ReflectionObject($admin);
112+
$collection->addResource(new FileResource($reflection->getFileName()));
113+
}
114+
115+
$collection->addPrefix($prefix);
116+
return $collection;
117+
}
118+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Sonata project.
5+
*
6+
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Sonata\AdminBundle\DependencyInjection;
13+
14+
use Symfony\Component\DependencyInjection\Definition;
15+
use Symfony\Component\DependencyInjection\ContainerBuilder;
16+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
17+
use Symfony\Component\DependencyInjection\Reference;
18+
use Symfony\Component\DependencyInjection\ContainerInterface;
19+
20+
/**
21+
* This code append Admin security roles
22+
*
23+
* @author Thomas Rabaix <thomas.rabaix@sonata-project.org>
24+
* @author Fabien Potencier <fabien@symfony.com>
25+
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
26+
*/
27+
class AddSecurityCallsPass implements CompilerPassInterface
28+
{
29+
/**
30+
* {@inheritDoc}
31+
*/
32+
public function process(ContainerBuilder $container)
33+
{
34+
$definition = $container->getDefinition('sonata_dummy_security');
35+
$container->removeDefinition('sonata_dummy_security');
36+
37+
$config = $definition->getArguments();
38+
39+
$this->createAuthorization($config, $container);
40+
$this->createRoleHierarchy($config, $container);
41+
}
42+
43+
private function createRoleHierarchy($config, ContainerBuilder $container)
44+
{
45+
if (!isset($config['role_hierarchy'])) {
46+
$container->removeDefinition('security.access.role_hierarchy_voter');
47+
48+
return;
49+
}
50+
51+
$parameters = (array) $container->getParameter('security.role_hierarchy.roles');
52+
53+
$container->setParameter('security.role_hierarchy.roles', array_merge($parameters, $config['role_hierarchy']));
54+
$container->removeDefinition('security.access.simple_role_voter');
55+
}
56+
57+
private function createAuthorization($config, ContainerBuilder $container)
58+
{
59+
if (!$config['access_control']) {
60+
return;
61+
}
62+
63+
foreach ($config['access_control'] as $access) {
64+
$matcher = $this->createRequestMatcher(
65+
$container,
66+
$access['path'],
67+
$access['host'],
68+
count($access['methods']) === 0 ? null : $access['methods'],
69+
$access['ip']
70+
);
71+
72+
$container->getDefinition('security.access_map')
73+
->addMethodCall('add', array($matcher, $access['roles'], $access['requires_channel']));
74+
}
75+
}
76+
77+
private function createRequestMatcher($container, $path = null, $host = null, $methods = null, $ip = null, array $attributes = array())
78+
{
79+
$serialized = serialize(array($path, $host, $methods, $ip, $attributes));
80+
$id = 'security.request_matcher.'.md5($serialized).sha1($serialized);
81+
82+
if (isset($this->requestMatchers[$id])) {
83+
return $this->requestMatchers[$id];
84+
}
85+
86+
// only add arguments that are necessary
87+
$arguments = array($path, $host, $methods, $ip, $attributes);
88+
while (count($arguments) > 0 && !end($arguments)) {
89+
array_pop($arguments);
90+
}
91+
92+
$container
93+
->register($id, '%security.matcher.class%')
94+
->setPublic(false)
95+
->setArguments($arguments)
96+
;
97+
98+
return $this->requestMatchers[$id] = new Reference($id);
99+
}
100+
101+
}

‎DependencyInjection/Configuration.php

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111

1212
namespace Sonata\AdminBundle\DependencyInjection;
1313

14-
use Symfony\Component\Config\Definition\Builder;
15-
16-
use Symfony\Component\Config\Definition\Builder\NodeBuilder;
1714
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
15+
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
16+
use Symfony\Component\Config\Definition\ConfigurationInterface;
17+
1818

1919
/**
2020
* This class contains the configuration information for the bundle
@@ -36,6 +36,15 @@ public function getConfigTree($kernelDebug)
3636
$treeBuilder = new TreeBuilder();
3737
$rootNode = $treeBuilder->root('sonata_admin', 'array');
3838

39+
$this->addTemplateSection($rootNode);
40+
$this->addAccessControlSection($rootNode);
41+
$this->addRoleHierarchySection($rootNode);
42+
43+
return $treeBuilder->buildTree();
44+
}
45+
46+
private function addTemplateSection(ArrayNodeDefinition $rootNode)
47+
{
3948
$rootNode
4049
->children()
4150
->arrayNode('templates')
@@ -46,7 +55,57 @@ public function getConfigTree($kernelDebug)
4655
->end()
4756
->end()
4857
->end();
58+
}
4959

50-
return $treeBuilder->buildTree();
60+
private function addAccessControlSection(ArrayNodeDefinition $rootNode)
61+
{
62+
$rootNode
63+
->fixXmlConfig('rule', 'access_control')
64+
->children()
65+
->arrayNode('access_control')
66+
->cannotBeOverwritten()
67+
->prototype('array')
68+
->children()
69+
->scalarNode('requires_channel')->defaultNull()->end()
70+
->scalarNode('path')->defaultNull()->end()
71+
->scalarNode('host')->defaultNull()->end()
72+
->scalarNode('ip')->defaultNull()->end()
73+
->arrayNode('methods')
74+
->beforeNormalization()->ifString()->then(function($v) { return preg_split('/\s*,\s*/', $v); })->end()
75+
->prototype('scalar')->end()
76+
->end()
77+
->end()
78+
->fixXmlConfig('role')
79+
->children()
80+
->arrayNode('roles')
81+
->beforeNormalization()->ifString()->then(function($v) { return preg_split('/\s*,\s*/', $v); })->end()
82+
->prototype('scalar')->end()
83+
->end()
84+
->end()
85+
->end()
86+
->end()
87+
->end()
88+
;
89+
}
90+
91+
private function addRoleHierarchySection(ArrayNodeDefinition $rootNode)
92+
{
93+
$rootNode
94+
->fixXmlConfig('role', 'role_hierarchy')
95+
->children()
96+
->arrayNode('role_hierarchy')
97+
->useAttributeAsKey('id')
98+
->prototype('array')
99+
->performNoDeepMerging()
100+
->beforeNormalization()->ifString()->then(function($v) { return array('value' => $v); })->end()
101+
->beforeNormalization()
102+
->ifTrue(function($v) { return is_array($v) && isset($v['value']); })
103+
->then(function($v) { return preg_split('/\s*,\s*/', $v['value']); })
104+
->end()
105+
->prototype('scalar')->end()
106+
->end()
107+
->end()
108+
->end()
109+
;
51110
}
52111
}

‎DependencyInjection/SonataAdminExtension.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ class SonataAdminExtension extends Extension
3737
)
3838
);
3939

40+
protected $requestMatchers = array();
41+
4042
/**
4143
*
4244
* @param array $configs An array of configuration settings
@@ -57,6 +59,8 @@ public function load(array $configs, ContainerBuilder $container)
5759

5860
// setups parameters with values in config.yml, default values from external files used if not
5961
$this->configSetupTemplates($config, $container);
62+
63+
$container->setDefinition('sonata_dummy_security', new Definition('stdClass', $config));
6064
}
6165

6266
protected function configSetupTemplates($config, $container)

‎Form/FormMapper.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,4 +190,15 @@ public function getAdmin()
190190
{
191191
return $this->admin;
192192
}
193+
194+
/**
195+
* @param string $name
196+
* @param mixed $type
197+
* @param array $options
198+
* @return void
199+
*/
200+
public function create($name, $type = null, array $options = array())
201+
{
202+
return $this->formBuilder->create($name, $type, $options);
203+
}
193204
}

0 commit comments

Comments
 (0)
Please sign in to comment.