Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 83
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiSetGlobalAccountStatus
0.00% covered (danger)
0.00%
0 / 83
0.00% covered (danger)
0.00%
0 / 7
272
0.00% covered (danger)
0.00%
0 / 1
 execute
0.00% covered (danger)
0.00%
0 / 40
0.00% covered (danger)
0.00%
0 / 1
90
 getStateHash
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 getAllowedParams
0.00% covered (danger)
0.00%
0 / 27
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
 mustBePosted
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isWriteMode
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
1<?php
2/**
3 * Created on Oct 17, 2012
4 *
5 * CentralAuth extension
6 *
7 * Copyright (C) 2012 Alex Monk (krenair@gmail.com)
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 * http://www.gnu.org/copyleft/gpl.html
23 */
24
25namespace MediaWiki\Extension\CentralAuth\Api;
26
27use MediaWiki\Api\ApiBase;
28use MediaWiki\Extension\CentralAuth\User\CentralAuthUser;
29use Wikimedia\ParamValidator\ParamValidator;
30
31/**
32 * API module to lock/hide a global account.
33 *
34 * @ingroup API
35 * @ingroup Extensions
36 */
37class ApiSetGlobalAccountStatus extends ApiBase {
38
39    /** @var array<string, int> Mapping for string hidden values to the new int "level" values. */
40    private const HIDDEN_LEVEL_MAPPING = [
41        ''           => CentralAuthUser::HIDDEN_LEVEL_NONE,
42        'lists'      => CentralAuthUser::HIDDEN_LEVEL_LISTS,
43        'suppressed' => CentralAuthUser::HIDDEN_LEVEL_SUPPRESSED,
44    ];
45
46    /** @var array<int, string> Mapping for new int "level" hidden values to the string values. */
47    private const HIDDEN_LEVEL_MAPPING_REVERSE = [
48        CentralAuthUser::HIDDEN_LEVEL_NONE       => '',
49        CentralAuthUser::HIDDEN_LEVEL_LISTS      => 'lists',
50        CentralAuthUser::HIDDEN_LEVEL_SUPPRESSED => 'suppressed',
51    ];
52
53    public function execute() {
54        // Heavily based on code from SpecialCentralAuth::doSubmit
55        $params = $this->extractRequestParams();
56        $this->requireAtLeastOneParameter( $params, 'locked', 'hidden' );
57
58        $this->checkUserRightsAny( 'centralauth-lock' );
59
60        $globalUser = CentralAuthUser::getPrimaryInstanceByName( $params['user'] );
61        if ( !$globalUser->exists() ||
62            ( $globalUser->isSuppressed() && !$this->getAuthority()->isAllowed( 'centralauth-suppress' ) )
63        ) {
64            $this->dieWithError(
65                [ 'nosuchusershort', wfEscapeWikiText( $globalUser->getName() ) ], 'nosuchuser'
66            );
67        }
68
69        if ( !$params['locked'] ) {
70            // Don't lock or unlock
71            $setLocked = null;
72        } else {
73            $setLocked = $params['locked'] === 'lock';
74        }
75
76        if ( $params['hidden'] == null ) {
77            $setHidden = null;
78        } else {
79            $setHidden = self::HIDDEN_LEVEL_MAPPING[$params['hidden']];
80        }
81
82        $reason = $params['reason'];
83        $stateCheck = $params['statecheck'];
84
85        if ( $stateCheck && $stateCheck !== $this->getStateHash( $globalUser ) ) {
86            $this->dieWithError( 'apierror-centralauth-editconflict', 'editconflict' );
87        }
88
89        $status = $globalUser->adminLockHide(
90            $setLocked,
91            $setHidden,
92            $reason,
93            $this->getContext()
94        );
95
96        $hidden = self::HIDDEN_LEVEL_MAPPING_REVERSE[$globalUser->getHiddenLevelInt()];
97
98        // Logging etc
99        if ( $status->isGood() ) {
100            $this->getResult()->addValue( null, $this->getModuleName(), [
101                'user' => $globalUser->getName(),
102                'locked' => $globalUser->isLocked(),
103                'hidden' => $hidden,
104                'reason' => $reason
105            ] );
106        } else {
107            $error = $this->getErrorFormatter()->arrayFromStatus( $status );
108            $this->getResult()->addValue( 'error', null, $error );
109            $this->getResult()->addValue( null, $this->getModuleName(), [
110                'user' => $globalUser->getName(),
111                'locked' => $globalUser->isLocked(),
112                'hidden' => $hidden,
113            ] );
114        }
115    }
116
117    /**
118     * Calculates a state hash for edit conflict detection. This is separate
119     * from {@link CentralAuthUser::getStateHash()} as it uses the 'old'
120     * (non-normalized string) values for the hidden level value.
121     *
122     * @param CentralAuthUser $user
123     * @return string
124     */
125    private function getStateHash( CentralAuthUser $user ): string {
126        $parts = [
127            (string)$user->getId(),
128            $user->getName(),
129            self::HIDDEN_LEVEL_MAPPING_REVERSE[$user->getHiddenLevelInt()],
130            $user->isLocked() ? '1' : '0'
131        ];
132
133        return md5( implode( ':', $parts ) );
134    }
135
136    /** @inheritDoc */
137    public function getAllowedParams() {
138        return [
139            'user' => [
140                ParamValidator::PARAM_TYPE => 'string',
141                ParamValidator::PARAM_REQUIRED => true
142            ],
143            'locked' => [
144                ParamValidator::PARAM_TYPE => [
145                    'lock',
146                    'unlock',
147                    // Unset - basically means 'do not modify lock status'
148                    ''
149                ]
150            ],
151            'hidden' => [
152                ParamValidator::PARAM_TYPE => [
153                    '',
154                    'lists',
155                    'suppressed'
156                ]
157            ],
158            'reason' => [
159                ParamValidator::PARAM_TYPE => 'string'
160            ],
161            'statecheck' => [
162                ParamValidator::PARAM_TYPE => 'string',
163                ParamValidator::PARAM_REQUIRED => false
164            ],
165        ];
166    }
167
168    /** @inheritDoc */
169    protected function getExamplesMessages() {
170        return [
171            'action=setglobalaccountstatus&user=Example&locked=lock&hidden=&reason=Spam'
172                => 'apihelp-setglobalaccountstatus-example-1',
173            'action=setglobalaccountstatus&user=Example&locked=unlock&hidden=suppressed&reason=I%20can'
174                => 'apihelp-setglobalaccountstatus-example-2',
175        ];
176    }
177
178    /** @inheritDoc */
179    public function mustBePosted() {
180        return true;
181    }
182
183    /** @inheritDoc */
184    public function isWriteMode() {
185        return true;
186    }
187
188    /** @inheritDoc */
189    public function needsToken() {
190        return 'setglobalaccountstatus';
191    }
192}