@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add a "refresh" action for external accounts

Summary:
Ref T1536. This is equivalent to logging out and logging back in again, but a bit less disruptive for users. For some providers (like Google), this may eventually do something different (Google has a "force" parameter which forces re-auth and is ostensibly required to refresh long-lived tokens).

Broadly, this process fixes OAuth accounts with busted access tokens so we can do API stuff. For other accounts, it mostly just syncs profile pictures.

Test Plan:
Refreshed LDAP and Oauth accounts, linked OAuth accounts, hit error conditions.

{F47390}
{F47391}
{F47392}
{F47393}
{F47394}
{F47395}

Reviewers: btrahan, chad

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T1536

Differential Revision: https://secure.phabricator.com/D6290

+108 -22
+2 -1
src/applications/auth/application/PhabricatorApplicationAuth.php
··· 66 66 'start/' => 'PhabricatorAuthStartController', 67 67 'validate/' => 'PhabricatorAuthValidateController', 68 68 'unlink/(?P<pkey>[^/]+)/' => 'PhabricatorAuthUnlinkController', 69 - 'link/(?P<pkey>[^/]+)/' => 'PhabricatorAuthLinkController', 69 + '(?P<action>link|refresh)/(?P<pkey>[^/]+)/' 70 + => 'PhabricatorAuthLinkController', 70 71 'confirmlink/(?P<akey>[^/]+)/' 71 72 => 'PhabricatorAuthConfirmLinkController', 72 73 ),
+74 -17
src/applications/auth/controller/PhabricatorAuthLinkController.php
··· 3 3 final class PhabricatorAuthLinkController 4 4 extends PhabricatorAuthController { 5 5 6 + private $action; 6 7 private $providerKey; 7 8 8 9 public function willProcessRequest(array $data) { 9 10 $this->providerKey = $data['pkey']; 11 + $this->action = $data['action']; 10 12 } 11 13 12 14 public function processRequest() { ··· 19 21 return new Aphront404Response(); 20 22 } 21 23 22 - if (!$provider->shouldAllowAccountLink()) { 23 - return $this->renderErrorPage( 24 - pht('Account Not Linkable'), 25 - array( 26 - pht('This provider is not configured to allow linking.'), 27 - )); 24 + switch ($this->action) { 25 + case 'link': 26 + if (!$provider->shouldAllowAccountLink()) { 27 + return $this->renderErrorPage( 28 + pht('Account Not Linkable'), 29 + array( 30 + pht('This provider is not configured to allow linking.'), 31 + )); 32 + } 33 + break; 34 + case 'refresh': 35 + if (!$provider->shouldAllowAccountRefresh()) { 36 + return $this->renderErrorPage( 37 + pht('Account Not Refreshable'), 38 + array( 39 + pht('This provider does not allow refreshing.'), 40 + )); 41 + } 42 + break; 43 + default: 44 + return new Aphront400Response(); 28 45 } 29 46 30 47 $account = id(new PhabricatorExternalAccount())->loadOneWhere( ··· 32 49 $provider->getProviderType(), 33 50 $provider->getProviderDomain(), 34 51 $viewer->getPHID()); 35 - if ($account) { 36 - return $this->renderErrorPage( 37 - pht('Account Already Linked'), 38 - array( 39 - pht( 40 - 'Your Phabricator account is already linked to an external '. 41 - 'account for this provider.'), 42 - )); 52 + 53 + switch ($this->action) { 54 + case 'link': 55 + if ($account) { 56 + return $this->renderErrorPage( 57 + pht('Account Already Linked'), 58 + array( 59 + pht( 60 + 'Your Phabricator account is already linked to an external '. 61 + 'account for this provider.'), 62 + )); 63 + } 64 + break; 65 + case 'refresh': 66 + if (!$account) { 67 + return $this->renderErrorPage( 68 + pht('No Account Linked'), 69 + array( 70 + pht( 71 + 'You do not have a linked account on this provider, and thus '. 72 + 'can not refresh it.'), 73 + )); 74 + } 75 + break; 76 + default: 77 + return new Aphront400Response(); 43 78 } 44 79 45 80 $panel_uri = '/settings/panel/external/'; 46 81 47 82 $request->setCookie('phcid', Filesystem::readRandomCharacters(16)); 48 - $form = $provider->buildLinkForm($this); 83 + switch ($this->action) { 84 + case 'link': 85 + $form = $provider->buildLinkForm($this); 86 + break; 87 + case 'refresh': 88 + $form = $provider->buildRefreshForm($this); 89 + break; 90 + default: 91 + return new Aphront400Response(); 92 + } 49 93 50 94 if ($provider->isLoginFormAButton()) { 51 95 require_celerity_resource('auth-css'); ··· 57 101 $form); 58 102 } 59 103 104 + switch ($this->action) { 105 + case 'link': 106 + $name = pht('Link Account'); 107 + $title = pht('Link %s Account', $provider->getProviderName()); 108 + break; 109 + case 'refresh': 110 + $name = pht('Refresh Account'); 111 + $title = pht('Refresh %s Account', $provider->getProviderName()); 112 + break; 113 + default: 114 + return new Aphront400Response(); 115 + } 116 + 60 117 $crumbs = $this->buildApplicationCrumbs(); 61 118 $crumbs->addCrumb( 62 119 id(new PhabricatorCrumbView()) ··· 64 121 ->setHref($panel_uri)); 65 122 $crumbs->addCrumb( 66 123 id(new PhabricatorCrumbView()) 67 - ->setName($provider->getProviderName())); 124 + ->setName($provider->getProviderName($name))); 68 125 69 126 return $this->buildApplicationPage( 70 127 array( ··· 72 129 $form, 73 130 ), 74 131 array( 75 - 'title' => pht('Link %s Account', $provider->getProviderName()), 132 + 'title' => $title, 76 133 'dust' => true, 77 134 'device' => true, 78 135 ));
+5 -4
src/applications/auth/controller/PhabricatorAuthLoginController.php
··· 50 50 $provider->getProviderName())); 51 51 } 52 52 } else if ($viewer->getPHID() == $account->getUserPHID()) { 53 - return $this->renderError( 54 - pht( 55 - 'This external account ("%s") is already linked to your '. 56 - 'Phabricator account.')); 53 + // This is either an attempt to re-link an existing and already 54 + // linked account (which is silly) or a refresh of an external account 55 + // (e.g., an OAuth account). 56 + return id(new AphrontRedirectResponse()) 57 + ->setURI('/settings/panel/external/'); 57 58 } else { 58 59 return $this->renderError( 59 60 pht(
+9
src/applications/auth/provider/PhabricatorAuthProvider.php
··· 154 154 return $this->renderLoginForm($controller->getRequest(), $mode = 'link'); 155 155 } 156 156 157 + public function shouldAllowAccountRefresh() { 158 + return true; 159 + } 160 + 161 + public function buildRefreshForm( 162 + PhabricatorAuthLinkController $controller) { 163 + return $this->renderLoginForm($controller->getRequest(), $mode = 'refresh'); 164 + } 165 + 157 166 protected function renderLoginForm( 158 167 AphrontRequest $request, 159 168 $mode) {
+4
src/applications/auth/provider/PhabricatorAuthProviderLDAP.php
··· 68 68 $dialog->setTitle(pht('Link LDAP Account')); 69 69 $dialog->addSubmitButton(pht('Link Accounts')); 70 70 $dialog->addCancelButton($this->getSettingsURI()); 71 + } else if ($mode == 'refresh') { 72 + $dialog->setTitle(pht('Refresh LDAP Account')); 73 + $dialog->addSubmitButton(pht('Refresh Account')); 74 + $dialog->addCancelButton($this->getSettingsURI()); 71 75 } else { 72 76 if ($this->shouldAllowRegistration()) { 73 77 $dialog->setTitle(pht('Login or Register with LDAP'));
+2
src/applications/auth/provider/PhabricatorAuthProviderOAuth.php
··· 38 38 39 39 if ($mode == 'link') { 40 40 $button_text = pht('Link External Account'); 41 + } else if ($mode == 'refresh') { 42 + $button_text = pht('Refresh Account Link'); 41 43 } else if ($this->shouldAllowRegistration()) { 42 44 $button_text = pht('Login or Register'); 43 45 } else {
+4
src/applications/auth/provider/PhabricatorAuthProviderPassword.php
··· 246 246 return; 247 247 } 248 248 249 + public function shouldAllowAccountRefresh() { 250 + return false; 251 + } 252 + 249 253 }
+8
src/applications/settings/panel/PhabricatorSettingsPanelExternalAccounts.php
··· 70 70 71 71 $can_unlink = $can_unlink && (!$can_login || ($login_accounts > 1)); 72 72 73 + $can_refresh = $provider && $provider->shouldAllowAccountRefresh(); 74 + if ($can_refresh) { 75 + $item->addAction( 76 + id(new PHUIListItemView()) 77 + ->setIcon('refresh') 78 + ->setHref('/auth/refresh/'.$account->getProviderKey().'/')); 79 + } 80 + 73 81 $item->addAction( 74 82 id(new PHUIListItemView()) 75 83 ->setIcon('delete')