diff options
author | Brian Evans <grknight@gentoo.org> | 2020-10-06 11:22:15 -0400 |
---|---|---|
committer | Brian Evans <grknight@gentoo.org> | 2020-10-06 11:22:15 -0400 |
commit | 4a2d3a0b7596731e11ef9257138653bec81d6fd3 (patch) | |
tree | 0e33fdfd38e791cc9ffe46f11f954a7dce86d618 /OAuth/src/Entity/AccessTokenEntity.php | |
parent | OpenIDConnect: Fix newly protected function (diff) | |
download | extensions-4a2d3a0b7596731e11ef9257138653bec81d6fd3.tar.gz extensions-4a2d3a0b7596731e11ef9257138653bec81d6fd3.tar.bz2 extensions-4a2d3a0b7596731e11ef9257138653bec81d6fd3.zip |
Add OAuth for API access
Signed-off-by: Brian Evans <grknight@gentoo.org>
Diffstat (limited to 'OAuth/src/Entity/AccessTokenEntity.php')
-rw-r--r-- | OAuth/src/Entity/AccessTokenEntity.php | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/OAuth/src/Entity/AccessTokenEntity.php b/OAuth/src/Entity/AccessTokenEntity.php new file mode 100644 index 00000000..f7ab4f3a --- /dev/null +++ b/OAuth/src/Entity/AccessTokenEntity.php @@ -0,0 +1,150 @@ +<?php + +namespace MediaWiki\Extensions\OAuth\Entity; + +use InvalidArgumentException; +use League\OAuth2\Server\CryptKey; +use League\OAuth2\Server\Entities\AccessTokenEntityInterface; +use League\OAuth2\Server\Entities\ScopeEntityInterface; +use League\OAuth2\Server\Entities\Traits\AccessTokenTrait; +use League\OAuth2\Server\Entities\Traits\EntityTrait; +use League\OAuth2\Server\Entities\Traits\TokenEntityTrait; +use League\OAuth2\Server\Exception\OAuthServerException; +use MediaWiki\Extensions\OAuth\Backend\ConsumerAcceptance; +use MediaWiki\Extensions\OAuth\Backend\Utils; +use MediaWiki\MediaWikiServices; +use Throwable; +use User; + +class AccessTokenEntity implements AccessTokenEntityInterface { + use AccessTokenTrait; + use EntityTrait; + use TokenEntityTrait; + + /** + * @var ClientEntity + */ + protected $client; + + /** + * User approval of the client + * + * @var ConsumerAcceptance|bool + */ + private $approval = false; + + /** + * @param ClientEntity $clientEntity + * @param ScopeEntityInterface[] $scopes + * @param string|null $userIdentifier + */ + public function __construct( + ClientEntity $clientEntity, array $scopes, $userIdentifier = null + ) { + $this->approval = $this->setApprovalFromClientScopesUser( + $clientEntity, $scopes, $userIdentifier + ); + + $this->setClient( $clientEntity ); + if ( $clientEntity->getOwnerOnly() ) { + if ( $userIdentifier !== null && $userIdentifier !== $clientEntity->getUserId() ) { + throw new InvalidArgumentException( + '$userIdentifier must be null, or match the client owner user id,' . + ' for owner-only clients, ' . $userIdentifier . ' given' + ); + } + foreach ( $clientEntity->getScopes() as $scope ) { + $this->addScope( $scope ); + } + $this->setUserIdentifier( $clientEntity->getUserId() ); + } else { + foreach ( $scopes as $scope ) { + if ( !in_array( $scope->getIdentifier(), $clientEntity->getGrants() ) ) { + continue; + } + $this->addScope( $scope ); + } + $this->setUserIdentifier( $userIdentifier ); + } + + $this->confirmClientUsable(); + } + + /** + * Get the approval that allows this AT to be created + * + * @return ConsumerAcceptance + */ + public function getApproval() { + return $this->approval; + } + + /** + * Set configured private key + */ + public function setPrivateKeyFromConfig() { + $oauthConfig = MediaWikiServices::getInstance()->getConfigFactory()->makeConfig( 'mwoauth' ); + // Private key to sign the token + $privateKey = new CryptKey( $oauthConfig->get( 'OAuth2PrivateKey' ) ); + $this->setPrivateKey( $privateKey ); + } + + /** + * Get the client that the token was issued to. + * + * @return ClientEntity + */ + public function getClient() { + return $this->client; + } + + /** + * @param ClientEntity $clientEntity + * @param array $scopes + * @param null $userIdentifier + * @return ConsumerAcceptance|bool + */ + private function setApprovalFromClientScopesUser( + ClientEntity $clientEntity, array $scopes, $userIdentifier = null + ) { + if ( $clientEntity->getOwnerOnly() && $userIdentifier === null ) { + $userIdentifier = $clientEntity->getUserId(); + $scopes = $clientEntity->getScopes(); + } + try { + $user = Utils::getLocalUserFromCentralId( $userIdentifier ); + $approval = $clientEntity->getCurrentAuthorization( $user, wfWikiID() ); + } catch ( Throwable $ex ) { + return false; + } + if ( !$approval ) { + return $approval; + } + + $approvedScopes = $approval->getGrants(); + $notApproved = array_filter( + $scopes, + function ( ScopeEntityInterface $scope ) use ( $approvedScopes ) { + return !in_array( $scope->getIdentifier(), $approvedScopes, true ); + } + ); + + return empty( $notApproved ) ? $approval : false; + } + + private function confirmClientUsable() { + $userId = $this->getUserIdentifier() ?? 0; + $user = Utils::getLocalUserFromCentralId( $userId ); + if ( !$user ) { + $user = User::newFromId( 0 ); + } + + if ( !$this->getClient()->isUsableBy( $user ) ) { + throw OAuthServerException::accessDenied( + 'Client ' . $this->getClient()->getIdentifier() . + ' is not usable by user with ID ' . $user->getId() + ); + } + } + +} |