MediaWiki REL1_35
SpecialChangeCredentials.php
Go to the documentation of this file.
1<?php
2
8
15 protected static $allowedActions = [ AuthManager::ACTION_CHANGE ];
16
17 protected static $messagePrefix = 'changecredentials';
18
20 protected static $loadUserData = true;
21
22 public function __construct( $name = 'ChangeCredentials' ) {
23 parent::__construct( $name, 'editmyprivateinfo' );
24 }
25
26 protected function getGroupName() {
27 return 'users';
28 }
29
30 public function isListed() {
31 $this->loadAuth( '' );
32 return (bool)$this->authRequests;
33 }
34
35 public function doesWrites() {
36 return true;
37 }
38
39 protected function getDefaultAction( $subPage ) {
40 return AuthManager::ACTION_CHANGE;
41 }
42
43 protected function getPreservedParams( $withToken = false ) {
44 $request = $this->getRequest();
45 $params = parent::getPreservedParams( $withToken );
46 $params += [
47 'returnto' => $request->getVal( 'returnto' ),
48 'returntoquery' => $request->getVal( 'returntoquery' ),
49 ];
50 return $params;
51 }
52
53 public function execute( $subPage ) {
54 $this->setHeaders();
55 $this->outputHeader();
56
57 $this->loadAuth( $subPage );
58
59 if ( !$subPage ) {
60 $this->showSubpageList();
61 return;
62 }
63
64 if ( !$this->authRequests ) {
65 // messages used: changecredentials-invalidsubpage, removecredentials-invalidsubpage
66 $this->showSubpageList( $this->msg( static::$messagePrefix . '-invalidsubpage', $subPage ) );
67 return;
68 }
69
70 $this->getOutput()->addBacklinkSubtitle( $this->getPageTitle() );
71
72 $status = $this->trySubmit();
73
74 if ( $status === false || !$status->isOK() ) {
75 $this->displayForm( $status );
76 return;
77 }
78
79 $response = $status->getValue();
80
81 switch ( $response->status ) {
82 case AuthenticationResponse::PASS:
83 $this->success();
84 break;
85 case AuthenticationResponse::FAIL:
86 $this->displayForm( Status::newFatal( $response->message ) );
87 break;
88 default:
89 throw new LogicException( 'invalid AuthenticationResponse' );
90 }
91 }
92
93 protected function loadAuth( $subPage, $authAction = null, $reset = false ) {
94 parent::loadAuth( $subPage, $authAction );
95 if ( $subPage ) {
96 $foundReqs = [];
97 foreach ( $this->authRequests as $req ) {
98 if ( $req->getUniqueId() === $subPage ) {
99 $foundReqs[] = $req;
100 }
101 }
102 if ( count( $foundReqs ) > 1 ) {
103 throw new LogicException( 'Multiple AuthenticationRequest objects with same ID!' );
104 }
105 $this->authRequests = $foundReqs;
106 }
107 }
108
110 public function onAuthChangeFormFields(
111 array $requests, array $fieldInfo, array &$formDescriptor, $action
112 ) {
113 parent::onAuthChangeFormFields( $requests, $fieldInfo, $formDescriptor, $action );
114
115 // Add some UI flair for password changes, the most common use case for this page.
116 if ( AuthenticationRequest::getRequestByClass( $this->authRequests,
117 PasswordAuthenticationRequest::class )
118 ) {
119 $formDescriptor = self::mergeDefaultFormDescriptor( $fieldInfo, $formDescriptor, [
120 'password' => [
121 'autocomplete' => 'new-password',
122 'placeholder-message' => 'createacct-yourpassword-ph',
123 'help-message' => 'createacct-useuniquepass',
124 ],
125 'retype' => [
126 'autocomplete' => 'new-password',
127 'placeholder-message' => 'createacct-yourpasswordagain-ph',
128 ],
129 ] );
130 }
131 }
132
133 protected function getAuthFormDescriptor( $requests, $action ) {
134 if ( !static::$loadUserData ) {
135 return [];
136 } else {
137 $descriptor = parent::getAuthFormDescriptor( $requests, $action );
138
139 $any = false;
140 foreach ( $descriptor as &$field ) {
141 if ( $field['type'] === 'password' && $field['name'] !== 'retype' ) {
142 $any = true;
143 if ( isset( $field['cssclass'] ) ) {
144 $field['cssclass'] .= ' mw-changecredentials-validate-password';
145 } else {
146 $field['cssclass'] = 'mw-changecredentials-validate-password';
147 }
148 }
149 }
150
151 if ( $any ) {
152 $this->getOutput()->addModules( 'mediawiki.misc-authed-ooui' );
153 }
154
155 return $descriptor;
156 }
157 }
158
159 protected function getAuthForm( array $requests, $action ) {
160 $form = parent::getAuthForm( $requests, $action );
161 $req = reset( $requests );
162 $info = $req->describeCredentials();
163
164 $form->addPreText(
165 Html::openElement( 'dl' )
166 . Html::element( 'dt', [], $this->msg( 'credentialsform-provider' )->text() )
167 . Html::element( 'dd', [], $info['provider'] )
168 . Html::element( 'dt', [], $this->msg( 'credentialsform-account' )->text() )
169 . Html::element( 'dd', [], $info['account'] )
170 . Html::closeElement( 'dl' )
171 );
172
173 // messages used: changecredentials-submit removecredentials-submit
174 $form->setSubmitTextMsg( static::$messagePrefix . '-submit' );
175 $form->showCancel()->setCancelTarget( $this->getReturnUrl() ?: Title::newMainPage() );
176
177 return $form;
178 }
179
180 protected function needsSubmitButton( array $requests ) {
181 // Change/remove forms show are built from a single AuthenticationRequest and do not allow
182 // for redirect flow; they always need a submit button.
183 return true;
184 }
185
186 public function handleFormSubmit( $data ) {
187 // remove requests do not accept user input
188 $requests = $this->authRequests;
189 if ( static::$loadUserData ) {
190 $requests = AuthenticationRequest::loadRequestsFromSubmission( $this->authRequests, $data );
191 }
192
193 $response = $this->performAuthenticationStep( $this->authAction, $requests );
194
195 // we can't handle FAIL or similar as failure here since it might require changing the form
196 return Status::newGood( $response );
197 }
198
202 protected function showSubpageList( $error = null ) {
203 $out = $this->getOutput();
204
205 if ( $error ) {
206 $out->addHTML( $error->parse() );
207 }
208
209 $groupedRequests = [];
210 foreach ( $this->authRequests as $req ) {
211 $info = $req->describeCredentials();
212 $groupedRequests[(string)$info['provider']][] = $req;
213 }
214
216 $out->addHTML( Html::openElement( 'dl' ) );
217 foreach ( $groupedRequests as $group => $members ) {
218 $out->addHTML( Html::element( 'dt', [], $group ) );
219 foreach ( $members as $req ) {
221 $info = $req->describeCredentials();
222 $out->addHTML( Html::rawElement( 'dd', [],
223 $linkRenderer->makeLink(
224 $this->getPageTitle( $req->getUniqueId() ),
225 $info['account']
226 )
227 ) );
228 }
229 }
230 $out->addHTML( Html::closeElement( 'dl' ) );
231 }
232
233 protected function success() {
234 $session = $this->getRequest()->getSession();
235 $user = $this->getUser();
236 $out = $this->getOutput();
237 $returnUrl = $this->getReturnUrl();
238
239 // change user token and update the session
240 SessionManager::singleton()->invalidateSessionsForUser( $user );
241 $session->setUser( $user );
242 $session->resetId();
243
244 if ( $returnUrl ) {
245 $out->redirect( $returnUrl );
246 } else {
247 // messages used: changecredentials-success removecredentials-success
248 $out->wrapWikiMsg( "<div class=\"successbox\">\n$1\n</div>", static::$messagePrefix
249 . '-success' );
250 $out->returnToMain();
251 }
252 }
253
257 protected function getReturnUrl() {
258 $request = $this->getRequest();
259 $returnTo = $request->getText( 'returnto' );
260 $returnToQuery = $request->getText( 'returntoquery', '' );
261
262 if ( !$returnTo ) {
263 return null;
264 }
265
266 $title = Title::newFromText( $returnTo );
267 return $title->getFullUrlForRedirect( $returnToQuery );
268 }
269
270 protected function getRequestBlacklist() {
271 return $this->getConfig()->get( 'ChangeCredentialsBlacklist' );
272 }
273}
A special page subclass for authentication-related special pages.
string $authAction
one of the AuthManager::ACTION_* constants.
performAuthenticationStep( $action, array $requests)
displayForm( $status)
Display the form.
AuthenticationRequest[] $authRequests
string $subPage
Subpage of the special page.
static mergeDefaultFormDescriptor(array $fieldInfo, array $formDescriptor, array $defaultFormDescriptor)
Apply defaults to a form descriptor, without creating non-existend fields.
getRequest()
Get the WebRequest being used for this instance.
trySubmit()
Attempts to do an authentication step with the submitted data.
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 Stable to extend.
This serves as the entry point to the MediaWiki session handling system.
Special change to change credentials (such as the password).
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
getAuthForm(array $requests, $action)
Stable to override.
doesWrites()
Indicates whether this special page may perform database writes.
handleFormSubmit( $data)
Submit handler callback for HTMLForm.
getPreservedParams( $withToken=false)
Returns URL query parameters which can be used to reload the page (or leave and return) while preserv...
isListed()
Whether this special page is listed in Special:SpecialPages Stable to override.
getRequestBlacklist()
Allows blacklisting certain request types.
getAuthFormDescriptor( $requests, $action)
Generates a HTMLForm descriptor array from a set of authentication requests.
loadAuth( $subPage, $authAction=null, $reset=false)
Load or initialize $authAction, $authRequests and $subPage.
__construct( $name='ChangeCredentials')
getDefaultAction( $subPage)
Get the default action for this special page, if none is given via URL/POST data.
needsSubmitButton(array $requests)
Returns true if the form built from the given AuthenticationRequests needs a submit button.
static $loadUserData
Change action needs user data; remove action does not.
execute( $subPage)
Default execute method Checks user permissions.
onAuthChangeFormFields(array $requests, array $fieldInfo, array &$formDescriptor, $action)
Change the form descriptor that determines how a field will look in the authentication form....
outputHeader( $summaryMessageKey='')
Outputs a summary message on top of special pages Per default the message key is the canonical name o...
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!...
getOutput()
Get the OutputPage being used for this instance.
getUser()
Shortcut to get the User executing this instance.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getConfig()
Shortcut to get main config object.
getPageTitle( $subpage=false)
Get a self-referential title object.
MediaWiki Linker LinkRenderer null $linkRenderer