alex-kalanis /
restful
| 1 | <?php |
||
| 2 | |||
| 3 | namespace kalanis\Restful\Application\UI; |
||
| 4 | |||
| 5 | |||
| 6 | use Nette\Http\Url; |
||
| 7 | use kalanis\OAuth2\Application\IOAuthPresenter; |
||
| 8 | use kalanis\OAuth2\Exceptions\InvalidGrantException; |
||
| 9 | use kalanis\OAuth2\Exceptions\InvalidStateException; |
||
| 10 | use kalanis\OAuth2\Exceptions\OAuthException; |
||
| 11 | use kalanis\OAuth2\Exceptions\UnauthorizedClientException; |
||
| 12 | use kalanis\OAuth2\Exceptions\UnsupportedResponseTypeException; |
||
| 13 | use kalanis\OAuth2\Grant\GrantContext; |
||
| 14 | use kalanis\OAuth2\Grant\GrantType; |
||
| 15 | use kalanis\OAuth2\Grant\IGrant; |
||
| 16 | use kalanis\OAuth2\Storage\AuthorizationCodes\AuthorizationCodeFacade; |
||
| 17 | use kalanis\OAuth2\Storage\Clients\IClient; |
||
| 18 | use kalanis\OAuth2\Storage\Clients\IClientStorage; |
||
| 19 | use kalanis\OAuth2\Storage\Exceptions\TokenException; |
||
| 20 | use Traversable; |
||
| 21 | |||
| 22 | |||
| 23 | /** |
||
| 24 | * OAuth2Presenter |
||
| 25 | * @package kalanis\Restful\Application |
||
| 26 | */ |
||
| 27 | class OAuth2Presenter extends ResourcePresenter implements IOAuthPresenter |
||
| 28 | { |
||
| 29 | |||
| 30 | #[\Nette\DI\Attributes\Inject] |
||
| 31 | protected AuthorizationCodeFacade $authorizationCode; |
||
| 32 | |||
| 33 | #[\Nette\DI\Attributes\Inject] |
||
| 34 | public IClientStorage $clientStorage; |
||
| 35 | |||
| 36 | #[\Nette\DI\Attributes\Inject] |
||
| 37 | public GrantContext $grantContext; |
||
| 38 | |||
| 39 | protected IClient $client; |
||
| 40 | |||
| 41 | /** |
||
| 42 | * Issue an authorization code |
||
| 43 | * @param string $responseType |
||
| 44 | * @param string $redirectUrl |
||
| 45 | * @param string|null $scope |
||
| 46 | * @throws UnauthorizedClientException |
||
| 47 | * @throws UnsupportedResponseTypeException |
||
| 48 | * @return void |
||
| 49 | * |
||
| 50 | */ |
||
| 51 | public function issueAuthorizationCode(string $responseType, string $redirectUrl, ?string $scope = null): void |
||
| 52 | { |
||
| 53 | try { |
||
| 54 | if ('code' !== $responseType) { |
||
| 55 | throw new UnsupportedResponseTypeException; |
||
| 56 | } |
||
| 57 | if (!$this->client->getId()) { |
||
| 58 | throw new UnauthorizedClientException; |
||
| 59 | } |
||
| 60 | |||
| 61 | $scope = array_filter(explode(',', str_replace(' ', ',', strval($scope)))); |
||
| 62 | $code = $this->authorizationCode->create($this->client, $this->user->getId(), $scope); |
||
| 63 | $data = ['code' => $code->getAuthorizationCode()]; |
||
| 64 | $this->oauthResponse($data, $redirectUrl); |
||
| 65 | } catch (OAuthException $e) { |
||
| 66 | $this->oauthError($e); |
||
| 67 | } catch (TokenException) { |
||
| 68 | $this->oauthError(new InvalidGrantException()); |
||
| 69 | } |
||
| 70 | } |
||
| 71 | |||
| 72 | /** |
||
| 73 | * Send OAuth response |
||
| 74 | * @param array<string, mixed>|Traversable<string, mixed> $data |
||
| 75 | * @param string|null $redirectUrl |
||
| 76 | * @param int $code |
||
| 77 | */ |
||
| 78 | public function oauthResponse(iterable $data, ?string $redirectUrl = null, int $code = 200): void |
||
|
0 ignored issues
–
show
|
|||
| 79 | { |
||
| 80 | if ($data instanceof Traversable) { |
||
| 81 | $data = iterator_to_array($data); |
||
| 82 | } |
||
| 83 | $data = (array) $data; |
||
| 84 | |||
| 85 | // Redirect, if there is URL |
||
| 86 | if (!is_null($redirectUrl)) { |
||
| 87 | $url = new Url($redirectUrl); |
||
| 88 | if ('token' == $this->getParameter('response_type')) { |
||
| 89 | $url->setFragment(http_build_query($data)); |
||
| 90 | } else { |
||
| 91 | $url->appendQuery($data); |
||
| 92 | } |
||
| 93 | $this->redirectUrl($url); |
||
| 94 | } |
||
| 95 | |||
| 96 | // else send JSON response |
||
| 97 | foreach ($data as $key => $value) { |
||
| 98 | $this->resource->$key = $value; |
||
| 99 | } |
||
| 100 | $this->sendResource(null); |
||
| 101 | } |
||
| 102 | |||
| 103 | /** |
||
| 104 | * Provide OAuth2 error response (redirect or at least JSON) |
||
| 105 | * @param OAuthException $exception |
||
| 106 | */ |
||
| 107 | public function oauthError(OAuthException $exception): void |
||
| 108 | { |
||
| 109 | $error = ['error' => $exception->getKey(), 'error_description' => $exception->getMessage()]; |
||
| 110 | $redirect = $this->getParameter('redirect_uri'); |
||
| 111 | $this->oauthResponse($error, is_null($redirect) ? null : strval($redirect), $exception->getCode()); |
||
| 112 | } |
||
| 113 | |||
| 114 | /** |
||
| 115 | * Issue an access token |
||
| 116 | * @param string|null $grantType |
||
| 117 | * @param string|null $redirectUrl |
||
| 118 | */ |
||
| 119 | public function issueAccessToken(?string $grantType = null, ?string $redirectUrl = null): void |
||
| 120 | { |
||
| 121 | try { |
||
| 122 | if (!is_null($grantType)) { |
||
| 123 | $grantType = $this->grantContext->getGrantType($grantType); |
||
| 124 | } else { |
||
| 125 | $grantType = $this->getGrantType(); |
||
| 126 | } |
||
| 127 | |||
| 128 | $response = $grantType->getAccessToken(); |
||
| 129 | $this->oauthResponse($response, $redirectUrl); |
||
| 130 | } catch (OAuthException $e) { |
||
| 131 | $this->oauthError($e); |
||
| 132 | } catch (TokenException) { |
||
| 133 | $this->oauthError(new InvalidGrantException); |
||
| 134 | } |
||
| 135 | } |
||
| 136 | |||
| 137 | /** |
||
| 138 | * Get grant type |
||
| 139 | * @throws UnsupportedResponseTypeException |
||
| 140 | */ |
||
| 141 | public function getGrantType(): IGrant |
||
| 142 | { |
||
| 143 | $request = $this->getHttpRequest(); |
||
| 144 | $grantType = strval($request->getPost(GrantType::GRANT_TYPE_KEY)); |
||
| 145 | try { |
||
| 146 | return $this->grantContext->getGrantType($grantType); |
||
| 147 | } catch (InvalidStateException $e) { |
||
| 148 | throw new UnsupportedResponseTypeException('Trying to use unknown grant type ' . $grantType, $e); |
||
| 149 | } |
||
| 150 | } |
||
| 151 | |||
| 152 | /** |
||
| 153 | * On presenter startup |
||
| 154 | */ |
||
| 155 | protected function startup(): void |
||
| 156 | { |
||
| 157 | parent::startup(); |
||
| 158 | $clientId = $this->getParameter(GrantType::CLIENT_ID_KEY); |
||
| 159 | $clientSecret = $this->getParameter(GrantType::CLIENT_SECRET_KEY); |
||
| 160 | $client = $this->clientStorage->getClient( |
||
| 161 | is_numeric($clientId) ? intval($clientId) : strval($clientId), |
||
| 162 | is_null($clientSecret) ? null : strval($clientSecret) |
||
| 163 | ); |
||
| 164 | if (!$client) { |
||
| 165 | throw new \LogicException('Cannot load client info'); |
||
| 166 | } |
||
| 167 | $this->client = $client; |
||
| 168 | } |
||
| 169 | } |
||
| 170 |
This check looks for parameters that have been defined for a function or method, but which are not used in the method body.