|
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
|
|
|
|