summaryrefslogtreecommitdiff
blob: f7ab4f3a03449aece90f80104f9cb5dd2104d662 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
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()
			);
		}
	}

}