[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 <?php 2 3 abstract class PhabricatorOAuth1AuthProvider 4 extends PhabricatorOAuthAuthProvider { 5 6 protected $adapter; 7 8 const PROPERTY_CONSUMER_KEY = 'oauth1:consumer:key'; 9 const PROPERTY_CONSUMER_SECRET = 'oauth1:consumer:secret'; 10 const PROPERTY_PRIVATE_KEY = 'oauth1:private:key'; 11 12 const TEMPORARY_TOKEN_TYPE = 'oauth1:request:secret'; 13 14 protected function getIDKey() { 15 return self::PROPERTY_CONSUMER_KEY; 16 } 17 18 protected function getSecretKey() { 19 return self::PROPERTY_CONSUMER_SECRET; 20 } 21 22 protected function configureAdapter(PhutilOAuth1AuthAdapter $adapter) { 23 $config = $this->getProviderConfig(); 24 $adapter->setConsumerKey($config->getProperty(self::PROPERTY_CONSUMER_KEY)); 25 $secret = $config->getProperty(self::PROPERTY_CONSUMER_SECRET); 26 if (strlen($secret)) { 27 $adapter->setConsumerSecret(new PhutilOpaqueEnvelope($secret)); 28 } 29 $adapter->setCallbackURI(PhabricatorEnv::getURI($this->getLoginURI())); 30 return $adapter; 31 } 32 33 protected function renderLoginForm(AphrontRequest $request, $mode) { 34 $attributes = array( 35 'method' => 'POST', 36 'uri' => $this->getLoginURI(), 37 ); 38 return $this->renderStandardLoginButton($request, $mode, $attributes); 39 } 40 41 public function processLoginRequest( 42 PhabricatorAuthLoginController $controller) { 43 44 $request = $controller->getRequest(); 45 $adapter = $this->getAdapter(); 46 $account = null; 47 $response = null; 48 49 if ($request->isHTTPPost()) { 50 // Add a CSRF code to the callback URI, which we'll verify when 51 // performing the login. 52 53 $client_code = $this->getAuthCSRFCode($request); 54 55 $callback_uri = $adapter->getCallbackURI(); 56 $callback_uri = $callback_uri.$client_code.'/'; 57 $adapter->setCallbackURI($callback_uri); 58 59 $uri = $adapter->getClientRedirectURI(); 60 61 $this->saveHandshakeTokenSecret( 62 $client_code, 63 $adapter->getTokenSecret()); 64 65 $response = id(new AphrontRedirectResponse()) 66 ->setIsExternal(true) 67 ->setURI($uri); 68 return array($account, $response); 69 } 70 71 $denied = $request->getStr('denied'); 72 if (strlen($denied)) { 73 // Twitter indicates that the user cancelled the login attempt by 74 // returning "denied" as a parameter. 75 throw new PhutilAuthUserAbortedException(); 76 } 77 78 // NOTE: You can get here via GET, this should probably be a bit more 79 // user friendly. 80 81 $this->verifyAuthCSRFCode($request, $controller->getExtraURIData()); 82 83 $token = $request->getStr('oauth_token'); 84 $verifier = $request->getStr('oauth_verifier'); 85 86 if (!$token) { 87 throw new Exception("Expected 'oauth_token' in request!"); 88 } 89 90 if (!$verifier) { 91 throw new Exception("Expected 'oauth_verifier' in request!"); 92 } 93 94 $adapter->setToken($token); 95 $adapter->setVerifier($verifier); 96 97 $client_code = $this->getAuthCSRFCode($request); 98 $token_secret = $this->loadHandshakeTokenSecret($client_code); 99 $adapter->setTokenSecret($token_secret); 100 101 // NOTE: As a side effect, this will cause the OAuth adapter to request 102 // an access token. 103 104 try { 105 $account_id = $adapter->getAccountID(); 106 } catch (Exception $ex) { 107 // TODO: Handle this in a more user-friendly way. 108 throw $ex; 109 } 110 111 if (!strlen($account_id)) { 112 $response = $controller->buildProviderErrorResponse( 113 $this, 114 pht( 115 'The OAuth provider failed to retrieve an account ID.')); 116 117 return array($account, $response); 118 } 119 120 return array($this->loadOrCreateAccount($account_id), $response); 121 } 122 123 public function processEditForm( 124 AphrontRequest $request, 125 array $values) { 126 127 $key_ckey = self::PROPERTY_CONSUMER_KEY; 128 $key_csecret = self::PROPERTY_CONSUMER_SECRET; 129 130 return $this->processOAuthEditForm( 131 $request, 132 $values, 133 pht('Consumer key is required.'), 134 pht('Consumer secret is required.')); 135 } 136 137 public function extendEditForm( 138 AphrontRequest $request, 139 AphrontFormView $form, 140 array $values, 141 array $issues) { 142 143 return $this->extendOAuthEditForm( 144 $request, 145 $form, 146 $values, 147 $issues, 148 pht('OAuth Consumer Key'), 149 pht('OAuth Consumer Secret')); 150 } 151 152 public function renderConfigPropertyTransactionTitle( 153 PhabricatorAuthProviderConfigTransaction $xaction) { 154 155 $author_phid = $xaction->getAuthorPHID(); 156 $old = $xaction->getOldValue(); 157 $new = $xaction->getNewValue(); 158 $key = $xaction->getMetadataValue( 159 PhabricatorAuthProviderConfigTransaction::PROPERTY_KEY); 160 161 switch ($key) { 162 case self::PROPERTY_CONSUMER_KEY: 163 if (strlen($old)) { 164 return pht( 165 '%s updated the OAuth consumer key for this provider from '. 166 '"%s" to "%s".', 167 $xaction->renderHandleLink($author_phid), 168 $old, 169 $new); 170 } else { 171 return pht( 172 '%s set the OAuth consumer key for this provider to '. 173 '"%s".', 174 $xaction->renderHandleLink($author_phid), 175 $new); 176 } 177 case self::PROPERTY_CONSUMER_SECRET: 178 if (strlen($old)) { 179 return pht( 180 '%s updated the OAuth consumer secret for this provider.', 181 $xaction->renderHandleLink($author_phid)); 182 } else { 183 return pht( 184 '%s set the OAuth consumer secret for this provider.', 185 $xaction->renderHandleLink($author_phid)); 186 } 187 } 188 189 return parent::renderConfigPropertyTransactionTitle($xaction); 190 } 191 192 protected function synchronizeOAuthAccount( 193 PhabricatorExternalAccount $account) { 194 $adapter = $this->getAdapter(); 195 196 $oauth_token = $adapter->getToken(); 197 $oauth_token_secret = $adapter->getTokenSecret(); 198 199 $account->setProperty('oauth1.token', $oauth_token); 200 $account->setProperty('oauth1.token.secret', $oauth_token_secret); 201 } 202 203 public function willRenderLinkedAccount( 204 PhabricatorUser $viewer, 205 PHUIObjectItemView $item, 206 PhabricatorExternalAccount $account) { 207 208 $item->addAttribute(pht('OAuth1 Account')); 209 210 parent::willRenderLinkedAccount($viewer, $item, $account); 211 } 212 213 214 /* -( Temporary Secrets )-------------------------------------------------- */ 215 216 217 private function saveHandshakeTokenSecret($client_code, $secret) { 218 $key = $this->getHandshakeTokenKeyFromClientCode($client_code); 219 $type = $this->getTemporaryTokenType(self::TEMPORARY_TOKEN_TYPE); 220 221 // Wipe out an existing token, if one exists. 222 $token = id(new PhabricatorAuthTemporaryTokenQuery()) 223 ->setViewer(PhabricatorUser::getOmnipotentUser()) 224 ->withObjectPHIDs(array($key)) 225 ->withTokenTypes(array($type)) 226 ->executeOne(); 227 if ($token) { 228 $token->delete(); 229 } 230 231 // Save the new secret. 232 id(new PhabricatorAuthTemporaryToken()) 233 ->setObjectPHID($key) 234 ->setTokenType($type) 235 ->setTokenExpires(time() + phutil_units('1 hour in seconds')) 236 ->setTokenCode($secret) 237 ->save(); 238 } 239 240 private function loadHandshakeTokenSecret($client_code) { 241 $key = $this->getHandshakeTokenKeyFromClientCode($client_code); 242 $type = $this->getTemporaryTokenType(self::TEMPORARY_TOKEN_TYPE); 243 244 $token = id(new PhabricatorAuthTemporaryTokenQuery()) 245 ->setViewer(PhabricatorUser::getOmnipotentUser()) 246 ->withObjectPHIDs(array($key)) 247 ->withTokenTypes(array($type)) 248 ->withExpired(false) 249 ->executeOne(); 250 251 if (!$token) { 252 throw new Exception( 253 pht( 254 'Unable to load your OAuth1 token secret from storage. It may '. 255 'have expired. Try authenticating again.')); 256 } 257 258 return $token->getTokenCode(); 259 } 260 261 private function getTemporaryTokenType($core_type) { 262 // Namespace the type so that multiple providers don't step on each 263 // others' toes if a user starts Mediawiki and Bitbucket auth at the 264 // same time. 265 266 return $core_type.':'.$this->getProviderConfig()->getID(); 267 } 268 269 private function getHandshakeTokenKeyFromClientCode($client_code) { 270 // NOTE: This is very slightly coersive since the TemporaryToken table 271 // expects an "objectPHID" as an identifier, but nothing about the storage 272 // is bound to PHIDs. 273 274 return 'oauth1:secret/'.$client_code; 275 } 276 277 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |