diff -r 000000000000 -r 7f95f8617b0b vendor/bundles/JMS/SecurityExtraBundle/DependencyInjection/Compiler/SecureMethodInvocationsPass.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/bundles/JMS/SecurityExtraBundle/DependencyInjection/Compiler/SecureMethodInvocationsPass.php Sat Sep 24 15:40:41 2011 +0200 @@ -0,0 +1,193 @@ + + * + * 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\Analysis\ServiceAnalyzer; +use JMS\SecurityExtraBundle\Metadata\ServiceMetadata; +use JMS\SecurityExtraBundle\Metadata\ClassMetadata; +use JMS\SecurityExtraBundle\Generator\ProxyClassGenerator; +use JMS\SecurityExtraBundle\Metadata\Driver\DriverChain; +use \ReflectionClass; +use \ReflectionMethod; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\Security\Core\SecurityContext; + +/** + * Modifies the container, and sets the proxy classes where needed + * + * @author Johannes M. Schmitt + */ +class SecureMethodInvocationsPass implements CompilerPassInterface +{ + private $cacheDir; + private $generator; + private $cacheMetadata; + + public function __construct($cacheDir) + { + $cacheDir .= '/security/'; + if (!file_exists($cacheDir)) { + @mkdir($cacheDir, 0777, true); + } + if (false === is_writable($cacheDir)) { + throw new \RuntimeException('Cannot write to cache folder: '.$cacheDir); + } + $this->cacheDir = $cacheDir; + + if (!file_exists($cacheDir.'SecurityProxies/')) { + @mkdir($cacheDir.'SecurityProxies/', 0777, true); + } + if (false === is_writeable($cacheDir.'SecurityProxies/')) { + throw new \RuntimeException('Cannot write to cache folder: '.$cacheDir.'SecurityProxies/'); + } + + $this->generator = new ProxyClassGenerator(); + $this->createOrLoadCacheMetadata(); + } + + /** + * {@inheritDoc} + */ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('security.access.method_interceptor')) { + return; + } + + $services = $container->findTaggedServiceIds('security.secure_service'); + $secureNotAll = !$container->getParameter('security.extra.secure_all_services'); + + $parameterBag = $container->getParameterBag(); + foreach ($container->getDefinitions() as $id => $definition) { + if ($secureNotAll && !isset($services[$id])) { + continue; + } + + if ((null === $class = $definition->getClass()) || !class_exists($class)) { + if ($secureNotAll) { + throw new \RuntimeException(sprintf('Could not find class "%s" for "%s".', $class, $id)); + } + + continue; + } + + if (null !== $definition->getFactoryMethod() || $definition->isAbstract() || $definition->isSynthetic()) { + if ($secureNotAll) { + throw new \RuntimeException(sprintf('You cannot secure service "%s", because it is either created by a factory, or an abstract/synthetic service.', $id)); + } + + continue; + } + + $this->processDefinition($container, $id, $definition); + } + + $this->writeCacheMetadata(); + } + + private function processDefinition(ContainerBuilder $container, $id, Definition $definition) + { + if ($this->needsReAssessment($id, $definition)) { + $analyzer = new ServiceAnalyzer($definition->getClass(), $container->get('annotation_reader')); + $analyzer->analyze(); + + $files = array(); + foreach ($analyzer->getFiles() as $file) { + $container->addResource($file = new FileResource($file)); + $files[] = $file; + } + + $metadata = $analyzer->getMetadata(); + $proxyClass = $path = null; + if (true === $metadata->isProxyRequired()) { + list($newClassName, $content) = $this->generator->generate($definition, $metadata); + file_put_contents($path = $this->cacheDir.'SecurityProxies/'.$newClassName.'.php', $content); + $definition->setFile($path); + $definition->setClass($proxyClass = 'SecurityProxies\\'.$newClassName); + $definition->addMethodCall('jmsSecurityExtraBundle__setMethodSecurityInterceptor', array(new Reference('security.access.method_interceptor'))); + } else if (isset($this->cacheMetadata[$id]['proxy_class'])) { + @unlink($this->cacheDir.$this->cacheMetadata[$id]['proxy_class'].'.php'); + } + + $this->cacheMetadata[$id] = array( + 'class' => $definition->getClass(), + 'proxy_class' => $proxyClass, + 'proxy_file' => $path, + 'analyze_time' => time(), + 'files' => $files, + ); + } else { + foreach ($this->cacheMetadata[$id]['files'] as $file) { + $container->addResource($file); + } + + if (null !== $proxyClass = $this->cacheMetadata[$id]['proxy_class']) { + $definition->setFile($this->cacheMetadata[$id]['proxy_file']); + $definition->setClass($proxyClass); + $definition->addMethodCall('jmsSecurityExtraBundle__setMethodSecurityInterceptor', array(new Reference('security.access.method_interceptor'))); + } + } + } + + private function needsReAssessment($id, Definition $definition) + { + if (!isset($this->cacheMetadata[$id])) { + return true; + } + + $metadata = $this->cacheMetadata[$id]; + if ($metadata['class'] !== $definition->getClass()) { + return true; + } + + $lastAnalyzed = $metadata['analyze_time']; + foreach ($metadata['files'] as $file) { + if (false === $file->isFresh($lastAnalyzed)) { + return true; + } + } + + return false; + } + + private function createOrLoadCacheMetadata() + { + if (file_exists($this->cacheDir.'cache.meta')) { + if (!is_readable($this->cacheDir.'cache.meta')) { + throw new \RuntimeException('Cannot load security cache meta data from: '.$this->cacheDir.'cache.meta'); + } + + $this->cacheMetadata = unserialize(file_get_contents($this->cacheDir.'cache.meta')); + } else { + set_time_limit(0); + $this->cacheMetadata = array(); + } + } + + private function writeCacheMetadata() + { + if (false === file_put_contents($this->cacheDir.'cache.meta', serialize($this->cacheMetadata))) { + throw new \RuntimeException('Could not write to cache file: '.$this->cacheDir.'cache.meta'); + } + } +} \ No newline at end of file