diff --git a/OATHAuth.alias.php b/OATHAuth.alias.php index fc20b548..6947347e 100644 --- a/OATHAuth.alias.php +++ b/OATHAuth.alias.php @@ -12,7 +12,8 @@ $specialPageAliases = []; $specialPageAliases['en'] = [ 'DisableOATHForUser' => [ 'DisableOATHForUser' ], 'OATHManage' => [ 'Manage_Two-factor_authentication', 'OATH_Manage', 'OATHManage', - 'OATH', 'Two-factor_authentication', 'OATHAuth' ] + 'OATH', 'Two-factor_authentication', 'OATHAuth' ], + 'VerifyOATHForUser' => [ 'VerifyOATHForUser' ], ]; /** Arabic (العربية) */ diff --git a/extension.json b/extension.json index e792e4c4..a07b5035 100644 --- a/extension.json +++ b/extension.json @@ -90,12 +90,14 @@ }, "SpecialPages": { "DisableOATHForUser": "\\MediaWiki\\Extension\\OATHAuth\\Special\\DisableOATHForUser", + "VerifyOATHForUser": "\\MediaWiki\\Extension\\OATHAuth\\Special\\VerifyOATHForUser", "OATHManage": "\\MediaWiki\\Extension\\OATHAuth\\Special\\OATHManage" }, "AvailableRights": [ "oathauth-enable", "oathauth-api-all", "oathauth-disable-for-user", + "oathauth-verify-user", "oathauth-view-log" ], "GroupPermissions": { @@ -108,6 +110,7 @@ }, "sysop": { "oathauth-disable-for-user": true, + "oathauth-verify-user": true, "oathauth-view-log": true } }, diff --git a/i18n/en.json b/i18n/en.json index 748f8408..0081b319 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -28,13 +28,17 @@ "oathauth-step4": "Step 4: Verification", "oathauth-entertoken": "Enter a code from your authentication device to verify:", "oathauth-disable-for-user": "Disable two-factor authentication for a user", + "oathauth-verify-for-user": "Verify if a user has two-factor authentication enabled", "right-oathauth-disable-for-user": "Disable two-factor authentication for a user", "action-oathauth-disable-for-user": "disable two-factor authentication for a user", + "right-oathauth-verify-user": "Verify whether a user has two-factor authentication enabled", + "action-oathauth-verify-user": "verify whether a user has two-factor authentication enabled", "right-oathauth-view-log": "Access to log of two-factor authentication changes", "action-oathauth-view-log": "access to log of two-factor authentication changes", "oathauth-disable-intro": "With great power, comes great responsibility", "oathauth-enteruser": "Username:", - "oathauth-enterreason": "Reason for disabling:", + "oathauth-enterdisablereason": "Reason for disabling:", + "oathauth-enterverifyreason": "Reason for checking:", "oathauth-user-not-does-not-have-oath-enabled": "User doesn't have two-factor authentication enabled, so nothing to disable", "right-oathauth-enable": "Enable two-factor authentication", "action-oathauth-enable": "enable two-factor authentication", @@ -52,6 +56,7 @@ "oath-log-name": "Two-factor authentication log", "oath-log-header": "These events track changes to users' two-factor authentication status.", "logentry-oath-disable-other": "$1 {{GENDER:$2|disabled}} the two-factor authentication of $3", + "logentry-oath-verify": "$1 {{GENDER:$2|checked}} if $3 had two-factor authentication enabled", "oauthauth-ui-no-module": "None enabled", "oathauth-module-invalid": "The OATHAuth module that the user has registered is invalid.", "oathauth-module-totp-label": "TOTP (one-time token)", @@ -75,5 +80,7 @@ "oathauth-switch-method-warning-header": "Confirm switching to a different authentication method", "oathauth-switch-method-warning": "By switching to $2 two-factor authentication method, current method ($1) will be disabled, and all the data associated with the current authentication method will be deleted", "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-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." } diff --git a/i18n/qqq.json b/i18n/qqq.json index 1fa6cc87..d0fb0d41 100644 --- a/i18n/qqq.json +++ b/i18n/qqq.json @@ -40,13 +40,17 @@ "oathauth-step4": "Label for step 4 information on Special:OATH", "oathauth-entertoken": "Label on input field on Special:OATH asking user to enter token", "oathauth-disable-for-user": "Title of the special page to disable other users' two-factor authentication (OATH)", + "oathauth-verify-for-user": "Title of the special page to verify other users' two-factor authentication (OATH) status", "right-oathauth-disable-for-user": "{{doc-right|oathauth-disable-for-user}}", "action-oathauth-disable-for-user": "{{doc-action|oathauth-disable-for-user}}", + "right-oathauth-verify-user": "{{doc-right|oathauth-verify-user}}", + "action-oathauth-verify-user": "{{doc-action|oathauth-verify-user}}", "right-oathauth-view-log": "{{doc-right|oathauth-view-log}}", "action-oathauth-view-log": "{{doc-action|oathauth-view-log}}", "oathauth-disable-intro": "Intro message for special to disable other users' two-factor authentication (OATH)", - "oathauth-enteruser": "Label for user field in special page to disable", - "oathauth-enterreason": "Label for field for reason in special page to disable", + "oathauth-enteruser": "Label for user field in special page to verify or disable two-factor authentication", + "oathauth-enterdisablereason": "Label for field for reason in special page to disable two-factor authentication", + "oathauth-enterverifyreason": "Label for field for reason in special page to verify two-factor authentication", "oathauth-user-not-does-not-have-oath-enabled": "Error message when user does not have two-factor authentication (OATH) enabled", "right-oathauth-enable": "{{doc-right|oathauth-enable}}", "action-oathauth-enable": "{{doc-action|oathauth-enable}}", @@ -64,6 +68,7 @@ "oath-log-name": "The Special:Log log name that appears in the drop-down on the Special:Log page", "oath-log-header": "The Special:Log description that appears on the Special:Log page when you filter logs on this specific log name", "logentry-oath-disable-other": "The template of the log entry message\n\n* $1 is the person disabling the two-factor authentication.\n* $2 is the user whose 2FA is being disabled.\n* $3 is the user whose 2FA is being disabled (again).", + "logentry-oath-verify": "The template of the log entry message\n\n* $1 is the person verifying the two-factor authentication.\n* $2 is the user whose 2FA is being verified.\n* $3 is the user whose 2FA is being verified (again).", "oauthauth-ui-no-module": "User preference value when no 2FA module is enabled", "oathauth-module-invalid": "Error message when the OATHAuth module registered by user is invalid", "oathauth-module-totp-label": "User preference value when the TOTP module is enabled", @@ -87,5 +92,7 @@ "oathauth-switch-method-warning-header": "Page title for warning page when switching to an alternative 2FA method", "oathauth-switch-method-warning": "Generic message warning the user of token/data loss when switching to an alternative method.\n$1 - Current method name, $2 - Name of the method that is being switched to", "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-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" } diff --git a/src/Special/DisableOATHForUser.php b/src/Special/DisableOATHForUser.php index 520769a7..a34a30cc 100644 --- a/src/Special/DisableOATHForUser.php +++ b/src/Special/DisableOATHForUser.php @@ -97,7 +97,7 @@ class DisableOATHForUser extends FormSpecialPage { 'reason' => [ 'type' => 'text', 'default' => '', - 'label-message' => 'oathauth-enterreason', + 'label-message' => 'oathauth-enterdisablereason', 'name' => 'reason', 'required' => true, ], diff --git a/src/Special/VerifyOATHForUser.php b/src/Special/VerifyOATHForUser.php new file mode 100644 index 00000000..a93690e8 --- /dev/null +++ b/src/Special/VerifyOATHForUser.php @@ -0,0 +1,160 @@ +userRepo = MediaWikiServices::getInstance()->getService( 'OATHUserRepository' ); + } + + public function doesWrites() { + return true; + } + + /** + * @return string + */ + protected function getLoginSecurityLevel() { + return $this->getName(); + } + + /** + * @param HTMLForm $form + */ + public function alterForm( HTMLForm $form ) { + $form->setMessagePrefix( 'oathauth' ); + $form->getOutput()->setPageTitle( $this->msg( 'oathauth-verify-for-user' ) ); + } + + /** + * @return string + */ + protected function getDisplayFormat() { + return 'ooui'; + } + + /** + * @return bool + */ + public function requiresUnblock() { + return true; + } + + /** + * @param string $par + */ + public function execute( $par ) { + $this->getOutput()->disallowUserJs(); + parent::execute( $par ); + } + + /** + * @return array[] + */ + protected function getFormFields() { + return [ + 'user' => [ + 'type' => 'user', + 'default' => '', + 'label-message' => 'oathauth-enteruser', + 'name' => 'user', + 'required' => true, + ], + 'reason' => [ + 'type' => 'text', + 'default' => '', + 'label-message' => 'oathauth-enterverifyreason', + 'name' => 'reason', + 'required' => true, + ], + ]; + } + + /** + * @param array $formData + * @return array|true + * @throws ConfigException + * @throws MWException + */ + public function onSubmit( array $formData ) { + $this->targetUser = $formData['user']; + $user = User::newFromName( $this->targetUser ); + if ( !$user || $user->getId() === 0 ) { + return [ 'oathauth-user-not-found' ]; + } + $oathUser = $this->userRepo->findByUser( $user ); + + if ( !( $oathUser->getModule() instanceof IModule ) || + !$oathUser->getModule()->isEnabled( $oathUser ) ) { + $result = self::OATHAUTH_NOT_ENABLED; + } else { + $result = self::OATHAUTH_IS_ENABLED; + } + + $this->enabledStatus = $result; + + $logEntry = new ManualLogEntry( 'oath', 'verify' ); + $logEntry->setPerformer( $this->getUser() ); + $logEntry->setTarget( $user->getUserPage() ); + $logEntry->setComment( $formData['reason'] ); + $logEntry->insert(); + + LoggerFactory::getInstance( 'authentication' )->info( + 'OATHAuth status checked for {usertarget} by {user} from {clientip}', [ + 'user' => $this->getUser()->getName(), + 'usertarget' => $this->targetUser, + 'clientip' => $this->getRequest()->getIP(), + ] + ); + + return true; + } + + /** + * @throws MWException + */ + public function onSuccess() { + switch ( $this->enabledStatus ) { + case self::OATHAUTH_IS_ENABLED: + $msg = 'oathauth-verify-enabled'; + break; + case self::OATHAUTH_NOT_ENABLED: + $msg = 'oathauth-verify-disabled'; + break; + default: + throw new MWException( + 'Verification was successful but status is unknown' + ); + } + + $out = $this->getOutput(); + $out->addBacklinkSubtitle( $this->getPageTitle() ); + $out->addWikiMsg( $msg, $this->targetUser ); + } + +}