Page MenuHomePhabricator

Watching pages (AJAX) requires WriteAPI
Closed, ResolvedPublic

Description

The new† AJAX method of watching pages, implemented in /resources/mediawiki.page/mediawiki.page.watch.ajax.js, makes uses of the MW API methods for watching pages (http://www.mediawiki.org/wiki/API:Watch).

Unfortunately, this API method is part of the editing API and as such requires $wgEnableWriteAPI to be enabled *and* the writeapi right.

As $wgEnableWriteAPI is enabled by default since 1.14, it’s somewhat safe to assume that it is enabled. The writeapi right however should not be required for a basic functionality such as watching pages.

On our wiki, we have the writeapi enabled for autoconfirmed users and above. This is mainly to prevent vandals to create new users and then use the API to quickly vandalize the wiki (yes, that has happened before).

Now with the new AJAX functionality, this implies that only autoconfirmed users can watch pages. This is a terrible usability issue.

Watching pages, which is a very fundamental functionality for registered users, should not be restricted by either the writeapi right, or even the $egEnableWriteAPI setting. It makes perfect sense for wikis to disable the write API altogether while still expecting users to be able to watch pages.

In a first step, I would argue why watching pages via the API even requires the write API. Yes, it is a “changing” operation, but it should not be considered an operation that changes wiki *content*. And it is restricted to the current user anyway.

In a second step though, I think that such AJAX functionality should not make use of the API at all, *if* said API can be disabled. Watching pages should use a separate API which is not affected by the $wgEnableWriteAPI and $wgEnableAPI setting.

† I actually have no idea when that was added; I have been stuck on a old MW version for quite a while now.


Version: 1.20.x
Severity: major

Details

Reference
bz47480

Event Timeline

bzimport raised the priority of this task from to Medium.Nov 22 2014, 1:24 AM
bzimport set Reference to bz47480.
bzimport added a subscriber: Unknown Object (MLST).

(In reply to comment #0)

The new† AJAX method of watching pages, implemented in
/resources/mediawiki.page/mediawiki.page.watch.ajax.js, makes uses of the
MW
API methods for watching pages (http://www.mediawiki.org/wiki/API:Watch).

Unfortunately, this API method is part of the editing API and as such
requires
$wgEnableWriteAPI to be enabled *and* the writeapi right.

As $wgEnableWriteAPI is enabled by default since 1.14, it’s somewhat safe
to
assume that it is enabled. The writeapi right however should not be
required
for a basic functionality such as watching pages.

On our wiki, we have the writeapi enabled for autoconfirmed users and
above.
This is mainly to prevent vandals to create new users and then use the API to
quickly vandalize the wiki (yes, that has happened before).

Now with the new AJAX functionality, this implies that only autoconfirmed
users
can watch pages. This is a terrible usability issue.

Watching pages, which is a very fundamental functionality for registered
users,
should not be restricted by either the writeapi right, or even the
$egEnableWriteAPI setting. It makes perfect sense for wikis to disable the
write API altogether while still expecting users to be able to watch pages.

In a first step, I would argue why watching pages via the API even requires
the
write API. Yes, it is a “changing” operation, but it should not be considered
an operation that changes wiki *content*. And it is restricted to the current
user anyway.

Seems a bit of a daft exclusion case. Same problem occurs if the api is disabled completely. Should api watch still work? Should we really add numerous exceptions for action=watch?

(In reply to comment #1)

Should api watch still work? Should we really add numerous
exceptions for action=watch?

I think the questio is rather, if the normal API should be used for watching pages at all. After all that function is accessed from the normal wiki website served via index.php.

Before, it used a special “API” in index.php to (un)watch pages, and I think it should continue to do that, as long as the API is not a guaranteed feature (i.e. as long as you can turn it off).

I recommend removing the old 'write-mode' switch entirely, as front-end code nowadays expects to have full read-write access via the API.

I bumped up the importance to High, since this essentially means you either break your Watch tab, or are forced to open up access to the write API, which can be a significant security issue.

This has been reported as an error here [1] ;)

The "watch/unwatch" tab is actually a link, in the form of index.php?title=PAGENAME&action=watch&token=WATCHTOKEN.

If I open that link in a new tab, it performs the watch/unwatch action without problems.

What I suggest here is to detect if the api is disabled, or just the writeapi, and if that's true, let the browser follow the link instead of using the api. We already have wgEnableWriteAPI as a JavaScript variable [2], so mediawiki.action.watch should check it and act accordingly.


[1] https://www.mediawiki.org/wiki/Thread:Project:Support_desk/Error_when_trying_to_watch_pages

[2] https://www.mediawiki.org/wiki/Manual:Interface/JavaScript#mw.config

Adding +javascript keyword, as per my proposal at comment #6

(In reply to comment #4)

I recommend removing the old 'write-mode' switch entirely, as front-end code
nowadays expects to have full read-write access via the API.

I agree, this should be WONTFIX.

Alex: what else in the base MediaWiki software expects write access to the API?

The feedback module, but nothing that comes in the basic install with no extensions or custom JS etc. as far as I'm aware (I doubt any modern wikis run from the base software without any extensions or helpful JS stuff though).

True, but the base software should gracefully handle any reasonable rights configuration a wiki might want. A number of wikis I've seen, including our own and obviously the OP's, disable or limit access to the API to discourage unauthorized bot editing. It's not a perfect system, but it's one of a number of security measures you can take. I'm strongly opposed to the idea that the base software would force a specific security configuration on all users, or break basic functionality.

I do think core should gracefully handle any legitimate configurations.

On this particular issue, I will say that at this point, I think the write api is as secure as the web interface. I know early on there were a lot of bugs, and a lot of places where access controls in the api didn't match the website. But as far as I'm aware, those have all been fixed.

It's nice that users can turn it off and remove that attack surface entirely, but I'm not sure how much that's worth against the tradeoff in development costs.

(In reply to comment #6)

What I suggest here is to detect if the api is disabled, or just the
writeapi,
and if that's true, let the browser follow the link instead of using the api.
We already have wgEnableWriteAPI as a JavaScript variable [2], so
mediawiki.action.watch should check it and act accordingly.

I think that would be a very good way to solve this. I didn’t realize that the tab link is already correct. The simplest way would be to just not make this use an AJAX call when the WriteAPI is disabled. I’ll try to come up with a patch.

Thanks, Patrick! From what little I know of the MW code, I don't think it'll be a problem, but if it's too difficult to detect writeapi permissions for the current user (as in the scenario the OP describes) at runtime for whatever reason, even adding something like $wgWatchUseWriteApi to LocalSettings would be sufficient for most cases, I would imagine.

(In reply to comment #14)

if it's too difficult to detect writeapi permissions for the
current user (as in the scenario the OP describes) at runtime for whatever
reason, even adding something like $wgWatchUseWriteApi to LocalSettings would
be sufficient for most cases, I would imagine.

Well the simplest way would be to just have it on the server-side say “nope, you don’t get AJAX for this”, so it does not even need to be handled by JavaScript.

Btw. I am the OP ;)

[not high priority, resetting]

Okay, maybe this is not as simple as I would it like to be. The AJAX'ifying happens in mediawiki.page.watch.ajax.js[1]. As we don’t want to serve different JS modules based on the user rights, we would have to add some kind of switch to the module that won’t execute the function then.

We *could* use the mediaWiki.user module[2] to get the rights of the current user, but that would involve another API call, so that’s not a good idea.

We could also either add some switch class to the body tag which the ajax module filters for, or define another config variable that indicates if the write API is available right now or not. But I’m not sure what the official stance on adding such things is for MediaWiki.

And then another option would be to add a "noapi" class to all the generated watch links which then again could be filtered for.

What do you think? What would be an acceptable solution?

[1] https://git.wikimedia.org/blob/mediawiki%2Fcore/HEAD/resources%2Fmediawiki.page%2Fmediawiki.page.watch.ajax.js#L103
[2] http://www.mediawiki.org/wiki/ResourceLoader/Default_modules#mediaWiki.user

What can you do when you encounter this bug? My users and I have been wrestling with this kind of problem for four months now and I was wondering if it may be related.

What can you do when you encounter this bug?

The only thing you can do to make it work is to enable Write-API [1], and give your users the necessary group rights to access it [2]. For example the following configuration disallows the write API for unregistered users but allows it for registered users, making them use the watch API:

$wgGroupPermissions['*']['writeapi'] = false;
$wgGroupPermissions['user']['writeapi'] = true;

[1] http://www.mediawiki.org/wiki/Manual:$wgEnableWriteAPI
[2] http://www.mediawiki.org/wiki/Manual:User_rights

Thanks. Unfortunately, that doesn't seem to work on my installation, MW 1.19.2. Is there perhaps any specific order in which those configuration statements should be given in localsettings.php ?

See the MW thread here:

http://www.mediawiki.org/wiki/Thread:Project:Support_desk/Watchlist_error_after_upgrade_to_1.18.1

No javascript conflict was detected other than this:

TypeError: $link.attr(...) is undefined

which refers to

accesskeyTip = $link.attr( 'title' ).match( mw.util.tooltipAccessKeyRegexp ),

[Edit:] It probably is the skin I'm using after all, which appears to have broken following a change in MediaWiki (whether or not it is related to the one outlined above).

The reason that I dismissed this before is that switching to another skin, such as Vector, did not have any positive effect on the watch / unwatch action. One can only guess why, but I did notice later on, in quite another cotext, that changes relating to Ajax require some time to take effect.

gerrit 65785 adds checks for writeapi permission before loading the javascript module.