mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/OATHAuth
synced 2024-11-27 17:50:15 +00:00
Require OATHAuth for membership in specified user groups
Users in groups listed in $wgOATHRequiredForGroups (default none) must have two-factor authentication enabled otherwise their membership in those groups will be disabled. This is done using the UserEffectiveGroups hook, which allows dynamically adding or removing user groups. If a user doesn't have 2FA enabled, it will appear to them as if they aren't a member of the group at all. Special:Preferences will show which groups are disabled. In the future it would be good to have a hook into PermissionsError to show this as well. The UserGetRights hook is used to ensure the user still has the "oathauth-enable" user right in case it was only granted to them as part of the user group they are disabled from. On the outside, Special:ListUsers will still show the user as a member of the group. The API list=users&prop=groups|groupmemberships will show inconsistent informaiton, groups will remove disabled groups while groupmemberships will not. This functionality was somewhat already available with $wgOATHExclusiveRights, except that implementation has flaws outlined at T150562#6078263 and haven't been resolved in I69af6a58e4 for over a year now. If this works out, it's expected that will be deprecated/removed. Bug: T150562 Change-Id: I07ebddafc6f2233ccec216fa8ac6e996553499fb
This commit is contained in:
parent
15a1792b2a
commit
498dcfeb80
|
@ -44,7 +44,9 @@
|
|||
"AuthChangeFormFields": "main",
|
||||
"LoadExtensionSchemaUpdates": "\\MediaWiki\\Extension\\OATHAuth\\Hook\\LoadExtensionSchemaUpdates\\UpdateTables::callback",
|
||||
"GetPreferences": "main",
|
||||
"getUserPermissionsErrors": "main"
|
||||
"getUserPermissionsErrors": "main",
|
||||
"UserEffectiveGroups": "main",
|
||||
"UserGetRights": "main"
|
||||
},
|
||||
"HookHandlers": {
|
||||
"main": {
|
||||
|
@ -52,7 +54,8 @@
|
|||
"services": [
|
||||
"OATHUserRepository",
|
||||
"PermissionManager",
|
||||
"MainConfig"
|
||||
"MainConfig",
|
||||
"UserGroupManager"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@ -74,6 +77,9 @@
|
|||
},
|
||||
"OATHExclusiveRights": {
|
||||
"value": []
|
||||
},
|
||||
"OATHRequiredForGroups": {
|
||||
"value": []
|
||||
}
|
||||
},
|
||||
"ResourceModules": {
|
||||
|
|
|
@ -83,5 +83,7 @@
|
|||
"oathauth-totp-disable-warning": "You will no longer be able to use the authentication device registered with this account. All scratch-tokens associated with this account will be invalidated.",
|
||||
"oathauth-invalidrequest": "Invalid request",
|
||||
"oathauth-verify-enabled": "{{GENDER:$1|$1}} has two-factor authentication enabled.",
|
||||
"oathauth-verify-disabled": "{{GENDER:$1|$1}} does not have two-factor authentication enabled."
|
||||
"oathauth-verify-disabled": "{{GENDER:$1|$1}} does not have two-factor authentication enabled.",
|
||||
"oathauth-prefs-disabledgroups": "Disabled {{PLURAL:$1|group|groups}}:",
|
||||
"oathauth-prefs-disabledgroups-help": "{{GENDER:$2|Your membership}} in {{PLURAL:$1|this group|these groups}} is disabled until you enable [[Special:Manage Two-factor authentication|two-factor authentication]]."
|
||||
}
|
||||
|
|
|
@ -95,5 +95,7 @@
|
|||
"oathauth-totp-disable-warning": "TOTP specific warning message when disabling/switching to alternative 2FA method",
|
||||
"oathauth-invalidrequest": "Generic error message that is displayed when request cannot be processed due to an unpredicted reason",
|
||||
"oathauth-verify-enabled": "Notice that a user has 2FA enabled, shown on success at [[Special:VerifyOATHForUser]].\n$1 - Name of user",
|
||||
"oathauth-verify-disabled": "Notice that a user does not have 2FA enabled, shown on success at [[Special:VerifyOATHForUser]].\n$1 - Name of user"
|
||||
"oathauth-verify-disabled": "Notice that a user does not have 2FA enabled, shown on success at [[Special:VerifyOATHForUser]].\n$1 - Name of user",
|
||||
"oathauth-prefs-disabledgroups": "Label on Special:Preferences for groups in which the user's membership has been disabled for a lack of two-factor authentication.\n$1 - Number of groups",
|
||||
"oathauth-prefs-disabledgroups-help": "Help message shown on Special:Preferences for the {{mw-msg|oathauth-prefs-disabledgroups}} field.\n$1 - Number of groups\n$2 - User name for GENDER"
|
||||
}
|
||||
|
|
|
@ -7,20 +7,27 @@ use MediaWiki\Auth\AuthenticationRequest;
|
|||
use MediaWiki\Extension\OATHAuth\OATHAuth;
|
||||
use MediaWiki\Extension\OATHAuth\OATHUserRepository;
|
||||
use MediaWiki\Permissions\Hook\GetUserPermissionsErrorsHook;
|
||||
use MediaWiki\Permissions\Hook\UserGetRightsHook;
|
||||
use MediaWiki\Permissions\PermissionManager;
|
||||
use MediaWiki\Preferences\Hook\GetPreferencesHook;
|
||||
use MediaWiki\SpecialPage\Hook\AuthChangeFormFieldsHook;
|
||||
use MediaWiki\User\Hook\UserEffectiveGroupsHook;
|
||||
use MediaWiki\User\UserGroupManager;
|
||||
use OOUI\ButtonWidget;
|
||||
use OOUI\HorizontalLayout;
|
||||
use OOUI\LabelWidget;
|
||||
use RequestContext;
|
||||
use SpecialPage;
|
||||
use Title;
|
||||
use User;
|
||||
use UserGroupMembership;
|
||||
|
||||
class HookHandler implements
|
||||
AuthChangeFormFieldsHook,
|
||||
GetPreferencesHook,
|
||||
getUserPermissionsErrorsHook
|
||||
getUserPermissionsErrorsHook,
|
||||
UserEffectiveGroupsHook,
|
||||
UserGetRightsHook
|
||||
{
|
||||
/**
|
||||
* @var OATHUserRepository
|
||||
|
@ -32,6 +39,11 @@ class HookHandler implements
|
|||
*/
|
||||
private $permissionManager;
|
||||
|
||||
/**
|
||||
* @var UserGroupManager
|
||||
*/
|
||||
private $userGroupManager;
|
||||
|
||||
/**
|
||||
* @var Config
|
||||
*/
|
||||
|
@ -41,11 +53,13 @@ class HookHandler implements
|
|||
* @param OATHUserRepository $userRepo
|
||||
* @param PermissionManager $permissionManager
|
||||
* @param Config $config
|
||||
* @param UserGroupManager $userGroupManager
|
||||
*/
|
||||
public function __construct( $userRepo, $permissionManager, $config ) {
|
||||
public function __construct( $userRepo, $permissionManager, $config, $userGroupManager ) {
|
||||
$this->userRepo = $userRepo;
|
||||
$this->permissionManager = $permissionManager;
|
||||
$this->config = $config;
|
||||
$this->userGroupManager = $userGroupManager;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -123,9 +137,73 @@ class HookHandler implements
|
|||
'section' => 'personal/info',
|
||||
];
|
||||
|
||||
$dbGroups = $this->userGroupManager->getUserGroups( $user );
|
||||
$disabledGroups = $this->getDisabledGroups( $user, $dbGroups );
|
||||
if ( $module === null && $disabledGroups ) {
|
||||
$context = RequestContext::getMain();
|
||||
$list = [];
|
||||
foreach ( $disabledGroups as $disabledGroup ) {
|
||||
$list[] = UserGroupMembership::getLink( $disabledGroup, $context, 'html' );
|
||||
}
|
||||
$info = $context->getLanguage()->commaList( $list );
|
||||
$disabledInfo = [ 'oathauth-disabledgroups' => [
|
||||
// @phan-suppress-next-line SecurityCheck-XSS T183174
|
||||
'type' => 'info',
|
||||
'label-message' => [ 'oathauth-prefs-disabledgroups',
|
||||
\Message::numParam( count( $disabledGroups ) ) ],
|
||||
'help-message' => [ 'oathauth-prefs-disabledgroups-help',
|
||||
\Message::numParam( count( $disabledGroups ) ), $user->getName() ],
|
||||
'default' => $info,
|
||||
'raw' => true,
|
||||
'section' => 'personal/info',
|
||||
] ];
|
||||
// Insert right after "Member of groups"
|
||||
$preferences = wfArrayInsertAfter( $preferences, $disabledInfo, 'usergroups' );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the groups that this user is supposed to be in, but are disabled
|
||||
* because 2FA isn't enabled
|
||||
*
|
||||
* @param User $user
|
||||
* @param string[] $groups All groups the user is supposed to be in
|
||||
* @return string[] Groups the user should be disabled in
|
||||
*/
|
||||
private function getDisabledGroups( User $user, array $groups ): array {
|
||||
$requiredGroups = $this->config->get( 'OATHRequiredForGroups' );
|
||||
// Bail early if:
|
||||
// * No configured restricted groups
|
||||
// * The user is not in any of the restricted groups
|
||||
$intersect = array_intersect( $groups, $requiredGroups );
|
||||
if ( !$requiredGroups || !$intersect ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$oathUser = $this->userRepo->findByUser( $user );
|
||||
if ( $oathUser->getModule() === null ) {
|
||||
// Not enabled, strip the groups
|
||||
return $intersect;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove groups if 2FA is required for them and it's not enabled
|
||||
*
|
||||
* @param User $user User to get groups for
|
||||
* @param string[] &$groups Current effective groups
|
||||
*/
|
||||
public function onUserEffectiveGroups( $user, &$groups ) {
|
||||
$disabledGroups = $this->getDisabledGroups( $user, $groups );
|
||||
if ( $disabledGroups ) {
|
||||
$groups = array_diff( $groups, $disabledGroups );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Title $title
|
||||
* @param User $user
|
||||
|
@ -150,4 +228,23 @@ class HookHandler implements
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* If a user has groups disabled for not having 2FA enabled, make sure they
|
||||
* have "oathauth-enable" so they can turn it on
|
||||
*
|
||||
* @param User $user User to get rights for
|
||||
* @param string[] &$rights Current rights
|
||||
*/
|
||||
public function onUserGetRights( $user, &$rights ) {
|
||||
if ( in_array( 'oathauth-enable', $rights ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$dbGroups = $this->userGroupManager->getUserGroups( $user );
|
||||
if ( $this->getDisabledGroups( $user, $dbGroups ) ) {
|
||||
// Has some disabled groups, add oathauth-enable
|
||||
$rights[] = 'oathauth-enable';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue