Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/auth/controller/PhabricatorAuthLoginController.php
13402 views
1
<?php
2
3
final class PhabricatorAuthLoginController
4
extends PhabricatorAuthController {
5
6
private $providerKey;
7
private $extraURIData;
8
private $provider;
9
10
public function shouldRequireLogin() {
11
return false;
12
}
13
14
public function shouldAllowRestrictedParameter($parameter_name) {
15
// Whitelist the OAuth 'code' parameter.
16
17
if ($parameter_name == 'code') {
18
return true;
19
}
20
21
return parent::shouldAllowRestrictedParameter($parameter_name);
22
}
23
24
public function getExtraURIData() {
25
return $this->extraURIData;
26
}
27
28
public function handleRequest(AphrontRequest $request) {
29
$viewer = $this->getViewer();
30
$this->providerKey = $request->getURIData('pkey');
31
$this->extraURIData = $request->getURIData('extra');
32
33
$response = $this->loadProvider();
34
if ($response) {
35
return $response;
36
}
37
38
$invite = $this->loadInvite();
39
$provider = $this->provider;
40
41
try {
42
list($account, $response) = $provider->processLoginRequest($this);
43
} catch (PhutilAuthUserAbortedException $ex) {
44
if ($viewer->isLoggedIn()) {
45
// If a logged-in user cancels, take them back to the external accounts
46
// panel.
47
$next_uri = '/settings/panel/external/';
48
} else {
49
// If a logged-out user cancels, take them back to the auth start page.
50
$next_uri = '/';
51
}
52
53
// User explicitly hit "Cancel".
54
$dialog = id(new AphrontDialogView())
55
->setUser($viewer)
56
->setTitle(pht('Authentication Canceled'))
57
->appendChild(
58
pht('You canceled authentication.'))
59
->addCancelButton($next_uri, pht('Continue'));
60
return id(new AphrontDialogResponse())->setDialog($dialog);
61
}
62
63
if ($response) {
64
return $response;
65
}
66
67
if (!$account) {
68
throw new Exception(
69
pht(
70
'Auth provider failed to load an account from %s!',
71
'processLoginRequest()'));
72
}
73
74
if ($account->getUserPHID()) {
75
// The account is already attached to a Phabricator user, so this is
76
// either a login or a bad account link request.
77
if (!$viewer->isLoggedIn()) {
78
if ($provider->shouldAllowLogin()) {
79
return $this->processLoginUser($account);
80
} else {
81
return $this->renderError(
82
pht(
83
'The external service ("%s") you just authenticated with is '.
84
'not configured to allow logins on this server. An '.
85
'administrator may have recently disabled it.',
86
$provider->getProviderName()));
87
}
88
} else if ($viewer->getPHID() == $account->getUserPHID()) {
89
// This is either an attempt to re-link an existing and already
90
// linked account (which is silly) or a refresh of an external account
91
// (e.g., an OAuth account).
92
return id(new AphrontRedirectResponse())
93
->setURI('/settings/panel/external/');
94
} else {
95
return $this->renderError(
96
pht(
97
'The external service ("%s") you just used to log in is already '.
98
'associated with another %s user account. Log in to the '.
99
'other %s account and unlink the external account before '.
100
'linking it to a new %s account.',
101
$provider->getProviderName(),
102
PlatformSymbols::getPlatformServerName(),
103
PlatformSymbols::getPlatformServerName(),
104
PlatformSymbols::getPlatformServerName()));
105
}
106
} else {
107
// The account is not yet attached to a Phabricator user, so this is
108
// either a registration or an account link request.
109
if (!$viewer->isLoggedIn()) {
110
if ($provider->shouldAllowRegistration() || $invite) {
111
return $this->processRegisterUser($account);
112
} else {
113
return $this->renderError(
114
pht(
115
'The external service ("%s") you just authenticated with is '.
116
'not configured to allow registration on this server. An '.
117
'administrator may have recently disabled it.',
118
$provider->getProviderName()));
119
}
120
} else {
121
122
// If the user already has a linked account on this provider, prevent
123
// them from linking a second account. This can happen if they swap
124
// logins and then refresh the account link.
125
126
// There's no technical reason we can't allow you to link multiple
127
// accounts from a single provider; disallowing this is currently a
128
// product deciison. See T2549.
129
130
$existing_accounts = id(new PhabricatorExternalAccountQuery())
131
->setViewer($viewer)
132
->withUserPHIDs(array($viewer->getPHID()))
133
->withProviderConfigPHIDs(
134
array(
135
$provider->getProviderConfigPHID(),
136
))
137
->execute();
138
if ($existing_accounts) {
139
return $this->renderError(
140
pht(
141
'Your %s account is already connected to an external '.
142
'account on this service ("%s"), but you are currently logged '.
143
'in to the service with a different account. Log out of the '.
144
'external service, then log back in with the correct account '.
145
'before refreshing the account link.',
146
PlatformSymbols::getPlatformServerName(),
147
$provider->getProviderName()));
148
}
149
150
if ($provider->shouldAllowAccountLink()) {
151
return $this->processLinkUser($account);
152
} else {
153
return $this->renderError(
154
pht(
155
'The external service ("%s") you just authenticated with is '.
156
'not configured to allow account linking on this server. An '.
157
'administrator may have recently disabled it.',
158
$provider->getProviderName()));
159
}
160
}
161
}
162
163
// This should be unreachable, but fail explicitly if we get here somehow.
164
return new Aphront400Response();
165
}
166
167
private function processLoginUser(PhabricatorExternalAccount $account) {
168
$user = id(new PhabricatorUser())->loadOneWhere(
169
'phid = %s',
170
$account->getUserPHID());
171
172
if (!$user) {
173
return $this->renderError(
174
pht(
175
'The external account you just logged in with is not associated '.
176
'with a valid %s user account.',
177
PlatformSymbols::getPlatformServerName()));
178
}
179
180
return $this->loginUser($user);
181
}
182
183
private function processRegisterUser(PhabricatorExternalAccount $account) {
184
$account_secret = $account->getAccountSecret();
185
$register_uri = $this->getApplicationURI('register/'.$account_secret.'/');
186
return $this->setAccountKeyAndContinue($account, $register_uri);
187
}
188
189
private function processLinkUser(PhabricatorExternalAccount $account) {
190
$account_secret = $account->getAccountSecret();
191
$confirm_uri = $this->getApplicationURI('confirmlink/'.$account_secret.'/');
192
return $this->setAccountKeyAndContinue($account, $confirm_uri);
193
}
194
195
private function setAccountKeyAndContinue(
196
PhabricatorExternalAccount $account,
197
$next_uri) {
198
199
if ($account->getUserPHID()) {
200
throw new Exception(pht('Account is already registered or linked.'));
201
}
202
203
// Regenerate the registration secret key, set it on the external account,
204
// set a cookie on the user's machine, and redirect them to registration.
205
// See PhabricatorAuthRegisterController for discussion of the registration
206
// key.
207
208
$registration_key = Filesystem::readRandomCharacters(32);
209
$account->setProperty(
210
'registrationKey',
211
PhabricatorHash::weakDigest($registration_key));
212
213
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
214
$account->save();
215
unset($unguarded);
216
217
$this->getRequest()->setTemporaryCookie(
218
PhabricatorCookies::COOKIE_REGISTRATION,
219
$registration_key);
220
221
return id(new AphrontRedirectResponse())->setURI($next_uri);
222
}
223
224
private function loadProvider() {
225
$provider = PhabricatorAuthProvider::getEnabledProviderByKey(
226
$this->providerKey);
227
228
if (!$provider) {
229
return $this->renderError(
230
pht(
231
'The account you are attempting to log in with uses a nonexistent '.
232
'or disabled authentication provider (with key "%s"). An '.
233
'administrator may have recently disabled this provider.',
234
$this->providerKey));
235
}
236
237
$this->provider = $provider;
238
239
return null;
240
}
241
242
protected function renderError($message) {
243
return $this->renderErrorPage(
244
pht('Login Failed'),
245
array($message));
246
}
247
248
public function buildProviderPageResponse(
249
PhabricatorAuthProvider $provider,
250
$content) {
251
252
$crumbs = $this->buildApplicationCrumbs();
253
$viewer = $this->getViewer();
254
255
if ($viewer->isLoggedIn()) {
256
$crumbs->addTextCrumb(pht('Link Account'), $provider->getSettingsURI());
257
} else {
258
$crumbs->addTextCrumb(pht('Login'), $this->getApplicationURI('start/'));
259
260
$content = array(
261
$this->newCustomStartMessage(),
262
$content,
263
);
264
}
265
266
$crumbs->addTextCrumb($provider->getProviderName());
267
$crumbs->setBorder(true);
268
269
return $this->newPage()
270
->setTitle(pht('Login'))
271
->setCrumbs($crumbs)
272
->appendChild($content);
273
}
274
275
public function buildProviderErrorResponse(
276
PhabricatorAuthProvider $provider,
277
$message) {
278
279
$message = pht(
280
'Authentication provider ("%s") encountered an error while attempting '.
281
'to log in. %s', $provider->getProviderName(), $message);
282
283
return $this->renderError($message);
284
}
285
286
}
287
288