[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

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

   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  }


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