From 063269a00a1e015437256810cd8ff50fd7b90ae6 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 22 Feb 2011 10:24:49 -0800 Subject: [PATCH] Store OAuth tokens and more OAuth account info. Summary: Test Plan: Reviewers: CC: --- resources/sql/patches/003.more_oauth.sql | 6 ++ .../oauth/PhabricatorOAuthLoginController.php | 91 +++++++++++++------ .../PhabricatorUserSettingsController.php | 50 +++++++++- .../people/controller/settings/__init__.php | 1 + .../PhabricatorUserOAuthInfo.php | 35 +++++++ .../people/storage/useroauthinfo/__init__.php | 2 + src/view/utils/viewutils.php | 11 ++- 7 files changed, 164 insertions(+), 32 deletions(-) create mode 100644 resources/sql/patches/003.more_oauth.sql diff --git a/resources/sql/patches/003.more_oauth.sql b/resources/sql/patches/003.more_oauth.sql new file mode 100644 index 0000000000..6a10199e0a --- /dev/null +++ b/resources/sql/patches/003.more_oauth.sql @@ -0,0 +1,6 @@ +alter table phabricator_user.user_oauthinfo add accountURI varchar(255); +alter table phabricator_user.user_oauthinfo add accountName varchar(255); +alter table phabricator_user.user_oauthinfo add token varchar(255); +alter table phabricator_user.user_oauthinfo add tokenExpires int unsigned; +alter table phabricator_user.user_oauthinfo add tokenScope varchar(255); +alter table phabricator_user.user_oauthinfo add tokenStatus varchar(255); diff --git a/src/applications/auth/controller/oauth/PhabricatorOAuthLoginController.php b/src/applications/auth/controller/oauth/PhabricatorOAuthLoginController.php index 6238e30ad0..4c79dbfc4b 100644 --- a/src/applications/auth/controller/oauth/PhabricatorOAuthLoginController.php +++ b/src/applications/auth/controller/oauth/PhabricatorOAuthLoginController.php @@ -21,6 +21,7 @@ class PhabricatorOAuthLoginController extends PhabricatorAuthController { private $provider; private $userID; private $accessToken; + private $tokenExpires; public function shouldRequireLogin() { return false; @@ -91,6 +92,13 @@ class PhabricatorOAuthLoginController extends PhabricatorAuthController { if (!$token) { return $this->buildErrorResponse(new PhabricatorOAuthFailureView()); } + + if (idx($data, 'expires')) { + $this->tokenExpires = time() + $data['expires']; + } + + } else { + $this->tokenExpires = $request->getInt('expires'); } $userinfo_uri = new PhutilURI($provider->getUserInfoURI()); @@ -148,6 +156,7 @@ class PhabricatorOAuthLoginController extends PhabricatorAuthController { '

Link your '.$provider_name.' account to your Phabricator '. 'account?

'); $dialog->addHiddenInput('token', $token); + $dialog->addHiddenInput('expires', $this->tokenExpires); $dialog->addSubmitButton('Link Accounts'); $dialog->addCancelButton('/settings/page/'.$provider_key.'/'); @@ -156,8 +165,7 @@ class PhabricatorOAuthLoginController extends PhabricatorAuthController { $oauth_info = new PhabricatorUserOAuthInfo(); $oauth_info->setUserID($current_user->getID()); - $oauth_info->setOAuthProvider($provider_key); - $oauth_info->setOAuthUID($user_id); + $this->configureOAuthInfo($oauth_info); $oauth_info->save(); return id(new AphrontRedirectResponse()) @@ -170,6 +178,10 @@ class PhabricatorOAuthLoginController extends PhabricatorAuthController { 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()) @@ -184,31 +196,17 @@ class PhabricatorOAuthLoginController extends PhabricatorAuthController { $known_email = id(new PhabricatorUser()) ->loadOneWhere('email = %s', $oauth_email); if ($known_email) { - $known_oauth = id(new PhabricatorUserOAuthInfo())->loadOneWhere( - 'userID = %d AND oauthProvider = %s', - $known_email->getID(), - $provider->getProviderKey()); - if ($known_oauth) { - $provider_name = $provider->getName(); - throw new Exception( - "The email associated with the ".$provider_name." account you ". - "just logged in with is already associated with another ". - "Phabricator account which is, in turn, associated with a ". - $provider_name." account different from the one you just logged ". - "in with."); - } + $dialog = new AphrontDialogView(); + $dialog->setUser($current_user); + $dialog->setTitle('Already Linked to Another Account'); + $dialog->appendChild( + '

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.

'); + $dialog->addCancelButton('/login/'); - $oauth_info = new PhabricatorUserOAuthInfo(); - $oauth_info->setUserID($known_email->getID()); - $oauth_info->setOAuthProvider($provider->getProviderKey()); - $oauth_info->setOAuthUID($user_id); - $oauth_info->save(); - - $session_key = $known_email->establishSession('web'); - $request->setCookie('phusr', $known_email->getUsername()); - $request->setCookie('phsid', $session_key); - return id(new AphrontRedirectResponse()) - ->setURI('/'); + return id(new AphrontDialogResponse())->setDialog($dialog); } } @@ -279,8 +277,7 @@ class PhabricatorOAuthLoginController extends PhabricatorAuthController { $oauth_info = new PhabricatorUserOAuthInfo(); $oauth_info->setUserID($user->getID()); - $oauth_info->setOAuthProvider($provider->getProviderKey()); - $oauth_info->setOAuthUID($user_id); + $this->configureOAuthInfo($oauth_info); $oauth_info->save(); $session_key = $user->establishSession('web'); @@ -312,6 +309,7 @@ class PhabricatorOAuthLoginController extends PhabricatorAuthController { $form = new AphrontFormView(); $form ->addHiddenInput('token', $token) + ->addHiddenInput('expires', $this->tokenExpires) ->setUser($request->getUser()) ->setAction($provider->getRedirectURI()) ->appendChild( @@ -410,8 +408,45 @@ class PhabricatorOAuthLoginController extends PhabricatorAuthController { 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->setOAuthUID($this->retrieveUserID()); + $oauth_info->setAccountURI($this->retrieveAccountURI()); + $oauth_info->setAccountName($this->retrieveUserNameSuggestion()); + + $oauth_info->setToken($this->accessToken); + $oauth_info->setTokenStatus(PhabricatorUserOAuthInfo::TOKEN_STATUS_GOOD); + + // If we have out-of-date expiration info, just clear it out. Then replace + // it with good info if the provider gave it to us. + $expires = $oauth_info->getTokenExpires(); + if ($expires <= time()) { + $expires = null; + } + if ($this->tokenExpires) { + $expires = $this->tokenExpires; + } + $oauth_info->setTokenExpires($expires); + } + } diff --git a/src/applications/people/controller/settings/PhabricatorUserSettingsController.php b/src/applications/people/controller/settings/PhabricatorUserSettingsController.php index 203f813f04..8202d8a226 100644 --- a/src/applications/people/controller/settings/PhabricatorUserSettingsController.php +++ b/src/applications/people/controller/settings/PhabricatorUserSettingsController.php @@ -348,8 +348,15 @@ class PhabricatorUserSettingsController extends PhabricatorPeopleController { ->appendChild( id(new AphrontFormStaticControl()) ->setLabel($provider_name.' ID') - ->setValue($oauth_info->getOAuthUID())); - + ->setValue($oauth_info->getOAuthUID())) + ->appendChild( + id(new AphrontFormStaticControl()) + ->setLabel($provider_name.' Name') + ->setValue($oauth_info->getAccountName())) + ->appendChild( + id(new AphrontFormStaticControl()) + ->setLabel($provider_name.' URI') + ->setValue($oauth_info->getAccountURI())); $unlink = 'Unlink '.$provider_name.' Account'; $unlink_form = new AphrontFormView(); @@ -363,6 +370,45 @@ class PhabricatorUserSettingsController extends PhabricatorPeopleController { id(new AphrontFormSubmitControl()) ->addCancelButton('/oauth/'.$provider_key.'/unlink/', $unlink)); $forms['Unlink Account'] = $unlink_form; + + $expires = $oauth_info->getTokenExpires(); + if ($expires) { + if ($expires <= time()) { + $expires = "Expired"; + } else { + $expires = phabricator_format_timestamp($expires); + } + } else { + $expires = 'No Information Available'; + } + + $scope = $oauth_info->getTokenScope(); + if (!$scope) { + $scope = 'No Information Available'; + } + + $status = $oauth_info->getTokenStatus(); + $status = PhabricatorUserOAuthInfo::getReadableTokenStatus($status); + + $token_form = new AphrontFormView(); + $token_form + ->setUser($user) + ->appendChild( + '

insert rap about tokens

') + ->appendChild( + id(new AphrontFormStaticControl()) + ->setLabel('Token Status') + ->setValue($status)) + ->appendChild( + id(new AphrontFormStaticControl()) + ->setLabel('Expires') + ->setValue($expires)) + ->appendChild( + id(new AphrontFormStaticControl()) + ->setLabel('Scope') + ->setValue($scope)); + + $forms['Account Token Information'] = $token_form; } $panel = new AphrontPanelView(); diff --git a/src/applications/people/controller/settings/__init__.php b/src/applications/people/controller/settings/__init__.php index b57f21861e..5a6d1c5814 100644 --- a/src/applications/people/controller/settings/__init__.php +++ b/src/applications/people/controller/settings/__init__.php @@ -25,6 +25,7 @@ phutil_require_module('phabricator', 'view/form/control/textarea'); phutil_require_module('phabricator', 'view/form/error'); phutil_require_module('phabricator', 'view/layout/panel'); phutil_require_module('phabricator', 'view/layout/sidenav'); +phutil_require_module('phabricator', 'view/utils'); phutil_require_module('phutil', 'markup'); phutil_require_module('phutil', 'utils'); diff --git a/src/applications/people/storage/useroauthinfo/PhabricatorUserOAuthInfo.php b/src/applications/people/storage/useroauthinfo/PhabricatorUserOAuthInfo.php index 742bbf7591..d5647228f9 100644 --- a/src/applications/people/storage/useroauthinfo/PhabricatorUserOAuthInfo.php +++ b/src/applications/people/storage/useroauthinfo/PhabricatorUserOAuthInfo.php @@ -18,8 +18,43 @@ class PhabricatorUserOAuthInfo extends PhabricatorUserDAO { + const TOKEN_STATUS_NONE = 'none'; + const TOKEN_STATUS_GOOD = 'good'; + const TOKEN_STATUS_FAIL = 'fail'; + const TOKEN_STATUS_EXPIRED = 'xpyr'; + protected $userID; protected $oauthProvider; protected $oauthUID; + protected $accountURI; + protected $accountName; + + protected $token; + protected $tokenExpires; + protected $tokenScope; + protected $tokenStatus; + + public function getTokenStatus() { + if (!$this->token) { + return self::TOKEN_STATUS_NONE; + } + + if ($this->tokenExpires && $this->tokenExpires <= time()) { + return self::TOKEN_STATUS_EXPIRED; + } + + return $this->tokenStatus; + } + + public static function getReadableTokenStatus($status) { + static $map = array( + self::TOKEN_STATUS_NONE => 'No Token', + self::TOKEN_STATUS_GOOD => 'Token Good', + self::TOKEN_STATUS_FAIL => 'Token Failed', + self::TOKEN_STATUS_EXPIRED => 'Token Expired', + ); + return idx($map, $status, 'Unknown'); + } + } diff --git a/src/applications/people/storage/useroauthinfo/__init__.php b/src/applications/people/storage/useroauthinfo/__init__.php index 1bb43159e3..27c214d149 100644 --- a/src/applications/people/storage/useroauthinfo/__init__.php +++ b/src/applications/people/storage/useroauthinfo/__init__.php @@ -8,5 +8,7 @@ phutil_require_module('phabricator', 'applications/people/storage/base'); +phutil_require_module('phutil', 'utils'); + phutil_require_source('PhabricatorUserOAuthInfo.php'); diff --git a/src/view/utils/viewutils.php b/src/view/utils/viewutils.php index be9835c5e6..e0f3ec922e 100644 --- a/src/view/utils/viewutils.php +++ b/src/view/utils/viewutils.php @@ -27,8 +27,15 @@ function phabricator_format_relative_time($duration) { function phabricator_format_timestamp($epoch) { $difference = (time() - $epoch); + if ($difference < 0) { + $difference = -$difference; + $relative = 'from now'; + } else { + $relative = 'ago'; + } + if ($difference < 60 * 60 * 24) { - return phabricator_format_relative_time($difference).' ago'; + return phabricator_format_relative_time($difference).' '.$relative; } else if (date('Y') == date('Y', $epoch)) { return date('M j, g:i A', $epoch); } else { @@ -43,7 +50,7 @@ function phabricator_format_units_generic( $precision = 0, &$remainder = null) { - $is_negative = false; + $is_negative = false; if ($n < 0) { $is_negative = true; $n = abs($n);