authz: Group grants by riskiness

Bug: T290790
Change-Id: Ib7a195c167f82e686c4ede45388957f9988bf75d
This commit is contained in:
Gergő Tisza 2023-04-22 15:33:41 +02:00 committed by Bartosz Dziewoński
parent c8b9d023b4
commit 183372c995
11 changed files with 214 additions and 1 deletions

View file

@ -31,6 +31,8 @@ For notes on 1.41.x and older releases, see HISTORY.
* $wgConditionalUserOptions: Makes it possible to define user properties with
defaults varying by user, without growing the user_properties table. Typical
use-case is to enable a feature only for users created after a certain date.
* $wgGrantRiskGroups: Grant risk levels, used to indicate on various UIs which
grants should be considered risky. (T290790)
* …
==== Changed configuration ====

View file

@ -5693,6 +5693,42 @@ config-schema:
site administrators.
@see self::GrantPermissions
@since 1.27
GrantRiskGroups:
default:
basic: low
editpage: low
createeditmovepage: low
editprotected: vandalism
patrol: low
uploadfile: low
uploadeditmovefile: low
sendemail: security
viewmywatchlist: low
editviewmywatchlist: low
editmycssjs: security
editmyoptions: security
editinterface: vandalism
editsiteconfig: security
rollback: low
blockusers: vandalism
delete: vandalism
viewdeleted: vandalism
viewrestrictedlogs: security
protect: vandalism
oversight: security
createaccount: low
mergehistory: vandalism
import: security
highvolume: low
privateinfo: security
type: object
description: |-
Group grants by risk level. Keys are grant names (i.e. keys from GrantPermissions),
values are GrantsInfo::RISK_* constants.
Note that this classification is only informative; merely applying 'security' or 'internal'
to a grant won't prevent it from being available. It's used to give guidance to users
in various interfaces about the riskiness of the various grants.
@since 1.42
EnableBotPasswords:
default: true
type: boolean

View file

