Completely restored authorization_code grant for user side.

Reworked oauth_sessions table.
Added extension to use MariaDB's JSON columns.
Rewritten tests for authorization_code grant for client side.
Deprecate some old shit.
[skip ci]
This commit is contained in:
ErickSkrauch
2019-09-18 02:14:05 +03:00
parent 8a1d7148d0
commit 45101d6453
27 changed files with 418 additions and 404 deletions

View File

@@ -4,7 +4,6 @@ declare(strict_types=1);
namespace api\components\OAuth2;
use api\components\OAuth2\Keys\EmptyKey;
use api\components\OAuth2\Repositories;
use DateInterval;
use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Grant;

View File

@@ -1,72 +0,0 @@
<?php
namespace api\components\OAuth2\Repositories;
use api\components\OAuth2\Entities\AuthCodeEntity;
use common\components\Redis\Key;
use common\components\Redis\Set;
use League\OAuth2\Server\Entity\AuthCodeEntity as OriginalAuthCodeEntity;
use League\OAuth2\Server\Entity\ScopeEntity;
use League\OAuth2\Server\Storage\AbstractStorage;
use League\OAuth2\Server\Storage\AuthCodeInterface;
use yii\helpers\Json;
class AuthCodeStorage extends AbstractStorage implements AuthCodeInterface {
public $dataTable = 'oauth_auth_codes';
public function get($code) {
$result = Json::decode((new Key($this->dataTable, $code))->getValue());
if ($result === null) {
return null;
}
$entity = new AuthCodeEntity($this->server);
$entity->setId($result['id']);
$entity->setExpireTime($result['expire_time']);
$entity->setSessionId($result['session_id']);
$entity->setRedirectUri($result['client_redirect_uri']);
return $entity;
}
public function create($token, $expireTime, $sessionId, $redirectUri) {
$payload = Json::encode([
'id' => $token,
'expire_time' => $expireTime,
'session_id' => $sessionId,
'client_redirect_uri' => $redirectUri,
]);
$this->key($token)->setValue($payload)->expireAt($expireTime);
}
public function getScopes(OriginalAuthCodeEntity $token) {
$scopes = $this->scopes($token->getId());
$scopesEntities = [];
foreach ($scopes as $scope) {
if ($this->server->getScopeStorage()->get($scope) !== null) {
$scopesEntities[] = (new ScopeEntity($this->server))->hydrate(['id' => $scope]);
}
}
return $scopesEntities;
}
public function associateScope(OriginalAuthCodeEntity $token, ScopeEntity $scope) {
$this->scopes($token->getId())->add($scope->getId())->expireAt($token->getExpireTime());
}
public function delete(OriginalAuthCodeEntity $token) {
$this->key($token->getId())->delete();
$this->scopes($token->getId())->delete();
}
private function key(string $token): Key {
return new Key($this->dataTable, $token);
}
private function scopes(string $token): Set {
return new Set($this->dataTable, $token, 'scopes');
}
}

View File

@@ -29,8 +29,6 @@ class ClientRepository implements ClientRepositoryInterface {
return false;
}
// TODO: there is missing behavior of checking redirectUri. Is it now bundled into grant?
return true;
}

View File

