Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 93
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 / 93
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 / 11
0.00% covered (danger)
0.00%
0 / 1
12
 newFromUserConsumerWiki
0.00% covered (danger)
0.00%
0 / 16
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 FormatJson;
6use IContextSource;
7use IDBAccessObject;
8use MediaWiki\MediaWikiServices;
9use Wikimedia\Rdbms\IDatabase;
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        $row = $db->selectRow( static::getTable(),
104            array_values( static::getFieldColumnMap() ),
105            [ 'oaac_access_token' => (string)$token ],
106            __METHOD__,
107            ( $flags & IDBAccessObject::READ_LOCKING ) ? [ 'FOR UPDATE' ] : []
108        );
109
110        if ( $row ) {
111            $consumer = new self();
112            $consumer->loadFromRow( $db, $row );
113            return $consumer;
114        } else {
115            return false;
116        }
117    }
118
119    /**
120     * @param IDatabase $db
121     * @param int $userId of user who authorized (central wiki's id)
122     * @param Consumer $consumer
123     * @param string $wiki wiki associated with the acceptance
124     * @param int $flags ConsumerAcceptance::READ_* bitfield
125     * @param int $oauthVersion
126     * @return ConsumerAcceptance|bool
127     */
128    public static function newFromUserConsumerWiki(
129        IDatabase $db, $userId, $consumer,
130        $wiki, $flags = 0, $oauthVersion = Consumer::OAUTH_VERSION_1
131    ) {
132        $row = $db->selectRow( static::getTable(),
133            array_values( static::getFieldColumnMap() ),
134            [
135                'oaac_user_id' => $userId,
136                'oaac_consumer_id' => $consumer->getId(),
137                'oaac_oauth_version' => $oauthVersion,
138                'oaac_wiki' => (string)$wiki
139            ],
140            __METHOD__,
141            ( $flags & IDBAccessObject::READ_LOCKING ) ? [ 'FOR UPDATE' ] : []
142        );
143
144        if ( $row ) {
145            $consumer = new self();
146            $consumer->loadFromRow( $db, $row );
147            return $consumer;
148        } else {
149            return false;
150        }
151    }
152
153    /**
154     * Database ID.
155     * @return int
156     */
157    public function getId() {
158        return $this->get( 'id' );
159    }
160
161    /**
162     * Wiki on which the user has authorized the consumer to access their account. Wiki ID or '*'
163     * for all.
164     * @return string
165     */
166    public function getWiki() {
167        return $this->get( 'wiki' );
168    }
169
170    /**
171     * Central user ID of the authorizing user.
172     * @return int
173     */
174    public function getUserId() {
175        return $this->get( 'userId' );
176    }
177
178    /**
179     * Database ID of the consumer.
180     * @return int
181     */
182    public function getConsumerId() {
183        return $this->get( 'consumerId' );
184    }
185
186    /**
187     * The access token for the OAuth protocol
188     * @return string
189     */
190    public function getAccessToken() {
191        return $this->get( 'accessToken' );
192    }
193
194    /**
195     * Secret key used to derive the access secret for the OAuth protocol.
196     * The actual access secret will be calculated via Utils::hmacDBSecret() to mitigate
197     * DB leaks.
198     * @return string
199     */
200    public function getAccessSecret() {
201        return $this->get( 'accessSecret' );
202    }
203
204    /**
205     * The list of grants which have been granted.
206     * @return string[]
207     */
208    public function getGrants() {
209        return $this->get( 'grants' );
210    }
211
212    /**
213     * Date of the authorization, in TS_MW format.
214     * @return string
215     */
216    public function getAccepted() {
217        return $this->get( 'accepted' );
218    }
219
220    /**
221     * @return int
222     */
223    public function getOAuthVersion() {
224        return (int)$this->get( 'oauth_version' );
225    }
226
227    protected function normalizeValues() {
228        $this->userId = (int)$this->userId;
229        $this->consumerId = (int)$this->consumerId;
230        $this->accepted = wfTimestamp( TS_MW, $this->accepted );
231        $this->grants = (array)$this->grants;
232    }
233
234    protected function encodeRow( IDatabase $db, $row ) {
235        if ( (int)$row['oaac_user_id'] === 0 ) {
236            throw new MWOAuthException( 'mwoauth-consumer-access-no-user', [
237                'consumer_id' => $row['oaac_consumer_id'],
238            ] );
239        }
240        // For compatibility with other wikis in the farm, un-remap some grants
241        foreach ( Consumer::$mapBackCompatGrants as $old => $new ) {
242            while ( ( $i = array_search( $new, $row['oaac_grants'], true ) ) !== false ) {
243                $row['oaac_grants'][$i] = $old;
244            }
245        }
246
247        $row['oaac_grants'] = FormatJson::encode( $row['oaac_grants'] );
248        $row['oaac_accepted'] = $db->timestamp( $row['oaac_accepted'] );
249        return $row;
250    }
251
252    protected function decodeRow( IDatabase $db, $row ) {
253        $row['oaac_grants'] = FormatJson::decode( $row['oaac_grants'], true );
254        $row['oaac_accepted'] = wfTimestamp( TS_MW, $row['oaac_accepted'] );
255
256        // For backwards compatibility, remap some grants
257        foreach ( Consumer::$mapBackCompatGrants as $old => $new ) {
258            while ( ( $i = array_search( $old, $row['oaac_grants'], true ) ) !== false ) {
259                $row['oaac_grants'][$i] = $new;
260            }
261        }
262
263        return $row;
264    }
265
266    protected function userCanSee( $name, IContextSource $context ) {
267        $centralUserId = Utils::getCentralIdFromLocalUser( $context->getUser() );
268        $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
269
270        if ( $this->userId != $centralUserId
271            && !$permissionManager->userHasRight( $context->getUser(), 'mwoauthviewprivate' )
272        ) {
273            return $context->msg( 'mwoauth-field-private' );
274        } else {
275            return true;
276        }
277    }
278
279    protected function userCanSeePrivate( $name, IContextSource $context ) {
280        $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
281
282        if ( !$permissionManager->userHasRight( $context->getUser(), 'mwoauthviewprivate' ) ) {
283            return $context->msg( 'mwoauth-field-private' );
284        } else {
285            return $this->userCanSee( $name, $context );
286        }
287    }
288
289    protected function userCanSeeSecret( $name, IContextSource $context ) {
290        return $context->msg( 'mwoauth-field-private' );
291    }
292}