MediaWiki master
ApiUserrights.php
Go to the documentation of this file.
1<?php
2
26namespace MediaWiki\Api;
27
28use ChangeTags;
41
45class ApiUserrights extends ApiBase {
46
48
50 private $mUser = null;
51
52 private UserGroupManager $userGroupManager;
53 private WatchedItemStoreInterface $watchedItemStore;
54
55 public function __construct(
56 ApiMain $mainModule,
57 string $moduleName,
58 UserGroupManager $userGroupManager,
59 WatchedItemStoreInterface $watchedItemStore,
60 WatchlistManager $watchlistManager,
61 UserOptionsLookup $userOptionsLookup
62 ) {
63 parent::__construct( $mainModule, $moduleName );
64 $this->userGroupManager = $userGroupManager;
65 $this->watchedItemStore = $watchedItemStore;
66
67 // Variables needed in ApiWatchlistTrait trait
68 $this->watchlistExpiryEnabled = $this->getConfig()->get( MainConfigNames::WatchlistExpiry );
69 $this->watchlistMaxDuration =
71 $this->watchlistManager = $watchlistManager;
72 $this->userOptionsLookup = $userOptionsLookup;
73 }
74
75 public function execute() {
76 $pUser = $this->getUser();
77
78 // Deny if the user is blocked and doesn't have the full 'userrights' permission.
79 // This matches what Special:UserRights does for the web UI.
80 if ( !$this->getAuthority()->isAllowed( 'userrights' ) ) {
81 $block = $pUser->getBlock( IDBAccessObject::READ_LATEST );
82 if ( $block && $block->isSitewide() ) {
83 $this->dieBlocked( $block );
84 }
85 }
86
88
89 // Figure out expiry times from the input
90 $expiry = (array)$params['expiry'];
91 $add = (array)$params['add'];
92 if ( !$add ) {
93 $expiry = [];
94 } elseif ( count( $expiry ) !== count( $add ) ) {
95 if ( count( $expiry ) === 1 ) {
96 $expiry = array_fill( 0, count( $add ), $expiry[0] );
97 } else {
98 $this->dieWithError( [
99 'apierror-toofewexpiries',
100 count( $expiry ),
101 count( $add )
102 ] );
103 }
104 }
105
106 // Validate the expiries
107 $groupExpiries = [];
108 foreach ( $expiry as $index => $expiryValue ) {
109 $group = $add[$index];
110 $groupExpiries[$group] = SpecialUserRights::expiryToTimestamp( $expiryValue );
111
112 if ( $groupExpiries[$group] === false ) {
113 $this->dieWithError( [ 'apierror-invalidexpiry', wfEscapeWikiText( $expiryValue ) ] );
114 }
115
116 // not allowed to have things expiring in the past
117 if ( $groupExpiries[$group] && $groupExpiries[$group] < wfTimestampNow() ) {
118 $this->dieWithError( [ 'apierror-pastexpiry', wfEscapeWikiText( $expiryValue ) ] );
119 }
120 }
121
122 $user = $this->getUrUser( $params );
123
124 $tags = $params['tags'];
125
126 // Check if user can add tags
127 if ( $tags !== null ) {
128 $ableToTag = ChangeTags::canAddTagsAccompanyingChange( $tags, $this->getAuthority() );
129 if ( !$ableToTag->isOK() ) {
130 $this->dieStatus( $ableToTag );
131 }
132 }
133
134 $form = new SpecialUserRights();
135 $form->setContext( $this->getContext() );
136 $r = [];
137 $r['user'] = $user->getName();
138 $r['userid'] = $user->getId( $user->getWikiId() );
139 [ $r['added'], $r['removed'] ] = $form->doSaveUserGroups(
140 $user,
141 $add,
142 // Don't pass null to doSaveUserGroups() for array params, cast to empty array
143 (array)$params['remove'],
144 $params['reason'],
145 (array)$tags,
146 $groupExpiries
147 );
148
149 $watchlistExpiry = $this->getExpiryFromParams( $params );
150 $watchuser = $params['watchuser'];
151 $userPage = Title::makeTitle( NS_USER, $user->getName() );
152 if ( $watchuser && $user->getWikiId() === UserIdentity::LOCAL ) {
153 $this->setWatch( 'watch', $userPage, $this->getUser(), null, $watchlistExpiry );
154 } else {
155 $watchuser = false;
156 $watchlistExpiry = null;
157 }
158 $r['watchuser'] = $watchuser;
159 if ( $watchlistExpiry !== null ) {
160 $r['watchlistexpiry'] = $this->getWatchlistExpiry(
161 $this->watchedItemStore,
162 $userPage,
163 $this->getUser()
164 );
165 }
166
167 $result = $this->getResult();
168 ApiResult::setIndexedTagName( $r['added'], 'group' );
169 ApiResult::setIndexedTagName( $r['removed'], 'group' );
170 $result->addValue( null, $this->getModuleName(), $r );
171 }
172
177 private function getUrUser( array $params ) {
178 if ( $this->mUser !== null ) {
179 return $this->mUser;
180 }
181
182 $this->requireOnlyOneParameter( $params, 'user', 'userid' );
183
184 $user = $params['user'] ?? '#' . $params['userid'];
185
186 $form = new SpecialUserRights();
187 $form->setContext( $this->getContext() );
188 $status = $form->fetchUser( $user );
189 if ( !$status->isOK() ) {
190 $this->dieStatus( $status );
191 }
192
193 $this->mUser = $status->value;
194
195 return $status->value;
196 }
197
198 public function mustBePosted() {
199 return true;
200 }
201
202 public function isWriteMode() {
203 return true;
204 }
205
206 public function getAllowedParams( $flags = 0 ) {
207 $allGroups = $this->userGroupManager->listAllGroups();
208
209 if ( $flags & ApiBase::GET_VALUES_FOR_HELP ) {
210 sort( $allGroups );
211 }
212
213 $params = [
214 'user' => [
215 ParamValidator::PARAM_TYPE => 'user',
216 UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name', 'id' ],
217 ],
218 'userid' => [
219 ParamValidator::PARAM_TYPE => 'integer',
220 ParamValidator::PARAM_DEPRECATED => true,
221 ],
222 'add' => [
223 ParamValidator::PARAM_TYPE => $allGroups,
224 ParamValidator::PARAM_ISMULTI => true
225 ],
226 'expiry' => [
227 ParamValidator::PARAM_ISMULTI => true,
228 ParamValidator::PARAM_ALLOW_DUPLICATES => true,
229 ParamValidator::PARAM_DEFAULT => 'infinite',
230 ],
231 'remove' => [
232 ParamValidator::PARAM_TYPE => $allGroups,
233 ParamValidator::PARAM_ISMULTI => true
234 ],
235 'reason' => [
236 ParamValidator::PARAM_DEFAULT => ''
237 ],
238 'token' => [
239 // Standard definition automatically inserted
240 ApiBase::PARAM_HELP_MSG_APPEND => [ 'api-help-param-token-webui' ],
241 ],
242 'tags' => [
243 ParamValidator::PARAM_TYPE => 'tags',
244 ParamValidator::PARAM_ISMULTI => true
245 ],
246 'watchuser' => false,
247 ];
248
249 // Params appear in the docs in the order they are defined,
250 // which is why this is here and not at the bottom.
251 // @todo Find better way to support insertion at arbitrary position
252 if ( $this->watchlistExpiryEnabled ) {
253 $params += [
254 'watchlistexpiry' => [
255 ParamValidator::PARAM_TYPE => 'expiry',
256 ExpiryDef::PARAM_MAX => $this->watchlistMaxDuration,
257 ExpiryDef::PARAM_USE_MAX => true,
258 ]
259 ];
260 }
261
262 return $params;
263 }
264
265 public function needsToken() {
266 return 'userrights';
267 }
268
269 protected function getWebUITokenSalt( array $params ) {
270 return $this->getUrUser( $params )->getName();
271 }
272
273 protected function getExamplesMessages() {
274 return [
275 'action=userrights&user=FooBot&add=bot&remove=sysop|bureaucrat&token=123ABC'
276 => 'apihelp-userrights-example-user',
277 'action=userrights&userid=123&add=bot&remove=sysop|bureaucrat&token=123ABC'
278 => 'apihelp-userrights-example-userid',
279 'action=userrights&user=SometimeSysop&add=sysop&expiry=1%20month&token=123ABC'
280 => 'apihelp-userrights-example-expiry',
281 ];
282 }
283
284 public function getHelpUrls() {
285 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:User_group_membership';
286 }
287}
288
290class_alias( ApiUserrights::class, 'ApiUserrights' );
const NS_USER
Definition Defines.php:67
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
wfEscapeWikiText( $input)
Escapes the given text so that it may be output using addWikiText() without any linking,...
array $params
The job parameters.
Recent changes tagging.
static canAddTagsAccompanyingChange(array $tags, ?Authority $performer=null, $checkBlock=true)
Is it OK to allow the user to apply all the specified tags at the same time as they edit/make the cha...
This abstract class implements many basic API functions, and is the base of all API classes.
Definition ApiBase.php:76
dieWithError( $msg, $code=null, $data=null, $httpCode=0)
Abort execution with an error.
Definition ApiBase.php:1577
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:571
getResult()
Get the result object.
Definition ApiBase.php:710
const PARAM_HELP_MSG_APPEND
((string|array|Message)[]) Specify additional i18n messages to append to the normal message for this ...
Definition ApiBase.php:192
dieBlocked(Block $block)
Throw an ApiUsageException, which will (if uncaught) call the main module's error handler and die wit...
Definition ApiBase.php:1605
dieStatus(StatusValue $status)
Throw an ApiUsageException based on the Status object.
Definition ApiBase.php:1632
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:851
requireOnlyOneParameter( $params,... $required)
Die if 0 or more than one of a certain set of parameters is set and not false.
Definition ApiBase.php:990
const GET_VALUES_FOR_HELP
getAllowedParams() flag: When this is set, the result could take longer to generate,...
Definition ApiBase.php:262
This is the main API class, used for both external and internal processing.
Definition ApiMain.php:78
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
getWebUITokenSalt(array $params)
Fetch the salt used in the Web UI corresponding to this module.
getHelpUrls()
Return links to more detailed help pages about the module.
isWriteMode()
Indicates whether this module requires write access to the wiki.
getExamplesMessages()
Returns usage examples for this module.
__construct(ApiMain $mainModule, string $moduleName, UserGroupManager $userGroupManager, WatchedItemStoreInterface $watchedItemStore, WatchlistManager $watchlistManager, UserOptionsLookup $userOptionsLookup)
needsToken()
Returns the token type this module requires in order to execute.
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
mustBePosted()
Indicates whether this module must be called with a POST request.
A class containing constants representing the names of configuration variables.
const WatchlistExpiry
Name constant for the WatchlistExpiry setting, for use with Config::get()
const WatchlistExpiryMaxDuration
Name constant for the WatchlistExpiryMaxDuration setting, for use with Config::get()
Type definition for user types.
Definition UserDef.php:27
Special page to allow managing user group membership.
static expiryToTimestamp( $expiry)
Converts a user group membership expiry string into a timestamp.
Represents a title within MediaWiki.
Definition Title.php:78
Provides access to user options.
Service for formatting and validating API parameters.
Type definition for expiry timestamps.
Definition ExpiryDef.php:17
trait ApiWatchlistTrait
An ApiWatchlistTrait adds class properties and convenience methods for APIs that allow you to watch a...
Interface for objects representing user identity.
Interface for database access objects.
getExpiryFromParams(array $params)
Get formatted expiry from the given parameters, or null if no expiry was provided.
setWatch(string $watch, PageIdentity $page, User $user, ?string $userOption=null, ?string $expiry=null)
Set a watch (or unwatch) based the based on a watchlist parameter.
getWatchlistExpiry(WatchedItemStoreInterface $store, PageIdentity $page, UserIdentity $user)
Get existing expiry from the database.