Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 68
0.00% covered (danger)
0.00%
0 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
WebAuthnManageForm
0.00% covered (danger)
0.00%
0 / 68
0.00% covered (danger)
0.00%
0 / 8
240
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 getHTML
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getButtons
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
6
 onSuccess
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 onSubmit
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
20
 getDescriptors
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
6
 removeKey
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 authenticate
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace MediaWiki\Extension\WebAuthn\HTMLForm;
4
5use IContextSource;
6use MediaWiki\Config\ConfigException;
7use MediaWiki\Extension\OATHAuth\HTMLForm\OATHAuthOOUIHTMLForm;
8use MediaWiki\Extension\OATHAuth\IModule;
9use MediaWiki\Extension\OATHAuth\OATHUser;
10use MediaWiki\Extension\OATHAuth\OATHUserRepository;
11use MediaWiki\Extension\WebAuthn\Authenticator;
12use MediaWiki\Extension\WebAuthn\HTMLField\RegisteredKeyLayout;
13use MediaWiki\Extension\WebAuthn\Key\WebAuthnKey;
14use MediaWiki\Extension\WebAuthn\Module\WebAuthn;
15use MediaWiki\MediaWikiServices;
16use MediaWiki\SpecialPage\SpecialPage;
17use MWException;
18use OOUI\ButtonWidget;
19
20class WebAuthnManageForm extends OATHAuthOOUIHTMLForm {
21
22    /**
23     * @var bool
24     */
25    protected $panelPadded = false;
26
27    /**
28     * @var bool
29     */
30    protected $panelFramed = false;
31
32    /**
33     * @var WebAuthn
34     */
35    protected $module;
36
37    /**
38     * @inheritDoc
39     */
40    public function __construct(
41        OATHUser $oathUser,
42        OATHUserRepository $oathRepo,
43        IModule $module,
44        IContextSource $context
45    ) {
46        parent::__construct( $oathUser, $oathRepo, $module, $context );
47
48        $this->setId( 'webauthn-manage-form' );
49        $this->suppressDefaultSubmit();
50    }
51
52    /**
53     * @inheritDoc
54     */
55    public function getHTML( $submitResult ) {
56        $this->getOutput()->addModules( 'ext.webauthn.manage' );
57        return parent::getHTML( $submitResult );
58    }
59
60    /**
61     * @return ButtonWidget|string
62     * @throws ConfigException
63     * @throws MWException
64     */
65    public function getButtons() {
66        $moduleConfig = $this->module->getConfig()->get( 'maxKeysPerUser' );
67        if ( count( $this->oathUser->getKeys() ) >= (int)$moduleConfig ) {
68            return '';
69        }
70        return new ButtonWidget( [
71            'id' => 'button_add_key',
72            'flags' => [ 'progressive', 'primary' ],
73            'disabled' => true,
74            'label' => wfMessage( 'webauthn-ui-add-key' )->plain(),
75            'href' => SpecialPage::getTitleFor( 'OATHManage' )->getLocalURL( [
76                'module' => 'webauthn',
77                'action' => WebAuthn::ACTION_ADD_KEY
78            ] ),
79            'infusable' => true
80        ] );
81    }
82
83    /**
84     * Add content to output when operation was successful
85     */
86    public function onSuccess() {
87        $this->getOutput()->redirect(
88            SpecialPage::getTitleFor( 'OATHManage' )->getLocalURL()
89        );
90    }
91
92    /**
93     * @param array $formData
94     * @return array|bool
95     * @throws ConfigException
96     * @throws MWException
97     */
98    public function onSubmit( array $formData ) {
99        if ( !isset( $formData['credential'] ) || !$this->authenticate( $formData['credential'] ) ) {
100            return [ 'oathauth-failedtovalidateoath' ];
101        }
102        if ( isset( $formData['remove_key'] ) ) {
103            return $this->removeKey( $formData['remove_key'] );
104        }
105        return true;
106    }
107
108    /**
109     * @return array
110     * @throws ConfigException
111     * @throws MWException
112     */
113    protected function getDescriptors() {
114        /** @var OATHUserRepository $userRepo */
115        $userRepo = MediaWikiServices::getInstance()->getService( 'OATHUserRepository' );
116        /** @var OATHUser $oathUser */
117        $oathUser = $userRepo->findByUser( $this->getUser() );
118        /** @var WebAuthnKey[] $keys */
119        $keys = $oathUser->getKeys();
120
121        $registeredKeys = [];
122        foreach ( $keys as $idx => $key ) {
123            $registeredKeys["reg_key_$idx"] = [
124                'type' => 'null',
125                'default' => [
126                    'name' => $key->getFriendlyName(),
127                    'signCount' => $key->getSignCounter()
128                ],
129                'raw' => true,
130                'class' => RegisteredKeyLayout::class,
131                'section' => 'webauthn-registered-keys-section-name'
132            ];
133        }
134
135        return $registeredKeys + [
136            'edit_key' => [
137                'type' => 'hidden',
138                'name' => 'edit_key'
139            ],
140            'remove_key' => [
141                'type' => 'hidden',
142                'name' => 'remove_key'
143            ],
144            'credential' => [
145                'type' => 'hidden',
146                'name' => 'credential'
147            ]
148        ];
149    }
150
151    /**
152     * @param string $key Friendly name
153     * @return array|bool
154     * @throws MWException
155     * @throws ConfigException
156     */
157    private function removeKey( $key ) {
158        $key = $this->module->getKeyByFriendlyName( $key, $this->oathUser );
159        if ( !$key ) {
160            return [ 'webauthn-error-cannot-remove-key' ];
161        }
162
163        $this->oathRepo->removeKey( $this->oathUser, $key, $this->getRequest()->getIP(), true );
164        return true;
165    }
166
167    /**
168     * @param string $credential
169     * @return bool
170     * @throws ConfigException
171     */
172    private function authenticate( $credential ) {
173        $authenticator = Authenticator::factory( $this->getUser(), $this->getRequest() );
174        if ( !$authenticator->isEnabled() ) {
175            return false;
176        }
177
178        $authenticationResult = $authenticator->continueAuthentication( [
179            'credential' => $credential
180        ] );
181
182        return $authenticationResult->isGood();
183    }
184}