Completed
Branch master (f93894)
by
unknown
27:35
created

continueSecondaryAuthentication()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace MediaWiki\Auth;
4
5
use StatusValue;
6
use User;
7
8
/**
9
 * Links third-party authentication to the user's account
10
 *
11
 * If the user logged into linking provider accounts that aren't linked to a
12
 * local user, this provider will prompt the user to link them after a
13
 * successful login or account creation.
14
 *
15
 * To avoid confusing behavior, this provider should be later in the
16
 * configuration list than any provider that can abort the authentication
17
 * process, so that it is only invoked for successful authentication.
18
 */
19
class ConfirmLinkSecondaryAuthenticationProvider extends AbstractSecondaryAuthenticationProvider {
20
21
	public function getAuthenticationRequests( $action, array $options ) {
22
		return [];
23
	}
24
25
	public function beginSecondaryAuthentication( $user, array $reqs ) {
26
		return $this->beginLinkAttempt( $user, 'AuthManager::authnState' );
27
	}
28
29
	public function continueSecondaryAuthentication( $user, array $reqs ) {
30
		return $this->continueLinkAttempt( $user, 'AuthManager::authnState', $reqs );
31
	}
32
33
	public function beginSecondaryAccountCreation( $user, $creator, array $reqs ) {
34
		return $this->beginLinkAttempt( $user, 'AuthManager::accountCreationState' );
35
	}
36
37
	public function continueSecondaryAccountCreation( $user, $creator, array $reqs ) {
38
		return $this->continueLinkAttempt( $user, 'AuthManager::accountCreationState', $reqs );
39
	}
40
41
	/**
42
	 * Begin the link attempt
43
	 * @param User $user
44
	 * @param string $key Session key to look in
45
	 * @return AuthenticationResponse
46
	 */
47
	protected function beginLinkAttempt( $user, $key ) {
48
		$session = $this->manager->getRequest()->getSession();
49
		$state = $session->getSecret( $key );
50
		if ( !is_array( $state ) ) {
51
			return AuthenticationResponse::newAbstain();
52
		}
53
54
		$maybeLink = array_filter( $state['maybeLink'], function ( $req ) use ( $user ) {
55
			if ( !$req->action ) {
56
				$req->action = AuthManager::ACTION_CHANGE;
57
			}
58
			$req->username = $user->getName();
59
			return $this->manager->allowsAuthenticationDataChange( $req )->isGood();
60
		} );
61
		if ( !$maybeLink ) {
62
			return AuthenticationResponse::newAbstain();
63
		}
64
65
		$req = new ConfirmLinkAuthenticationRequest( $maybeLink );
66
		return AuthenticationResponse::newUI(
67
			[ $req ],
68
			wfMessage( 'authprovider-confirmlink-message' )
69
		);
70
	}
71
72
	/**
73
	 * Continue the link attempt
74
	 * @param User $user
75
	 * @param string $key Session key to look in
76
	 * @param AuthenticationRequest[] $reqs
77
	 * @return AuthenticationResponse
78
	 */
79
	protected function continueLinkAttempt( $user, $key, array $reqs ) {
80
		$req = ButtonAuthenticationRequest::getRequestByName( $reqs, 'linkOk' );
81
		if ( $req ) {
82
			return AuthenticationResponse::newPass();
83
		}
84
85
		$req = AuthenticationRequest::getRequestByClass( $reqs, ConfirmLinkAuthenticationRequest::class );
86
		if ( !$req ) {
87
			// WTF? Retry.
88
			return $this->beginLinkAttempt( $user, $key );
89
		}
90
91
		$session = $this->manager->getRequest()->getSession();
92
		$state = $session->getSecret( $key );
93
		if ( !is_array( $state ) ) {
94
			return AuthenticationResponse::newAbstain();
95
		}
96
97
		$maybeLink = [];
98
		foreach ( $state['maybeLink'] as $linkReq ) {
99
			$maybeLink[$linkReq->getUniqueId()] = $linkReq;
100
		}
101
		if ( !$maybeLink ) {
102
			return AuthenticationResponse::newAbstain();
103
		}
104
105
		$state['maybeLink'] = [];
106
		$session->setSecret( $key, $state );
107
108
		$statuses = [];
109
		$anyFailed = false;
110
		foreach ( $req->confirmedLinkIDs as $id ) {
111
			if ( isset( $maybeLink[$id] ) ) {
112
				$req = $maybeLink[$id];
113
				$req->username = $user->getName();
114
				if ( !$req->action ) {
115
					// Make sure the action is set, but don't override it if
116
					// the provider filled it in.
117
					$req->action = AuthManager::ACTION_CHANGE;
118
				}
119
				$status = $this->manager->allowsAuthenticationDataChange( $req );
120
				$statuses[] = [ $req, $status ];
121
				if ( $status->isGood() ) {
122
					$this->manager->changeAuthenticationData( $req );
123
				} else {
124
					$anyFailed = true;
125
				}
126
			}
127
		}
128
		if ( !$anyFailed ) {
129
			return AuthenticationResponse::newPass();
130
		}
131
132
		$combinedStatus = \Status::newGood();
133
		foreach ( $statuses as $data ) {
134
			list( $req, $status ) = $data;
135
			$descriptionInfo = $req->describeCredentials();
136
			$description = wfMessage(
137
				'authprovider-confirmlink-option',
138
				$descriptionInfo['provider']->text(), $descriptionInfo['account']->text()
139
			)->text();
140
			if ( $status->isGood() ) {
141
				$combinedStatus->error( wfMessage( 'authprovider-confirmlink-success-line', $description ) );
142
			} else {
143
				$combinedStatus->error( wfMessage(
144
					'authprovider-confirmlink-failed-line', $description, $status->getMessage()->text()
145
				) );
146
			}
147
		}
148
		return AuthenticationResponse::newUI(
149
			[
150
				new ButtonAuthenticationRequest(
151
					'linkOk', wfMessage( 'ok' ), wfMessage( 'authprovider-confirmlink-ok-help' )
152
				)
153
			],
154
			$combinedStatus->getMessage( 'authprovider-confirmlink-failed' )
155
		);
156
	}
157
}
158