Quellcode durchsuchen

Custom Authentication provider

Guillermo Espinoza vor 7 Jahren
Ursprung
Commit
271d2bb7e6

+ 11 - 0
AuthBundle.php

@@ -2,8 +2,19 @@
 
 namespace AuthBundle;
 
+use AuthBundle\DependencyInjection\Security\Factory\OAuthProxyFactory;
 use Symfony\Component\HttpKernel\Bundle\Bundle;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
 
 class AuthBundle extends Bundle
 {
+    
+    public function build(ContainerBuilder $container)
+    {
+        parent::build($container);
+
+        $extension = $container->getExtension('security');
+        $extension->addSecurityListenerFactory(new OAuthProxyFactory());
+    }
+
 }

+ 0 - 17
Controller/DefaultController.php

@@ -1,17 +0,0 @@
-<?php
-
-namespace AuthBundle\Controller;
-
-use Symfony\Bundle\FrameworkBundle\Controller\Controller;
-use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
-
-class DefaultController extends Controller
-{
-    /**
-     * @Route("/")
-     */
-    public function indexAction()
-    {
-        return $this->render('AuthBundle:Default:index.html.twig');
-    }
-}

+ 62 - 0
DependencyInjection/Security/Factory/OAuthProxyFactory.php

@@ -0,0 +1,62 @@
+<?php
+
+namespace AuthBundle\DependencyInjection\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 OAuthProxyFactory implements SecurityFactoryInterface
+{
+
+    /**
+     * 
+     * @param ContainerBuilder $container
+     * @param string $id
+     * @param type $config
+     * @param UserProviderInterface $userProvider
+     * @param type $defaultEntryPoint
+     * 
+     * @return array
+     */
+    public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
+    {
+        $providerId = 'security.authentication.provider.oauth_proxy.'.$id;
+        $container
+            ->setDefinition($providerId, new DefinitionDecorator('auth.oauth_proxy.provider'))
+            ->replaceArgument(0, new Reference($userProvider))
+        ;
+
+        $listenerId = 'security.authentication.listener.oauth_proxy.'.$id;
+        $listener = $container->setDefinition($listenerId, new DefinitionDecorator('auth.oauth_proxy.listener'));
+
+        return array($providerId, $listenerId, $defaultEntryPoint);
+    }
+
+    /**
+     * @return string
+     */
+    public function getPosition()
+    {
+        return 'pre_auth';
+    }
+
+    /**
+     * @return string
+     */
+    public function getKey()
+    {
+        return 'oauth_proxy';
+    }
+
+    /**
+     * @param NodeDefinition $node
+     */
+    public function addConfiguration(NodeDefinition $node)
+    {
+        
+    }
+
+}

+ 12 - 2
Resources/config/services.yml

@@ -1,5 +1,15 @@
 services:
     
-    auth_security_oauthproxyauthenticator:
-        class: AuthBundle\Security\OAuthProxyAuthenticator 
+    auth.access_token.service:
+        class: AuthBundle\Services\AccessTokenService
         arguments: [ '%client_id%', '%client_secret%', '%access_token_url%', '%infos_url%' ]
+        
+    auth.oauth_proxy.provider:
+        class: AuthBundle\Security\Authentication\Provider\OAuthProxyProvider
+        arguments: [ '@base_oauth_bundle.oauth_user_provider' ]
+        public: false
+        
+    auth.oauth_proxy.listener:
+        class: AuthBundle\Security\Firewall\OAuthProxyListener
+        arguments: [ '@security.token_storage', '@security.authentication.manager', '@auth.access_token.service' ]
+        public: false

+ 0 - 1
Resources/views/Default/index.html.twig

@@ -1 +0,0 @@
-Hello World!

+ 58 - 0
Security/Authentication/Provider/OAuthProxyProvider.php

