MainConfiguration.php 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. <?php
  2. /*
  3. * This file is part of the Symfony framework.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * This source file is subject to the MIT license that is bundled
  8. * with this source code in the file LICENSE.
  9. */
  10. namespace Symfony\Bundle\SecurityBundle\DependencyInjection;
  11. use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AbstractFactory;
  12. use Symfony\Component\Config\Definition\Builder\TreeBuilder;
  13. use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
  14. use Symfony\Component\Config\Definition\ConfigurationInterface;
  15. /**
  16. * This class contains the configuration information for the following tags:
  17. *
  18. * * security.config
  19. * * security.acl
  20. *
  21. * This information is solely responsible for how the different configuration
  22. * sections are normalized, and merged.
  23. *
  24. * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  25. */
  26. class MainConfiguration implements ConfigurationInterface
  27. {
  28. private $factories;
  29. /**
  30. * Constructor.
  31. *
  32. * @param array $factories
  33. */
  34. public function __construct(array $factories)
  35. {
  36. $this->factories = $factories;
  37. }
  38. /**
  39. * Generates the configuration tree builder.
  40. *
  41. * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder
  42. */
  43. public function getConfigTreeBuilder()
  44. {
  45. $tb = new TreeBuilder();
  46. $rootNode = $tb->root('security');
  47. $rootNode
  48. ->children()
  49. ->scalarNode('access_denied_url')->defaultNull()->end()
  50. ->scalarNode('session_fixation_strategy')->cannotBeEmpty()->defaultValue('migrate')->end()
  51. ->booleanNode('hide_user_not_found')->defaultTrue()->end()
  52. ->booleanNode('always_authenticate_before_granting')->defaultFalse()->end()
  53. ->arrayNode('access_decision_manager')
  54. ->addDefaultsIfNotSet()
  55. ->children()
  56. ->scalarNode('strategy')->defaultValue('affirmative')->end()
  57. ->booleanNode('allow_if_all_abstain')->defaultFalse()->end()
  58. ->booleanNode('allow_if_equal_granted_denied')->defaultTrue()->end()
  59. ->end()
  60. ->end()
  61. ->end()
  62. // add a faux-entry for factories, so that no validation error is thrown
  63. ->fixXmlConfig('factory', 'factories')
  64. ->children()
  65. ->arrayNode('factories')->ignoreExtraKeys()->end()
  66. ->end()
  67. ;
  68. $this->addAclSection($rootNode);
  69. $this->addEncodersSection($rootNode);
  70. $this->addProvidersSection($rootNode);
  71. $this->addFirewallsSection($rootNode, $this->factories);
  72. $this->addAccessControlSection($rootNode);
  73. $this->addRoleHierarchySection($rootNode);
  74. return $tb;
  75. }
  76. private function addAclSection(ArrayNodeDefinition $rootNode)
  77. {
  78. $rootNode
  79. ->children()
  80. ->arrayNode('acl')
  81. ->children()
  82. ->scalarNode('connection')->end()
  83. ->arrayNode('cache')
  84. ->addDefaultsIfNotSet()
  85. ->children()
  86. ->scalarNode('id')->end()
  87. ->scalarNode('prefix')->defaultValue('sf2_acl_')->end()
  88. ->end()
  89. ->end()
  90. ->scalarNode('provider')->end()
  91. ->arrayNode('tables')
  92. ->addDefaultsIfNotSet()
  93. ->children()
  94. ->scalarNode('class')->defaultValue('acl_classes')->end()
  95. ->scalarNode('entry')->defaultValue('acl_entries')->end()
  96. ->scalarNode('object_identity')->defaultValue('acl_object_identities')->end()
  97. ->scalarNode('object_identity_ancestors')->defaultValue('acl_object_identity_ancestors')->end()
  98. ->scalarNode('security_identity')->defaultValue('acl_security_identities')->end()
  99. ->end()
  100. ->end()
  101. ->arrayNode('voter')
  102. ->addDefaultsIfNotSet()
  103. ->children()
  104. ->booleanNode('allow_if_object_identity_unavailable')->defaultTrue()->end()
  105. ->end()
  106. ->end()
  107. ->end()
  108. ->end()
  109. ->end()
  110. ;
  111. }
  112. private function addRoleHierarchySection(ArrayNodeDefinition $rootNode)
  113. {
  114. $rootNode
  115. ->fixXmlConfig('role', 'role_hierarchy')
  116. ->children()
  117. ->arrayNode('role_hierarchy')
  118. ->useAttributeAsKey('id')
  119. ->prototype('array')
  120. ->performNoDeepMerging()
  121. ->beforeNormalization()->ifString()->then(function($v) { return array('value' => $v); })->end()
  122. ->beforeNormalization()
  123. ->ifTrue(function($v) { return is_array($v) && isset($v['value']); })
  124. ->then(function($v) { return preg_split('/\s*,\s*/', $v['value']); })
  125. ->end()
  126. ->prototype('scalar')->end()
  127. ->end()
  128. ->end()
  129. ->end()
  130. ;
  131. }
  132. private function addAccessControlSection(ArrayNodeDefinition $rootNode)
  133. {
  134. $rootNode
  135. ->fixXmlConfig('rule', 'access_control')
  136. ->children()
  137. ->arrayNode('access_control')
  138. ->cannotBeOverwritten()
  139. ->prototype('array')
  140. ->children()
  141. ->scalarNode('requires_channel')->defaultNull()->end()
  142. ->scalarNode('path')->defaultNull()->end()
  143. ->scalarNode('host')->defaultNull()->end()
  144. ->scalarNode('ip')->defaultNull()->end()
  145. ->arrayNode('methods')
  146. ->beforeNormalization()->ifString()->then(function($v) { return preg_split('/\s*,\s*/', $v); })->end()
  147. ->prototype('scalar')->end()
  148. ->end()
  149. ->end()
  150. ->fixXmlConfig('role')
  151. ->children()
  152. ->arrayNode('roles')
  153. ->beforeNormalization()->ifString()->then(function($v) { return preg_split('/\s*,\s*/', $v); })->end()
  154. ->prototype('scalar')->end()
  155. ->end()
  156. ->end()
  157. ->end()
  158. ->end()
  159. ->end()
  160. ;
  161. }
  162. private function addFirewallsSection(ArrayNodeDefinition $rootNode, array $factories)
  163. {
  164. $firewallNodeBuilder = $rootNode
  165. ->fixXmlConfig('firewall')
  166. ->children()
  167. ->arrayNode('firewalls')
  168. ->isRequired()
  169. ->requiresAtLeastOneElement()
  170. ->disallowNewKeysInSubsequentConfigs()
  171. ->useAttributeAsKey('name')
  172. ->prototype('array')
  173. ->children()
  174. ;
  175. $firewallNodeBuilder
  176. ->scalarNode('pattern')->end()
  177. ->booleanNode('security')->defaultTrue()->end()
  178. ->scalarNode('request_matcher')->end()
  179. ->scalarNode('access_denied_url')->end()
  180. ->scalarNode('access_denied_handler')->end()
  181. ->scalarNode('entry_point')->end()
  182. ->scalarNode('provider')->end()
  183. ->booleanNode('stateless')->defaultFalse()->end()
  184. ->scalarNode('context')->cannotBeEmpty()->end()
  185. ->arrayNode('logout')
  186. ->treatTrueLike(array())
  187. ->canBeUnset()
  188. ->children()
  189. ->scalarNode('path')->defaultValue('/logout')->end()
  190. ->scalarNode('target')->defaultValue('/')->end()
  191. ->scalarNode('success_handler')->end()
  192. ->booleanNode('invalidate_session')->defaultTrue()->end()
  193. ->end()
  194. ->fixXmlConfig('delete_cookie')
  195. ->children()
  196. ->arrayNode('delete_cookies')
  197. ->beforeNormalization()
  198. ->ifTrue(function($v) { return is_array($v) && is_int(key($v)); })
  199. ->then(function($v) { return array_map(function($v) { return array('name' => $v); }, $v); })
  200. ->end()
  201. ->useAttributeAsKey('name')
  202. ->prototype('array')
  203. ->children()
  204. ->scalarNode('path')->defaultNull()->end()
  205. ->scalarNode('domain')->defaultNull()->end()
  206. ->end()
  207. ->end()
  208. ->end()
  209. ->end()
  210. ->fixXmlConfig('handler')
  211. ->children()
  212. ->arrayNode('handlers')
  213. ->prototype('scalar')->end()
  214. ->end()
  215. ->end()
  216. ->end()
  217. ->arrayNode('anonymous')
  218. ->canBeUnset()
  219. ->children()
  220. ->scalarNode('key')->defaultValue(uniqid())->end()
  221. ->end()
  222. ->end()
  223. ->arrayNode('switch_user')
  224. ->canBeUnset()
  225. ->children()
  226. ->scalarNode('provider')->end()
  227. ->scalarNode('parameter')->defaultValue('_switch_user')->end()
  228. ->scalarNode('role')->defaultValue('ROLE_ALLOWED_TO_SWITCH')->end()
  229. ->end()
  230. ->end()
  231. ;
  232. $abstractFactoryKeys = array();
  233. foreach ($factories as $factoriesAtPosition) {
  234. foreach ($factoriesAtPosition as $factory) {
  235. $name = str_replace('-', '_', $factory->getKey());
  236. $factoryNode = $firewallNodeBuilder->arrayNode($name)
  237. ->canBeUnset()
  238. ;
  239. if ($factory instanceof AbstractFactory) {
  240. $abstractFactoryKeys[] = $name;
  241. }
  242. $factory->addConfiguration($factoryNode);
  243. }
  244. }
  245. // check for unreachable check paths
  246. $firewallNodeBuilder
  247. ->end()
  248. ->validate()
  249. ->ifTrue(function($v) {
  250. return true === $v['security'] && isset($v['pattern']) && !isset($v['request_matcher']);
  251. })
  252. ->then(function($firewall) use($abstractFactoryKeys) {
  253. foreach ($abstractFactoryKeys as $k) {
  254. if (!isset($firewall[$k]['check_path'])) {
  255. continue;
  256. }
  257. if (false !== strpos('/', $firewall[$k]['check_path']) && !preg_match('#'.$firewall['pattern'].'#', $firewall[$k]['check_path'])) {
  258. throw new \LogicException(sprintf('The check_path "%s" for login method "%s" is not matched by the firewall pattern "%s".', $firewall[$k]['check_path'], $k, $firewall['pattern']));
  259. }
  260. }
  261. return $firewall;
  262. })
  263. ->end()
  264. ;
  265. }
  266. private function addProvidersSection(ArrayNodeDefinition $rootNode)
  267. {
  268. $rootNode
  269. ->fixXmlConfig('provider')
  270. ->children()
  271. ->arrayNode('providers')
  272. ->disallowNewKeysInSubsequentConfigs()
  273. ->isRequired()
  274. ->requiresAtLeastOneElement()
  275. ->useAttributeAsKey('name')
  276. ->prototype('array')
  277. ->children()
  278. ->scalarNode('id')->end()
  279. ->arrayNode('entity')
  280. ->children()
  281. ->scalarNode('class')->isRequired()->cannotBeEmpty()->end()
  282. ->scalarNode('property')->defaultNull()->end()
  283. ->end()
  284. ->end()
  285. ->end()
  286. ->fixXmlConfig('provider')
  287. ->children()
  288. ->arrayNode('providers')
  289. ->beforeNormalization()
  290. ->ifString()
  291. ->then(function($v) { return preg_split('/\s*,\s*/', $v); })
  292. ->end()
  293. ->prototype('scalar')->end()
  294. ->end()
  295. ->end()
  296. ->fixXmlConfig('user')
  297. ->children()
  298. ->arrayNode('users')
  299. ->useAttributeAsKey('name')
  300. ->prototype('array')
  301. ->children()
  302. ->scalarNode('password')->defaultValue(uniqid())->end()
  303. ->arrayNode('roles')
  304. ->beforeNormalization()->ifString()->then(function($v) { return preg_split('/\s*,\s*/', $v); })->end()
  305. ->prototype('scalar')->end()
  306. ->end()
  307. ->end()
  308. ->end()
  309. ->end()
  310. ->end()
  311. ->end()
  312. ->end()
  313. ->end()
  314. ;
  315. }
  316. private function addEncodersSection(ArrayNodeDefinition $rootNode)
  317. {
  318. $rootNode
  319. ->fixXmlConfig('encoder')
  320. ->children()
  321. ->arrayNode('encoders')
  322. ->requiresAtLeastOneElement()
  323. ->useAttributeAsKey('class')
  324. ->prototype('array')
  325. ->canBeUnset()
  326. ->performNoDeepMerging()
  327. ->beforeNormalization()->ifString()->then(function($v) { return array('algorithm' => $v); })->end()
  328. ->children()
  329. ->scalarNode('algorithm')->cannotBeEmpty()->end()
  330. ->booleanNode('ignore_case')->defaultFalse()->end()
  331. ->booleanNode('encode_as_base64')->defaultTrue()->end()
  332. ->scalarNode('iterations')->defaultValue(5000)->end()
  333. ->scalarNode('id')->end()
  334. ->end()
  335. ->end()
  336. ->end()
  337. ->end()
  338. ;
  339. }
  340. }