Permalink
Browse files

first commit

  • Loading branch information...
0 parents commit dbe14caa9dba216006cf93febf253af28392acaf David Joos committed Dec 5, 2011
@@ -0,0 +1,25 @@
+<?php
+
+namespace Escape\WSSEAuthenticationBundle\DependencyInjection;
+
+use Symfony\Component\Config\Definition\Builder\TreeBuilder;
+use Symfony\Component\Config\Definition\ConfigurationInterface;
+
+class Configuration implements ConfigurationInterface
+{
+ public function getConfigTreeBuilder()
+ {
+ $treeBuilder = new TreeBuilder();
+ $rootNode = $treeBuilder->root('escape_wsseauthentication');
+
+ $rootNode
+ ->children()
+ ->scalarNode('provider_class')->defaultValue('Escape\WSSEAuthenticationBundle\Security\Authentication\Provider\Provider')->end()
+ ->scalarNode('listener_class')->defaultValue('Escape\WSSEAuthenticationBundle\Security\Firewall\Listener')->end()
+ ->scalarNode('factory_class')->defaultValue('Escape\WSSEAuthenticationBundle\Security\Factory\WSSEFactory')->end()
+ ->end()
+ ;
+
+ return $treeBuilder;
+ }
+}
@@ -0,0 +1,27 @@
+<?php
+
+namespace Escape\WSSEAuthenticationBundle\DependencyInjection;
+
+use Symfony\Component\HttpKernel\DependencyInjection\Extension;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
+use Symfony\Component\Config\FileLocator;
+
+class EscapeWSSEAuthenticationExtension extends Extension
+{
+ public function load(array $configs, ContainerBuilder $container)
+ {
+ $configuration = new Configuration();
+ $config = $this->processConfiguration($configuration, $configs);
+
+ $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
+ $loader->load('services.yml');
+
+ $container->setParameter('wsseauthentication.provider.class', $config['provider_class']);
+ $container->setParameter('wsseauthentication.listener.class', $config['listener_class']);
+ //$container->setParameter('wsseauthentication.factory.class', $config['factory_class']);
+
+ $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
+ $loader->load('services.yml');
+ }
+}
@@ -0,0 +1,9 @@
+<?php
+
+namespace Escape\WSSEAuthenticationBundle;
+
+use Symfony\Component\HttpKernel\Bundle\Bundle;
+
+class EscapeWSSEAuthenticationBundle extends Bundle
+{
+}
@@ -0,0 +1,52 @@
+## Introduction
+
+The EscapeRackspaceCloudFiles bundle is a simple and easy way to use the namespaced version of php-cloudfiles with Symfony2 applications
+
+## Installation
+
+app/autoload.php
+
+```
+$loader->registerNamespaces(array(
+ //other namespaces
+ 'Escape' => __DIR__.'/../vendor/bundles',
+ ));
+```
+
+app/AppKernel.php
+
+```
+public function registerBundles()
+{
+ return array(
+ //other bundles
+ new Escape\WSSEAuthenticationBundle\EscapeWSSEAuthenticationBundle(),
+ );
+ ...
+```
+
+## Configuration
+
+app/config/config.yml
+
+```
+# Escape Rackspace Cloud Files configuration
+escape_wsseauthentication:
+ provider_class: Escape\WSSEAuthenticationBundle\Security\Authentication\Provider\Provider
+ listener_class: Escape\WSSEAuthenticationBundle\Security\Firewall\Listener
+ factory_class: Escape\WSSEAuthenticationBundle\Security\Factory\WSSEFactory
+```
+
+## Usage example
+
+app/config/security.yml
+
+```
+firewalls:
+ wsse_secured:
+ pattern: ^/api/.*
+ wsse: true
+
+factories:
+ - "%kernel.root_dir%/../vendor/bundles/Escape/WSSEAuthenticationBundle/Resources/config/security_factories.yml"
+```
@@ -0,0 +1,5 @@
+services:
+ security.authentication.factory.wsse:
+ class: %wsseauthentication.factory.class%
+ tags:
+ - { name: security.listener.factory }
@@ -0,0 +1,8 @@
+services:
+ wsse.security.authentication.provider:
+ class: %wsseauthentication.provider.class%
+ arguments: ['', %kernel.cache_dir%/security/nonces]
+
+ wsse.security.authentication.listener:
+ class: %wsseauthentication.listener.class%
+ arguments: [@security.context, @security.authentication.manager]
@@ -0,0 +1,63 @@
+<?php
+
+namespace Escape\WSSEAuthenticationBundle\Security\Authentication\Provider;
+
+use Escape\WSSEAuthenticationBundle\Security\Authentication\Token\UserToken;
+
+use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
+use Symfony\Component\Security\Core\User\UserProviderInterface;
+use Symfony\Component\Security\Core\Exception\AuthenticationException;
+use Symfony\Component\Security\Core\Exception\NonceExpiredException;
+use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
+
+class Provider implements AuthenticationProviderInterface
+{
+ private $userProvider;
+ private $cacheDir;
+
+ public function __construct(UserProviderInterface $userProvider, $cacheDir)
+ {
+ $this->userProvider = $userProvider;
+ $this->cacheDir = $cacheDir;
+ }
+
+ public function authenticate(TokenInterface $token)
+ {
+ $user = $this->userProvider->loadUserByUsername($token->getUsername());
+
+ if($user && $this->validateDigest($token->digest, $token->nonce, $token->created, $user->getPassword()))
+ {
+ $authenticatedToken = new UserToken($user->getRoles());
+ $authenticatedToken->setUser($user);
+
+ return $authenticatedToken;
+ }
+
+ throw new AuthenticationException('The WSSE authentication failed.');
+ }
+
+ protected function validateDigest($digest, $nonce, $created, $secret)
+ {
+ //expire timestamp after 5 minutes
+ if(time() - strtotime($created) > 300)
+ return false;
+
+// //validate nonce is unique within 5 minutes
+// if(file_exists($this->cacheDir.'/'.$nonce) && file_get_contents($this->cacheDir.'/'.$nonce) + 300 < time())
+// throw new NonceExpiredException('Previously used nonce detected');
+
+//@todo store nonces
+// file_put_contents($this->cacheDir.'/'.$nonce, time());
+
+ //validate secret
+ //$expected = base64_encode(sha1(base64_decode($nonce.$created.$secret)/* .$created.$secret */, true));
+ $expected = base64_encode(sha1(base64_decode($nonce).$created.$secret, true));
+
+ return $digest === $expected;
+ }
+
+ public function supports(TokenInterface $token)
+ {
+ return $token instanceof UserToken;
+ }
+}
@@ -0,0 +1,17 @@
+<?php
+
+namespace Escape\WSSEAuthenticationBundle\Security\Authentication\Token;
+
+use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;
+
+class UserToken extends AbstractToken
+{
+ public $created;
+ public $digest;
+ public $nonce;
+
+ public function getCredentials()
+ {
+ return '';
+ }
+}
@@ -0,0 +1,39 @@
+<?php
+
+namespace Escape\WSSEAuthenticationBundle\Security\Factory;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\DependencyInjection\DefinitionDecorator;
+use Symfony\Component\Config\Definition\Builder\NodeDefinition;
+use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface;
+
+class WSSEFactory implements SecurityFactoryInterface
+{
+ public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
+ {
+ $providerId = 'security.authentication.provider.wsse.'.$id;
+ $container
+ ->setDefinition($providerId, new DefinitionDecorator('wsse.security.authentication.provider'))
+ ->replaceArgument(0, new Reference($userProvider));
+
+ $listenerId = 'security.authentication.listener.wsse.'.$id;
+ $listener = $container->setDefinition($listenerId, new DefinitionDecorator('wsse.security.authentication.listener'));
+
+ return array($providerId, $listenerId, $defaultEntryPoint);
+ }
+
+ public function getPosition()
+ {
+ return 'pre_auth';
+ }
+
+ public function getKey()
+ {
+ return 'wsse';
+ }
+
+ public function addConfiguration(NodeDefinition $node)
+ {
+ }
+}
@@ -0,0 +1,71 @@
+<?php
+
+namespace Escape\WSSEAuthenticationBundle\Security\Firewall;
+
+use Escape\WSSEAuthenticationBundle\Security\Authentication\Token\UserToken;
+
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\Security\Http\Firewall\ListenerInterface;
+use Symfony\Component\Security\Core\Exception\AuthenticationException;
+use Symfony\Component\Security\Core\SecurityContextInterface;
+use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
+use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
+
+class Listener implements ListenerInterface
+{
+ protected $securityContext;
+ protected $authenticationManager;
+
+ public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager)
+ {
+ $this->securityContext = $securityContext;
+ $this->authenticationManager = $authenticationManager;
+ }
+
+ public function handle(GetResponseEvent $event)
+ {
+ $request = $event->getRequest();
+
+ if($request->headers->has('X-WSSE'))
+ {
+ $wsseRegex = '/UsernameToken Username="([^"]+)", PasswordDigest="([^"]+)", Nonce="([^"]+)", Created="([^"]+)"/';
+
+ if(preg_match($wsseRegex, $request->headers->get('X-WSSE'), $matches))
+ {
+ $token = new UserToken();
+ $token->setUser($matches[1]);
+
+ $token->digest = $matches[2];
+ $token->nonce = $matches[3];
+ $token->created = $matches[4];
+
+ try
+ {
+ $returnValue = $this->authenticationManager->authenticate($token);
+
+ if($returnValue instanceof TokenInterface)
+ return $this->securityContext->setToken($returnValue);
+ else if($returnValue instanceof Response)
+ return $event->setResponse($returnValue);
+ }
+ catch(AuthenticationException $e)
+ {
+ //you might want to log something here
+ }
+ }
+
+ $response = new Response();
+ $response->setStatusCode(403);//forbidden
+ $event->setResponse($response);
+ }
+ else
+ {
+ $response = new Response();
+ $response->setStatusCode(401);//unauthorized
+ $event->setResponse($response);
+
+ return;
+ }
+ }
+}

0 comments on commit dbe14ca

Please sign in to comment.