@@ -1,97 +0,0 @@
<?php
namespace api\components\OAuth2\Repositories;
use api\components\OAuth2\Entities\AuthCodeEntity;
use api\components\OAuth2\Entities\SessionEntity;
use common\models\OauthSession;
use ErrorException;
use League\OAuth2\Server\Entity\AccessTokenEntity as OriginalAccessTokenEntity;
use League\OAuth2\Server\Entity\AuthCodeEntity as OriginalAuthCodeEntity;
use League\OAuth2\Server\Entity\ScopeEntity;
use League\OAuth2\Server\Entity\SessionEntity as OriginalSessionEntity;
use League\OAuth2\Server\Storage\AbstractStorage;
use League\OAuth2\Server\Storage\SessionInterface;
use yii\db\Exception;
class SessionStorage extends AbstractStorage implements SessionInterface {
/**
* @param string $sessionId
* @return SessionEntity|null
*/
public function getById($sessionId) {
return $this->hydrate($this->getSessionModel($sessionId));
}
public function getByAccessToken(OriginalAccessTokenEntity $accessToken) {
throw new ErrorException('This method is not implemented and should not be used');
}
public function getByAuthCode(OriginalAuthCodeEntity $authCode) {
if (!$authCode instanceof AuthCodeEntity) {
throw new ErrorException('This module assumes that $authCode typeof ' . AuthCodeEntity::class);
}
return $this->getById($authCode->getSessionId());
}
public function getScopes(OriginalSessionEntity $session) {
$result = [];
foreach ($this->getSessionModel($session->getId())->getScopes() as $scope) {
if ($this->server->getScopeStorage()->get($scope) !== null) {
$result[] = (new ScopeEntity($this->server))->hydrate(['id' => $scope]);
}
}
return $result;
}
public function create($ownerType, $ownerId, $clientId, $clientRedirectUri = null) {
$sessionId = OauthSession::find()
->select('id')
->andWhere([
'client_id' => $clientId,
'owner_type' => $ownerType,
'owner_id' => (string)$ownerId, // Casts as a string to make the indexes work, because the varchar field
])->scalar();
if ($sessionId === false) {
$model = new OauthSession();
$model->client_id = $clientId;
$model->owner_type = $ownerType;
$model->owner_id = $ownerId;
$model->client_redirect_uri = $clientRedirectUri;
if (!$model->save()) {
throw new Exception('Cannot save ' . OauthSession::class . ' model.');
}
$sessionId = $model->id;
}
return $sessionId;
}
public function associateScope(OriginalSessionEntity $session, ScopeEntity $scope) {
$this->getSessionModel($session->getId())->getScopes()->add($scope->getId());
}
private function getSessionModel(string $sessionId): OauthSession {
$session = OauthSession::findOne($sessionId);
if ($session === null) {
throw new ErrorException('Cannot find oauth session');
}
return $session;
}
private function hydrate(OauthSession $sessionModel) {
$entity = new SessionEntity($this->server);
$entity->setId($sessionModel->id);
$entity->setClientId($sessionModel->client_id);
$entity->setOwner($sessionModel->owner_type, $sessionModel->owner_id);
return $entity;
}
}

View File

@@ -0,0 +1,97 @@
<?php
declare(strict_types=1);
namespace api\components\OAuth2\RequestTypes;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\UserEntityInterface;
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
class AuthorizationRequestProxy extends AuthorizationRequest {
/**
* @var AuthorizationRequest
*/
private $authorizationRequest;
public function __construct(AuthorizationRequest $authorizationRequest) {
$this->authorizationRequest = $authorizationRequest;
}
public function getOriginalAuthorizationRequest(): AuthorizationRequest {
return $this->authorizationRequest;
}
public function getGrantTypeId(): string {
return $this->authorizationRequest->getGrantTypeId();
}
public function setGrantTypeId($grantTypeId): void {
$this->authorizationRequest->setGrantTypeId($grantTypeId);
}
public function getClient(): ClientEntityInterface {
return $this->authorizationRequest->getClient();
}
public function setClient(ClientEntityInterface $client): void {
$this->authorizationRequest->setClient($client);
}
public function getUser(): UserEntityInterface {
return $this->authorizationRequest->getUser();
}
public function setUser(UserEntityInterface $user): void {
$this->authorizationRequest->setUser($user);
}
public function getScopes(): array {
return $this->authorizationRequest->getScopes();
}
public function setScopes(array $scopes): void {
$this->authorizationRequest->setScopes($scopes);
}
public function isAuthorizationApproved(): bool {
return $this->authorizationRequest->isAuthorizationApproved();
}
public function setAuthorizationApproved($authorizationApproved): void {
$this->authorizationRequest->setAuthorizationApproved($authorizationApproved);
}
public function getRedirectUri(): ?string {
return $this->authorizationRequest->getRedirectUri();
}
public function setRedirectUri($redirectUri): void {
$this->authorizationRequest->setRedirectUri($redirectUri);
}
public function getState(): ?string {
return $this->authorizationRequest->getState();
}
public function setState($state): void {
$this->authorizationRequest->setState($state);
}
public function getCodeChallenge(): string {
return $this->authorizationRequest->getCodeChallenge();
}
public function setCodeChallenge($codeChallenge): void {
$this->authorizationRequest->setCodeChallenge($codeChallenge);
}
public function getCodeChallengeMethod(): string {
return $this->authorizationRequest->getCodeChallengeMethod();
}
public function setCodeChallengeMethod($codeChallengeMethod): void {
$this->authorizationRequest->setCodeChallengeMethod($codeChallengeMethod);
}
}