@@ -0,0 +1,58 @@
+<?php
+
+namespace AuthBundle\Security\Authentication\Provider;
+
+use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
+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\Authentication\Token\TokenInterface;
+
+class OAuthProxyProvider implements AuthenticationProviderInterface
+{
+
+    /**
+     * @var UserProviderInterface 
+     */
+    private $userProvider;
+
+
+    /**
+     * @param UserProviderInterface $userProvider
+     */
+    public function __construct(UserProviderInterface $userProvider)
+    {
+        $this->userProvider = $userProvider;
+    }
+
+    /**
+     * @param TokenInterface $token
+     * 
+     * @return OAuthToken
+     * 
+     * @throws AuthenticationException
+     */
+    public function authenticate(TokenInterface $token)
+    {
+        $user = $token->getUser();   
+        if ($user) {
+            $authenticatedToken = new UsernamePasswordToken($user, null, "api", $user->getRoles());
+            $authenticatedToken->setUser($user);
+            
+            return $authenticatedToken;
+        }
+
+        throw new AuthenticationException('The OAuth authentication failed.');
+    }
+
+    /**
+     * @param TokenInterface $token
+     * 
+     * @return boolean
+     */
+    public function supports(TokenInterface $token)
+    {
+        return $token instanceof UsernamePasswordToken;
+    }
+
+}

+ 96 - 0
Security/Firewall/OAuthProxyListener.php

@@ -0,0 +1,96 @@
+<?php
+
+namespace AuthBundle\Security\Firewall;
+
+use AuthBundle\Services\AccessTokenService;
+use Base\OAuthClientBundle\Security\Core\User\CustomOAuthUser;
+use HWI\Bundle\OAuthBundle\Security\Core\Authentication\Token\OAuthToken;
+use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
+use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
+use Symfony\Component\Security\Core\Exception\AuthenticationException;
+use Symfony\Component\Security\Http\Firewall\ListenerInterface;
+
+class OAuthProxyListener implements ListenerInterface
+{
+
+    /**
+     * @var TokenStorageInterface 
+     */
+    protected $tokenStorage;
+
+    /**
+     * @var AuthenticationManagerInterface 
+     */
+    protected $authenticationManager;
+    
+    /**
+     * @var AccessTokenService
+     */
+    protected $accessTokenService;
+
+
+    /**
+     * @param TokenStorageInterface $tokenStorage
+     * @param AuthenticationManagerInterface $authenticationManager
+     * @param AccessTokenService $accessTokenService
+     */
+    public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, AccessTokenService $accessTokenService)
+    {
+        $this->tokenStorage = $tokenStorage;
+        $this->authenticationManager = $authenticationManager;
+        $this->accessTokenService = $accessTokenService;
+    }
+
+    /**
+     * @param GetResponseEvent $event
+     * 
+     * @return type
+     */
+    public function handle(GetResponseEvent $event)
+    {
+        $request = $event->getRequest();
+
+        if ($request->headers->has("php-auth-user") && $request->headers->has("php-auth-pw")) {
+            $username = $request->headers->get("php-auth-user");
+            $password = $request->headers->get("php-auth-pw");
+        } else {
+            return;
+        }
+        
+        $token = $this->accessTokenService->getToken($username, $password);
+        $auth_info = $this->accessTokenService->getUserInfo($username, $password);
+                
+        $user = new CustomOAuthUser($username);
+        $user->setRoles($auth_info['roles']);
+        $user->setTenancies($auth_info['tenancies']);
+        $user->setTenancyCurrent($auth_info['tenancyCurrent']);
+        
+        $token = new UsernamePasswordToken($user, null, "api", $user->getRoles());
+        
+        try {
+            $authToken = $this->authenticationManager->authenticate($token);
+            $this->tokenStorage->setToken($authToken);
+            
+            return;
+        } catch (AuthenticationException $failed) {
+            // ... you might log something here
+            // To deny the authentication clear the token. This will redirect to the login page.
+            // Make sure to only clear your token, not those of other authentication listeners.
+             $token = $this->tokenStorage->getToken();
+             if ($token instanceof UsernamePasswordToken && $this->providerKey === $token->getProviderKey()) {
+                 $this->tokenStorage->setToken(null);
+             }
+
+             return;
+        }
+
+        // By default deny authorization
+        $response = new Response();
+        $response->setStatusCode(Response::HTTP_FORBIDDEN);
+        $event->setResponse($response);
+    }
+
+}

+ 0 - 166
Security/OAuthProxyAuthenticator.php

