Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
54.39% |
31 / 57 |
|
40.00% |
4 / 10 |
CRAP | |
0.00% |
0 / 1 |
AccessTokenRepository | |
54.39% |
31 / 57 |
|
40.00% |
4 / 10 |
44.43 | |
0.00% |
0 / 1 |
__construct | |
40.00% |
2 / 5 |
|
0.00% |
0 / 1 |
2.86 | |||
getNewToken | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
persistNewAccessToken | |
87.50% |
7 / 8 |
|
0.00% |
0 / 1 |
2.01 | |||
revokeAccessToken | |
14.29% |
1 / 7 |
|
0.00% |
0 / 1 |
4.52 | |||
isAccessTokenRevoked | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
2 | |||
deleteForApprovalId | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
getApprovalId | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
6 | |||
getDbDataFromTokenEntity | |
80.00% |
8 / 10 |
|
0.00% |
0 / 1 |
3.07 | |||
getTableName | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getIdentifierField | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\OAuth\Repository; |
4 | |
5 | use League\OAuth2\Server\Entities\AccessTokenEntityInterface; |
6 | use League\OAuth2\Server\Entities\ClientEntityInterface; |
7 | use League\OAuth2\Server\Entities\ScopeEntityInterface; |
8 | use League\OAuth2\Server\Exception\OAuthServerException; |
9 | use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException; |
10 | use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface; |
11 | use MediaWiki\Extension\OAuth\Entity\AccessTokenEntity; |
12 | use MediaWiki\Extension\OAuth\Entity\ClientEntity; |
13 | use MediaWiki\MediaWikiServices; |
14 | |
15 | class AccessTokenRepository extends DatabaseRepository implements AccessTokenRepositoryInterface { |
16 | private const FIELD_EXPIRES = 'oaat_expires'; |
17 | private const FIELD_ACCEPTANCE_ID = 'oaat_acceptance_id'; |
18 | private const FIELD_REVOKED = 'oaat_revoked'; |
19 | |
20 | /** @var string */ |
21 | private $issuer; |
22 | |
23 | /** |
24 | * @param string|null $issuer |
25 | */ |
26 | public function __construct( |
27 | string $issuer = null |
28 | ) { |
29 | if ( !$issuer ) { |
30 | // TODO: When the extension is converted to proper use of DI, |
31 | // this needs to be always injected. |
32 | $issuer = MediaWikiServices::getInstance() |
33 | ->getMainConfig() |
34 | ->get( 'CanonicalServer' ); |
35 | } |
36 | $this->issuer = $issuer; |
37 | } |
38 | |
39 | /** |
40 | * Create a new access token |
41 | * |
42 | * @param ClientEntityInterface|ClientEntity $clientEntity |
43 | * @param ScopeEntityInterface[] $scopes |
44 | * @param string|int|null $userIdentifier |
45 | * @return AccessTokenEntityInterface |
46 | * @throws OAuthServerException |
47 | */ |
48 | public function getNewToken( ClientEntityInterface $clientEntity, |
49 | array $scopes, $userIdentifier = null ) { |
50 | return new AccessTokenEntity( $clientEntity, $scopes, |
51 | $this->issuer, $userIdentifier ); |
52 | } |
53 | |
54 | /** |
55 | * Persists a new access token to permanent storage. |
56 | * |
57 | * @param AccessTokenEntityInterface|AccessTokenEntity $accessTokenEntity |
58 | * |
59 | * @throws UniqueTokenIdentifierConstraintViolationException |
60 | */ |
61 | public function persistNewAccessToken( AccessTokenEntityInterface $accessTokenEntity ) { |
62 | if ( $this->identifierExists( $accessTokenEntity->getIdentifier() ) ) { |
63 | throw UniqueTokenIdentifierConstraintViolationException::create(); |
64 | } |
65 | |
66 | $data = $this->getDbDataFromTokenEntity( $accessTokenEntity ); |
67 | |
68 | $this->getDB( DB_PRIMARY )->newInsertQueryBuilder() |
69 | ->insertInto( $this->getTableName() ) |
70 | ->row( $data ) |
71 | ->caller( __METHOD__ ) |
72 | ->execute(); |
73 | } |
74 | |
75 | /** |
76 | * Revoke an access token. |
77 | * |
78 | * @param string $tokenId |
79 | */ |
80 | public function revokeAccessToken( $tokenId ) { |
81 | if ( $this->identifierExists( $tokenId ) ) { |
82 | $this->getDB( DB_PRIMARY )->newUpdateQueryBuilder() |
83 | ->update( $this->getTableName() ) |
84 | ->set( [ static::FIELD_REVOKED => 1 ] ) |
85 | ->where( [ $this->getIdentifierField() => $tokenId ] ) |
86 | ->caller( __METHOD__ ) |
87 | ->execute(); |
88 | } |
89 | } |
90 | |
91 | /** |
92 | * Check if the access token has been revoked. |
93 | * |
94 | * @param string $tokenId |
95 | * |
96 | * @return bool Return true if this token has been revoked |
97 | */ |
98 | public function isAccessTokenRevoked( $tokenId ) { |
99 | $row = $this->getDB()->selectRow( |
100 | $this->getTableName(), |
101 | [ static::FIELD_REVOKED ], |
102 | [ $this->getIdentifierField() => $tokenId ], |
103 | __METHOD__ |
104 | ); |
105 | if ( !$row ) { |
106 | return true; |
107 | } |
108 | return (bool)$row->{static::FIELD_REVOKED}; |
109 | } |
110 | |
111 | /** |
112 | * Delete all access tokens issued with provided approval |
113 | * |
114 | * @param int $approvalId |
115 | */ |
116 | public function deleteForApprovalId( $approvalId ) { |
117 | $this->getDB( DB_PRIMARY )->newDeleteQueryBuilder() |
118 | ->deleteFrom( $this->getTableName() ) |
119 | ->where( [ static::FIELD_ACCEPTANCE_ID => $approvalId ] ) |
120 | ->caller( __METHOD__ ) |
121 | ->execute(); |
122 | } |
123 | |
124 | /** |
125 | * Get ID of the approval bound to this AT |
126 | * |
127 | * @param string $tokenId |
128 | * @return bool|int |
129 | */ |
130 | public function getApprovalId( $tokenId ) { |
131 | $row = $this->getDB()->selectRow( |
132 | $this->getTableName(), |
133 | [ static::FIELD_ACCEPTANCE_ID ], |
134 | [ $this->getIdentifierField() => $tokenId ], |
135 | __METHOD__ |
136 | ); |
137 | |
138 | if ( $row ) { |
139 | return (int)$row->{static::FIELD_ACCEPTANCE_ID}; |
140 | } |
141 | |
142 | return false; |
143 | } |
144 | |
145 | private function getDbDataFromTokenEntity( AccessTokenEntity $accessTokenEntity ) { |
146 | $expiry = $accessTokenEntity->getExpiryDateTime()->getTimestamp(); |
147 | if ( $expiry > 9223371197536780800 ) { |
148 | $expiry = 'infinity'; |
149 | } |
150 | return [ |
151 | $this->getIdentifierField() => $accessTokenEntity->getIdentifier(), |
152 | static::FIELD_EXPIRES => $this->getDB()->encodeExpiry( $expiry ), |
153 | static::FIELD_ACCEPTANCE_ID => $accessTokenEntity->getApproval() ? |
154 | $accessTokenEntity->getApproval()->getId() : |
155 | 0 |
156 | ]; |
157 | } |
158 | |
159 | protected function getTableName(): string { |
160 | return 'oauth2_access_tokens'; |
161 | } |
162 | |
163 | protected function getIdentifierField(): string { |
164 | return 'oaat_identifier'; |
165 | } |
166 | } |