@ -3031,6 +3031,12 @@ $wgGrantPermissions = null;
*/
$wgGrantPermissionGroups = null;
/**
* Config variable stub for the GrantRiskGroups setting, for use by phpdoc and IDEs.
* @see MediaWiki\MainConfigSchema::GrantRiskGroups
*/
$wgGrantRiskGroups = null;
/**
* Config variable stub for the EnableBotPasswords setting, for use by phpdoc and IDEs.
* @see MediaWiki\MainConfigSchema::EnableBotPasswords

View file

@ -786,6 +786,15 @@
}
}
},
"GrantRiskGroups": {
"type": "object",
"description": "Map of grants to their risk category",
"patternProperties": {
"^[a-z]+$": {
"type": "string"
}
}
},
"ImplicitGroups": {
"type": "array",
"description": "Implicit groups"

View file

@ -949,6 +949,15 @@
}
}
},
"GrantRiskGroups": {
"type": "object",
"description": "Map of grants to their risk category",
"patternProperties": {
"^[a-z]+$": {
"type": "string"
}
}
},
"ImplicitGroups": {
"type": "array",
"description": "Implicit groups"

View file

@ -3046,6 +3046,12 @@ class MainConfigNames {
*/
public const GrantPermissionGroups = 'GrantPermissionGroups';
/**
* Name constant for the GrantRiskGroups setting, for use with Config::get()
* @see MainConfigSchema::GrantRiskGroups
*/
public const GrantRiskGroups = 'GrantRiskGroups';
/**
* Name constant for the EnableBotPasswords setting, for use with Config::get()
* @see MainConfigSchema::EnableBotPasswords

View file

@ -47,6 +47,7 @@ use LocalisationCache;
use LocalRepo;
use LogFormatter;
use MediaWiki\Deferred\SiteStatsUpdate;
use MediaWiki\Permissions\GrantsInfo;
use MediaWiki\Request\WebRequest;
use MediaWiki\Settings\Source\JsonSchemaTrait;
use MediaWiki\Site\MediaWikiSite;
@ -9061,7 +9062,7 @@ class MainConfigSchema {
'default' =>
[
// Hidden grants are implicitly present
'basic' => 'hidden',
'basic' => 'hidden',
'editpage' => 'page-interaction',
'createeditmovepage' => 'page-interaction',
@ -9100,6 +9101,48 @@ class MainConfigSchema {
'additionalProperties' => [ 'type' => 'string', ],
];
/**
* Group grants by risk level. Keys are grant names (i.e. keys from GrantPermissions),
* values are GrantsInfo::RISK_* constants.
*
* Note that this classification is only informative; merely applying 'security' or 'internal'
* to a grant won't prevent it from being available. It's used to give guidance to users
* in various interfaces about the riskiness of the various grants.
*
* @since 1.42
*/
public const GrantRiskGroups = [
'default' => [
'basic' => GrantsInfo::RISK_LOW,
'editpage' => GrantsInfo::RISK_LOW,
'createeditmovepage' => GrantsInfo::RISK_LOW,
'editprotected' => GrantsInfo::RISK_VANDALISM,
'patrol' => GrantsInfo::RISK_LOW,
'uploadfile' => GrantsInfo::RISK_LOW,
'uploadeditmovefile' => GrantsInfo::RISK_LOW,
'sendemail' => GrantsInfo::RISK_SECURITY,
'viewmywatchlist' => GrantsInfo::RISK_LOW,
'editviewmywatchlist' => GrantsInfo::RISK_LOW,
'editmycssjs' => GrantsInfo::RISK_SECURITY,
'editmyoptions' => GrantsInfo::RISK_SECURITY,
'editinterface' => GrantsInfo::RISK_VANDALISM,
'editsiteconfig' => GrantsInfo::RISK_SECURITY,
'rollback' => GrantsInfo::RISK_LOW,
'blockusers' => GrantsInfo::RISK_VANDALISM,
'delete' => GrantsInfo::RISK_VANDALISM,
'viewdeleted' => GrantsInfo::RISK_VANDALISM,
'viewrestrictedlogs' => GrantsInfo::RISK_SECURITY,
'protect' => GrantsInfo::RISK_VANDALISM,
'oversight' => GrantsInfo::RISK_SECURITY,
'createaccount' => GrantsInfo::RISK_LOW,
'mergehistory' => GrantsInfo::RISK_VANDALISM,
'import' => GrantsInfo::RISK_SECURITY,
'highvolume' => GrantsInfo::RISK_LOW,
'privateinfo' => GrantsInfo::RISK_SECURITY,
],
'type' => 'map',
];
/**
* @since 1.27
*/

View file

@ -31,12 +31,44 @@ use MediaWiki\MainConfigNames;
* @since 1.38
*/
class GrantsInfo {
/**
* Risk level classification for grants which aren't particularly risky. These grants might
* be abused, e.g. for vandalism, but the effect is easy to undo and the efficiency of abusing
* them isn't particularly different from registering new user accounts and using those for
* abuse.
* Note that risk levels depend on the use case; the default classification is meant for
* "normal" (public, open registration) wikis. Classification for e.g. a private wiki holding
* confidential information could be quite different.
*/
public const RISK_LOW = 'low';
/**
* Risk level classification for grants which can be used for disruptive vandalism or other
* kinds of abuse that couldn't be achieved just by registering new accounts, such as main
* page vandalism, vandalism of popular templates, page merge vandalism, or blocks.
*/
public const RISK_VANDALISM = 'vandalism';
/**
* Risk level classification for grants which can be used to cause damage that is hard or
* impossible to undo, such as exfiltrating sensitive private data or creating security
* vulnerabilities.
*/
public const RISK_SECURITY = 'security';
/**
* Risk level classification for grants which are used for internal purposes and should not
* be handed out.
*/
public const RISK_INTERNAL = 'internal';
/**
* @internal For use by ServiceWiring
*/
public const CONSTRUCTOR_OPTIONS = [
MainConfigNames::GrantPermissions,
MainConfigNames::GrantPermissionGroups,
MainConfigNames::GrantRiskGroups,
];
/** @var ServiceOptions */
@ -137,4 +169,21 @@ class GrantsInfo {
}
return $grants;
}
/**
* Returns a map of grant name => risk group. The risk groups are the GrantsInfo::RISK_*
* constants, plus $default for grants where the risk level is not defined.
* @param string $default Default risk group to assign to grants for which no risk group
* is configured. $default does not have to be one of the RISK_* constants.
* @return string[]
* @since 1.42
*/
public function getRiskGroupsByGrant( string $default = 'unknown' ): array {
$res = [];
$grantRiskGroups = $this->options->get( MainConfigNames::GrantRiskGroups );
foreach ( $this->options->get( MainConfigNames::GrantPermissions ) as $grant => $_ ) {
$res[$grant] = $grantRiskGroups[$grant] ?? $default;
}
return $res;
}
}

