Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 97
0.00% covered (danger)
0.00%
0 / 19
CRAP
0.00% covered (danger)
0.00%
0 / 1
ConsumerAcceptance
0.00% covered (danger)
0.00%
0 / 97
0.00% covered (danger)
0.00%
0 / 19
992
0.00% covered (danger)
0.00%
0 / 1
 getSchema
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
2
 getFieldPermissionChecks
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
2
 newFromToken
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
12
 newFromUserConsumerWiki
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
12
 getId
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getWiki
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getUserId
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getConsumerId
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getAccessToken
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getAccessSecret
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getGrants
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getAccepted
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getOAuthVersion
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 normalizeValues
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 encodeRow
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
20
 decodeRow
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 userCanSee
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 userCanSeePrivate
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 userCanSeeSecret
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace MediaWiki\Extension\OAuth\Backend;
4
5use MediaWiki\Context\IContextSource;
6use MediaWiki\Json\FormatJson;
7use MediaWiki\MediaWikiServices;
8use Wikimedia\Rdbms\IDatabase;
9use Wikimedia\Rdbms\IDBAccessObject;
10
11/**
12 * (c) Aaron Schulz 2013, GPL
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License along
25 * with this program; if not, write to the Free Software Foundation, Inc.,
26 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 * http://www.gnu.org/copyleft/gpl.html
28 */
29
30/**
31 * Representation of an OAuth consumer acceptance.
32 * Created when the user clicks through the OAuth authorization dialog, this allows
33 * the specified consumer to perform actions in the name of the user
34 * (subject to the grant and wiki restrictions stored in the acceptance object).
35 */
36class ConsumerAcceptance extends MWOAuthDAO {
37    /** @var int Unique ID */
38    protected $id;
39
40    /** @var string Wiki ID the application can be used on (or "*" for all) */
41    protected $wiki;
42
43    /** @var int Publisher user ID (on central wiki) */
44    protected $userId;
45
46    /** @var int */
47    protected $consumerId;
48
49    /** @var string Hex token */
50    protected $accessToken;
51
52    /** @var string Secret HMAC key */
53    protected $accessSecret;
54
55    /** @var string[] List of grants */
56    protected $grants;
57
58    /** @var string TS_MW timestamp of acceptance */
59    protected $accepted;
60
61    /** @var string */
62    protected $oauth_version;
63
64    protected static function getSchema() {
65        return [
66            'table'               => 'oauth_accepted_consumer',
67            'fieldColumnMap'      => [
68                'id'              => 'oaac_id',
69                'wiki'            => 'oaac_wiki',
70                'userId'          => 'oaac_user_id',
71                'consumerId'      => 'oaac_consumer_id',
72                'accessToken'     => 'oaac_access_token',
73                'accessSecret'    => 'oaac_access_secret',
74                'grants'          => 'oaac_grants',
75                'accepted'        => 'oaac_accepted',
76                'oauth_version'   => 'oaac_oauth_version',
77            ],
78            'idField'             => 'id',
79            'autoIncrField'       => 'id',
80        ];
81    }
82
83    protected static function getFieldPermissionChecks() {
84        return [
85            'wiki'          => 'userCanSee',
86            'userId'        => 'userCanSee',
87            'consumerId'    => 'userCanSee',
88            'accessToken'   => 'userCanSeePrivate',
89            'accessSecret'  => 'userCanSeeSecret',
90            'grants'        => 'userCanSee',
91            'accepted'      => 'userCanSee',
92            'oauth_version' => 'userCanSee',
93        ];
94    }
95
96    /**
97     * @param IDatabase $db
98     * @param string $token Access token
99     * @param int $flags ConsumerAcceptance::READ_* bitfield
100     * @return ConsumerAcceptance|bool
101     */
102    public static function newFromToken( IDatabase $db, $token, $flags = 0 ) {
103        $queryBuilder = $db->newSelectQueryBuilder()
104            ->select( array_values( static::getFieldColumnMap() ) )
105            ->from( static::getTable() )
106            ->where( [ 'oaac_access_token' => (string)$token ] )
107            ->caller( __METHOD__ );
108        if ( $flags & IDBAccessObject::READ_LOCKING ) {
109            $queryBuilder->forUpdate();
110        }
111        $row = $queryBuilder->fetchRow();
112
113        if ( $row ) {
114            $consumer = new self();
115            $consumer->loadFromRow( $db, $row );
116            return $consumer;
117        } else {
118            return false;
119        }
120    }
121
122    /**
123     * @param IDatabase $db
124     * @param int $userId of user who authorized (central wiki's id)
125     * @param Consumer $consumer
126     * @param string $wiki wiki associated with the acceptance
127     * @param int $flags ConsumerAcceptance::READ_* bitfield
128     * @param int $oauthVersion
129     * @return ConsumerAcceptance|bool
130     */
131    public static function newFromUserConsumerWiki(
132        IDatabase $db, $userId, $consumer,
133        $wiki, $flags = 0, $oauthVersion = Consumer::OAUTH_VERSION_1
134    ) {
135        $queryBuilder = $db->newSelectQueryBuilder()
136            ->select( array_values( static::getFieldColumnMap() ) )
137            ->from( static::getTable() )
138            ->where( [
139                'oaac_user_id' => $userId,
140                'oaac_consumer_id' => $consumer->getId(),
141                'oaac_oauth_version' => $oauthVersion,
142                'oaac_wiki' => (string)$wiki
143            ] )
144            ->caller( __METHOD__ );
145        if ( $flags & IDBAccessObject::READ_LOCKING ) {
146            $queryBuilder->forUpdate();
147        }
148        $row = $queryBuilder->fetchRow();
149
150        if ( $row ) {
151            $consumer = new self();
152            $consumer->loadFromRow( $db, $row );
153            return $consumer;
154        } else {
155            return false;
156        }
157    }
158
159    /**
160     * Database ID.
161     * @return int
162     */
163    public function getId() {
164        return $this->get( 'id' );
165    }
166
167    /**
168     * Wiki on which the user has authorized the consumer to access their account. Wiki ID or '*'
169     * for all.
170     * @return string
171     */
172    public function getWiki() {
173        return $this->get( 'wiki' );
174    }
175
176    /**
177     * Central user ID of the authorizing user.
178     * @return int
179     */
180    public function getUserId() {
181        return $this->get( 'userId' );
182    }
183
184    /**
185     * Database ID of the consumer.
186     * @return int
187     */
188    public function getConsumerId() {
189        return $this->get( 'consumerId' );
190    }
191
192    /**
193     * The access token for the OAuth protocol
194     * @return string
195     */
196    public function getAccessToken() {
197        return $this->get( 'accessToken' );
198    }
199
200    /**
201     * Secret key used to derive the access secret for the OAuth protocol.
202     * The actual access secret will be calculated via Utils::hmacDBSecret() to mitigate
203     * DB leaks.
204     * @return string
205     */
206    public function getAccessSecret() {
207        return $this->get( 'accessSecret' );
208    }
209
210    /**
211     * The list of grants which have been granted.
212     * @return string[]
213     */
214    public function getGrants() {
215        return $this->get( 'grants' );
216    }
217
218    /**
219     * Date of the authorization, in TS_MW format.
220     * @return string
221     */
222    public function getAccepted() {
223        return $this->get( 'accepted' );
224    }
225
226    /**
227     * @return int
228     */
229    public function getOAuthVersion() {
230        return (int)$this->get( 'oauth_version' );
231    }
232
233    protected function normalizeValues() {
234        $this->userId = (int)$this->userId;
235        $this->consumerId = (int)$this->consumerId;
236        $this->accepted = wfTimestamp( TS_MW, $this->accepted );
237        $this->grants = (array)$this->grants;
238    }
239
240    protected function encodeRow( IDatabase $db, $row ) {
241        if ( (int)$row['oaac_user_id'] === 0 ) {
242            throw new MWOAuthException( 'mwoauth-consumer-access-no-user', [
243                'consumer_id' => $row['oaac_consumer_id'],
244            ] );
245        }
246        // For compatibility with other wikis in the farm, un-remap some grants
247        foreach ( Consumer::$mapBackCompatGrants as $old => $new ) {
248            // phpcs:ignore Generic.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
249            while ( ( $i = array_search( $new, $row['oaac_grants'], true ) ) !== false ) {
250                $row['oaac_grants'][$i] = $old;
251            }
252        }
253
254        $row['oaac_grants'] = FormatJson::encode( $row['oaac_grants'] );
255        $row['oaac_accepted'] = $db->timestamp( $row['oaac_accepted'] );
256        return $row;
257    }
258
259    protected function decodeRow( IDatabase $db, $row ) {
260        $row['oaac_grants'] = FormatJson::decode( $row['oaac_grants'], true );
261        $row['oaac_accepted'] = wfTimestamp( TS_MW, $row['oaac_accepted'] );
262
263        // For backwards compatibility, remap some grants
264        foreach ( Consumer::$mapBackCompatGrants as $old => $new ) {
265            // phpcs:ignore Generic.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
266            while ( ( $i = array_search( $old, $row['oaac_grants'], true ) ) !== false ) {
267                $row['oaac_grants'][$i] = $new;
268            }
269        }
270
271        return $row;
272    }
273
274    protected function userCanSee( $name, IContextSource $context ) {
275        $centralUserId = Utils::getCentralIdFromLocalUser( $context->getUser() );
276        $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
277
278        if ( $this->userId != $centralUserId
279            && !$permissionManager->userHasRight( $context->getUser(), 'mwoauthviewprivate' )
280        ) {
281            return $context->msg( 'mwoauth-field-private' );
282        } else {
283            return true;
284        }
285    }
286
287    protected function userCanSeePrivate( $name, IContextSource $context ) {
288        $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
289
290        if ( !$permissionManager->userHasRight( $context->getUser(), 'mwoauthviewprivate' ) ) {
291            return $context->msg( 'mwoauth-field-private' );
292        } else {
293            return $this->userCanSee( $name, $context );
294        }
295    }
296
297    protected function userCanSeeSecret( $name, IContextSource $context ) {
298        return $context->msg( 'mwoauth-field-private' );
299    }
300}