@@ -1,166 +0,0 @@
-<?php
-
-namespace AuthBundle\Security;
-
-use Buzz\Listener\BasicAuthListener;
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpFoundation\Response;
-use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
-use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken;
-use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
-use Symfony\Component\Security\Core\Exception\AuthenticationException;
-use Symfony\Component\Security\Core\User\UserProviderInterface;
-use Symfony\Component\Security\Http\Authentication\SimplePreAuthenticatorInterface;
-use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
-
-class OAuthProxyAuthenticator implements SimplePreAuthenticatorInterface, AuthenticationFailureHandlerInterface
-{
-
-    /**
-     * @var string
-     */
-    private $client_id;
-
-    /**
-     * @var string
-     */
-    private $client_secret;
-
-    /**
-     * @var string
-     */
-    private $access_token_url;
-
-    /**
-     * @var string
-     */
-    private $user_info_url;
-
-
-    /**
-     * @param string $client_id
-     * @param string $client_secret
-     * @param string $access_token_url
-     * @param string $user_info_url
-     */
-    public function __construct($client_id, $client_secret, $access_token_url, $user_info_url)
-    {
-        $this->client_id = $client_id;
-        $this->client_secret = $client_secret;
-        $this->access_token_url = $access_token_url;
-        $this->user_info_url = $user_info_url;
-    }
-
-    /**
-     * @param Request $request
-     * @param string $providerKey
-     * 
-     * @return AnonymousToken|PreAuthenticatedToken
-     */
-    public function createToken(Request $request, $providerKey)
-    {
-        if ($request->headers->has("php-auth-user") and $request->headers->has("php-auth-pw")) {
-            return new PreAuthenticatedToken($request->headers->get("php-auth-user"), $request->headers->get("php-auth-pw"), $providerKey);
-        }
-
-        return new AnonymousToken("anon.", "anon.");
-    }
-
-    /**
-     * @param TokenInterface $token
-     * @param string $providerKey
-     * 
-     * @return boolean
-     */
-    public function supportsToken(TokenInterface $token, $providerKey)
-    {
-        return $token instanceof PreAuthenticatedToken && $token->getProviderKey() === $providerKey;
-    }
-
-    /**
-     * @param TokenInterface $token
-     * @param UserProviderInterface $userProvider
-     * @param string $providerKey
-     * 
-     * @return PreAuthenticatedToken
-     */
-    public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
-    {
-        $password = $token->getCredentials();
-        $username = $token->getUsername();
-
-        $browser = new \Buzz\Browser();
-        $token = @json_decode(file_get_contents("/tmp/." . base64_encode($username . ":" . $password)), true);
-        if (!isset($token["access_token"])) {
-            $listener = new BasicAuthListener($this->client_id, $this->client_secret);
-            $browser->addListener($listener);
-            $body = ['grant_type' => 'password',
-                'username' => $username,
-                'password' => $password,
-            ];
-
-            $response = $browser->post($this->access_token_url, ['Content-Type' => 'application/x-www-form-urlencoded'], http_build_query($body));
-            $token = json_decode($response->getContent(), true);
-            if ($token['expires_in']) {
-                $token["expires_at"] = time() + $token['expires_in'];
-            } else {
-                $token["expires_at"] = time() + 3600;
-            }
-
-            file_put_contents("/tmp/." . base64_encode($username . ":" . $password), json_encode($token));
-        }
-
-        if (isset($token["expires_at"]) and $token["expires_at"] >= time()) {
-            $listener = new BasicAuthListener($this->client_id, $this->client_secret);
-            $browser->addListener($listener);
-            $body = [
-                'grant_type' => 'refresh_token',
-                'refresh_token' => $token['refresh_token'],
-            ];
-
-            $response = $browser->post($this->access_token_url, ['Content-Type' => 'application/x-www-form-urlencoded'], http_build_query($body));
-            $token = json_decode($response->getContent(), true);
-            if ($token['expires_in']) {
-                $token["expires_at"] = time() + $token['expires_in'];
-            } else {
-                $token["expires_at"] = time() + 3600;
-            }
-
-            file_put_contents("/tmp/." . base64_encode($username . ":" . $password), json_encode($token));
-        }
-
-        if (!isset($token["user_info"])) {
-            $oauth_headers = [
-                "Authorization" => ucfirst($token["token_type"]) . " " . $token["access_token"],
-            ];
-            $response = $browser->get($this->user_info_url, $oauth_headers);
-            $auth_info = json_decode($response->getContent(), true);
-            $token["user_info"] = $auth_info;
-
-            file_put_contents("/tmp/." . base64_encode($username . ":" . $password), json_encode($token));
-        } else {
-            $auth_info = $token["user_info"];
-        }
-
-        $user = $userProvider->loadUserByUsername($auth_info["username"]);
-        $user->setRoles($auth_info["roles"]);
-        $user->setTenancyCurrent($auth_info["tenancyCurrent"]);
-
-        return new PreAuthenticatedToken($user, array(), $providerKey, $user->getRoles());
-    }
-
-    /**
-     * @param Request $request
-     * @param AuthenticationException $exception
-     * 
-     * @return Response
-     */
-    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
-    {
-        return new Response(
-                // this contains information about *why* authentication failed
-                // use it, or return your own message
-                strtr($exception->getMessageKey(), $exception->getMessageData()), 401);
-    }
-
-}

