Failed Conditions
Push — master ( 1bae70...6a9de1 )
by Florent
40:14
created

processWithAuthenticatedUser()   B

Complexity

Conditions 9
Paths 12

Size

Total Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 34
rs 8.0555
c 0
b 0
f 0
cc 9
nc 12
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * The MIT License (MIT)
7
 *
8
 * Copyright (c) 2014-2018 Spomky-Labs
9
 *
10
 * This software may be modified and distributed under the terms
11
 * of the MIT license.  See the LICENSE file for details.
12
 */
13
14
namespace OAuth2Framework\Component\AuthorizationEndpoint;
15
16
use Base64Url\Base64Url;
17
use Http\Message\ResponseFactory;
18
use OAuth2Framework\Component\AuthorizationEndpoint\AuthorizationRequest\AuthorizationRequest;
19
use OAuth2Framework\Component\AuthorizationEndpoint\AuthorizationRequest\AuthorizationRequestLoader;
20
use OAuth2Framework\Component\AuthorizationEndpoint\Consent\ConsentRepository;
21
use OAuth2Framework\Component\AuthorizationEndpoint\Exception\OAuth2AuthorizationException;
22
use OAuth2Framework\Component\AuthorizationEndpoint\ParameterChecker\ParameterCheckerManager;
23
use OAuth2Framework\Component\AuthorizationEndpoint\User\UserAccountDiscovery;
24
use OAuth2Framework\Component\AuthorizationEndpoint\User\UserAuthenticationCheckerManager;
25
use OAuth2Framework\Component\Core\Message\OAuth2Error;
26
use OAuth2Framework\ServerBundle\Tests\TestBundle\Entity\UserAccount;
27
use Psr\Http\Message\ResponseInterface;
28
use Psr\Http\Message\ServerRequestInterface;
29
use Psr\Http\Server\RequestHandlerInterface;
30
use Symfony\Component\HttpFoundation\Session\SessionInterface;
31
32
abstract class AuthorizationEndpoint extends AbstractEndpoint
33
{
34
    private $authorizationRequestLoader;
35
36
    private $parameterCheckerManager;
37
38
    private $userAccountDiscovery;
39
40
    private $userCheckerManager;
41
42
    private $consentRepository;
43
44
    public function __construct(ResponseFactory $responseFactory, AuthorizationRequestLoader $authorizationRequestLoader, ParameterCheckerManager $parameterCheckerManager, UserAccountDiscovery $userAccountDiscovery, UserAuthenticationCheckerManager $userCheckerManager, SessionInterface $session, ?ConsentRepository $consentRepository)
45
    {
46
        parent::__construct($responseFactory, $session);
47
        $this->authorizationRequestLoader = $authorizationRequestLoader;
48
        $this->parameterCheckerManager = $parameterCheckerManager;
49
        $this->userAccountDiscovery = $userAccountDiscovery;
50
        $this->userCheckerManager = $userCheckerManager;
51
        $this->consentRepository = $consentRepository;
52
    }
53
54
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
55
    {
56
        $authorization = $this->loadAuthorization($request);
57
58
        try {
59
            $userAccount = $this->userAccountDiscovery->getCurrentAccount();
60
61
            if (null !== $userAccount) {
62
                return $this->processWithAuthenticatedUser($authorization, $userAccount);
63
            } else {
64
                return $this->processWithUnauthenticatedUser($authorization);
65
            }
66
        } catch (OAuth2AuthorizationException $e) {
67
            throw $e;
68
        } catch (OAuth2Error $e) {
69
            throw new OAuth2AuthorizationException($e->getMessage(), $e->getErrorDescription(), $authorization, $e);
70
        } catch (\Exception $e) {
71
            throw new OAuth2AuthorizationException(OAuth2Error::ERROR_INVALID_REQUEST, $e->getMessage(), $authorization, $e);
72
        }
73
    }
74
75
    private function processWithAuthenticatedUser(AuthorizationRequest $authorization, UserAccount $userAccount): ResponseInterface
76
    {
77
        $authorization->setUserAccount($userAccount);
78
        $isAuthenticationNeeded = $this->userCheckerManager->isAuthenticationNeeded($authorization);
79
        $isConsentNeeded = !$this->consentRepository || !$this->consentRepository->hasConsentBeenGiven($authorization);
80
81
        switch (true) {
82
            case $authorization->hasPrompt('none'):
83
                if ($isConsentNeeded) {
84
                    throw new OAuth2AuthorizationException(OAuth2Error::ERROR_INTERACTION_REQUIRED, 'The resource owner consent is required.', $authorization);
85
                }
86
                $authorization->allow();
87
                $routeName = 'oauth2_server_process_endpoint';
88
                break;
89
            case $authorization->hasPrompt('select_account'):
90
                $routeName = 'oauth2_server_select_account_endpoint';
91
                break;
92
            case $authorization->hasPrompt('login') || $isAuthenticationNeeded:
93
                $routeName = 'oauth2_server_login_endpoint';
94
                break;
95
            case $authorization->hasPrompt('consent') || $isConsentNeeded:
96
                $routeName = 'oauth2_server_consent_endpoint';
97
                break;
98
            default:
99
                $routeName = 'oauth2_server_consent_endpoint';
100
                break;
101
        }
102
103
        $authorizationId = Base64Url::encode(random_bytes(64));
104
        $this->saveAuthorization($authorizationId, $authorization);
105
        $redirectTo = $this->getRouteFor($routeName, $authorizationId);
106
107
        return $this->createRedirectResponse($redirectTo);
108
    }
109
110
    private function processWithUnauthenticatedUser(AuthorizationRequest $authorization): ResponseInterface
111
    {
112
        if ($authorization->hasPrompt('none')) {
113
            $isConsentNeeded = !$this->consentRepository || !$this->consentRepository->hasConsentBeenGiven($authorization);
114
            if ($isConsentNeeded) {
115
                throw new OAuth2AuthorizationException(OAuth2Error::ERROR_LOGIN_REQUIRED, 'The resource owner is not logged in.', $authorization);
116
            }
117
            $authorization->allow();
118
            $routeName = 'oauth2_server_process_endpoint';
119
        } else {
120
            $routeName = 'oauth2_server_login_endpoint';
121
        }
122
123
        $authorizationId = Base64Url::encode(random_bytes(64));
124
        $this->saveAuthorization($authorizationId, $authorization);
125
        $redirectTo = $this->getRouteFor($routeName, $authorizationId);
126
127
        return $this->createRedirectResponse($redirectTo);
128
    }
129
130
    private function loadAuthorization(ServerRequestInterface $request): AuthorizationRequest
131
    {
132
        try {
133
            $authorization = $this->authorizationRequestLoader->load($request);
134
            $this->parameterCheckerManager->check($authorization);
135
136
            return $authorization;
137
        } catch (OAuth2AuthorizationException $e) {
138
            throw $e;
139
        } catch (OAuth2Error $e) {
140
            throw $e;
141
        } catch (\Exception $e) {
142
            throw new OAuth2Error(400, OAuth2Error::ERROR_INVALID_REQUEST, $e->getMessage());
143
        }
144
    }
145
146
    abstract protected function getRouteFor(string $action, string $authorizationId): string;
147
}
148