MediaWiki master
SpecialChangeCredentials.php
Go to the documentation of this file.
1<?php
2
3namespace MediaWiki\Specials;
4
5use LogicException;
17
28 protected static $allowedActions = [ AuthManager::ACTION_CHANGE ];
29
30 protected static $messagePrefix = 'changecredentials';
31
33 protected static $loadUserData = true;
34
38 public function __construct( AuthManager $authManager ) {
39 parent::__construct( 'ChangeCredentials', 'editmyprivateinfo' );
40 $this->setAuthManager( $authManager );
41 }
42
43 protected function getGroupName() {
44 return 'login';
45 }
46
47 public function isListed() {
48 $this->loadAuth( '' );
49 return (bool)$this->authRequests;
50 }
51
52 public function doesWrites() {
53 return true;
54 }
55
56 protected function getDefaultAction( $subPage ) {
57 return AuthManager::ACTION_CHANGE;
58 }
59
60 public function execute( $subPage ) {
61 $this->setHeaders();
62 $this->outputHeader();
63
64 $this->loadAuth( $subPage );
65
66 if ( !$subPage ) {
67 $this->showSubpageList();
68 return;
69 }
70
71 if ( !$this->authRequests ) {
72 // messages used: changecredentials-invalidsubpage, removecredentials-invalidsubpage
73 $this->showSubpageList( $this->msg( static::$messagePrefix . '-invalidsubpage', $subPage ) );
74 return;
75 }
76
77 $out = $this->getOutput();
78 $out->addModules( 'mediawiki.special.changecredentials' );
79 $out->addBacklinkSubtitle( $this->getPageTitle() );
80 $status = $this->trySubmit();
81
82 if ( $status === false || !$status->isOK() ) {
83 $this->displayForm( $status );
84 return;
85 }
86
87 $response = $status->getValue();
88
89 switch ( $response->status ) {
90 case AuthenticationResponse::PASS:
91 $this->success();
92 break;
93 case AuthenticationResponse::FAIL:
94 $this->displayForm( Status::newFatal( $response->message ) );
95 break;
96 default:
97 throw new LogicException( 'invalid AuthenticationResponse' );
98 }
99 }
100
101 protected function loadAuth( $subPage, $authAction = null, $reset = false ) {
102 parent::loadAuth( $subPage, $authAction );
103 if ( $subPage ) {
104 $foundReqs = [];
105 foreach ( $this->authRequests as $req ) {
106 if ( $req->getUniqueId() === $subPage ) {
107 $foundReqs[] = $req;
108 }
109 }
110 if ( count( $foundReqs ) > 1 ) {
111 throw new LogicException( 'Multiple AuthenticationRequest objects with same ID!' );
112 }
113 $this->authRequests = $foundReqs;
114 }
115 }
116
118 public function onAuthChangeFormFields(
119 array $requests, array $fieldInfo, array &$formDescriptor, $action
120 ) {
121 parent::onAuthChangeFormFields( $requests, $fieldInfo, $formDescriptor, $action );
122
123 // Add some UI flair for password changes, the most common use case for this page.
124 if ( AuthenticationRequest::getRequestByClass( $this->authRequests,
125 PasswordAuthenticationRequest::class )
126 ) {
127 $formDescriptor = self::mergeDefaultFormDescriptor( $fieldInfo, $formDescriptor, [
128 'password' => [
129 'autocomplete' => 'new-password',
130 'placeholder-message' => 'createacct-yourpassword-ph',
131 'help-message' => 'createacct-useuniquepass',
132 ],
133 'retype' => [
134 'autocomplete' => 'new-password',
135 'placeholder-message' => 'createacct-yourpasswordagain-ph',
136 ],
137 // T263927 - the Chromium password form guide recommends always having a username field
138 'username' => [
139 'type' => 'text',
140 'baseField' => 'password',
141 'autocomplete' => 'username',
142 'nodata' => true,
143 'readonly' => true,
144 'cssclass' => 'mw-htmlform-hidden-field',
145 'label-message' => 'userlogin-yourname',
146 'placeholder-message' => 'userlogin-yourname-ph',
147 ],
148 ] );
149 }
150 }
151
152 protected function getAuthFormDescriptor( $requests, $action ) {
153 if ( !static::$loadUserData ) {
154 return [];
155 }
156
157 $descriptor = parent::getAuthFormDescriptor( $requests, $action );
158
159 $any = false;
160 foreach ( $descriptor as &$field ) {
161 if ( $field['type'] === 'password' && $field['name'] !== 'retype' ) {
162 $any = true;
163 if ( isset( $field['cssclass'] ) ) {
164 $field['cssclass'] .= ' mw-changecredentials-validate-password';
165 } else {
166 $field['cssclass'] = 'mw-changecredentials-validate-password';
167 }
168 }
169 }
170 unset( $field );
171
172 if ( $any ) {
173 $this->getOutput()->addModules( 'mediawiki.misc-authed-ooui' );
174 }
175
176 return $descriptor;
177 }
178
179 protected function getAuthForm( array $requests, $action ) {
180 $form = parent::getAuthForm( $requests, $action );
181 $req = reset( $requests );
182 $info = $req->describeCredentials();
183
184 $form->addPreHtml(
185 Html::openElement( 'dl' )
186 . Html::element( 'dt', [], $this->msg( 'credentialsform-provider' )->text() )
187 . Html::element( 'dd', [], $info['provider']->text() )
188 . Html::element( 'dt', [], $this->msg( 'credentialsform-account' )->text() )
189 . Html::element( 'dd', [], $info['account']->text() )
190 . Html::closeElement( 'dl' )
191 );
192
193 // messages used: changecredentials-submit removecredentials-submit
194 $form->setSubmitTextMsg( static::$messagePrefix . '-submit' );
195 $form->showCancel()->setCancelTarget( $this->getReturnUrl() ?: Title::newMainPage() );
196 $form->setSubmitID( 'change_credentials_submit' );
197 return $form;
198 }
199
200 protected function needsSubmitButton( array $requests ) {
201 // Change/remove forms show are built from a single AuthenticationRequest and do not allow
202 // for redirect flow; they always need a submit button.
203 return true;
204 }
205
206 public function handleFormSubmit( $data ) {
207 // remove requests do not accept user input
208 $requests = $this->authRequests;
209 if ( static::$loadUserData ) {
210 $requests = AuthenticationRequest::loadRequestsFromSubmission( $this->authRequests, $data );
211 }
212
213 $response = $this->performAuthenticationStep( $this->authAction, $requests );
214
215 // we can't handle FAIL or similar as failure here since it might require changing the form
216 return Status::newGood( $response );
217 }
218
222 protected function showSubpageList( $error = null ) {
223 $out = $this->getOutput();
224
225 if ( $error ) {
226 $out->addHTML( $error->parse() );
227 }
228
229 $groupedRequests = [];
230 foreach ( $this->authRequests as $req ) {
231 $info = $req->describeCredentials();
232 $groupedRequests[$info['provider']->text()][] = $req;
233 }
234
235 $linkRenderer = $this->getLinkRenderer();
236 $out->addHTML( Html::openElement( 'dl' ) );
237 foreach ( $groupedRequests as $group => $members ) {
238 $out->addHTML( Html::element( 'dt', [], $group ) );
239 foreach ( $members as $req ) {
241 $info = $req->describeCredentials();
242 $out->addHTML( Html::rawElement( 'dd', [],
243 $linkRenderer->makeLink(
244 $this->getPageTitle( $req->getUniqueId() ),
245 $info['account']->text()
246 )
247 ) );
248 }
249 }
250 $out->addHTML( Html::closeElement( 'dl' ) );
251 }
252
253 protected function success() {
254 $session = $this->getRequest()->getSession();
255 $user = $this->getUser();
256 $out = $this->getOutput();
257 $returnUrl = $this->getReturnUrl();
258
259 // change user token and update the session
260 SessionManager::singleton()->invalidateSessionsForUser( $user );
261 $session->setUser( $user );
262 $session->resetId();
263
264 if ( $returnUrl ) {
265 $out->redirect( $returnUrl );
266 } else {
267 // messages used: changecredentials-success removecredentials-success
268 $out->addHTML(
269 Html::successBox(
270 $out->msg( static::$messagePrefix . '-success' )->parse()
271 )
272 );
273 $out->returnToMain();
274 }
275 }
276
280 protected function getReturnUrl() {
281 $request = $this->getRequest();
282 $returnTo = $request->getText( 'returnto' );
283 $returnToQuery = $request->getText( 'returntoquery', '' );
284
285 if ( !$returnTo ) {
286 return null;
287 }
288
289 return Title::newFromText( $returnTo )->getFullUrlForRedirect( $returnToQuery );
290 }
291
292 protected function getRequestBlacklist() {
294 }
295}
296
298class_alias( SpecialChangeCredentials::class, 'SpecialChangeCredentials' );
This serves as the entry point to the authentication system.
This is a value object for authentication requests.
describeCredentials()
Describe the credentials represented by this request.
This is a value object to hold authentication response data.
This is a value object for authentication requests with a username and password.
This class is a collection of static functions that serve two purposes:
Definition Html.php:56
A class containing constants representing the names of configuration variables.
const ChangeCredentialsBlacklist
Name constant for the ChangeCredentialsBlacklist setting, for use with Config::get()
The Message class deals with fetching and processing of interface message into a variety of formats.
Definition Message.php:158
This serves as the entry point to the MediaWiki session handling system.
A special page subclass for authentication-related special pages.
string $authAction
one of the AuthManager::ACTION_* constants.
static mergeDefaultFormDescriptor(array $fieldInfo, array $formDescriptor, array $defaultFormDescriptor)
Apply defaults to a form descriptor, without creating non-existent fields.
trySubmit()
Attempts to do an authentication step with the submitted data.
getRequest()
Get the WebRequest being used for this instance.
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
getUser()
Shortcut to get the User executing this instance.
setAuthManager(AuthManager $authManager)
Set the injected AuthManager from the special page constructor.
getPageTitle( $subpage=false)
Get a self-referential title object.
getConfig()
Shortcut to get main config object.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getOutput()
Get the OutputPage being used for this instance.
outputHeader( $summaryMessageKey='')
Outputs a summary message on top of special pages By default the message key is the canonical name of...
Change user credentials, such as the password.
onAuthChangeFormFields(array $requests, array $fieldInfo, array &$formDescriptor, $action)
Change the form descriptor that determines how a field will look in the authentication form....
needsSubmitButton(array $requests)
Returns true if the form built from the given AuthenticationRequests needs a submit button.
loadAuth( $subPage, $authAction=null, $reset=false)
Load or initialize $authAction, $authRequests and $subPage.
handleFormSubmit( $data)
Submit handler callback for HTMLForm.
execute( $subPage)
Default execute method Checks user permissions.
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
isListed()
Whether this special page is listed in Special:SpecialPages.
getRequestBlacklist()
Allows blacklisting certain request types.
static $loadUserData
Change action needs user data; remove action does not.
getDefaultAction( $subPage)
Get the default action for this special page if none is given via URL/POST data.
getAuthFormDescriptor( $requests, $action)
Generates a HTMLForm descriptor array from a set of authentication requests.
doesWrites()
Indicates whether this special page may perform database writes.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:54
Represents a title within MediaWiki.
Definition Title.php:79
element(SerializerNode $parent, SerializerNode $node, $contents)