+ 134 - 0
Services/AccessTokenService.php

@@ -0,0 +1,134 @@
+<?php
+
+namespace AuthBundle\Services;
+
+use Buzz\Listener\BasicAuthListener;
+
+class AccessTokenService
+{
+
+    /**
+     * @var string
+     */
+    private $client_id;
+
+    /**
+     * @var string
+     */
+    private $client_secret;
+
+    /**
+     * @var string
+     */
+    private $access_token_url;
+
+    /**
+     * @var string
+     */
+    private $user_info_url;
+
+
+    /**
+     * @param string $client_id
+     * @param string $client_secret
+     * @param string $access_token_url
+     * @param string $user_info_url
+     */
+    public function __construct($client_id, $client_secret, $access_token_url, $user_info_url)
+    {
+        $this->client_id = $client_id;
+        $this->client_secret = $client_secret;
+        $this->access_token_url = $access_token_url;
+        $this->user_info_url = $user_info_url;
+    }
+
+    /**
+     * @param string $username
+     * @param string $password
+     * 
+     * @return array
+     */
+    public function getToken($username, $password)
+    {
+        $token = @json_decode(file_get_contents("/tmp/." . base64_encode($username . ":" . $password)), true);
+        if (!isset($token["access_token"])) {
+            $body = [
+                'grant_type' => 'password',
+                'username' => $username,
+                'password' => $password,
+            ];
+            $token = $this->updateToken($username, $password, $body);
+        }
+
+        if (isset($token["expires_at"]) && $token["expires_at"] <= time()) {
+            $body = [
+                'grant_type' => 'refresh_token',
+                'refresh_token' => $token['refresh_token'],
+            ];
+            $token = $this->updateToken($username, $password, $body);
+        }
+
+        return $token;
+    }
+
+    /**
+     * @param string $username
+     * @param string $password
+     * 
+     * @return array
+     */
+    public function getUserInfo($username, $password)
+    {
+        $auth_info = array();
+        $token = $this->getToken($username, $password);
+        if (!isset($token["user_info"]) && isset($token["token_type"]) && isset($token["access_token"])) {
+            $auth_info = $this->updateUserInfo($username, $password, $token);
+        } elseif (isset($token["user_info"])) {
+            $auth_info = $token["user_info"];
+        }
+
+        return $auth_info;
+    }
+
+    /**
+     * @param string $username
+     * @param string $password
+     * @param array $body
+     * 
+     * @return array
+     */
+    private function updateToken($username, $password, $body)
+    {
+        $browser = new \Buzz\Browser();
+        $listener = new BasicAuthListener($this->client_id, $this->client_secret);
+        $browser->addListener($listener);
+        $response = $browser->post($this->access_token_url, ['Content-Type' => 'application/x-www-form-urlencoded'], http_build_query($body));
+        $token = json_decode($response->getContent(), true);
+        $token["expires_at"] = time() + (isset($token['expires_in']) ? $token['expires_in'] : 3600);
+        file_put_contents("/tmp/." . base64_encode($username . ":" . $password), json_encode($token));
+
+        return $token;
+    }
+
+    /**
+     * @param string $username
+     * @param string $password
+     * @param array $token
+     * 
+     * @return array
+     */
+    private function updateUserInfo($username, $password, $token)
+    {
+        $oauth_headers = [
+            "Authorization" => ucfirst($token["token_type"]) . " " . $token["access_token"],
+        ];
+        $browser = new \Buzz\Browser();
+        $response = $browser->get($this->user_info_url, $oauth_headers);
+        $auth_info = json_decode($response->getContent(), true);
+        $token["user_info"] = $auth_info;
+        file_put_contents("/tmp/." . base64_encode($username . ":" . $password), json_encode($token));
+
+        return $auth_info;
+    }
+
+}