View file

@ -1753,6 +1753,34 @@ return [
'highvolume' => 'high-volume',
'privateinfo' => 'private-information',
],
'GrantRiskGroups' => [
'basic' => 'low',
'editpage' => 'low',
'createeditmovepage' => 'low',
'editprotected' => 'vandalism',
'patrol' => 'low',
'uploadfile' => 'low',
'uploadeditmovefile' => 'low',
'sendemail' => 'security',
'viewmywatchlist' => 'low',
'editviewmywatchlist' => 'low',
'editmycssjs' => 'security',
'editmyoptions' => 'security',
'editinterface' => 'vandalism',
'editsiteconfig' => 'security',
'rollback' => 'low',
'blockusers' => 'vandalism',
'delete' => 'vandalism',
'viewdeleted' => 'vandalism',
'viewrestrictedlogs' => 'security',
'protect' => 'vandalism',
'oversight' => 'security',
'createaccount' => 'low',
'mergehistory' => 'vandalism',
'import' => 'security',
'highvolume' => 'low',
'privateinfo' => 'security',
],
'EnableBotPasswords' => true,
'BotPasswordsCluster' => false,
'BotPasswordsDatabase' => false,
@ -2812,6 +2840,7 @@ return [
'PasswordAttemptThrottle' => 'array',
'GrantPermissions' => 'object',
'GrantPermissionGroups' => 'object',
'GrantRiskGroups' => 'object',
'EnableBotPasswords' => 'boolean',
'BotPasswordsCluster' => [
0 => 'string',

View file

@ -40,6 +40,7 @@ class ExtensionProcessor implements Processor {
MainConfigNames::FilterLogTypes,
MainConfigNames::GrantPermissionGroups,
MainConfigNames::GrantPermissions,
MainConfigNames::GrantRiskGroups,
MainConfigNames::GroupPermissions,
MainConfigNames::GroupsAddToSelf,
MainConfigNames::GroupsRemoveFromSelf,

View file

@ -32,6 +32,13 @@ class GrantsInfoTest extends MediaWikiUnitTestCase {
'normal' => 'normal-group',
'admin' => 'admin',
],
MainConfigNames::GrantRiskGroups => [
'hidden1' => 'low',
'hidden2' => 'low',
'normal' => 'low',
'normal2' => 'vandalism',
'admin' => 'security',
],
];
$this->grantsInfo = new GrantsInfo(
@ -138,4 +145,20 @@ class GrantsInfoTest extends MediaWikiUnitTestCase {
);
}
/**
* @covers ::getRiskGroupsByGrant
*/
public function testGetRiskGroupsByGrant() {
$this->assertSame(
[
'hidden1' => 'low',
'hidden2' => 'low',
'normal' => 'low',
'normal2' => 'vandalism',
'admin' => 'security',
],
$this->grantsInfo->getRiskGroupsByGrant()
);
}
}