MediaWiki REL1_34
TOTPEnableForm.php
Go to the documentation of this file.
1<?php
2
4
7use Html;
8use Status;
9
15 public function getHTML( $submitResult ) {
16 $this->getOutput()->addModules( 'ext.oath.totp.showqrcode' );
17 $this->getOutput()->addModuleStyles( 'ext.oath.totp.showqrcode.styles' );
18
19 return parent::getHTML( $submitResult );
20 }
21
25 public function onSuccess() {
26 $this->getOutput()->addWikiMsg( 'oathauth-validatedoath' );
27 }
28
29 protected function getDescriptors() {
30 $keyData = $this->getRequest()->getSessionData( 'oathauth_totp_key' ) ?? [];
31 $key = TOTPKey::newFromArray( $keyData );
32 if ( !$key instanceof TOTPKey ) {
34 $this->getRequest()->setSessionData(
35 'oathauth_totp_key',
36 $key->jsonSerialize()
37 );
38 }
39
40 $secret = $key->getSecret();
41 $label = "{$this->oathUser->getIssuer()}:{$this->oathUser->getAccount()}";
42 $qrcodeUrl = "otpauth://totp/"
43 . rawurlencode( $label )
44 . "?secret="
45 . rawurlencode( $secret )
46 . "&issuer="
47 . rawurlencode( $this->oathUser->getIssuer() );
48
49 $qrcodeElement = Html::element( 'div', [
50 'data-mw-qrcode-url' => $qrcodeUrl,
51 'class' => 'mw-display-qrcode',
52 // Include width/height, so js won't re-arrange layout
53 // And non-js users will have this hidden with CSS
54 'style' => 'width: 256px; height: 256px;'
55 ] );
56
57 return [
58 'app' => [
59 'type' => 'info',
60 'default' => $this->msg( 'oathauth-step1-test' )->escaped(),
61 'raw' => true,
62 'section' => 'step1',
63 ],
64 'qrcode' => [
65 'type' => 'info',
66 'default' => $qrcodeElement,
67 'raw' => true,
68 'section' => 'step2',
69 ],
70 'manual' => [
71 'type' => 'info',
72 'label-message' => 'oathauth-step2alt',
73 'default' =>
74 '<strong>' . $this->msg( 'oathauth-account' )->escaped() . '</strong><br/>'
75 . htmlspecialchars( $this->oathUser->getAccount() ) . '<br/><br/>'
76 . '<strong>' . $this->msg( 'oathauth-secret' )->escaped() . '</strong><br/>'
77 . '<kbd>' . $this->getSecretForDisplay( $key ) . '</kbd><br/>',
78 'raw' => true,
79 'section' => 'step2',
80 ],
81 'scratchtokens' => [
82 'type' => 'info',
83 'default' =>
84 $this->msg( 'oathauth-scratchtokens' )
85 . $this->createResourceList( $this->getScratchTokensForDisplay( $key ) ),
86 'raw' => true,
87 'section' => 'step3',
88 ],
89 'token' => [
90 'type' => 'text',
91 'default' => '',
92 'label-message' => 'oathauth-entertoken',
93 'name' => 'token',
94 'section' => 'step4',
95 'dir' => 'ltr',
96 'autocomplete' => false,
97 'spellcheck' => false,
98 ]
99 ];
100 }
101
106 private function createResourceList( $resources ) {
107 $resourceList = '';
108 foreach ( $resources as $resource ) {
109 $resourceList .= Html::rawElement( 'li', [], Html::rawElement( 'kbd', [], $resource ) );
110 }
111 return Html::rawElement( 'ul', [], $resourceList );
112 }
113
122 protected function getSecretForDisplay( TOTPKey $key ) {
123 return $this->tokenFormatterFunction( $key->getSecret() );
124 }
125
134 protected function getScratchTokensForDisplay( TOTPKey $key ) {
135 return array_map( [ $this, 'tokenFormatterFunction' ], $key->getScratchTokens() );
136 }
137
144 private function tokenFormatterFunction( $token ) {
145 return implode( ' ', str_split( $token, 4 ) );
146 }
147
154 public function onSubmit( array $formData ) {
155 $keyData = $this->getRequest()->getSessionData( 'oathauth_totp_key' ) ?? [];
156 $key = TOTPKey::newFromArray( $keyData );
157 if ( !$key instanceof TOTPKey ) {
158 return [ 'oathauth-invalidrequest' ];
159 }
160
161 if ( $key->isScratchToken( $formData['token'] ) ) {
162 // A scratch token is not allowed for enrollment
163 LoggerFactory::getInstance( 'authentication' )->info(
164 'OATHAuth {user} attempted to enable 2FA using a scratch token from {clientip}', [
165 'user' => $this->getUser()->getName(),
166 'clientip' => $this->getRequest()->getIP(),
167 ]
168 );
169 return [ 'oathauth-noscratchforvalidation' ];
170 }
171 if ( !$key->verify( [ 'token' => $formData['token'] ], $this->oathUser ) ) {
172 LoggerFactory::getInstance( 'authentication' )->info(
173 'OATHAuth {user} failed to provide a correct token while enabling 2FA from {clientip}', [
174 'user' => $this->getUser()->getName(),
175 'clientip' => $this->getRequest()->getIP(),
176 ]
177 );
178 return [ 'oathauth-failedtovalidateoath' ];
179 }
180
181 $this->getRequest()->setSessionData( 'oathauth_totp_key', null );
182 $this->oathUser->setKeys( [ $key ] );
183 $this->oathUser->setModule( $this->module );
184 $this->oathRepo->persist( $this->oathUser, $this->getRequest()->getIP() );
185
186 return true;
187 }
188}
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
This class is a collection of static functions that serve two purposes:
Definition Html.php:49
onSuccess()
Add content to output when operation was successful.
tokenFormatterFunction( $token)
Formats a key or scratch token by creating groups of 4 separated by space characters.
getSecretForDisplay(TOTPKey $key)
Retrieve the current secret for display purposes.
getScratchTokensForDisplay(TOTPKey $key)
Retrieve current scratch tokens for display purposes.
Class representing a two-factor key.
Definition TOTPKey.php:42
PSR-3 logger instance factory.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:40