]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blob - includes/auth/ConfirmLinkSecondaryAuthenticationProvider.php
MediaWiki 1.30.2
[autoinstalls/mediawiki.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                         'warning'
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                         'error'
156                 );
157         }
158 }