mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-22 23:02:42 +01:00
Modularize oauth.
This commit is contained in:
parent
eccc76dae6
commit
d3efdcff03
21 changed files with 649 additions and 374 deletions
|
@ -176,11 +176,25 @@ return array(
|
||||||
'amazon-ses.secret-key' => null,
|
'amazon-ses.secret-key' => null,
|
||||||
|
|
||||||
|
|
||||||
|
// -- Auth ------------------------------------------------------------------ //
|
||||||
|
|
||||||
|
// Can users login with a username/password, or by following the link from
|
||||||
|
// a password reset email? You can disable this and configure one or more
|
||||||
|
// OAuth providers instead.
|
||||||
|
'auth.password-auth-enabled' => true,
|
||||||
|
|
||||||
// -- Facebook ------------------------------------------------------------ //
|
// -- Facebook ------------------------------------------------------------ //
|
||||||
|
|
||||||
// Can users use Facebook credentials to login to Phabricator?
|
// Can users use Facebook credentials to login to Phabricator?
|
||||||
'facebook.auth-enabled' => false,
|
'facebook.auth-enabled' => false,
|
||||||
|
|
||||||
|
// Can users use Facebook credentials to create new Phabricator accounts?
|
||||||
|
'facebook.registration-enabled' => true,
|
||||||
|
|
||||||
|
// Are Facebook accounts permanently linked to Phabricator accounts, or can
|
||||||
|
// the user unlink them?
|
||||||
|
'facebook.auth-permanent' => false,
|
||||||
|
|
||||||
// The Facebook "Application ID" to use for Facebook API access.
|
// The Facebook "Application ID" to use for Facebook API access.
|
||||||
'facebook.application-id' => null,
|
'facebook.application-id' => null,
|
||||||
|
|
||||||
|
@ -193,6 +207,13 @@ return array(
|
||||||
// Can users use Github credentials to login to Phabricator?
|
// Can users use Github credentials to login to Phabricator?
|
||||||
'github.auth-enabled' => false,
|
'github.auth-enabled' => false,
|
||||||
|
|
||||||
|
// Can users use Github credentials to create new Phabricator accounts?
|
||||||
|
'github.registration-enabled' => true,
|
||||||
|
|
||||||
|
// Are Github accounts permanently linked to Phabricator accounts, or can
|
||||||
|
// the user unlink them?
|
||||||
|
'github.auth-permanent' => false,
|
||||||
|
|
||||||
// The Github "Client ID" to use for Github API access.
|
// The Github "Client ID" to use for Github API access.
|
||||||
'github.application-id' => null,
|
'github.application-id' => null,
|
||||||
|
|
||||||
|
@ -252,4 +273,7 @@ return array(
|
||||||
|
|
||||||
'aphront.default-application-configuration-class' =>
|
'aphront.default-application-configuration-class' =>
|
||||||
'AphrontDefaultApplicationConfiguration',
|
'AphrontDefaultApplicationConfiguration',
|
||||||
|
|
||||||
|
'controller.oauth-registration' =>
|
||||||
|
'PhabricatorOAuthDefaultRegistrationController',
|
||||||
);
|
);
|
||||||
|
|
|
@ -212,12 +212,14 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorMetaMTAMailingListsController' => 'applications/metamta/controller/mailinglists',
|
'PhabricatorMetaMTAMailingListsController' => 'applications/metamta/controller/mailinglists',
|
||||||
'PhabricatorMetaMTASendController' => 'applications/metamta/controller/send',
|
'PhabricatorMetaMTASendController' => 'applications/metamta/controller/send',
|
||||||
'PhabricatorMetaMTAViewController' => 'applications/metamta/controller/view',
|
'PhabricatorMetaMTAViewController' => 'applications/metamta/controller/view',
|
||||||
|
'PhabricatorOAuthDefaultRegistrationController' => 'applications/auth/controller/oauthregistration/default',
|
||||||
'PhabricatorOAuthDiagnosticsController' => 'applications/auth/controller/oauthdiagnostics',
|
'PhabricatorOAuthDiagnosticsController' => 'applications/auth/controller/oauthdiagnostics',
|
||||||
'PhabricatorOAuthFailureView' => 'applications/auth/view/oauthfailure',
|
'PhabricatorOAuthFailureView' => 'applications/auth/view/oauthfailure',
|
||||||
'PhabricatorOAuthLoginController' => 'applications/auth/controller/oauth',
|
'PhabricatorOAuthLoginController' => 'applications/auth/controller/oauth',
|
||||||
'PhabricatorOAuthProvider' => 'applications/auth/oauth/provider/base',
|
'PhabricatorOAuthProvider' => 'applications/auth/oauth/provider/base',
|
||||||
'PhabricatorOAuthProviderFacebook' => 'applications/auth/oauth/provider/facebook',
|
'PhabricatorOAuthProviderFacebook' => 'applications/auth/oauth/provider/facebook',
|
||||||
'PhabricatorOAuthProviderGithub' => 'applications/auth/oauth/provider/github',
|
'PhabricatorOAuthProviderGithub' => 'applications/auth/oauth/provider/github',
|
||||||
|
'PhabricatorOAuthRegistrationController' => 'applications/auth/controller/oauthregistration/base',
|
||||||
'PhabricatorOAuthUnlinkController' => 'applications/auth/controller/unlink',
|
'PhabricatorOAuthUnlinkController' => 'applications/auth/controller/unlink',
|
||||||
'PhabricatorObjectHandle' => 'applications/phid/handle',
|
'PhabricatorObjectHandle' => 'applications/phid/handle',
|
||||||
'PhabricatorObjectHandleData' => 'applications/phid/handle/data',
|
'PhabricatorObjectHandleData' => 'applications/phid/handle/data',
|
||||||
|
@ -463,11 +465,13 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorMetaMTAMailingListsController' => 'PhabricatorMetaMTAController',
|
'PhabricatorMetaMTAMailingListsController' => 'PhabricatorMetaMTAController',
|
||||||
'PhabricatorMetaMTASendController' => 'PhabricatorMetaMTAController',
|
'PhabricatorMetaMTASendController' => 'PhabricatorMetaMTAController',
|
||||||
'PhabricatorMetaMTAViewController' => 'PhabricatorMetaMTAController',
|
'PhabricatorMetaMTAViewController' => 'PhabricatorMetaMTAController',
|
||||||
|
'PhabricatorOAuthDefaultRegistrationController' => 'PhabricatorOAuthRegistrationController',
|
||||||
'PhabricatorOAuthDiagnosticsController' => 'PhabricatorAuthController',
|
'PhabricatorOAuthDiagnosticsController' => 'PhabricatorAuthController',
|
||||||
'PhabricatorOAuthFailureView' => 'AphrontView',
|
'PhabricatorOAuthFailureView' => 'AphrontView',
|
||||||
'PhabricatorOAuthLoginController' => 'PhabricatorAuthController',
|
'PhabricatorOAuthLoginController' => 'PhabricatorAuthController',
|
||||||
'PhabricatorOAuthProviderFacebook' => 'PhabricatorOAuthProvider',
|
'PhabricatorOAuthProviderFacebook' => 'PhabricatorOAuthProvider',
|
||||||
'PhabricatorOAuthProviderGithub' => 'PhabricatorOAuthProvider',
|
'PhabricatorOAuthProviderGithub' => 'PhabricatorOAuthProvider',
|
||||||
|
'PhabricatorOAuthRegistrationController' => 'PhabricatorAuthController',
|
||||||
'PhabricatorOAuthUnlinkController' => 'PhabricatorAuthController',
|
'PhabricatorOAuthUnlinkController' => 'PhabricatorAuthController',
|
||||||
'PhabricatorPHID' => 'PhabricatorPHIDDAO',
|
'PhabricatorPHID' => 'PhabricatorPHIDDAO',
|
||||||
'PhabricatorPHIDAllocateController' => 'PhabricatorPHIDController',
|
'PhabricatorPHIDAllocateController' => 'PhabricatorPHIDController',
|
||||||
|
|
|
@ -88,6 +88,7 @@ final class DarkConsoleCore {
|
||||||
$visible = $user->getConsoleVisible();
|
$visible = $user->getConsoleVisible();
|
||||||
|
|
||||||
if (!isset($plugins[$selected])) {
|
if (!isset($plugins[$selected])) {
|
||||||
|
reset($plugins);
|
||||||
$selected = key($plugins);
|
$selected = key($plugins);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,4 +41,8 @@ abstract class AphrontController {
|
||||||
return $this->request;
|
return $this->request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final public function delegateToController(AphrontController $controller) {
|
||||||
|
return $controller->processRequest();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,10 @@ class PhabricatorEmailLoginController extends PhabricatorAuthController {
|
||||||
public function processRequest() {
|
public function processRequest() {
|
||||||
$request = $this->getRequest();
|
$request = $this->getRequest();
|
||||||
|
|
||||||
|
if (!PhabricatorEnv::getEnvConfig('auth.password-auth-enabled')) {
|
||||||
|
return new Aphront400Response();
|
||||||
|
}
|
||||||
|
|
||||||
$e_email = true;
|
$e_email = true;
|
||||||
$e_captcha = true;
|
$e_captcha = true;
|
||||||
$errors = array();
|
$errors = array();
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'aphront/response/400');
|
||||||
phutil_require_module('phabricator', 'applications/auth/controller/base');
|
phutil_require_module('phabricator', 'applications/auth/controller/base');
|
||||||
phutil_require_module('phabricator', 'applications/metamta/storage/mail');
|
phutil_require_module('phabricator', 'applications/metamta/storage/mail');
|
||||||
phutil_require_module('phabricator', 'applications/people/storage/user');
|
phutil_require_module('phabricator', 'applications/people/storage/user');
|
||||||
|
|
|
@ -31,6 +31,10 @@ class PhabricatorEmailTokenController extends PhabricatorAuthController {
|
||||||
public function processRequest() {
|
public function processRequest() {
|
||||||
$request = $this->getRequest();
|
$request = $this->getRequest();
|
||||||
|
|
||||||
|
if (!PhabricatorEnv::getEnvConfig('auth.password-auth-enabled')) {
|
||||||
|
return new Aphront400Response();
|
||||||
|
}
|
||||||
|
|
||||||
$token = $this->token;
|
$token = $this->token;
|
||||||
$email = $request->getStr('email');
|
$email = $request->getStr('email');
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,11 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'aphront/response/400');
|
||||||
phutil_require_module('phabricator', 'aphront/response/redirect');
|
phutil_require_module('phabricator', 'aphront/response/redirect');
|
||||||
phutil_require_module('phabricator', 'applications/auth/controller/base');
|
phutil_require_module('phabricator', 'applications/auth/controller/base');
|
||||||
phutil_require_module('phabricator', 'applications/people/storage/user');
|
phutil_require_module('phabricator', 'applications/people/storage/user');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/env');
|
||||||
phutil_require_module('phabricator', 'view/form/base');
|
phutil_require_module('phabricator', 'view/form/base');
|
||||||
phutil_require_module('phabricator', 'view/form/control/submit');
|
phutil_require_module('phabricator', 'view/form/control/submit');
|
||||||
phutil_require_module('phabricator', 'view/form/error');
|
phutil_require_module('phabricator', 'view/form/error');
|
||||||
|
|
|
@ -30,6 +30,12 @@ class PhabricatorLoginController extends PhabricatorAuthController {
|
||||||
return id(new AphrontRedirectResponse())->setURI('/');
|
return id(new AphrontRedirectResponse())->setURI('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$password_auth = PhabricatorEnv::getEnvConfig('auth.password-auth-enabled');
|
||||||
|
|
||||||
|
$forms = array();
|
||||||
|
|
||||||
|
$error_view = null;
|
||||||
|
if ($password_auth) {
|
||||||
$error = false;
|
$error = false;
|
||||||
$username = $request->getCookie('phusr');
|
$username = $request->getCookie('phusr');
|
||||||
if ($request->isFormPost()) {
|
if ($request->isFormPost()) {
|
||||||
|
@ -61,7 +67,6 @@ class PhabricatorLoginController extends PhabricatorAuthController {
|
||||||
$error = true;
|
$error = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$error_view = null;
|
|
||||||
if ($error) {
|
if ($error) {
|
||||||
$error_view = new AphrontErrorView();
|
$error_view = new AphrontErrorView();
|
||||||
$error_view->setTitle('Bad username/password.');
|
$error_view->setTitle('Bad username/password.');
|
||||||
|
@ -81,17 +86,16 @@ class PhabricatorLoginController extends PhabricatorAuthController {
|
||||||
->setLabel('Password')
|
->setLabel('Password')
|
||||||
->setName('password')
|
->setName('password')
|
||||||
->setCaption(
|
->setCaption(
|
||||||
'<a href="/login/email/">Forgot your password? / Email Login</a>'))
|
'<a href="/login/email/">'.
|
||||||
|
'Forgot your password? / Email Login</a>'))
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormSubmitControl())
|
id(new AphrontFormSubmitControl())
|
||||||
->setValue('Login'));
|
->setValue('Login'));
|
||||||
|
|
||||||
|
|
||||||
$panel = new AphrontPanelView();
|
|
||||||
$panel->setHeader('Phabricator Login');
|
|
||||||
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
|
|
||||||
// $panel->setCreateButton('Register New Account', '/login/register/');
|
// $panel->setCreateButton('Register New Account', '/login/register/');
|
||||||
$panel->appendChild($form);
|
$forms['Phabricator Login'] = $form;
|
||||||
|
}
|
||||||
|
|
||||||
$providers = array(
|
$providers = array(
|
||||||
PhabricatorOAuthProvider::PROVIDER_FACEBOOK,
|
PhabricatorOAuthProvider::PROVIDER_FACEBOOK,
|
||||||
|
@ -117,6 +121,19 @@ class PhabricatorLoginController extends PhabricatorAuthController {
|
||||||
// does not seem like the most severe threat in the world, and generating
|
// does not seem like the most severe threat in the world, and generating
|
||||||
// CSRF for logged-out users is vaugely tricky.
|
// CSRF for logged-out users is vaugely tricky.
|
||||||
|
|
||||||
|
if ($provider->isProviderRegistrationEnabled()) {
|
||||||
|
$title = "Login or Register with {$provider_name}";
|
||||||
|
$body = "Login or register for Phabricator using your ".
|
||||||
|
"{$provider_name} account.";
|
||||||
|
$button = "Login or Register with {$provider_name}";
|
||||||
|
} else {
|
||||||
|
$title = "Login with {$provider_name}";
|
||||||
|
$body = "Login to your existing Phabricator account using your ".
|
||||||
|
"{$provider_name} account.<br /><br /><strong>You can not use ".
|
||||||
|
"{$provider_name} to register a new account.</strong>";
|
||||||
|
$button = "Login with {$provider_name}";
|
||||||
|
}
|
||||||
|
|
||||||
$auth_form = new AphrontFormView();
|
$auth_form = new AphrontFormView();
|
||||||
$auth_form
|
$auth_form
|
||||||
->setAction($auth_uri)
|
->setAction($auth_uri)
|
||||||
|
@ -126,16 +143,20 @@ class PhabricatorLoginController extends PhabricatorAuthController {
|
||||||
->setUser($request->getUser())
|
->setUser($request->getUser())
|
||||||
->setMethod('GET')
|
->setMethod('GET')
|
||||||
->appendChild(
|
->appendChild(
|
||||||
'<p class="aphront-form-instructions">Login or register for '.
|
'<p class="aphront-form-instructions">'.$body.'</p>')
|
||||||
'Phabricator using your '.$provider_name.' account.</p>')
|
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormSubmitControl())
|
id(new AphrontFormSubmitControl())
|
||||||
->setValue("Login with {$provider_name} \xC2\xBB"));
|
->setValue("{$button} \xC2\xBB"));
|
||||||
|
|
||||||
$panel->appendChild(
|
$forms[$title] = $auth_form;
|
||||||
'<br /><h1>Login or Register with '.$provider_name.'</h1>');
|
}
|
||||||
|
|
||||||
$panel->appendChild($auth_form);
|
$panel = new AphrontPanelView();
|
||||||
|
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
|
||||||
|
foreach ($forms as $name => $form) {
|
||||||
|
$panel->appendChild('<h1>'.$name.'</h1>');
|
||||||
|
$panel->appendChild($form);
|
||||||
|
$panel->appendChild('<br />');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->buildStandardPageResponse(
|
return $this->buildStandardPageResponse(
|
||||||
|
|
|
@ -10,6 +10,7 @@ phutil_require_module('phabricator', 'aphront/response/redirect');
|
||||||
phutil_require_module('phabricator', 'applications/auth/controller/base');
|
phutil_require_module('phabricator', 'applications/auth/controller/base');
|
||||||
phutil_require_module('phabricator', 'applications/auth/oauth/provider/base');
|
phutil_require_module('phabricator', 'applications/auth/oauth/provider/base');
|
||||||
phutil_require_module('phabricator', 'applications/people/storage/user');
|
phutil_require_module('phabricator', 'applications/people/storage/user');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/env');
|
||||||
phutil_require_module('phabricator', 'view/form/base');
|
phutil_require_module('phabricator', 'view/form/base');
|
||||||
phutil_require_module('phabricator', 'view/form/control/submit');
|
phutil_require_module('phabricator', 'view/form/control/submit');
|
||||||
phutil_require_module('phabricator', 'view/form/error');
|
phutil_require_module('phabricator', 'view/form/error');
|
||||||
|
|
|
@ -20,6 +20,7 @@ class PhabricatorOAuthLoginController extends PhabricatorAuthController {
|
||||||
|
|
||||||
private $provider;
|
private $provider;
|
||||||
private $userID;
|
private $userID;
|
||||||
|
|
||||||
private $accessToken;
|
private $accessToken;
|
||||||
private $tokenExpires;
|
private $tokenExpires;
|
||||||
|
|
||||||
|
@ -50,8 +51,169 @@ class PhabricatorOAuthLoginController extends PhabricatorAuthController {
|
||||||
return $this->buildErrorResponse($error_view);
|
return $this->buildErrorResponse($error_view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$error_response = $this->retrieveAccessToken($provider);
|
||||||
|
if ($error_response) {
|
||||||
|
return $error_response;
|
||||||
|
}
|
||||||
|
|
||||||
|
$userinfo_uri = new PhutilURI($provider->getUserInfoURI());
|
||||||
|
$userinfo_uri->setQueryParams(
|
||||||
|
array(
|
||||||
|
'access_token' => $this->accessToken,
|
||||||
|
));
|
||||||
|
|
||||||
|
$user_json = @file_get_contents($userinfo_uri);
|
||||||
|
$user_data = json_decode($user_json, true);
|
||||||
|
|
||||||
|
$provider->setUserData($user_data);
|
||||||
|
$provider->setAccessToken($this->accessToken);
|
||||||
|
|
||||||
|
$user_id = $provider->retrieveUserID();
|
||||||
|
$provider_key = $provider->getProviderKey();
|
||||||
|
|
||||||
|
$oauth_info = $this->retrieveOAuthInfo($provider);
|
||||||
|
|
||||||
|
if ($current_user->getPHID()) {
|
||||||
|
if ($oauth_info->getID()) {
|
||||||
|
if ($oauth_info->getUserID() != $current_user->getID()) {
|
||||||
|
$dialog = new AphrontDialogView();
|
||||||
|
$dialog->setUser($current_user);
|
||||||
|
$dialog->setTitle('Already Linked to Another Account');
|
||||||
|
$dialog->appendChild(
|
||||||
|
'<p>The '.$provider_name.' account you just authorized '.
|
||||||
|
'is already linked to another Phabricator account. Before you can '.
|
||||||
|
'associate your '.$provider_name.' account with this Phabriactor '.
|
||||||
|
'account, you must unlink it from the Phabricator account it is '.
|
||||||
|
'currently linked to.</p>');
|
||||||
|
$dialog->addCancelButton('/settings/page/'.$provider_key.'/');
|
||||||
|
|
||||||
|
return id(new AphrontDialogResponse())->setDialog($dialog);
|
||||||
|
} else {
|
||||||
|
return id(new AphrontRedirectResponse())
|
||||||
|
->setURI('/settings/page/'.$provider_key.'/');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$existing_oauth = id(new PhabricatorUserOAuthInfo())->loadOneWhere(
|
||||||
|
'userID = %d AND oauthProvider = %s',
|
||||||
|
$current_user->getID(),
|
||||||
|
$provider_key);
|
||||||
|
|
||||||
|
if ($existing_oauth) {
|
||||||
|
$dialog = new AphrontDialogView();
|
||||||
|
$dialog->setUser($current_user);
|
||||||
|
$dialog->setTitle('Already Linked to an Account From This Provider');
|
||||||
|
$dialog->appendChild(
|
||||||
|
'<p>The account you are logged in with is already linked to a '.
|
||||||
|
$provider_name.' account. Before you can link it to a different '.
|
||||||
|
$provider_name.' account, you must unlink the old account.</p>');
|
||||||
|
$dialog->addCancelButton('/settings/page/'.$provider_key.'/');
|
||||||
|
return id(new AphrontDialogResponse())->setDialog($dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$request->isDialogFormPost()) {
|
||||||
|
$dialog = new AphrontDialogView();
|
||||||
|
$dialog->setUser($current_user);
|
||||||
|
$dialog->setTitle('Link '.$provider_name.' Account');
|
||||||
|
$dialog->appendChild(
|
||||||
|
'<p>Link your '.$provider_name.' account to your Phabricator '.
|
||||||
|
'account?</p>');
|
||||||
|
$dialog->addHiddenInput('token', $provider->getAccessToken());
|
||||||
|
$dialog->addHiddenInput('expires', $oauth_info->getTokenExpires());
|
||||||
|
$dialog->addSubmitButton('Link Accounts');
|
||||||
|
$dialog->addCancelButton('/settings/page/'.$provider_key.'/');
|
||||||
|
|
||||||
|
return id(new AphrontDialogResponse())->setDialog($dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
$oauth_info->setUserID($current_user->getID());
|
||||||
|
$oauth_info->save();
|
||||||
|
|
||||||
|
return id(new AphrontRedirectResponse())
|
||||||
|
->setURI('/settings/page/'.$provider_key.'/');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Login with known auth.
|
||||||
|
|
||||||
|
if ($oauth_info->getID()) {
|
||||||
|
$known_user = id(new PhabricatorUser())->load($oauth_info->getUserID());
|
||||||
|
$session_key = $known_user->establishSession('web');
|
||||||
|
|
||||||
|
$oauth_info->save();
|
||||||
|
|
||||||
|
$request->setCookie('phusr', $known_user->getUsername());
|
||||||
|
$request->setCookie('phsid', $session_key);
|
||||||
|
return id(new AphrontRedirectResponse())
|
||||||
|
->setURI('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
$oauth_email = $provider->retrieveUserEmail();
|
||||||
|
if ($oauth_email) {
|
||||||
|
$known_email = id(new PhabricatorUser())
|
||||||
|
->loadOneWhere('email = %s', $oauth_email);
|
||||||
|
if ($known_email) {
|
||||||
|
$dialog = new AphrontDialogView();
|
||||||
|
$dialog->setUser($current_user);
|
||||||
|
$dialog->setTitle('Already Linked to Another Account');
|
||||||
|
$dialog->appendChild(
|
||||||
|
'<p>The '.$provider_name.' account you just authorized has an '.
|
||||||
|
'email address which is already in use by another Phabricator '.
|
||||||
|
'account. To link the accounts, log in to your Phabricator '.
|
||||||
|
'account and then go to Settings.</p>');
|
||||||
|
$dialog->addCancelButton('/login/');
|
||||||
|
|
||||||
|
return id(new AphrontDialogResponse())->setDialog($dialog);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$provider->isProviderRegistrationEnabled()) {
|
||||||
|
$dialog = new AphrontDialogView();
|
||||||
|
$dialog->setUser($current_user);
|
||||||
|
$dialog->setTitle('No Account Registration With '.$provider_name);
|
||||||
|
$dialog->appendChild(
|
||||||
|
'<p>You can not register a new account using '.$provider_name.'; '.
|
||||||
|
'you can only use your '.$provider_name.' account to log into an '.
|
||||||
|
'existing Phabricator account which you have registered through '.
|
||||||
|
'other means.</p>');
|
||||||
|
$dialog->addCancelButton('/login/');
|
||||||
|
|
||||||
|
return id(new AphrontDialogResponse())->setDialog($dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
$class = PhabricatorEnv::getEnvConfig('controller.oauth-registration');
|
||||||
|
PhutilSymbolLoader::loadClass($class);
|
||||||
|
$controller = newv($class, array($this->getRequest()));
|
||||||
|
|
||||||
|
$controller->setOAuthProvider($provider);
|
||||||
|
$controller->setOAuthInfo($oauth_info);
|
||||||
|
|
||||||
|
return $this->delegateToController($controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildErrorResponse(PhabricatorOAuthFailureView $view) {
|
||||||
|
$provider = $this->provider;
|
||||||
|
|
||||||
|
$provider_name = $provider->getProviderName();
|
||||||
|
$view->setOAuthProvider($provider);
|
||||||
|
|
||||||
|
return $this->buildStandardPageResponse(
|
||||||
|
$view,
|
||||||
|
array(
|
||||||
|
'title' => $provider_name.' Auth Failed',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function retrieveAccessToken(PhabricatorOAuthProvider $provider) {
|
||||||
|
$request = $this->getRequest();
|
||||||
|
|
||||||
$token = $request->getStr('token');
|
$token = $request->getStr('token');
|
||||||
if (!$token) {
|
if ($token) {
|
||||||
|
$this->tokenExpires = $request->getInt('expires');
|
||||||
|
$this->accessToken = $token;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
$client_id = $provider->getClientID();
|
$client_id = $provider->getClientID();
|
||||||
$client_secret = $provider->getClientSecret();
|
$client_secret = $provider->getClientSecret();
|
||||||
$redirect_uri = $provider->getRedirectURI();
|
$redirect_uri = $provider->getRedirectURI();
|
||||||
|
@ -89,7 +251,6 @@ class PhabricatorOAuthLoginController extends PhabricatorAuthController {
|
||||||
fclose($stream);
|
fclose($stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if ($response === false) {
|
if ($response === false) {
|
||||||
return $this->buildErrorResponse(new PhabricatorOAuthFailureView());
|
return $this->buildErrorResponse(new PhabricatorOAuthFailureView());
|
||||||
}
|
}
|
||||||
|
@ -106,351 +267,27 @@ class PhabricatorOAuthLoginController extends PhabricatorAuthController {
|
||||||
$this->tokenExpires = time() + $data['expires'];
|
$this->tokenExpires = time() + $data['expires'];
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
|
||||||
$this->tokenExpires = $request->getInt('expires');
|
|
||||||
}
|
|
||||||
|
|
||||||
$userinfo_uri = new PhutilURI($provider->getUserInfoURI());
|
|
||||||
$userinfo_uri->setQueryParams(
|
|
||||||
array(
|
|
||||||
'access_token' => $token,
|
|
||||||
));
|
|
||||||
|
|
||||||
$user_json = @file_get_contents($userinfo_uri);
|
|
||||||
$user_data = json_decode($user_json, true);
|
|
||||||
|
|
||||||
$this->accessToken = $token;
|
$this->accessToken = $token;
|
||||||
|
|
||||||
switch ($provider->getProviderKey()) {
|
return null;
|
||||||
case PhabricatorOAuthProvider::PROVIDER_GITHUB:
|
|
||||||
$user_data = $user_data['user'];
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
$this->userData = $user_data;
|
|
||||||
|
|
||||||
$user_id = $this->retrieveUserID();
|
private function retrieveOAuthInfo(PhabricatorOAuthProvider $provider) {
|
||||||
|
|
||||||
$known_oauth = id(new PhabricatorUserOAuthInfo())->loadOneWhere(
|
$oauth_info = id(new PhabricatorUserOAuthInfo())->loadOneWhere(
|
||||||
'oauthProvider = %s and oauthUID = %s',
|
'oauthProvider = %s and oauthUID = %s',
|
||||||
$provider->getProviderKey(),
|
$provider->getProviderKey(),
|
||||||
$user_id);
|
$provider->retrieveUserID());
|
||||||
|
|
||||||
if ($current_user->getPHID()) {
|
|
||||||
if ($known_oauth) {
|
|
||||||
if ($known_oauth->getUserID() != $current_user->getID()) {
|
|
||||||
$dialog = new AphrontDialogView();
|
|
||||||
$dialog->setUser($current_user);
|
|
||||||
$dialog->setTitle('Already Linked to Another Account');
|
|
||||||
$dialog->appendChild(
|
|
||||||
'<p>The '.$provider_name.' account you just authorized '.
|
|
||||||
'is already linked to another Phabricator account. Before you can '.
|
|
||||||
'associate your '.$provider_name.' account with this Phabriactor '.
|
|
||||||
'account, you must unlink it from the Phabricator account it is '.
|
|
||||||
'currently linked to.</p>');
|
|
||||||
$dialog->addCancelButton('/settings/page/'.$provider_key.'/');
|
|
||||||
|
|
||||||
return id(new AphrontDialogResponse())->setDialog($dialog);
|
|
||||||
} else {
|
|
||||||
return id(new AphrontRedirectResponse())
|
|
||||||
->setURI('/settings/page/'.$provider_key.'/');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$request->isDialogFormPost()) {
|
|
||||||
$dialog = new AphrontDialogView();
|
|
||||||
$dialog->setUser($current_user);
|
|
||||||
$dialog->setTitle('Link '.$provider_name.' Account');
|
|
||||||
$dialog->appendChild(
|
|
||||||
'<p>Link your '.$provider_name.' account to your Phabricator '.
|
|
||||||
'account?</p>');
|
|
||||||
$dialog->addHiddenInput('token', $token);
|
|
||||||
$dialog->addHiddenInput('expires', $this->tokenExpires);
|
|
||||||
$dialog->addSubmitButton('Link Accounts');
|
|
||||||
$dialog->addCancelButton('/settings/page/'.$provider_key.'/');
|
|
||||||
|
|
||||||
return id(new AphrontDialogResponse())->setDialog($dialog);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (!$oauth_info) {
|
||||||
$oauth_info = new PhabricatorUserOAuthInfo();
|
$oauth_info = new PhabricatorUserOAuthInfo();
|
||||||
$oauth_info->setUserID($current_user->getID());
|
|
||||||
$this->configureOAuthInfo($oauth_info);
|
|
||||||
$oauth_info->save();
|
|
||||||
|
|
||||||
return id(new AphrontRedirectResponse())
|
|
||||||
->setURI('/settings/page/'.$provider_key.'/');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Login with known auth.
|
|
||||||
|
|
||||||
if ($known_oauth) {
|
|
||||||
$known_user = id(new PhabricatorUser())->load($known_oauth->getUserID());
|
|
||||||
$session_key = $known_user->establishSession('web');
|
|
||||||
|
|
||||||
$this->configureOAuthInfo($known_oauth);
|
|
||||||
$known_oauth->save();
|
|
||||||
|
|
||||||
$request->setCookie('phusr', $known_user->getUsername());
|
|
||||||
$request->setCookie('phsid', $session_key);
|
|
||||||
return id(new AphrontRedirectResponse())
|
|
||||||
->setURI('/');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge accounts based on shared email. TODO: should probably get rid of
|
|
||||||
// this.
|
|
||||||
|
|
||||||
$oauth_email = $this->retrieveUserEmail();
|
|
||||||
if ($oauth_email) {
|
|
||||||
$known_email = id(new PhabricatorUser())
|
|
||||||
->loadOneWhere('email = %s', $oauth_email);
|
|
||||||
if ($known_email) {
|
|
||||||
$dialog = new AphrontDialogView();
|
|
||||||
$dialog->setUser($current_user);
|
|
||||||
$dialog->setTitle('Already Linked to Another Account');
|
|
||||||
$dialog->appendChild(
|
|
||||||
'<p>The '.$provider_name.' account you just authorized has an '.
|
|
||||||
'email address which is already in use by another Phabricator '.
|
|
||||||
'account. To link the accounts, log in to your Phabricator '.
|
|
||||||
'account and then go to Settings.</p>');
|
|
||||||
$dialog->addCancelButton('/login/');
|
|
||||||
|
|
||||||
return id(new AphrontDialogResponse())->setDialog($dialog);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$errors = array();
|
|
||||||
$e_username = true;
|
|
||||||
$e_email = true;
|
|
||||||
$e_realname = true;
|
|
||||||
|
|
||||||
$user = new PhabricatorUser();
|
|
||||||
|
|
||||||
$suggestion = $this->retrieveUsernameSuggestion();
|
|
||||||
$user->setUsername($suggestion);
|
|
||||||
|
|
||||||
$oauth_realname = $this->retreiveRealNameSuggestion();
|
|
||||||
|
|
||||||
if ($request->isFormPost()) {
|
|
||||||
|
|
||||||
$user->setUsername($request->getStr('username'));
|
|
||||||
$username = $user->getUsername();
|
|
||||||
$matches = null;
|
|
||||||
if (!strlen($user->getUsername())) {
|
|
||||||
$e_username = 'Required';
|
|
||||||
$errors[] = 'Username is required.';
|
|
||||||
} else if (!preg_match('/^[a-zA-Z0-9]+$/', $username, $matches)) {
|
|
||||||
$e_username = 'Invalid';
|
|
||||||
$errors[] = 'Username may only contain letters and numbers.';
|
|
||||||
} else {
|
|
||||||
$e_username = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($oauth_email) {
|
|
||||||
$user->setEmail($oauth_email);
|
|
||||||
} else {
|
|
||||||
$user->setEmail($request->getStr('email'));
|
|
||||||
if (!strlen($user->getEmail())) {
|
|
||||||
$e_email = 'Required';
|
|
||||||
$errors[] = 'Email is required.';
|
|
||||||
} else {
|
|
||||||
$e_email = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($oauth_realname) {
|
|
||||||
$user->setRealName($oauth_realname);
|
|
||||||
} else {
|
|
||||||
$user->setRealName($request->getStr('realname'));
|
|
||||||
if (!strlen($user->getStr('realname'))) {
|
|
||||||
$e_realname = 'Required';
|
|
||||||
$errors[] = 'Real name is required.';
|
|
||||||
} else {
|
|
||||||
$e_realname = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$errors) {
|
|
||||||
$image = $this->retreiveProfileImageSuggestion();
|
|
||||||
if ($image) {
|
|
||||||
$file = PhabricatorFile::newFromFileData(
|
|
||||||
$image,
|
|
||||||
array(
|
|
||||||
'name' => $provider->getProviderKey().'-profile.jpg'
|
|
||||||
));
|
|
||||||
$user->setProfileImagePHID($file->getPHID());
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$user->save();
|
|
||||||
|
|
||||||
$oauth_info = new PhabricatorUserOAuthInfo();
|
|
||||||
$oauth_info->setUserID($user->getID());
|
|
||||||
$this->configureOAuthInfo($oauth_info);
|
|
||||||
$oauth_info->save();
|
|
||||||
|
|
||||||
$session_key = $user->establishSession('web');
|
|
||||||
$request->setCookie('phusr', $user->getUsername());
|
|
||||||
$request->setCookie('phsid', $session_key);
|
|
||||||
return id(new AphrontRedirectResponse())->setURI('/');
|
|
||||||
} catch (AphrontQueryDuplicateKeyException $exception) {
|
|
||||||
|
|
||||||
$same_username = id(new PhabricatorUser())->loadOneWhere(
|
|
||||||
'userName = %s',
|
|
||||||
$user->getUserName());
|
|
||||||
|
|
||||||
$same_email = id(new PhabricatorUser())->loadOneWhere(
|
|
||||||
'email = %s',
|
|
||||||
$user->getEmail());
|
|
||||||
|
|
||||||
if ($same_username) {
|
|
||||||
$e_username = 'Duplicate';
|
|
||||||
$errors[] = 'That username or email is not unique.';
|
|
||||||
} else if ($same_email) {
|
|
||||||
$e_email = 'Duplicate';
|
|
||||||
$errors[] = 'That email is not unique.';
|
|
||||||
} else {
|
|
||||||
throw $exception;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$error_view = null;
|
|
||||||
if ($errors) {
|
|
||||||
$error_view = new AphrontErrorView();
|
|
||||||
$error_view->setTitle('Registration Failed');
|
|
||||||
$error_view->setErrors($errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
$form = new AphrontFormView();
|
|
||||||
$form
|
|
||||||
->addHiddenInput('token', $token)
|
|
||||||
->addHiddenInput('expires', $this->tokenExpires)
|
|
||||||
->setUser($request->getUser())
|
|
||||||
->setAction($provider->getRedirectURI())
|
|
||||||
->appendChild(
|
|
||||||
id(new AphrontFormTextControl())
|
|
||||||
->setLabel('Username')
|
|
||||||
->setName('username')
|
|
||||||
->setValue($user->getUsername())
|
|
||||||
->setError($e_username));
|
|
||||||
|
|
||||||
if (!$oauth_email) {
|
|
||||||
$form->appendChild(
|
|
||||||
id(new AphrontFormTextControl())
|
|
||||||
->setLabel('Email')
|
|
||||||
->setName('email')
|
|
||||||
->setValue($request->getStr('email'))
|
|
||||||
->setError($e_email));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$oauth_realname) {
|
|
||||||
$form->appendChild(
|
|
||||||
id(new AphrontFormTextControl())
|
|
||||||
->setLabel('Real Name')
|
|
||||||
->setName('realname')
|
|
||||||
->setValue($request->getStr('realname'))
|
|
||||||
->setError($e_realname));
|
|
||||||
}
|
|
||||||
|
|
||||||
$form
|
|
||||||
->appendChild(
|
|
||||||
id(new AphrontFormSubmitControl())
|
|
||||||
->setValue('Create Account'));
|
|
||||||
|
|
||||||
$panel = new AphrontPanelView();
|
|
||||||
$panel->setHeader('Create New Account');
|
|
||||||
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
|
|
||||||
$panel->appendChild($form);
|
|
||||||
|
|
||||||
return $this->buildStandardPageResponse(
|
|
||||||
array(
|
|
||||||
$error_view,
|
|
||||||
$panel,
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
'title' => 'Create New Account',
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function buildErrorResponse(PhabricatorOAuthFailureView $view) {
|
|
||||||
$provider = $this->provider;
|
|
||||||
|
|
||||||
$provider_name = $provider->getProviderName();
|
|
||||||
$view->setOAuthProvider($provider);
|
|
||||||
|
|
||||||
return $this->buildStandardPageResponse(
|
|
||||||
$view,
|
|
||||||
array(
|
|
||||||
'title' => $provider_name.' Auth Failed',
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function retrieveUserID() {
|
|
||||||
return $this->userData['id'];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function retrieveUserEmail() {
|
|
||||||
return $this->userData['email'];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function retrieveUsernameSuggestion() {
|
|
||||||
switch ($this->provider->getProviderKey()) {
|
|
||||||
case PhabricatorOAuthProvider::PROVIDER_FACEBOOK:
|
|
||||||
$matches = null;
|
|
||||||
$link = $this->userData['link'];
|
|
||||||
if (preg_match('@/([a-zA-Z0-9]+)$@', $link, $matches)) {
|
|
||||||
return $matches[1];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case PhabricatorOAuthProvider::PROVIDER_GITHUB:
|
|
||||||
return $this->userData['login'];
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function retreiveProfileImageSuggestion() {
|
|
||||||
switch ($this->provider->getProviderKey()) {
|
|
||||||
case PhabricatorOAuthProvider::PROVIDER_FACEBOOK:
|
|
||||||
$uri = 'https://graph.facebook.com/me/picture?access_token=';
|
|
||||||
return @file_get_contents($uri.$this->accessToken);
|
|
||||||
case PhabricatorOAuthProvider::PROVIDER_GITHUB:
|
|
||||||
$id = $this->userData['gravatar_id'];
|
|
||||||
if ($id) {
|
|
||||||
$uri = 'http://www.gravatar.com/avatar/'.$id.'?s=50';
|
|
||||||
return @file_get_contents($uri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function retrieveAccountURI() {
|
|
||||||
switch ($this->provider->getProviderKey()) {
|
|
||||||
case PhabricatorOAuthProvider::PROVIDER_FACEBOOK:
|
|
||||||
return $this->userData['link'];
|
|
||||||
case PhabricatorOAuthProvider::PROVIDER_GITHUB:
|
|
||||||
$username = $this->retrieveUsernameSuggestion();
|
|
||||||
if ($username) {
|
|
||||||
return 'https://github.com/'.$username;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function retreiveRealNameSuggestion() {
|
|
||||||
return $this->userData['name'];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function configureOAuthInfo(PhabricatorUserOAuthInfo $oauth_info) {
|
|
||||||
$provider = $this->provider;
|
|
||||||
|
|
||||||
$oauth_info->setOAuthProvider($provider->getProviderKey());
|
$oauth_info->setOAuthProvider($provider->getProviderKey());
|
||||||
$oauth_info->setOAuthUID($this->retrieveUserID());
|
$oauth_info->setOAuthUID($provider->retrieveUserID());
|
||||||
$oauth_info->setAccountURI($this->retrieveAccountURI());
|
}
|
||||||
$oauth_info->setAccountName($this->retrieveUserNameSuggestion());
|
|
||||||
|
|
||||||
$oauth_info->setToken($this->accessToken);
|
$oauth_info->setAccountURI($provider->retrieveUserAccountURI());
|
||||||
|
$oauth_info->setAccountName($provider->retrieveUserAccountName());
|
||||||
|
$oauth_info->setToken($provider->getAccessToken());
|
||||||
$oauth_info->setTokenStatus(PhabricatorUserOAuthInfo::TOKEN_STATUS_GOOD);
|
$oauth_info->setTokenStatus(PhabricatorUserOAuthInfo::TOKEN_STATUS_GOOD);
|
||||||
|
|
||||||
// If we have out-of-date expiration info, just clear it out. Then replace
|
// If we have out-of-date expiration info, just clear it out. Then replace
|
||||||
|
@ -463,6 +300,8 @@ class PhabricatorOAuthLoginController extends PhabricatorAuthController {
|
||||||
$expires = $this->tokenExpires;
|
$expires = $this->tokenExpires;
|
||||||
}
|
}
|
||||||
$oauth_info->setTokenExpires($expires);
|
$oauth_info->setTokenExpires($expires);
|
||||||
|
|
||||||
|
return $oauth_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,17 +12,13 @@ phutil_require_module('phabricator', 'aphront/response/redirect');
|
||||||
phutil_require_module('phabricator', 'applications/auth/controller/base');
|
phutil_require_module('phabricator', 'applications/auth/controller/base');
|
||||||
phutil_require_module('phabricator', 'applications/auth/oauth/provider/base');
|
phutil_require_module('phabricator', 'applications/auth/oauth/provider/base');
|
||||||
phutil_require_module('phabricator', 'applications/auth/view/oauthfailure');
|
phutil_require_module('phabricator', 'applications/auth/view/oauthfailure');
|
||||||
phutil_require_module('phabricator', 'applications/files/storage/file');
|
|
||||||
phutil_require_module('phabricator', 'applications/people/storage/user');
|
phutil_require_module('phabricator', 'applications/people/storage/user');
|
||||||
phutil_require_module('phabricator', 'applications/people/storage/useroauthinfo');
|
phutil_require_module('phabricator', 'applications/people/storage/useroauthinfo');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/env');
|
||||||
phutil_require_module('phabricator', 'view/dialog');
|
phutil_require_module('phabricator', 'view/dialog');
|
||||||
phutil_require_module('phabricator', 'view/form/base');
|
|
||||||
phutil_require_module('phabricator', 'view/form/control/submit');
|
|
||||||
phutil_require_module('phabricator', 'view/form/control/text');
|
|
||||||
phutil_require_module('phabricator', 'view/form/error');
|
|
||||||
phutil_require_module('phabricator', 'view/layout/panel');
|
|
||||||
|
|
||||||
phutil_require_module('phutil', 'parser/uri');
|
phutil_require_module('phutil', 'parser/uri');
|
||||||
|
phutil_require_module('phutil', 'symbols');
|
||||||
phutil_require_module('phutil', 'utils');
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2011 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
abstract class PhabricatorOAuthRegistrationController
|
||||||
|
extends PhabricatorAuthController {
|
||||||
|
|
||||||
|
private $oauthProvider;
|
||||||
|
private $oauthInfo;
|
||||||
|
|
||||||
|
final public function setOAuthInfo($info) {
|
||||||
|
$this->oauthInfo = $info;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function getOAuthInfo() {
|
||||||
|
return $this->oauthInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function setOAuthProvider($provider) {
|
||||||
|
$this->oauthProvider = $provider;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function getOAuthProvider() {
|
||||||
|
return $this->oauthProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'applications/auth/controller/base');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhabricatorOAuthRegistrationController.php');
|
|
@ -0,0 +1,175 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2011 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class PhabricatorOAuthDefaultRegistrationController
|
||||||
|
extends PhabricatorOAuthRegistrationController {
|
||||||
|
|
||||||
|
public function processRequest() {
|
||||||
|
$provider = $this->getOAuthProvider();
|
||||||
|
$oauth_info = $this->getOAuthInfo();
|
||||||
|
$request = $this->getRequest();
|
||||||
|
|
||||||
|
$errors = array();
|
||||||
|
$e_username = true;
|
||||||
|
$e_email = true;
|
||||||
|
$e_realname = true;
|
||||||
|
|
||||||
|
$user = new PhabricatorUser();
|
||||||
|
|
||||||
|
$user->setUsername($provider->retrieveUserAccountName());
|
||||||
|
$user->setRealName($provider->retrieveUserRealName());
|
||||||
|
$user->setEmail($provider->retrieveUserEmail());
|
||||||
|
|
||||||
|
if ($request->isFormPost()) {
|
||||||
|
|
||||||
|
$user->setUsername($request->getStr('username'));
|
||||||
|
$username = $user->getUsername();
|
||||||
|
$matches = null;
|
||||||
|
if (!strlen($user->getUsername())) {
|
||||||
|
$e_username = 'Required';
|
||||||
|
$errors[] = 'Username is required.';
|
||||||
|
} else if (!preg_match('/^[a-zA-Z0-9]+$/', $username, $matches)) {
|
||||||
|
$e_username = 'Invalid';
|
||||||
|
$errors[] = 'Username may only contain letters and numbers.';
|
||||||
|
} else {
|
||||||
|
$e_username = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user->getEmail() === null) {
|
||||||
|
$user->setEmail($request->getStr('email'));
|
||||||
|
if (!strlen($user->getEmail())) {
|
||||||
|
$e_email = 'Required';
|
||||||
|
$errors[] = 'Email is required.';
|
||||||
|
} else {
|
||||||
|
$e_email = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user->getRealName() === null) {
|
||||||
|
$user->setRealName($request->getStr('realname'));
|
||||||
|
if (!strlen($user->getStr('realname'))) {
|
||||||
|
$e_realname = 'Required';
|
||||||
|
$errors[] = 'Real name is required.';
|
||||||
|
} else {
|
||||||
|
$e_realname = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$errors) {
|
||||||
|
$image = $provider->retreiveUserProfileImage();
|
||||||
|
if ($image) {
|
||||||
|
$file = PhabricatorFile::newFromFileData(
|
||||||
|
$image,
|
||||||
|
array(
|
||||||
|
'name' => $provider->getProviderKey().'-profile.jpg'
|
||||||
|
));
|
||||||
|
$user->setProfileImagePHID($file->getPHID());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
$oauth_info->setUserID($user->getID());
|
||||||
|
$oauth_info->save();
|
||||||
|
|
||||||
|
$session_key = $user->establishSession('web');
|
||||||
|
$request->setCookie('phusr', $user->getUsername());
|
||||||
|
$request->setCookie('phsid', $session_key);
|
||||||
|
return id(new AphrontRedirectResponse())->setURI('/');
|
||||||
|
} catch (AphrontQueryDuplicateKeyException $exception) {
|
||||||
|
|
||||||
|
$same_username = id(new PhabricatorUser())->loadOneWhere(
|
||||||
|
'userName = %s',
|
||||||
|
$user->getUserName());
|
||||||
|
|
||||||
|
$same_email = id(new PhabricatorUser())->loadOneWhere(
|
||||||
|
'email = %s',
|
||||||
|
$user->getEmail());
|
||||||
|
|
||||||
|
if ($same_username) {
|
||||||
|
$e_username = 'Duplicate';
|
||||||
|
$errors[] = 'That username or email is not unique.';
|
||||||
|
} else if ($same_email) {
|
||||||
|
$e_email = 'Duplicate';
|
||||||
|
$errors[] = 'That email is not unique.';
|
||||||
|
} else {
|
||||||
|
throw $exception;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$error_view = null;
|
||||||
|
if ($errors) {
|
||||||
|
$error_view = new AphrontErrorView();
|
||||||
|
$error_view->setTitle('Registration Failed');
|
||||||
|
$error_view->setErrors($errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
$form = new AphrontFormView();
|
||||||
|
$form
|
||||||
|
->addHiddenInput('token', $provider->getAccessToken())
|
||||||
|
->addHiddenInput('expires', $oauth_info->getTokenExpires())
|
||||||
|
->setUser($request->getUser())
|
||||||
|
->setAction($provider->getRedirectURI())
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormTextControl())
|
||||||
|
->setLabel('Username')
|
||||||
|
->setName('username')
|
||||||
|
->setValue($user->getUsername())
|
||||||
|
->setError($e_username));
|
||||||
|
|
||||||
|
if ($provider->retrieveUserEmail() === null) {
|
||||||
|
$form->appendChild(
|
||||||
|
id(new AphrontFormTextControl())
|
||||||
|
->setLabel('Email')
|
||||||
|
->setName('email')
|
||||||
|
->setValue($request->getStr('email'))
|
||||||
|
->setError($e_email));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($provider->retrieveUserRealName () === null) {
|
||||||
|
$form->appendChild(
|
||||||
|
id(new AphrontFormTextControl())
|
||||||
|
->setLabel('Real Name')
|
||||||
|
->setName('realname')
|
||||||
|
->setValue($request->getStr('realname'))
|
||||||
|
->setError($e_realname));
|
||||||
|
}
|
||||||
|
|
||||||
|
$form
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormSubmitControl())
|
||||||
|
->setValue('Create Account'));
|
||||||
|
|
||||||
|
$panel = new AphrontPanelView();
|
||||||
|
$panel->setHeader('Create New Account');
|
||||||
|
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
|
||||||
|
$panel->appendChild($form);
|
||||||
|
|
||||||
|
return $this->buildStandardPageResponse(
|
||||||
|
array(
|
||||||
|
$error_view,
|
||||||
|
$panel,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'title' => 'Create New Account',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'aphront/response/redirect');
|
||||||
|
phutil_require_module('phabricator', 'applications/auth/controller/oauthregistration/base');
|
||||||
|
phutil_require_module('phabricator', 'applications/files/storage/file');
|
||||||
|
phutil_require_module('phabricator', 'applications/people/storage/user');
|
||||||
|
phutil_require_module('phabricator', 'view/form/base');
|
||||||
|
phutil_require_module('phabricator', 'view/form/control/submit');
|
||||||
|
phutil_require_module('phabricator', 'view/form/control/text');
|
||||||
|
phutil_require_module('phabricator', 'view/form/error');
|
||||||
|
phutil_require_module('phabricator', 'view/layout/panel');
|
||||||
|
|
||||||
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhabricatorOAuthDefaultRegistrationController.php');
|
|
@ -29,6 +29,12 @@ class PhabricatorOAuthUnlinkController extends PhabricatorAuthController {
|
||||||
$user = $request->getUser();
|
$user = $request->getUser();
|
||||||
|
|
||||||
$provider = $this->provider;
|
$provider = $this->provider;
|
||||||
|
|
||||||
|
if ($provider->isProviderLinkPermanent()) {
|
||||||
|
throw new Exception(
|
||||||
|
"You may not unlink accounts from this OAuth provider.");
|
||||||
|
}
|
||||||
|
|
||||||
$provider_name = $provider->getProviderName();
|
$provider_name = $provider->getProviderName();
|
||||||
$provider_key = $provider->getProviderKey();
|
$provider_key = $provider->getProviderKey();
|
||||||
|
|
||||||
|
|
|
@ -21,9 +21,13 @@ abstract class PhabricatorOAuthProvider {
|
||||||
const PROVIDER_FACEBOOK = 'facebook';
|
const PROVIDER_FACEBOOK = 'facebook';
|
||||||
const PROVIDER_GITHUB = 'github';
|
const PROVIDER_GITHUB = 'github';
|
||||||
|
|
||||||
|
private $accessToken;
|
||||||
|
|
||||||
abstract public function getProviderKey();
|
abstract public function getProviderKey();
|
||||||
abstract public function getProviderName();
|
abstract public function getProviderName();
|
||||||
abstract public function isProviderEnabled();
|
abstract public function isProviderEnabled();
|
||||||
|
abstract public function isProviderLinkPermanent();
|
||||||
|
abstract public function isProviderRegistrationEnabled();
|
||||||
abstract public function getRedirectURI();
|
abstract public function getRedirectURI();
|
||||||
abstract public function getClientID();
|
abstract public function getClientID();
|
||||||
abstract public function getClientSecret();
|
abstract public function getClientSecret();
|
||||||
|
@ -32,10 +36,27 @@ abstract class PhabricatorOAuthProvider {
|
||||||
abstract public function getUserInfoURI();
|
abstract public function getUserInfoURI();
|
||||||
abstract public function getMinimumScope();
|
abstract public function getMinimumScope();
|
||||||
|
|
||||||
|
abstract public function setUserData($data);
|
||||||
|
abstract public function retrieveUserID();
|
||||||
|
abstract public function retrieveUserEmail();
|
||||||
|
abstract public function retrieveUserAccountName();
|
||||||
|
abstract public function retrieveUserProfileImage();
|
||||||
|
abstract public function retrieveUserAccountURI();
|
||||||
|
abstract public function retrieveUserRealName();
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final public function setAccessToken($access_token) {
|
||||||
|
$this->accessToken = $access_token;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function getAccessToken() {
|
||||||
|
return $this->accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
public static function newProvider($which) {
|
public static function newProvider($which) {
|
||||||
switch ($which) {
|
switch ($which) {
|
||||||
case self::PROVIDER_FACEBOOK:
|
case self::PROVIDER_FACEBOOK:
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
class PhabricatorOAuthProviderFacebook extends PhabricatorOAuthProvider {
|
class PhabricatorOAuthProviderFacebook extends PhabricatorOAuthProvider {
|
||||||
|
|
||||||
|
private $userData;
|
||||||
|
|
||||||
public function getProviderKey() {
|
public function getProviderKey() {
|
||||||
return self::PROVIDER_FACEBOOK;
|
return self::PROVIDER_FACEBOOK;
|
||||||
}
|
}
|
||||||
|
@ -30,6 +32,14 @@ class PhabricatorOAuthProviderFacebook extends PhabricatorOAuthProvider {
|
||||||
return PhabricatorEnv::getEnvConfig('facebook.auth-enabled');
|
return PhabricatorEnv::getEnvConfig('facebook.auth-enabled');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isProviderLinkPermanent() {
|
||||||
|
return PhabricatorEnv::getEnvConfig('facebook.auth-permanent');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isProviderRegistrationEnabled() {
|
||||||
|
return PhabricatorEnv::getEnvConfig('facebook.registration-enabled');
|
||||||
|
}
|
||||||
|
|
||||||
public function getRedirectURI() {
|
public function getRedirectURI() {
|
||||||
return PhabricatorEnv::getURI('/oauth/facebook/login/');
|
return PhabricatorEnv::getURI('/oauth/facebook/login/');
|
||||||
}
|
}
|
||||||
|
@ -58,4 +68,39 @@ class PhabricatorOAuthProviderFacebook extends PhabricatorOAuthProvider {
|
||||||
return 'email';
|
return 'email';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setUserData($data) {
|
||||||
|
$this->userData = $data;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function retrieveUserID() {
|
||||||
|
return $this->userData['id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function retrieveUserEmail() {
|
||||||
|
return $this->userData['email'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function retrieveUserAccountName() {
|
||||||
|
$matches = null;
|
||||||
|
$link = $this->userData['link'];
|
||||||
|
if (preg_match('@/([a-zA-Z0-9]+)$@', $link, $matches)) {
|
||||||
|
return $matches[1];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function retrieveUserProfileImage() {
|
||||||
|
$uri = 'https://graph.facebook.com/me/picture?access_token=';
|
||||||
|
return @file_get_contents($uri.$this->getAccessToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function retrieveUserAccountURI() {
|
||||||
|
return $this->userData['link'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function retrieveUserRealName() {
|
||||||
|
return $this->userData['name'];
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
class PhabricatorOAuthProviderGithub extends PhabricatorOAuthProvider {
|
class PhabricatorOAuthProviderGithub extends PhabricatorOAuthProvider {
|
||||||
|
|
||||||
|
private $userData;
|
||||||
|
|
||||||
public function getProviderKey() {
|
public function getProviderKey() {
|
||||||
return self::PROVIDER_GITHUB;
|
return self::PROVIDER_GITHUB;
|
||||||
}
|
}
|
||||||
|
@ -30,6 +32,14 @@ class PhabricatorOAuthProviderGithub extends PhabricatorOAuthProvider {
|
||||||
return PhabricatorEnv::getEnvConfig('github.auth-enabled');
|
return PhabricatorEnv::getEnvConfig('github.auth-enabled');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isProviderLinkPermanent() {
|
||||||
|
return PhabricatorEnv::getEnvConfig('github.auth-permanent');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isProviderRegistrationEnabled() {
|
||||||
|
return PhabricatorEnv::getEnvConfig('github.registration-enabled');
|
||||||
|
}
|
||||||
|
|
||||||
public function getRedirectURI() {
|
public function getRedirectURI() {
|
||||||
return PhabricatorEnv::getURI('/oauth/github/login/');
|
return PhabricatorEnv::getURI('/oauth/github/login/');
|
||||||
}
|
}
|
||||||
|
@ -58,4 +68,42 @@ class PhabricatorOAuthProviderGithub extends PhabricatorOAuthProvider {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setUserData($data) {
|
||||||
|
$this->userData = $data['user'];
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function retrieveUserID() {
|
||||||
|
return $this->userData['id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function retrieveUserEmail() {
|
||||||
|
return $this->userData['email'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function retrieveUserAccountName() {
|
||||||
|
return $this->userData['login'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function retrieveUserProfileImage() {
|
||||||
|
$id = $this->userData['gravatar_id'];
|
||||||
|
if ($id) {
|
||||||
|
$uri = 'http://www.gravatar.com/avatar/'.$id.'?s=50';
|
||||||
|
return @file_get_contents($uri);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function retrieveUserAccountURI() {
|
||||||
|
$username = $this->retrieveUserAccountName();
|
||||||
|
if ($username) {
|
||||||
|
return 'https://github.com/'.$username;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function retrieveUserRealName() {
|
||||||
|
return $this->userData['name'];
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -358,6 +358,7 @@ class PhabricatorUserSettingsController extends PhabricatorPeopleController {
|
||||||
->setLabel($provider_name.' URI')
|
->setLabel($provider_name.' URI')
|
||||||
->setValue($oauth_info->getAccountURI()));
|
->setValue($oauth_info->getAccountURI()));
|
||||||
|
|
||||||
|
if (!$provider->isProviderLinkPermanent()) {
|
||||||
$unlink = 'Unlink '.$provider_name.' Account';
|
$unlink = 'Unlink '.$provider_name.' Account';
|
||||||
$unlink_form = new AphrontFormView();
|
$unlink_form = new AphrontFormView();
|
||||||
$unlink_form
|
$unlink_form
|
||||||
|
@ -370,6 +371,7 @@ class PhabricatorUserSettingsController extends PhabricatorPeopleController {
|
||||||
id(new AphrontFormSubmitControl())
|
id(new AphrontFormSubmitControl())
|
||||||
->addCancelButton('/oauth/'.$provider_key.'/unlink/', $unlink));
|
->addCancelButton('/oauth/'.$provider_key.'/unlink/', $unlink));
|
||||||
$forms['Unlink Account'] = $unlink_form;
|
$forms['Unlink Account'] = $unlink_form;
|
||||||
|
}
|
||||||
|
|
||||||
$expires = $oauth_info->getTokenExpires();
|
$expires = $oauth_info->getTokenExpires();
|
||||||
if ($expires) {
|
if ($expires) {
|
||||||
|
|
Loading…
Reference in a new issue