[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 <?php 2 3 abstract class PhabricatorOAuth2AuthProvider 4 extends PhabricatorOAuthAuthProvider { 5 6 const PROPERTY_APP_ID = 'oauth:app:id'; 7 const PROPERTY_APP_SECRET = 'oauth:app:secret'; 8 9 protected function getIDKey() { 10 return self::PROPERTY_APP_ID; 11 } 12 13 protected function getSecretKey() { 14 return self::PROPERTY_APP_SECRET; 15 } 16 17 18 protected function configureAdapter(PhutilOAuthAuthAdapter $adapter) { 19 $config = $this->getProviderConfig(); 20 $adapter->setClientID($config->getProperty(self::PROPERTY_APP_ID)); 21 $adapter->setClientSecret( 22 new PhutilOpaqueEnvelope( 23 $config->getProperty(self::PROPERTY_APP_SECRET))); 24 $adapter->setRedirectURI(PhabricatorEnv::getURI($this->getLoginURI())); 25 return $adapter; 26 } 27 28 protected function renderLoginForm(AphrontRequest $request, $mode) { 29 $adapter = $this->getAdapter(); 30 $adapter->setState($this->getAuthCSRFCode($request)); 31 32 $scope = $request->getStr('scope'); 33 if ($scope) { 34 $adapter->setScope($scope); 35 } 36 37 $attributes = array( 38 'method' => 'GET', 39 'uri' => $adapter->getAuthenticateURI(), 40 ); 41 42 return $this->renderStandardLoginButton($request, $mode, $attributes); 43 } 44 45 public function processLoginRequest( 46 PhabricatorAuthLoginController $controller) { 47 48 $request = $controller->getRequest(); 49 $adapter = $this->getAdapter(); 50 $account = null; 51 $response = null; 52 53 $error = $request->getStr('error'); 54 if ($error) { 55 $response = $controller->buildProviderErrorResponse( 56 $this, 57 pht( 58 'The OAuth provider returned an error: %s', 59 $error)); 60 61 return array($account, $response); 62 } 63 64 $this->verifyAuthCSRFCode($request, $request->getStr('state')); 65 66 $code = $request->getStr('code'); 67 if (!strlen($code)) { 68 $response = $controller->buildProviderErrorResponse( 69 $this, 70 pht( 71 'The OAuth provider did not return a "code" parameter in its '. 72 'response.')); 73 74 return array($account, $response); 75 } 76 77 $adapter->setCode($code); 78 79 // NOTE: As a side effect, this will cause the OAuth adapter to request 80 // an access token. 81 82 try { 83 $account_id = $adapter->getAccountID(); 84 } catch (Exception $ex) { 85 // TODO: Handle this in a more user-friendly way. 86 throw $ex; 87 } 88 89 if (!strlen($account_id)) { 90 $response = $controller->buildProviderErrorResponse( 91 $this, 92 pht( 93 'The OAuth provider failed to retrieve an account ID.')); 94 95 return array($account, $response); 96 } 97 98 return array($this->loadOrCreateAccount($account_id), $response); 99 } 100 101 public function processEditForm( 102 AphrontRequest $request, 103 array $values) { 104 105 return $this->processOAuthEditForm( 106 $request, 107 $values, 108 pht('Application ID is required.'), 109 pht('Application secret is required.')); 110 } 111 112 public function extendEditForm( 113 AphrontRequest $request, 114 AphrontFormView $form, 115 array $values, 116 array $issues) { 117 118 return $this->extendOAuthEditForm( 119 $request, 120 $form, 121 $values, 122 $issues, 123 pht('OAuth App ID'), 124 pht('OAuth App Secret')); 125 } 126 127 public function renderConfigPropertyTransactionTitle( 128 PhabricatorAuthProviderConfigTransaction $xaction) { 129 130 $author_phid = $xaction->getAuthorPHID(); 131 $old = $xaction->getOldValue(); 132 $new = $xaction->getNewValue(); 133 $key = $xaction->getMetadataValue( 134 PhabricatorAuthProviderConfigTransaction::PROPERTY_KEY); 135 136 switch ($key) { 137 case self::PROPERTY_APP_ID: 138 if (strlen($old)) { 139 return pht( 140 '%s updated the OAuth application ID for this provider from '. 141 '"%s" to "%s".', 142 $xaction->renderHandleLink($author_phid), 143 $old, 144 $new); 145 } else { 146 return pht( 147 '%s set the OAuth application ID for this provider to '. 148 '"%s".', 149 $xaction->renderHandleLink($author_phid), 150 $new); 151 } 152 case self::PROPERTY_APP_SECRET: 153 if (strlen($old)) { 154 return pht( 155 '%s updated the OAuth application secret for this provider.', 156 $xaction->renderHandleLink($author_phid)); 157 } else { 158 return pht( 159 '%s set the OAuth application secret for this provider.', 160 $xaction->renderHandleLink($author_phid)); 161 } 162 case self::PROPERTY_APP_NOTE: 163 if (strlen($old)) { 164 return pht( 165 '%s updated the OAuth application notes for this provider.', 166 $xaction->renderHandleLink($author_phid)); 167 } else { 168 return pht( 169 '%s set the OAuth application notes for this provider.', 170 $xaction->renderHandleLink($author_phid)); 171 } 172 173 } 174 175 return parent::renderConfigPropertyTransactionTitle($xaction); 176 } 177 178 protected function synchronizeOAuthAccount( 179 PhabricatorExternalAccount $account) { 180 $adapter = $this->getAdapter(); 181 182 $oauth_token = $adapter->getAccessToken(); 183 $account->setProperty('oauth.token.access', $oauth_token); 184 185 if ($adapter->supportsTokenRefresh()) { 186 $refresh_token = $adapter->getRefreshToken(); 187 $account->setProperty('oauth.token.refresh', $refresh_token); 188 } else { 189 $account->setProperty('oauth.token.refresh', null); 190 } 191 192 $expires = $adapter->getAccessTokenExpires(); 193 $account->setProperty('oauth.token.access.expires', $expires); 194 } 195 196 public function getOAuthAccessToken( 197 PhabricatorExternalAccount $account, 198 $force_refresh = false) { 199 200 if ($account->getProviderKey() !== $this->getProviderKey()) { 201 throw new Exception('Account does not match provider!'); 202 } 203 204 if (!$force_refresh) { 205 $access_expires = $account->getProperty('oauth.token.access.expires'); 206 $access_token = $account->getProperty('oauth.token.access'); 207 208 // Don't return a token with fewer than this many seconds remaining until 209 // it expires. 210 $shortest_token = 60; 211 if ($access_token) { 212 if ($access_expires === null || 213 $access_expires > (time() + $shortest_token)) { 214 return $access_token; 215 } 216 } 217 } 218 219 $refresh_token = $account->getProperty('oauth.token.refresh'); 220 if ($refresh_token) { 221 $adapter = $this->getAdapter(); 222 if ($adapter->supportsTokenRefresh()) { 223 $adapter->refreshAccessToken($refresh_token); 224 225 $this->synchronizeOAuthAccount($account); 226 $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 227 $account->save(); 228 unset($unguarded); 229 230 return $account->getProperty('oauth.token.access'); 231 } 232 } 233 234 return null; 235 } 236 237 public function willRenderLinkedAccount( 238 PhabricatorUser $viewer, 239 PHUIObjectItemView $item, 240 PhabricatorExternalAccount $account) { 241 242 // Get a valid token, possibly refreshing it. If we're unable to refresh 243 // it, render a message to that effect. The user may be able to repair the 244 // link by manually reconnecting. 245 246 $is_invalid = false; 247 try { 248 $oauth_token = $this->getOAuthAccessToken($account); 249 } catch (Exception $ex) { 250 $oauth_token = null; 251 $is_invalid = true; 252 } 253 254 $item->addAttribute(pht('OAuth2 Account')); 255 256 if ($oauth_token) { 257 $oauth_expires = $account->getProperty('oauth.token.access.expires'); 258 if ($oauth_expires) { 259 $item->addAttribute( 260 pht( 261 'Active OAuth Token (Expires: %s)', 262 phabricator_datetime($oauth_expires, $viewer))); 263 } else { 264 $item->addAttribute( 265 pht( 266 'Active OAuth Token')); 267 } 268 } else if ($is_invalid) { 269 $item->addAttribute(pht('Invalid OAuth Access Token')); 270 } else { 271 $item->addAttribute(pht('No OAuth Access Token')); 272 } 273 274 parent::willRenderLinkedAccount($viewer, $item, $account); 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 |