AnnotationDriver.php 5.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. <?php
  2. /*
  3. * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. namespace JMS\SecurityExtraBundle\Metadata\Driver;
  18. use JMS\SecurityExtraBundle\Exception\InvalidArgumentException;
  19. use Doctrine\Common\Annotations\Reader;
  20. use JMS\SecurityExtraBundle\Annotation\PreAuthorize;
  21. use JMS\SecurityExtraBundle\Annotation\RunAs;
  22. use JMS\SecurityExtraBundle\Annotation\SatisfiesParentSecurityPolicy;
  23. use JMS\SecurityExtraBundle\Annotation\Secure;
  24. use JMS\SecurityExtraBundle\Annotation\SecureParam;
  25. use JMS\SecurityExtraBundle\Annotation\SecureReturn;
  26. use JMS\SecurityExtraBundle\Metadata\ClassMetadata;
  27. use JMS\SecurityExtraBundle\Metadata\MethodMetadata;
  28. use Metadata\Driver\DriverInterface;
  29. use \ReflectionClass;
  30. use \ReflectionMethod;
  31. use JMS\SecurityExtraBundle\Security\Authorization\Expression\Expression;
  32. /**
  33. * Loads security annotations and converts them to metadata
  34. *
  35. * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  36. */
  37. class AnnotationDriver implements DriverInterface
  38. {
  39. private $reader;
  40. public function __construct(Reader $reader)
  41. {
  42. $this->reader = $reader;
  43. }
  44. public function loadMetadataForClass(ReflectionClass $reflection)
  45. {
  46. $metadata = new ClassMetadata($reflection->name);
  47. $classPreAuthorize = $this->reader->getClassAnnotation($reflection, 'JMS\SecurityExtraBundle\Annotation\PreAuthorize');
  48. foreach ($reflection->getMethods(ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED) as $method) {
  49. // check if the method was defined on this class
  50. if ($method->getDeclaringClass()->name !== $reflection->name) {
  51. continue;
  52. }
  53. $annotations = $this->reader->getMethodAnnotations($method);
  54. if (! $annotations && ! $classPreAuthorize) {
  55. continue;
  56. }
  57. if (null !== $methodMetadata = $this->convertMethodAnnotations($method, $annotations, $classPreAuthorize)) {
  58. $metadata->addMethodMetadata($methodMetadata);
  59. }
  60. }
  61. return $metadata;
  62. }
  63. private function convertMethodAnnotations(\ReflectionMethod $method, array $annotations, PreAuthorize $classPreAuthorize = null)
  64. {
  65. $parameters = array();
  66. foreach ($method->getParameters() as $index => $parameter) {
  67. $parameters[$parameter->getName()] = $index;
  68. }
  69. $methodMetadata = new MethodMetadata($method->class, $method->name);
  70. $hasSecurityMetadata = $hasPreRestrictions = false;
  71. foreach ($annotations as $annotation) {
  72. if ($annotation instanceof Secure) {
  73. $methodMetadata->roles = $annotation->roles;
  74. $hasSecurityMetadata = $hasPreRestrictions = true;
  75. } elseif ($annotation instanceof PreAuthorize) {
  76. $methodMetadata->roles = array(new Expression($annotation->expr));
  77. $hasSecurityMetadata = $hasPreRestrictions = true;
  78. } elseif ($annotation instanceof SecureParam) {
  79. if (!isset($parameters[$annotation->name])) {
  80. throw new InvalidArgumentException(sprintf('The parameter "%s" does not exist for method "%s".', $annotation->name, $method->name));
  81. }
  82. $methodMetadata->addParamPermissions($parameters[$annotation->name], $annotation->permissions);
  83. $hasSecurityMetadata = $hasPreRestrictions = true;
  84. } elseif ($annotation instanceof SecureReturn) {
  85. $methodMetadata->returnPermissions = $annotation->permissions;
  86. $hasSecurityMetadata = true;
  87. } elseif ($annotation instanceof SatisfiesParentSecurityPolicy) {
  88. $methodMetadata->satisfiesParentSecurityPolicy = true;
  89. $hasSecurityMetadata = true;
  90. } elseif ($annotation instanceof RunAs) {
  91. $methodMetadata->runAsRoles = $annotation->roles;
  92. $hasSecurityMetadata = true;
  93. }
  94. }
  95. // We use the following conditions to determine whether we should apply
  96. // a class-level @PreAuthorize annotation:
  97. //
  98. // - No other authorization that runs before the method invocation
  99. // must be configured. @Secure, @SecureParam, @PreAuthorize must
  100. // not be present; @SecureReturn would be fine though.
  101. //
  102. // - The method must be public, or alternatively publicOnly on
  103. // @PreAuthorize must be set to false.
  104. if ( ! $hasPreRestrictions && $classPreAuthorize
  105. && (!$classPreAuthorize->publicOnly || !$method->isProtected())) {
  106. $methodMetadata->roles = array(new Expression($classPreAuthorize->expr));
  107. $hasSecurityMetadata = true;
  108. }
  109. return $hasSecurityMetadata ? $methodMetadata : null;
  110. }
  111. }