MediaWiki  master
SpecialChangeCredentials.php
Go to the documentation of this file.
1 <?php
2 
7 
14  protected static $allowedActions = [ AuthManager::ACTION_CHANGE ];
15 
16  protected static $messagePrefix = 'changecredentials';
17 
19  protected static $loadUserData = true;
20 
21  public function __construct( $name = 'ChangeCredentials' ) {
22  parent::__construct( $name, 'editmyprivateinfo' );
23  }
24 
25  protected function getGroupName() {
26  return 'users';
27  }
28 
29  public function isListed() {
30  $this->loadAuth( '' );
31  return (bool)$this->authRequests;
32  }
33 
34  public function doesWrites() {
35  return true;
36  }
37 
38  protected function getDefaultAction( $subPage ) {
39  return AuthManager::ACTION_CHANGE;
40  }
41 
42  protected function getPreservedParams( $withToken = false ) {
43  $request = $this->getRequest();
44  $params = parent::getPreservedParams( $withToken );
45  $params += [
46  'returnto' => $request->getVal( 'returnto' ),
47  'returntoquery' => $request->getVal( 'returntoquery' ),
48  ];
49  return $params;
50  }
51 
52  public function execute( $subPage ) {
53  $this->setHeaders();
54  $this->outputHeader();
55 
56  $this->loadAuth( $subPage );
57 
58  if ( !$subPage ) {
59  $this->showSubpageList();
60  return;
61  }
62 
63  if ( !$this->authRequests ) {
64  // messages used: changecredentials-invalidsubpage, removecredentials-invalidsubpage
65  $this->showSubpageList( $this->msg( static::$messagePrefix . '-invalidsubpage', $subPage ) );
66  return;
67  }
68 
69  $this->getOutput()->addBacklinkSubtitle( $this->getPageTitle() );
70 
71  $status = $this->trySubmit();
72 
73  if ( $status === false || !$status->isOK() ) {
74  $this->displayForm( $status );
75  return;
76  }
77 
78  $response = $status->getValue();
79 
80  switch ( $response->status ) {
81  case AuthenticationResponse::PASS:
82  $this->success();
83  break;
84  case AuthenticationResponse::FAIL:
85  $this->displayForm( Status::newFatal( $response->message ) );
86  break;
87  default:
88  throw new LogicException( 'invalid AuthenticationResponse' );
89  }
90  }
91 
92  protected function loadAuth( $subPage, $authAction = null, $reset = false ) {
93  parent::loadAuth( $subPage, $authAction );
94  if ( $subPage ) {
95  $this->authRequests = array_filter( $this->authRequests, function ( $req ) use ( $subPage ) {
96  return $req->getUniqueId() === $subPage;
97  } );
98  if ( count( $this->authRequests ) > 1 ) {
99  throw new LogicException( 'Multiple AuthenticationRequest objects with same ID!' );
100  }
101  }
102  }
103 
104  protected function getAuthFormDescriptor( $requests, $action ) {
105  if ( !static::$loadUserData ) {
106  return [];
107  } else {
108  $descriptor = parent::getAuthFormDescriptor( $requests, $action );
109 
110  $any = false;
111  foreach ( $descriptor as &$field ) {
112  if ( $field['type'] === 'password' && $field['name'] !== 'retype' ) {
113  $any = true;
114  if ( isset( $field['cssclass'] ) ) {
115  $field['cssclass'] .= ' mw-changecredentials-validate-password';
116  } else {
117  $field['cssclass'] = 'mw-changecredentials-validate-password';
118  }
119  }
120  }
121 
122  if ( $any ) {
123  $this->getOutput()->addModules( 'mediawiki.misc-authed-ooui' );
124  }
125 
126  return $descriptor;
127  }
128  }
129 
130  protected function getAuthForm( array $requests, $action ) {
131  $form = parent::getAuthForm( $requests, $action );
132  $req = reset( $requests );
133  $info = $req->describeCredentials();
134 
135  $form->addPreText(
136  Html::openElement( 'dl' )
137  . Html::element( 'dt', [], $this->msg( 'credentialsform-provider' )->text() )
138  . Html::element( 'dd', [], $info['provider'] )
139  . Html::element( 'dt', [], $this->msg( 'credentialsform-account' )->text() )
140  . Html::element( 'dd', [], $info['account'] )
141  . Html::closeElement( 'dl' )
142  );
143 
144  // messages used: changecredentials-submit removecredentials-submit
145  $form->setSubmitTextMsg( static::$messagePrefix . '-submit' );
146  $form->showCancel()->setCancelTarget( $this->getReturnUrl() ?: Title::newMainPage() );
147 
148  return $form;
149  }
150 
151  protected function needsSubmitButton( array $requests ) {
152  // Change/remove forms show are built from a single AuthenticationRequest and do not allow
153  // for redirect flow; they always need a submit button.
154  return true;
155  }
156 
157  public function handleFormSubmit( $data ) {
158  // remove requests do not accept user input
159  $requests = $this->authRequests;
160  if ( static::$loadUserData ) {
161  $requests = AuthenticationRequest::loadRequestsFromSubmission( $this->authRequests, $data );
162  }
163 
164  $response = $this->performAuthenticationStep( $this->authAction, $requests );
165 
166  // we can't handle FAIL or similar as failure here since it might require changing the form
167  return Status::newGood( $response );
168  }
169 
173  protected function showSubpageList( $error = null ) {
174  $out = $this->getOutput();
175 
176  if ( $error ) {
177  $out->addHTML( $error->parse() );
178  }
179 
180  $groupedRequests = [];
181  foreach ( $this->authRequests as $req ) {
182  $info = $req->describeCredentials();
183  $groupedRequests[(string)$info['provider']][] = $req;
184  }
185 
186  $linkRenderer = $this->getLinkRenderer();
187  $out->addHTML( Html::openElement( 'dl' ) );
188  foreach ( $groupedRequests as $group => $members ) {
189  $out->addHTML( Html::element( 'dt', [], $group ) );
190  foreach ( $members as $req ) {
192  $info = $req->describeCredentials();
193  $out->addHTML( Html::rawElement( 'dd', [],
194  $linkRenderer->makeLink(
195  $this->getPageTitle( $req->getUniqueId() ),
196  $info['account']
197  )
198  ) );
199  }
200  }
201  $out->addHTML( Html::closeElement( 'dl' ) );
202  }
203 
204  protected function success() {
205  $session = $this->getRequest()->getSession();
206  $user = $this->getUser();
207  $out = $this->getOutput();
208  $returnUrl = $this->getReturnUrl();
209 
210  // change user token and update the session
211  SessionManager::singleton()->invalidateSessionsForUser( $user );
212  $session->setUser( $user );
213  $session->resetId();
214 
215  if ( $returnUrl ) {
216  $out->redirect( $returnUrl );
217  } else {
218  // messages used: changecredentials-success removecredentials-success
219  $out->wrapWikiMsg( "<div class=\"successbox\">\n$1\n</div>", static::$messagePrefix
220  . '-success' );
221  $out->returnToMain();
222  }
223  }
224 
228  protected function getReturnUrl() {
229  $request = $this->getRequest();
230  $returnTo = $request->getText( 'returnto' );
231  $returnToQuery = $request->getText( 'returntoquery', '' );
232 
233  if ( !$returnTo ) {
234  return null;
235  }
236 
237  $title = Title::newFromText( $returnTo );
238  return $title->getFullUrlForRedirect( $returnToQuery );
239  }
240 
241  protected function getRequestBlacklist() {
242  return $this->getConfig()->get( 'ChangeCredentialsBlacklist' );
243  }
244 }
static newFatal( $message,... $parameters)
Factory function for fatal errors.
Definition: StatusValue.php:69
$response
static element( $element, $attribs=[], $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:231
A special page subclass for authentication-related special pages.
static newMainPage(MessageLocalizer $localizer=null)
Create a new Title for the Main Page.
Definition: Title.php:648
static openElement( $element, $attribs=[])
Identical to rawElement(), but has no third parameter and omits the end tag (and the self-closing &#39;/&#39;...
Definition: Html.php:251
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:209
getOutput()
Get the OutputPage being used for this instance.
__construct( $name='ChangeCredentials')
getAuthForm(array $requests, $action)
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:81
static closeElement( $element)
Returns "</$element>".
Definition: Html.php:315
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes! ...
AuthenticationRequest [] $authRequests
getAuthFormDescriptor( $requests, $action)
displayForm( $status)
Display the form.
Special change to change credentials (such as the password).
getUser()
Shortcut to get the User executing this instance.
getConfig()
Shortcut to get main config object.
performAuthenticationStep( $action, array $requests)
string $subPage
Subpage of the special page.
static $loadUserData
Change action needs user data; remove action does not.
loadAuth( $subPage, $authAction=null, $reset=false)
outputHeader( $summaryMessageKey='')
Outputs a summary message on top of special pages Per default the message key is the canonical name o...
getPageTitle( $subpage=false)
Get a self-referential title object.
string $authAction
one of the AuthManager::ACTION_* constants.
MediaWiki Linker LinkRenderer null $linkRenderer
Definition: SpecialPage.php:67
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:319
trySubmit()
Attempts to do an authentication step with the submitted data.