SpecialChangeCredentials::showSubpageList()   B
last analyzed

Complexity

Conditions 5
Paths 12

Size

Total Lines 30
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 19
nc 12
nop 1
dl 0
loc 30
rs 8.439
c 0
b 0
f 0
1
<?php
2
3
use MediaWiki\Auth\AuthenticationRequest;
4
use MediaWiki\Auth\AuthenticationResponse;
5
use MediaWiki\Auth\AuthManager;
6
use MediaWiki\Session\SessionManager;
7
8
/**
9
 * Special change to change credentials (such as the password).
10
 *
11
 * Also does most of the work for SpecialRemoveCredentials.
12
 */
13
class SpecialChangeCredentials extends AuthManagerSpecialPage {
14
	protected static $allowedActions = [ AuthManager::ACTION_CHANGE ];
15
16
	protected static $messagePrefix = 'changecredentials';
17
18
	/** Change action needs user data; remove action does not */
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 onAuthChangeFormFields(
53
		array $requests, array $fieldInfo, array &$formDescriptor, $action
54
	) {
55
		// This method is never called for remove actions.
56
57
		$extraFields = [];
58
		Hooks::run( 'ChangePasswordForm', [ &$extraFields ], '1.27' );
59
		foreach ( $extraFields as $extra ) {
60
			list( $name, $label, $type, $default ) = $extra;
61
			$formDescriptor[$name] = [
62
				'type' => $type,
63
				'name' => $name,
64
				'label-message' => $label,
65
				'default' => $default,
66
			];
67
68
		}
69
70
		return parent::onAuthChangeFormFields( $requests, $fieldInfo, $formDescriptor, $action );
71
	}
72
73
	public function execute( $subPage ) {
74
		$this->setHeaders();
75
		$this->outputHeader();
76
77
		$this->loadAuth( $subPage );
78
79
		if ( !$subPage ) {
80
			$this->showSubpageList();
81
			return;
82
		}
83
84
		if ( !$this->authRequests ) {
85
			// messages used: changecredentials-invalidsubpage, removecredentials-invalidsubpage
86
			$this->showSubpageList( $this->msg( static::$messagePrefix . '-invalidsubpage', $subPage ) );
87
			return;
88
		}
89
90
		$status = $this->trySubmit();
91
92
		if ( $status === false || !$status->isOK() ) {
93
			$this->displayForm( $status );
94
			return;
95
		}
96
97
		$response = $status->getValue();
98
99
		switch ( $response->status ) {
100
			case AuthenticationResponse::PASS:
101
				$this->success();
102
				break;
103
			case AuthenticationResponse::FAIL:
104
				$this->displayForm( Status::newFatal( $response->message ) );
105
				break;
106
			default:
107
				throw new LogicException( 'invalid AuthenticationResponse' );
108
		}
109
	}
110
111
	protected function loadAuth( $subPage, $authAction = null, $reset = false ) {
112
		parent::loadAuth( $subPage, $authAction );
113
		if ( $subPage ) {
114
			$this->authRequests = array_filter( $this->authRequests, function ( $req ) use ( $subPage ) {
115
				return $req->getUniqueId() === $subPage;
116
			} );
117
			if ( count( $this->authRequests ) > 1 ) {
118
				throw new LogicException( 'Multiple AuthenticationRequest objects with same ID!' );
119
			}
120
		}
121
	}
122
123
	protected function getAuthFormDescriptor( $requests, $action ) {
124
		if ( !static::$loadUserData ) {
125
			return [];
126
		} else {
127
			return parent::getAuthFormDescriptor( $requests, $action );
128
		}
129
	}
130
131
	protected function getAuthForm( array $requests, $action ) {
132
		$form = parent::getAuthForm( $requests, $action );
133
		$req = reset( $requests );
134
		$info = $req->describeCredentials();
135
136
		$form->addPreText(
137
			Html::openElement( 'dl' )
138
			. Html::element( 'dt', [], wfMessage( 'credentialsform-provider' ) )
139
			. Html::element( 'dd', [], $info['provider'] )
140
			. Html::element( 'dt', [], wfMessage( 'credentialsform-account' ) )
141
			. Html::element( 'dd', [], $info['account'] )
142
			. Html::closeElement( 'dl' )
143
		);
144
145
		// messages used: changecredentials-submit removecredentials-submit
146
		$form->setSubmitTextMsg( static::$messagePrefix . '-submit' );
147
		$form->showCancel()->setCancelTarget( $this->getReturnUrl() ?: Title::newMainPage() );
0 ignored issues
show
Bug introduced by
It seems like $this->getReturnUrl() ?: \Title::newMainPage() can also be of type null; however, HTMLForm::setCancelTarget() does only seem to accept object<Title>|string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
148
149
		return $form;
150
	}
151
152
	protected function needsSubmitButton( array $requests ) {
153
		// Change/remove forms show are built from a single AuthenticationRequest and do not allow
154
		// for redirect flow; they always need a submit button.
155
		return true;
156
	}
157
158 View Code Duplication
	public function handleFormSubmit( $data ) {
159
		// remove requests do not accept user input
160
		$requests = $this->authRequests;
161
		if ( static::$loadUserData ) {
162
			$requests = AuthenticationRequest::loadRequestsFromSubmission( $this->authRequests, $data );
163
		}
164
165
		$response = $this->performAuthenticationStep( $this->authAction, $requests );
166
167
		// we can't handle FAIL or similar as failure here since it might require changing the form
168
		return Status::newGood( $response );
169
	}
170
171
	/**
172
	 * @param Message|null $error
173
	 */
174
	protected function showSubpageList( $error = null ) {
175
		$out = $this->getOutput();
176
177
		if ( $error ) {
178
			$out->addHTML( $error->parse() );
179
		}
180
181
		$groupedRequests = [];
182
		foreach ( $this->authRequests as $req ) {
183
			$info = $req->describeCredentials();
184
			$groupedRequests[(string)$info['provider']][] = $req;
185
		}
186
187
		$linkRenderer = $this->getLinkRenderer();
188
		$out->addHTML( Html::openElement( 'dl' ) );
189
		foreach ( $groupedRequests as $group => $members ) {
190
			$out->addHTML( Html::element( 'dt', [], $group ) );
191
			foreach ( $members as $req ) {
192
				/** @var AuthenticationRequest $req */
193
				$info = $req->describeCredentials();
194
				$out->addHTML( Html::rawElement( 'dd', [],
195
					$linkRenderer->makeLink(
196
						$this->getPageTitle( $req->getUniqueId() ),
197
						$info['account']
198
					)
199
				) );
200
			}
201
		}
202
		$out->addHTML( Html::closeElement( 'dl' ) );
203
	}
204
205
	protected function success() {
206
		$session = $this->getRequest()->getSession();
207
		$user = $this->getUser();
208
		$out = $this->getOutput();
209
		$returnUrl = $this->getReturnUrl();
210
211
		// change user token and update the session
212
		SessionManager::singleton()->invalidateSessionsForUser( $user );
213
		$session->setUser( $user );
214
		$session->resetId();
215
216
		if ( $returnUrl ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $returnUrl of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
217
			$out->redirect( $returnUrl );
218
		} else {
219
			// messages used: changecredentials-success removecredentials-success
220
			$out->wrapWikiMsg( "<div class=\"successbox\">\n$1\n</div>", static::$messagePrefix
221
				. '-success' );
222
			$out->returnToMain();
223
		}
224
	}
225
226
	/**
227
	 * @return string|null
228
	 */
229
	protected function getReturnUrl() {
230
		$request = $this->getRequest();
231
		$returnTo = $request->getText( 'returnto' );
232
		$returnToQuery = $request->getText( 'returntoquery', '' );
233
234
		if ( !$returnTo ) {
235
			return null;
236
		}
237
238
		$title = Title::newFromText( $returnTo );
239
		return $title->getFullURL( $returnToQuery );
240
	}
241
242
	protected function getRequestBlacklist() {
243
		return $this->getConfig()->get( 'ChangeCredentialsBlacklist' );
244
	}
245
}
246