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

at recaptime-dev/main 291 lines 8.3 kB view raw
1<?php 2 3abstract 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 $identifiers = $adapter->getAccountIdentifiers(); 84 } catch (Exception $ex) { 85 // TODO: Handle this in a more user-friendly way. 86 throw $ex; 87 } 88 89 if (!$identifiers) { 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 $account = $this->newExternalAccountForIdentifiers($identifiers); 99 100 return array($account, $response); 101 } 102 103 public function processEditForm( 104 AphrontRequest $request, 105 array $values) { 106 107 return $this->processOAuthEditForm( 108 $request, 109 $values, 110 pht('Application ID is required.'), 111 pht('Application secret is required.')); 112 } 113 114 public function extendEditForm( 115 AphrontRequest $request, 116 AphrontFormView $form, 117 array $values, 118 array $issues) { 119 120 return $this->extendOAuthEditForm( 121 $request, 122 $form, 123 $values, 124 $issues, 125 pht('OAuth App ID'), 126 pht('OAuth App Secret')); 127 } 128 129 public function renderConfigPropertyTransactionTitle( 130 PhabricatorAuthProviderConfigTransaction $xaction) { 131 132 $author_phid = $xaction->getAuthorPHID(); 133 $old = $xaction->getOldValue(); 134 $new = $xaction->getNewValue(); 135 $key = $xaction->getMetadataValue( 136 PhabricatorAuthProviderConfigTransaction::PROPERTY_KEY); 137 138 switch ($key) { 139 case self::PROPERTY_APP_ID: 140 if (phutil_nonempty_string($old)) { 141 return pht( 142 '%s updated the OAuth application ID for this provider from '. 143 '"%s" to "%s".', 144 $xaction->renderHandleLink($author_phid), 145 $old, 146 $new); 147 } else { 148 return pht( 149 '%s set the OAuth application ID for this provider to '. 150 '"%s".', 151 $xaction->renderHandleLink($author_phid), 152 $new); 153 } 154 case self::PROPERTY_APP_SECRET: 155 if (phutil_nonempty_string($old)) { 156 return pht( 157 '%s updated the OAuth application secret for this provider.', 158 $xaction->renderHandleLink($author_phid)); 159 } else { 160 return pht( 161 '%s set the OAuth application secret for this provider.', 162 $xaction->renderHandleLink($author_phid)); 163 } 164 case self::PROPERTY_NOTE: 165 if (phutil_nonempty_string($old)) { 166 return pht( 167 '%s updated the OAuth application notes for this provider.', 168 $xaction->renderHandleLink($author_phid)); 169 } else { 170 return pht( 171 '%s set the OAuth application notes for this provider.', 172 $xaction->renderHandleLink($author_phid)); 173 } 174 175 } 176 177 return parent::renderConfigPropertyTransactionTitle($xaction); 178 } 179 180 protected function synchronizeOAuthAccount( 181 PhabricatorExternalAccount $account) { 182 $adapter = $this->getAdapter(); 183 184 $oauth_token = $adapter->getAccessToken(); 185 $account->setProperty('oauth.token.access', $oauth_token); 186 187 if ($adapter->supportsTokenRefresh()) { 188 $refresh_token = $adapter->getRefreshToken(); 189 $account->setProperty('oauth.token.refresh', $refresh_token); 190 } else { 191 $account->setProperty('oauth.token.refresh', null); 192 } 193 194 $expires = $adapter->getAccessTokenExpires(); 195 $account->setProperty('oauth.token.access.expires', $expires); 196 } 197 198 public function getOAuthAccessToken( 199 PhabricatorExternalAccount $account, 200 $force_refresh = false) { 201 202 if ($account->getProviderConfigPHID() !== $this->getProviderConfigPHID()) { 203 throw new Exception(pht('Account does not match provider!')); 204 } 205 206 if (!$force_refresh) { 207 $access_expires = $account->getProperty('oauth.token.access.expires'); 208 $access_token = $account->getProperty('oauth.token.access'); 209 210 // Don't return a token with fewer than this many seconds remaining until 211 // it expires. 212 $shortest_token = 60; 213 if ($access_token) { 214 if ($access_expires === null || 215 $access_expires > (time() + $shortest_token)) { 216 return $access_token; 217 } 218 } 219 } 220 221 $refresh_token = $account->getProperty('oauth.token.refresh'); 222 if ($refresh_token) { 223 $adapter = $this->getAdapter(); 224 if ($adapter->supportsTokenRefresh()) { 225 $adapter->refreshAccessToken($refresh_token); 226 227 $this->synchronizeOAuthAccount($account); 228 $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 229 $account->save(); 230 unset($unguarded); 231 232 return $account->getProperty('oauth.token.access'); 233 } 234 } 235 236 return null; 237 } 238 239 public function willRenderLinkedAccount( 240 PhabricatorUser $viewer, 241 PHUIObjectItemView $item, 242 PhabricatorExternalAccount $account) { 243 244 // Get a valid token, possibly refreshing it. If we're unable to refresh 245 // it, render a message to that effect. The user may be able to repair the 246 // link by manually reconnecting. 247 248 $is_invalid = false; 249 try { 250 $oauth_token = $this->getOAuthAccessToken($account); 251 } catch (Exception $ex) { 252 $oauth_token = null; 253 $is_invalid = true; 254 } 255 256 $item->addAttribute(pht('OAuth2 Account')); 257 258 if ($oauth_token) { 259 $oauth_expires = $account->getProperty('oauth.token.access.expires'); 260 if ($oauth_expires) { 261 $item->addAttribute( 262 pht( 263 'Active OAuth Token (Expires: %s)', 264 phabricator_datetime($oauth_expires, $viewer))); 265 } else { 266 $item->addAttribute( 267 pht('Active OAuth Token')); 268 } 269 } else if ($is_invalid) { 270 $item->addAttribute(pht('Invalid OAuth Access Token')); 271 } else { 272 $item->addAttribute(pht('No OAuth Access Token')); 273 } 274 275 parent::willRenderLinkedAccount($viewer, $item, $account); 276 } 277 278 public function supportsAutoLogin() { 279 return true; 280 } 281 282 public function getAutoLoginURI(AphrontRequest $request) { 283 $csrf_code = $this->getAuthCSRFCode($request); 284 285 $adapter = $this->getAdapter(); 286 $adapter->setState($csrf_code); 287 288 return $adapter->getAuthenticateURI(); 289 } 290 291}