Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
94.83% covered (success)
94.83%
55 / 58
76.92% covered (warning)
76.92%
10 / 13
CRAP
0.00% covered (danger)
0.00%
0 / 1
VerifyOATHForUser
94.83% covered (success)
94.83%
55 / 58
76.92% covered (warning)
76.92%
10 / 13
18.04
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getRestriction
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getGroupName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 doesWrites
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getLoginSecurityLevel
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 alterForm
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getDisplayFormat
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 requiresUnblock
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 checkExecutePermissions
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 execute
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getFormFields
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
1
 onSubmit
95.83% covered (success)
95.83%
23 / 24
0.00% covered (danger)
0.00%
0 / 1
5
 onSuccess
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3namespace MediaWiki\Extension\OATHAuth\Special;
4
5use MediaWiki\CheckUser\Services\CheckUserInsert;
6use MediaWiki\Extension\OATHAuth\OATHUserRepository;
7use MediaWiki\HTMLForm\HTMLForm;
8use MediaWiki\Logger\LoggerFactory;
9use MediaWiki\Logging\ManualLogEntry;
10use MediaWiki\MediaWikiServices;
11use MediaWiki\Registration\ExtensionRegistry;
12use MediaWiki\SpecialPage\FormSpecialPage;
13use MediaWiki\Status\Status;
14use MediaWiki\User\CentralId\CentralIdLookup;
15use MediaWiki\User\User;
16use MediaWiki\User\UserFactory;
17
18class VerifyOATHForUser extends FormSpecialPage {
19    private bool $enabledStatus;
20    private string $targetUser;
21
22    public function __construct(
23        private readonly OATHUserRepository $userRepo,
24        private readonly UserFactory $userFactory,
25        private readonly CentralIdLookup $centralIdLookup,
26        private readonly ExtensionRegistry $extensionRegistry,
27    ) {
28        // messages used: verifyoathforuser (display "name" on Special:SpecialPages)
29        parent::__construct( 'VerifyOATHForUser' );
30    }
31
32    /** @inheritDoc */
33    public function getRestriction(): string {
34        // messages used: right-oathauth-verify-user, action-oathauth-verify-user
35        return 'oathauth-verify-user';
36    }
37
38    /** @inheritDoc */
39    protected function getGroupName() {
40        return 'users';
41    }
42
43    /** @inheritDoc */
44    public function doesWrites() {
45        return true;
46    }
47
48    /** @inheritDoc */
49    protected function getLoginSecurityLevel() {
50        return $this->getName();
51    }
52
53    /** @inheritDoc */
54    public function alterForm( HTMLForm $form ) {
55        $form->setMessagePrefix( 'oathauth' );
56        $form->getOutput()->setPageTitleMsg( $this->msg( 'oathauth-verify-for-user' ) );
57    }
58
59    /** @inheritDoc */
60    protected function getDisplayFormat() {
61        return 'ooui';
62    }
63
64    /** @inheritDoc */
65    public function requiresUnblock() {
66        return true;
67    }
68
69    protected function checkExecutePermissions( User $user ) {
70        $this->requireNamedUser();
71
72        parent::checkExecutePermissions( $user );
73    }
74
75    /** @inheritDoc */
76    public function execute( $par ) {
77        $this->getOutput()->disallowUserJs();
78        parent::execute( $par );
79    }
80
81    /** @inheritDoc */
82    protected function getFormFields() {
83        return [
84            'user' => [
85                'type' => 'user',
86                'default' => '',
87                'label-message' => 'oathauth-enteruser',
88                'name' => 'user',
89                'required' => true,
90                'excludetemp' => true,
91            ],
92            'reason' => [
93                'type' => 'text',
94                'default' => '',
95                'label-message' => 'oathauth-enterverifyreason',
96                'name' => 'reason',
97                'required' => true,
98            ],
99        ];
100    }
101
102    /** @inheritDoc */
103    public function onSubmit( array $formData ) {
104        $this->targetUser = $formData['user'];
105        $user = $this->userFactory->newFromName( $this->targetUser );
106        // T424117, same as T393253 - Check the username is valid, but don't check if
107        // it exists on the local wiki. Instead, check there is a valid central ID.
108        if ( !$user || $this->centralIdLookup->centralIdFromName( $formData['user'] ) === 0 ) {
109            return [ 'oathauth-user-not-found' ];
110        }
111
112        if ( $this->getUser()->pingLimiter( 'verify-2fa' ) ) {
113            return Status::newFatal( 'oathauth-throttled' );
114        }
115
116        $oathUser = $this->userRepo->findByUser( $user );
117
118        $this->enabledStatus = $oathUser->isTwoFactorAuthEnabled();
119
120        // messages used: logentry-oath-verify, log-action-oath-verify
121        $logEntry = new ManualLogEntry( 'oath', 'verify' );
122        $logEntry->setPerformer( $this->getUser() );
123        $logEntry->setTarget( $user->getUserPage() );
124        $logEntry->setComment( $formData['reason'] );
125        $logId = $logEntry->insert();
126
127        if ( $this->extensionRegistry->isLoaded( 'CheckUser' ) ) {
128            /** @var CheckUserInsert $checkUserInsert */
129            $checkUserInsert = MediaWikiServices::getInstance()->get( 'CheckUserInsert' );
130            $checkUserInsert->updateCheckUserData( $logEntry->getRecentChange( $logId ) );
131        }
132
133        LoggerFactory::getInstance( 'authentication' )->info(
134            'OATHAuth status checked for {usertarget} by {user} from {clientip}', [
135                'user' => $this->getUser()->getName(),
136                'usertarget' => $this->targetUser,
137                'clientip' => $this->getRequest()->getIP(),
138            ]
139        );
140
141        return true;
142    }
143
144    public function onSuccess() {
145        $msg = $this->enabledStatus ? 'oathauth-verify-enabled' : 'oathauth-verify-disabled';
146
147        $out = $this->getOutput();
148        $out->addBacklinkSubtitle( $this->getPageTitle() );
149        $out->addWikiMsg( $msg, $this->targetUser );
150    }
151
152}