Вход Регистрация
Файл: symfony-2.7/src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php
Строк: 436
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SymfonyComponentSecurityHttpFirewall;

use 
SymfonyComponentSecurityCoreUserUserProviderInterface;
use 
SymfonyComponentSecurityHttpEntryPointDigestAuthenticationEntryPoint;
use 
PsrLogLoggerInterface;
use 
SymfonyComponentHttpKernelEventGetResponseEvent;
use 
SymfonyComponentSecurityCoreAuthenticationTokenUsernamePasswordToken;
use 
SymfonyComponentSecurityCoreAuthenticationTokenStorageTokenStorageInterface;
use 
SymfonyComponentSecurityCoreExceptionBadCredentialsException;
use 
SymfonyComponentSecurityCoreExceptionAuthenticationServiceException;
use 
SymfonyComponentSecurityCoreExceptionUsernameNotFoundException;
use 
SymfonyComponentSecurityCoreExceptionNonceExpiredException;
use 
SymfonyComponentHttpFoundationRequest;
use 
SymfonyComponentSecurityCoreExceptionAuthenticationException;

/**
 * DigestAuthenticationListener implements Digest HTTP authentication.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class DigestAuthenticationListener implements ListenerInterface
{
    private 
$tokenStorage;
    private 
$provider;
    private 
$providerKey;
    private 
$authenticationEntryPoint;
    private 
$logger;

    public function 
__construct(TokenStorageInterface $tokenStorageUserProviderInterface $provider$providerKeyDigestAuthenticationEntryPoint $authenticationEntryPointLoggerInterface $logger null)
    {
        if (empty(
$providerKey)) {
            throw new 
InvalidArgumentException('$providerKey must not be empty.');
        }

        
$this->tokenStorage $tokenStorage;
        
$this->provider $provider;
        
$this->providerKey $providerKey;
        
$this->authenticationEntryPoint $authenticationEntryPoint;
        
$this->logger $logger;
    }

    
/**
     * Handles digest authentication.
     *
     * @param GetResponseEvent $event A GetResponseEvent instance
     *
     * @throws AuthenticationServiceException
     */
    
public function handle(GetResponseEvent $event)
    {
        
$request $event->getRequest();

        if (!
$header $request->server->get('PHP_AUTH_DIGEST')) {
            return;
        }

        
$digestAuth = new DigestData($header);

        if (
null !== $token $this->tokenStorage->getToken()) {
            if (
$token instanceof UsernamePasswordToken && $token->isAuthenticated() && $token->getUsername() === $digestAuth->getUsername()) {
                return;
            }
        }

        if (
null !== $this->logger) {
            
$this->logger->debug('Digest Authorization header received from user agent.', array('header' => $header));
        }

        try {
            
$digestAuth->validateAndDecode($this->authenticationEntryPoint->getKey(), $this->authenticationEntryPoint->getRealmName());
        } catch (
BadCredentialsException $e) {
            
$this->fail($event$request$e);

            return;
        }

        try {
            
$user $this->provider->loadUserByUsername($digestAuth->getUsername());

            if (
null === $user) {
                throw new 
AuthenticationServiceException('Digest User provider returned null, which is an interface contract violation');
            }

            
$serverDigestMd5 $digestAuth->calculateServerDigest($user->getPassword(), $request->getMethod());
        } catch (
UsernameNotFoundException $notFound) {
            
$this->fail($event$request, new BadCredentialsException(sprintf('Username %s not found.'$digestAuth->getUsername())));

            return;
        }

        if (
$serverDigestMd5 !== $digestAuth->getResponse()) {
            if (
null !== $this->logger) {
                
$this->logger->debug("Unexpected response from the DigestAuth received; is the header returning a clear text passwords?", array('expected' => $serverDigestMd5'received' => $digestAuth->getResponse()));
            }

            
$this->fail($event$request, new BadCredentialsException('Incorrect response'));

            return;
        }

        if (
$digestAuth->isNonceExpired()) {
            
$this->fail($event$request, new NonceExpiredException('Nonce has expired/timed out.'));

            return;
        }

        if (
null !== $this->logger) {
            
$this->logger->info('Digest authentication successful.', array('username' => $digestAuth->getUsername(), 'received' => $digestAuth->getResponse()));
        }

        
$this->tokenStorage->setToken(new UsernamePasswordToken($user$user->getPassword(), $this->providerKey));
    }

    private function 
