Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 55
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiOATHValidate
0.00% covered (danger)
0.00%
0 / 55
0.00% covered (danger)
0.00%
0 / 5
182
0.00% covered (danger)
0.00%
0 / 1
 execute
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 1
90
 isInternal
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 needsToken
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getAllowedParams
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 getExamplesMessages
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
17 */
18
19namespace MediaWiki\Extension\OATHAuth\Api\Module;
20
21use ApiBase;
22use ApiResult;
23use FormatJson;
24use MediaWiki\Extension\OATHAuth\IModule;
25use MediaWiki\Logger\LoggerFactory;
26use MediaWiki\MediaWikiServices;
27use Wikimedia\ParamValidator\ParamValidator;
28
29/**
30 * Validate an OATH token.
31 *
32 * @ingroup API
33 * @ingroup Extensions
34 */
35class ApiOATHValidate extends ApiBase {
36    public function execute() {
37        $this->requirePostedParameters( [ 'token', 'data' ] );
38        // messages used: right-oathauth-api-all, action-oathauth-api-all,
39        $this->checkUserRightsAny( 'oathauth-api-all' );
40
41        $params = $this->extractRequestParams();
42        if ( $params['user'] === null ) {
43            $user = $this->getUser();
44        } else {
45            $user = MediaWikiServices::getInstance()->getUserFactory()
46                ->newFromName( $params['user'] );
47            if ( $user === null ) {
48                $this->dieWithError( 'noname' );
49            }
50        }
51
52        // Don't increase pingLimiter, just check for limit exceeded.
53        if ( $user->pingLimiter( 'badoath', 0 ) ) {
54            $this->dieWithError( 'apierror-ratelimited' );
55        }
56
57        $result = [
58            ApiResult::META_BC_BOOLS => [ 'enabled', 'valid' ],
59            'enabled' => false,
60            'valid' => false,
61        ];
62
63        if ( $user->isNamed() ) {
64            $userRepo = MediaWikiServices::getInstance()->getService( 'OATHUserRepository' );
65            $authUser = $userRepo->findByUser( $user );
66            if ( $authUser ) {
67                $module = $authUser->getModule();
68                if ( $module instanceof IModule ) {
69                    $data = [];
70                    $decoded = FormatJson::decode( $params['data'], true );
71                    if ( is_array( $decoded ) ) {
72                        $data = $decoded;
73                    }
74
75                    $result['enabled'] = $module->isEnabled( $authUser );
76                    $result['valid'] = $module->verify( $authUser, $data ) !== false;
77
78                    if ( !$result['valid'] ) {
79                        // Increase rate limit counter for failed request
80                        $user->pingLimiter( 'badoath' );
81
82                        LoggerFactory::getInstance( 'authentication' )->info(
83                            'OATHAuth user {user} failed OTP token/recovery code from {clientip}',
84                            [
85                                'user'     => $user,
86                                'clientip' => $user->getRequest()->getIP(),
87                            ]
88                        );
89                    }
90                }
91            }
92        }
93
94        $this->getResult()->addValue( null, $this->getModuleName(), $result );
95    }
96
97    /** @inheritDoc */
98    public function isInternal() {
99        return true;
100    }
101
102    /** @inheritDoc */
103    public function needsToken() {
104        return 'csrf';
105    }
106
107    /**
108     * @return array
109     */
110    public function getAllowedParams() {
111        return [
112            'user' => [
113                ParamValidator::PARAM_TYPE => 'user',
114            ],
115            'data' => [
116                ParamValidator::PARAM_TYPE => 'string',
117                ParamValidator::PARAM_REQUIRED => true,
118            ]
119        ];
120    }
121
122    /**
123     * @return array
124     */
125    protected function getExamplesMessages() {
126        return [
127            'action=oathvalidate&data={"token":"123456"}&token=123ABC'
128                => 'apihelp-oathvalidate-example-1',
129            'action=oathvalidate&user=Example&data={"token":"123456"}&token=123ABC'
130                => 'apihelp-oathvalidate-example-3',
131        ];
132    }
133}