@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.

Generalize login flows for new registration

Summary:
Ref T1536. None of this code is reachable.

`PhabricatorAuthLoginController` provides a completely generic login/link flow, similar to how D6155 provides a generic registration flow.

`PhabricatorAuthProvider` wraps a `PhutilAuthAdapter` and glues the generic top-level flow to a concrete authentication provider.

Test Plan: Static only, code isn't meaningfully reachable.

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T1536

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

+258
+3
src/__phutil_library_map__.php
··· 815 815 'PhabricatorAuditReplyHandler' => 'applications/audit/mail/PhabricatorAuditReplyHandler.php', 816 816 'PhabricatorAuditStatusConstants' => 'applications/audit/constants/PhabricatorAuditStatusConstants.php', 817 817 'PhabricatorAuthController' => 'applications/auth/controller/PhabricatorAuthController.php', 818 + 'PhabricatorAuthLoginController' => 'applications/auth/controller/PhabricatorAuthLoginController.php', 819 + 'PhabricatorAuthProvider' => 'applications/auth/provider/PhabricatorAuthProvider.php', 818 820 'PhabricatorAuthRegisterController' => 'applications/auth/controller/PhabricatorAuthRegisterController.php', 819 821 'PhabricatorAuthenticationConfigOptions' => 'applications/config/option/PhabricatorAuthenticationConfigOptions.php', 820 822 'PhabricatorBarePageExample' => 'applications/uiexample/examples/PhabricatorBarePageExample.php', ··· 2669 2671 'PhabricatorAuditPreviewController' => 'PhabricatorAuditController', 2670 2672 'PhabricatorAuditReplyHandler' => 'PhabricatorMailReplyHandler', 2671 2673 'PhabricatorAuthController' => 'PhabricatorController', 2674 + 'PhabricatorAuthLoginController' => 'PhabricatorAuthController', 2672 2675 'PhabricatorAuthRegisterController' => 'PhabricatorAuthController', 2673 2676 'PhabricatorAuthenticationConfigOptions' => 'PhabricatorApplicationConfigOptions', 2674 2677 'PhabricatorBarePageExample' => 'PhabricatorUIExample',
+1
src/applications/auth/application/PhabricatorApplicationAuth.php
··· 32 32 public function getRoutes() { 33 33 return array( 34 34 '/auth/' => array( 35 + 'login/(?P<pkey>[^/]+)/' => 'PhabricatorAuthLoginController', 35 36 'register/(?P<akey>[^/]+)/' => 'PhabricatorAuthRegisterController', 36 37 ), 37 38 );
+160
src/applications/auth/controller/PhabricatorAuthLoginController.php
··· 1 + <?php 2 + 3 + final class PhabricatorAuthLoginController 4 + extends PhabricatorAuthController { 5 + 6 + private $providerKey; 7 + private $provider; 8 + 9 + public function shouldRequireLogin() { 10 + return false; 11 + } 12 + 13 + public function willProcessRequest(array $data) { 14 + $this->providerKey = $data['pkey']; 15 + } 16 + 17 + public function processRequest() { 18 + $request = $this->getRequest(); 19 + $viewer = $request->getUser(); 20 + 21 + $response = $this->loadProvider(); 22 + if ($response) { 23 + return $response; 24 + } 25 + 26 + $provider = $this->provider; 27 + 28 + list($account, $response) = $provider->processLoginRequest($this); 29 + if ($response) { 30 + return $response; 31 + } 32 + 33 + if ($account->getUserPHID()) { 34 + // The account is already attached to a Phabricator user, so this is 35 + // either a login or a bad account link request. 36 + if (!$viewer->isLoggedIn()) { 37 + if ($provider->shouldAllowLogin()) { 38 + return $this->processLoginUser($account); 39 + } else { 40 + return $this->renderError( 41 + pht( 42 + 'The external account ("%s") you just authenticated with is '. 43 + 'not configured to allow logins on this Phabricator install. '. 44 + 'An administrator may have recently disabled it.', 45 + $provider->getProviderName())); 46 + } 47 + } else if ($viewer->getPHID() == $account->getUserPHID()) { 48 + return $this->renderError( 49 + pht( 50 + 'This external account ("%s") is already linked to your '. 51 + 'Phabricator account.')); 52 + } else { 53 + return $this->renderError( 54 + pht( 55 + 'The external account ("%s") you just used to login is alerady '. 56 + 'associated with another Phabricator user account. Login to the '. 57 + 'other Phabricator account and unlink the external account before '. 58 + 'linking it to a new Phabricator account.', 59 + $provider->getProviderName())); 60 + } 61 + } else { 62 + // The account is not yet attached to a Phabricator user, so this is 63 + // either a registration or an account link request. 64 + if (!$viewer->isLoggedIn()) { 65 + if ($provider->shouldAllowRegistration()) { 66 + return $this->processRegisterUser($account); 67 + } else { 68 + return $this->renderError( 69 + pht( 70 + 'The external account ("%s") you just authenticated with is '. 71 + 'not configured to allow registration on this Phabricator '. 72 + 'install. An administrator may have recently disabled it.', 73 + $provider->getProviderName())); 74 + } 75 + } else { 76 + if ($provider->shouldAllowAccountLink()) { 77 + return $this->processLinkUser($account); 78 + } else { 79 + return $this->renderError( 80 + pht( 81 + 'The external account ("%s") you just authenticated with is '. 82 + 'not configured to allow account linking on this Phabricator '. 83 + 'install. An administrator may have recently disabled it.')); 84 + } 85 + } 86 + } 87 + 88 + // This should be unreachable, but fail explicitly if we get here somehow. 89 + return new Aphront400Response(); 90 + } 91 + 92 + private function processLoginUser(PhabricatorExternalAccount $account) { 93 + // TODO: Implement. 94 + return new Aphront404Response(); 95 + } 96 + 97 + private function processRegisterUser(PhabricatorExternalAccount $account) { 98 + if ($account->getUserPHID()) { 99 + throw new Exception("Account is already registered."); 100 + } 101 + 102 + // Regenerate the registration secret key, set it on the external account, 103 + // set a cookie on the user's machine, and redirect them to registration. 104 + // See PhabricatorAuthRegisterController for discussion of the registration 105 + // key. 106 + 107 + $registration_key = Filesystem::readRandomCharacters(32); 108 + $account->setProperty('registrationKey', $registration_key); 109 + 110 + $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 111 + $account->save(); 112 + unset($unguarded); 113 + 114 + $this->getRequest()->setCookie('phreg', $registration_key); 115 + 116 + $account_secret = $account->getAccountSecret(); 117 + $register_uri = $this->getApplicationURI('register/'.$account_secret.'/'); 118 + return id(new AphrontRedirectResponse())->setURI($register_uri); 119 + } 120 + 121 + private function processLinkUser(PhabricatorExternalAccount $account) { 122 + // TODO: Implement. 123 + return new Aphront404Response(); 124 + } 125 + 126 + private function loadProvider() { 127 + $provider = PhabricatorAuthProvider::getEnabledProviderByKey( 128 + $this->providerKey); 129 + 130 + if (!$provider) { 131 + return $this->renderError( 132 + pht( 133 + 'The account you are attempting to login with uses a nonexistent '. 134 + 'or disabled authentication provider (with key "%s"). An '. 135 + 'administrator may have recently disabled this provider.', 136 + $this->providerKey)); 137 + } 138 + 139 + $this->provider = $provider; 140 + 141 + return null; 142 + } 143 + 144 + private function renderError($message) { 145 + $title = pht('Login Failed'); 146 + 147 + $view = new AphrontErrorView(); 148 + $view->setTitle($title); 149 + $view->appendChild($message); 150 + 151 + return $this->buildApplicationPage( 152 + $view, 153 + array( 154 + 'title' => $title, 155 + 'device' => true, 156 + 'dust' => true, 157 + )); 158 + } 159 + 160 + }
+81
src/applications/auth/provider/PhabricatorAuthProvider.php
··· 1 + <?php 2 + 3 + abstract class PhabricatorAuthProvider { 4 + 5 + private $adapter; 6 + 7 + public function setAdapater(PhutilAuthAdapter $adapter) { 8 + $this->adapter = $adapter; 9 + return $this; 10 + } 11 + 12 + public function getAdapater() { 13 + if ($this->adapter === null) { 14 + throw new Exception("Call setAdapter() before getAdapter()!"); 15 + } 16 + return $this->adapter; 17 + } 18 + 19 + public function getProviderKey() { 20 + return $this->getAdapter()->getAdapterKey(); 21 + } 22 + 23 + public static function getAllProviders() { 24 + static $providers; 25 + 26 + if ($providers === null) { 27 + $objects = id(new PhutilSymbolLoader()) 28 + ->setAncestorClass(__CLASS__) 29 + ->loadObjects(); 30 + 31 + $providers = array(); 32 + $from_class_map = array(); 33 + foreach ($objects as $object) { 34 + $from_class = get_class($object); 35 + $object_providers = $object->createProviders(); 36 + assert_instances_of($object_providers, 'PhabricatorAuthProvider'); 37 + foreach ($object_providers as $provider) { 38 + $key = $provider->getProviderKey(); 39 + if (isset($providers[$key])) { 40 + $first_class = $from_class_map[$key]; 41 + throw new Exception( 42 + "PhabricatorAuthProviders '{$first_class}' and '{$from_class}' ". 43 + "both created authentication providers identified by key ". 44 + "'{$key}'. Provider keys must be unique."); 45 + } 46 + $providers[$key] = $provider; 47 + $from_class_map[$key] = $from_class; 48 + } 49 + } 50 + } 51 + 52 + return $providers; 53 + } 54 + 55 + public static function getEnabledProviders() { 56 + $providers = self::getAllProviders(); 57 + foreach ($providers as $key => $provider) { 58 + if (!$provider->isEnabled()) { 59 + unset($providers[$key]); 60 + } 61 + } 62 + return $providers; 63 + } 64 + 65 + public static function getEnabledProviderByKey($provider_key) { 66 + return idx(self::getEnabledProviders(), $provider_key); 67 + } 68 + 69 + abstract public function getProviderName(); 70 + abstract public function isEnabled(); 71 + abstract public function shouldAllowLogin(); 72 + abstract public function shouldAllowRegistration(); 73 + abstract public function shouldAllowAccountLink(); 74 + abstract public function processLoginRequest( 75 + PhabricatorAuthLoginController $controller); 76 + 77 + public function createProviders() { 78 + return array($this); 79 + } 80 + 81 + }
+13
src/applications/people/storage/PhabricatorExternalAccount.php
··· 37 37 return $tmp_usr; 38 38 } 39 39 40 + public function getProviderKey() { 41 + return $this->getAccountType().':'.$this->accountDomain(); 42 + } 43 + 40 44 public function save() { 41 45 if (!$this->getAccountSecret()) { 42 46 $this->setAccountSecret(Filesystem::readRandomCharacters(32)); 43 47 } 44 48 return parent::save(); 49 + } 50 + 51 + public function setProperty($key, $value) { 52 + $this->properties[$key] = $value; 53 + return $this; 54 + } 55 + 56 + public function getProperty($key, $default = null) { 57 + return idx($this->properties, $key, $default); 45 58 } 46 59 47 60 }