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