summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Evans <grknight@gentoo.org>2020-10-06 11:22:15 -0400
committerBrian Evans <grknight@gentoo.org>2020-10-06 11:22:15 -0400
commit4a2d3a0b7596731e11ef9257138653bec81d6fd3 (patch)
tree0e33fdfd38e791cc9ffe46f11f954a7dce86d618 /OAuth/src/Entity/AccessTokenEntity.php
parentOpenIDConnect: Fix newly protected function (diff)
downloadextensions-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.php150
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()
+ );
+ }
+ }
+
+}