fail(GetResponseEvent $eventRequest $requestAuthenticationException $authException)
    {
        
$token $this->tokenStorage->getToken();
        if (
$token instanceof UsernamePasswordToken && $this->providerKey === $token->getProviderKey()) {
            
$this->tokenStorage->setToken(null);
        }

        if (
null !== $this->logger) {
            
$this->logger->info('Digest authentication failed.', array('exception' => $authException));
        }

        
$event->setResponse($this->authenticationEntryPoint->start($request$authException));
    }
}

class 
DigestData
{
    private 
$elements = array();
    private 
$header;
    private 
$nonceExpiryTime;

    public function 
__construct($header)
    {
        
$this->header $header;
        
preg_match_all('/(w+)=("((?:[^"\\]|\\.)+)"|([^s,$]+))/'$header$matchesPREG_SET_ORDER);
        foreach (
$matches as $match) {
            if (isset(
$match[1]) && isset($match[3])) {
                
$this->elements[$match[1]] = isset($match[4]) ? $match[4] : $match[3];
            }
        }
    }

    public function 
getResponse()
    {
        return 
$this->elements['response'];
    }

    public function 
getUsername()
    {
        return 
strtr($this->elements['username'], array("\"" => """, "\\" => "\"));
    }

    public function validateAndDecode(
$entryPointKey$expectedRealm)
    {
        if (
$keys = array_diff(array('username', 'realm', 'nonce', 'uri', 'response'), array_keys($this->elements))) {
            throw new BadCredentialsException(sprintf('Missing mandatory digest value; received header "
%s" (%s)', $this->header, implode(', ', $keys)));
        }

        if ('auth' === 
$this->elements['qop']) {
            if (!isset(
$this->elements['nc']) || !isset($this->elements['cnonce'])) {
                throw new BadCredentialsException(sprintf('Missing mandatory digest value; received header "
%s"', $this->header));
            }
        }

        if (
$expectedRealm !== $this->elements['realm']) {
            throw new BadCredentialsException(sprintf('Response realm name "
%s" does not match system realm name of "%s".', $this->elements['realm'], $expectedRealm));
        }

        if (false === 
$nonceAsPlainText = base64_decode($this->elements['nonce'])) {
            throw new BadCredentialsException(sprintf('Nonce is not encoded in Base64; received nonce "
%s".', $this->elements['nonce']));
        }

        
$nonceTokens = explode(':', $nonceAsPlainText);

        if (2 !== count(
$nonceTokens)) {
            throw new BadCredentialsException(sprintf('Nonce should have yielded two tokens but was "
%s".', $nonceAsPlainText));
        }

        
$this->nonceExpiryTime = $nonceTokens[0];

        if (md5(
$this->nonceExpiryTime.':'.$entryPointKey) !== $nonceTokens[1]) {
            throw new BadCredentialsException(sprintf('Nonce token compromised "
%s".', $nonceAsPlainText));
        }
    }

    public function calculateServerDigest(
$password$httpMethod)
    {
        
$a2Md5 = md5(strtoupper($httpMethod).':'.$this->elements['uri']);
        
$a1Md5 = md5($this->elements['username'].':'.$this->elements['realm'].':'.$password);

        
$digest = $a1Md5.':'.$this->elements['nonce'];
        if (!isset(
$this->elements['qop'])) {
        } elseif ('auth' === 
$this->elements['qop']) {
            
$digest .= ':'.$this->elements['nc'].':'.$this->elements['cnonce'].':'.$this->elements['qop'];
        } else {
            throw new InvalidArgumentException('This method does not support a qop: "
%s".', $this->elements['qop']);
        }
        
$digest .= ':'.$a2Md5;

        return md5(
$digest);
    }

    public function isNonceExpired()
    {
        return 
$this->nonceExpiryTime < microtime(true);
    }
}
Онлайн: 0
Реклама