Page MenuHomePhabricator

Drop writeapi MediaWiki right
Open, MediumPublic

Description

The writeapi permission controls whether one is allowed to use action API endpoints which do some kind of write. This is not terribly useful:

  • By default it's given to all users, even anonymous ones.
  • Write permission handling is somewhat mixed up with write performance handling (ie. whether the API does DB writes) and as a result all kinds of conceptually non-write APIs require this permission.
    • Specifically, the login API requires it, so revoking it from anonymous users makes the site unusable unless it uses some very atypical authentication method.
  • All actions that actually change something have their own user rights so writeapi isn't really needed for access control.
  • writeapi check are applied at the API endpoints, so usually they can be circumvented by performing the same action via the web interface.

Requiring writeapi for login is a recent change (T283394: ApiLogin and ApiClientLogin should return true for isWriteMode()) necessitated by the coupling of write permission flags and write performance flags. A bunch of third-party wikis disabled writeapi for anonymous users; there is not much point to that, but the documentation kind of implied it's a useful thing to do (e.g. here) and it didn't break anything. But with the change to login APIs, it does break a lot of things, so let's get rid of it and turn Rest\Handler::isWriteAllowed() and similar into performance-only flags.

A possible counterargument would be that those flags come in pairs (e.g. with Rest\Handler::isReadAllowed()) and the read flag is meaningful for permission handling - there is no readapi right, just read, and that is used as the main access control mechanism for all kinds of things. So having those flags fulfill different functions would be unintuitive.

Event Timeline

+1 to this, removing it breaks visual editor too. It's a source of pain as a lot of users accidentally remove it not knowing what it does and then break things wonderfully.

+1 for removing writeapi. I was surprised a Semantic MediaWiki action checking internal state was failing when writeapi is false. In MediaWiki core, the API action=purge fails when writeapi is false, but action=purge succeed with the same user rights.

Comparing in MediaWiki core the API actions with isWriteMode() === true and mustBePosted() === true (possibly because needsToken() !== false), it appears that (currently on 0cf01bc091f) all actions with write mode must be POSTed, and there are 3 POSTed actions without write mode (*):

  • ApiCSPReport.php
  • ApiLogout.php
  • ApiValidatePassword.php

So except these 3 exceptions, these two functions have always the same value.


(*) Methodology:

  • Checking that when function mustBePosted() is defined, it always returns true (except ApiBase)
$ grep -r -A 3 'function mustBePosted' includes/api
  • Checking that when function needsToken() is defined, it always returns a string and never false (except ApiBase and ApiResetPassword)
$ grep -r -A 3 'function needsToken' includes/api
  • Checking that when function isWriteMode() is defined, it always returns true (except ApiBase and ApiResetPassword)
$ grep -r -A 3 'function isWriteMode' includes/api
  • Consequently when needsToken() is defined, then mustBePosted() is true (except ApiResetPassword), so we can compare files where ( mustBePosted() or needsToken() is defined ) with files where ( isWriteMode() is defined ).
$ diff <(grep -r -l -e 'function mustBePosted' -e 'function needsToken'  includes/api|sort) <(grep -r -l 'function isWriteMode'  includes/api|sort)
8d7
< includes/api/ApiCSPReport.php
17d15
< includes/api/ApiLogout.php
37d34
< includes/api/ApiValidatePassword.php
  • In ApiResetPassword, isWriteMode() and mustBePosted() are always the same value (see internal logic).
Reedy triaged this task as Medium priority.Feb 15 2024, 7:23 PM

Change 1003871 had a related patch set uploaded (by Reedy; author: Reedy):

[mediawiki/core@master] Drop writeapi userright

https://gerrit.wikimedia.org/r/1003871

Change 1003874 had a related patch set uploaded (by Reedy; author: Reedy):

[mediawiki/extensions/AbuseFilter@master] Remove usage of writeapi userright

https://gerrit.wikimedia.org/r/1003874

Change 1003875 had a related patch set uploaded (by Reedy; author: Reedy):

[mediawiki/extensions/Thanks@master] Remove usage of writeapi userright

https://gerrit.wikimedia.org/r/1003875

Change 1003876 had a related patch set uploaded (by Reedy; author: Reedy):

[mediawiki/extensions/Wikibase@master] Remove usage of writeapi userright

https://gerrit.wikimedia.org/r/1003876

Change 1003877 had a related patch set uploaded (by Reedy; author: Reedy):

[mediawiki/extensions/Translate@master] Remove usage of writeapi userright

https://gerrit.wikimedia.org/r/1003877

[19:11]  <    jfolv> Anyone familiar with AWB? We just upgraded from 1.35 to 1.39 and now AWB is saying "you're not allowed to edit this wiki through the API." Our pywikibot still works, but this doesn't, and I confirmed the writeapi permission to be present in Special:ListGroupRights. Maybe it has to do with changes to the API?
[19:14]  <    Reedy> Are you logging in using bot passwords?
[19:14]  <    Reedy> you might be missing a grant or two there
[19:14]  <    jfolv> No, I'm logging into my own account.,
[19:14]  <    jfolv> Standard username/pass
[19:15]  <    Reedy> it can still be your own account and using bot passwords ;)
[19:16]  <    jfolv> True, but I'm just using the same password I would normally use to log in.
[19:18]  <    Reedy> I don't think much changed with the API between those versions, and I don't remember having to make any changes to AWB either to really support it
[19:18]  <    Reedy> Is the wiki public?
[19:19]  <    jfolv> Yeah, it's Bulbapedia
[19:19]  <    jfolv> https://bulbapedia.bulbagarden.net
[19:19]  <    Reedy> That is from apierror-writeapidenied
[19:19]  <    Reedy> which is only guarded with
[19:19]  <    Reedy>             } elseif ( !$this->getAuthority()->isAllowed( 'writeapi' ) ) {
[19:19]  <    Reedy>                 $this->dieWithError( 'apierror-writeapidenied' );
[19:21]  <    jfolv> Weird... We do have the writeapi permission denied to *, but you have to log in to use the site at all, so I imagine that shouldn't be a problem with it set to true under user.
[19:22]  <    Reedy> I think writeapi is one of those rights that doesn't really work like you think it should
[19:22]  <    jfolv> Oh jeez, that actually was the problem.
[19:22]  <    Reedy> And I wouldn't be surprised if that's causing the problem in one way or another
[19:22]  <    Reedy> lmao
[19:22]  <    jfolv> Well, you were spot on. But why is that, exactly?
[19:22]  <    Reedy> Why to which bit?
[19:22]  <    jfolv> Why does it work abnormally? Shouldn't it check the permissions of the current group and not *?
[19:22]  <    Reedy> T294397