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