[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/applications/auth/provider/ -> PhabricatorOAuth2AuthProvider.php (source)

   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  }


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1