Page MenuHomePhabricator

Decide how to define action block IDs for extension actions
Open, Needs TriagePublic

Description

This task follows up on some discussions on https://gerrit.wikimedia.org/r/c/mediawiki/core/+/681005 about how to avoid conflicts in the integer values for blockable actions added by extensions (see comments on patch).

Problem

TL;DR We need a way of assigning integer IDs to blockable actions (as defined by the BlockActionInfo service), in such a way that the IDs for actions added by extensions don't collide.

How partial blocks are stored

Partial blocks against actions will be stored in the existing ipblocks_restrictions table, which has columns for the type of restriction (representing types page, namespace or action) and the value of the restriction (representing the particular page, namespace or action). The table looks like this:

MariaDB [mediawiki]> DESCRIBE ipblocks_restrictions;
+-----------+------------+------+-----+---------+-------+
| Field     | Type       | Null | Key | Default | Extra |
+-----------+------------+------+-----+---------+-------+
| ir_ipb_id | int(11)    | NO   | PRI | NULL    |       |
| ir_type   | tinyint(4) | NO   | PRI | NULL    |       |
| ir_value  | int(11)    | NO   | PRI | NULL    |       |
+-----------+------------+------+-----+---------+-------+
Actions aren't well-defined

Pages and namespaces have IDs, which are stored in ipblocks_restrictions.ir_value. Actions are very unstructured in MediaWiki. Some actions are respresented by a subclass of Action (recognised by the string returned by Action::getName() - e.g. 'history'); some are defined in the list PermissionManager::coreRights - e.g. 'sendemail'; some are simply strings that are passed around - e.g. 'create'. Actions don't have IDs, and there's no a definitive set of actions defined in one place.

Perhaps the most useful definition of an action, for the sake of partial action blocks, is:

a string that can get passed to PermissionManager::checkUserBlock as the $action argument

We need to define blockable actions

The BlockActionInfo service will define a set of blockable actions. These will be recognised by the string value of the action, but must be stored with an integer ID in ipblocks_restrictions. The BlockActionInfo service will map the string values to their IDs for blockable actions defined in MediaWiki core.

The problem is that extensions need to be able to define blockable actions too. So how do we ensure that their integer IDs don't collide?

Possible solutions

Table for mapping actions to IDs

Have a table that maps string actions to IDs. This is proposed in T194529#6210790 and discussed in https://gerrit.wikimedia.org/r/c/mediawiki/core/+/681005/3#message-899556e17feef8a8d0e8f40f8b5e4a7945099f96

As proposed above, the extensions need only register their string actions (which already must not collide), and core could handle assigning IDs. This is similar to what we do with content models and slot roles (using the content_models and slot_roles tables) and there is existing infrastructure to make it easy, specifically the NameTableStore and NameTableStoreFactory classes.

A clear advantage of this method is that IDs will never collide, without extensions needing to do anything special. I think this would clearly be the way to go if we were designing actions from scratch. But I think given the complexity of our Production setup, (multiple wikis, global features, etc) it could add some difficulties. None are insurmountable, but I think they could add maintenance burden.

  • Extension action IDs would get out of sync between different wikis, since there would be no enforcement to keep them consistent, and different wikis have different extensions installed. This would make it more difficult to compare blocks on different wikis: we'd need to be careful to join on the remote wiki's action block table whenever looking up a remote block, and moving data between wikis would need to be handled carefully.
  • If GlobalBlocking were ever updated to work with partial blocks we'd need to store the action string rather than the ID. (There would be a similar problem for pages of course, but global blocks against pages doesn't make much sense since pages differ across wikis. However, we could implement global blocks against namespaces quite easily, since the IDs are consistent.) It's conceivable that a global block against e.g. 'upload' could be a useful thing to implement someday.
  • The usual problem with storing extension data in a core table: if an extension were uninstalled, there could be nonsensical rows left behind in the block actions table unless we deleted them. (There could also be nonsensical blocks left behind, but this is a problem with either solution. It's also an existing problem with namespaces: T280798)
Hard-code the IDs and avoid conflicts via on-wiki documentation

This is how namespace IDs work: https://www.mediawiki.org/wiki/Extension_default_namespaces

Hard-coding is often a less satisfying solution, and having to manage the IDs on a wiki introduces some overhead which would be avoided by using the other solution. It also further entangles the code with the wiki.

The main advantage would be that it avoids the above complexity by keeping IDs consistent across wikis. In any relevant tables it would be clear what the ID meant. Managing the IDs would probably be a bit simpler than for namespaces, since the number of actions that can be partially blocked will likely be smaller.


We could go ahead with the second option while we're just testing with a handful of actions on beta, but we should decide which solution to implement before enabling on production sites.

Event Timeline

Aware that this proposal is essentially adding on another standard to our pile of standards, but could we add another column to the table like ir_action? And make ir_value something fixed or allowed to be null? ir_action would be the action string and we could check for it instead of ir_value whenever we have an action restriction (page and namespace restrictions would remain the same). This way we wouldn't need to manage ids or anything.

Hard-code the IDs and avoid conflicts via on-wiki documentation

If we do go with this approach, we should make a separate task for documenting this, which could cover:

  • providing a wiki page for extensions to document their blockable actions and IDs
  • mentioning the wiki page in the documentation for GetAllBlockActionsHook

Was about to file a ticket for this, but found this task. Added some notes

Thanks @DannyS712. From a cursory glance, NameTableStore looks promising, though I'm not familiar with it yet.

We'll go with option 2 ("Hard-code the IDs and avoid conflicts via on-wiki documentation") for now so we can move ahead with testing straight away, but it should be very possible to switch away from this to option 1 ("Table for mapping actions to IDs") in the future. I think the simplicity and flexibility of it option 2 is helpful while we're developing this - as is the ease with which we could switch to option 1.

Let's keep this task/discussion open, and In the meantime a proof-of-concept patch for option 1 might help with the discussion, if anyone has the inclination and capacity to make one.