Page MenuHomec4science

PhabricatorOAuth2AuthProvider.php
No OneTemporary

File Metadata

Created
Tue, Nov 26, 08:19

PhabricatorOAuth2AuthProvider.php

<?php
abstract class PhabricatorOAuth2AuthProvider
extends PhabricatorOAuthAuthProvider {
const PROPERTY_APP_ID = 'oauth:app:id';
const PROPERTY_APP_SECRET = 'oauth:app:secret';
protected function getIDKey() {
return self::PROPERTY_APP_ID;
}
protected function getSecretKey() {
return self::PROPERTY_APP_SECRET;
}
protected function configureAdapter(PhutilOAuthAuthAdapter $adapter) {
$config = $this->getProviderConfig();
$adapter->setClientID($config->getProperty(self::PROPERTY_APP_ID));
$adapter->setClientSecret(
new PhutilOpaqueEnvelope(
$config->getProperty(self::PROPERTY_APP_SECRET)));
$adapter->setRedirectURI(PhabricatorEnv::getURI($this->getLoginURI()));
return $adapter;
}
protected function renderLoginForm(AphrontRequest $request, $mode) {
$adapter = $this->getAdapter();
$adapter->setState($this->getAuthCSRFCode($request));
$scope = $request->getStr('scope');
if ($scope) {
$adapter->setScope($scope);
}
$attributes = array(
'method' => 'GET',
'uri' => $adapter->getAuthenticateURI(),
);
return $this->renderStandardLoginButton($request, $mode, $attributes);
}
public function processLoginRequest(
PhabricatorAuthLoginController $controller) {
$request = $controller->getRequest();
$adapter = $this->getAdapter();
$account = null;
$response = null;
$error = $request->getStr('error');
if ($error) {
$response = $controller->buildProviderErrorResponse(
$this,
pht(
'The OAuth provider returned an error: %s',
$error));
return array($account, $response);
}
$this->verifyAuthCSRFCode($request, $request->getStr('state'));
$code = $request->getStr('code');
if (!strlen($code)) {
$response = $controller->buildProviderErrorResponse(
$this,
pht(
'The OAuth provider did not return a "code" parameter in its '.
'response.'));
return array($account, $response);
}
$adapter->setCode($code);
// NOTE: As a side effect, this will cause the OAuth adapter to request
// an access token.
try {
$account_id = $adapter->getAccountID();
} catch (Exception $ex) {
// TODO: Handle this in a more user-friendly way.
throw $ex;
}
if (!strlen($account_id)) {
$response = $controller->buildProviderErrorResponse(
$this,
pht(
'The OAuth provider failed to retrieve an account ID.'));
return array($account, $response);
}
return array($this->loadOrCreateAccount($account_id), $response);
}
public function processEditForm(
AphrontRequest $request,
array $values) {
return $this->processOAuthEditForm(
$request,
$values,
pht('Application ID is required.'),
pht('Application secret is required.'));
}
public function extendEditForm(
AphrontRequest $request,
AphrontFormView $form,
array $values,
array $issues) {
return $this->extendOAuthEditForm(
$request,
$form,
$values,
$issues,
pht('OAuth App ID'),
pht('OAuth App Secret'));
}
public function renderConfigPropertyTransactionTitle(
PhabricatorAuthProviderConfigTransaction $xaction) {
$author_phid = $xaction->getAuthorPHID();
$old = $xaction->getOldValue();
$new = $xaction->getNewValue();
$key = $xaction->getMetadataValue(
PhabricatorAuthProviderConfigTransaction::PROPERTY_KEY);
switch ($key) {
case self::PROPERTY_APP_ID:
if (strlen($old)) {
return pht(
'%s updated the OAuth application ID for this provider from '.
'"%s" to "%s".',
$xaction->renderHandleLink($author_phid),
$old,
$new);
} else {
return pht(
'%s set the OAuth application ID for this provider to '.
'"%s".',
$xaction->renderHandleLink($author_phid),
$new);
}
case self::PROPERTY_APP_SECRET:
if (strlen($old)) {
return pht(
'%s updated the OAuth application secret for this provider.',
$xaction->renderHandleLink($author_phid));
} else {
return pht(
'%s set the OAuth application secret for this provider.',
$xaction->renderHandleLink($author_phid));
}
case self::PROPERTY_NOTE:
if (strlen($old)) {
return pht(
'%s updated the OAuth application notes for this provider.',
$xaction->renderHandleLink($author_phid));
} else {
return pht(
'%s set the OAuth application notes for this provider.',
$xaction->renderHandleLink($author_phid));
}
}
return parent::renderConfigPropertyTransactionTitle($xaction);
}
protected function synchronizeOAuthAccount(
PhabricatorExternalAccount $account) {
$adapter = $this->getAdapter();
$oauth_token = $adapter->getAccessToken();
$account->setProperty('oauth.token.access', $oauth_token);
if ($adapter->supportsTokenRefresh()) {
$refresh_token = $adapter->getRefreshToken();
$account->setProperty('oauth.token.refresh', $refresh_token);
} else {
$account->setProperty('oauth.token.refresh', null);
}
$expires = $adapter->getAccessTokenExpires();
$account->setProperty('oauth.token.access.expires', $expires);
}
public function getOAuthAccessToken(
PhabricatorExternalAccount $account,
$force_refresh = false) {
if ($account->getProviderKey() !== $this->getProviderKey()) {
throw new Exception(pht('Account does not match provider!'));
}
if (!$force_refresh) {
$access_expires = $account->getProperty('oauth.token.access.expires');
$access_token = $account->getProperty('oauth.token.access');
// Don't return a token with fewer than this many seconds remaining until
// it expires.
$shortest_token = 60;
if ($access_token) {
if ($access_expires === null ||
$access_expires > (time() + $shortest_token)) {
return $access_token;
}
}
}
$refresh_token = $account->getProperty('oauth.token.refresh');
if ($refresh_token) {
$adapter = $this->getAdapter();
if ($adapter->supportsTokenRefresh()) {
$adapter->refreshAccessToken($refresh_token);
$this->synchronizeOAuthAccount($account);
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$account->save();
unset($unguarded);
return $account->getProperty('oauth.token.access');
}
}
return null;
}
public function willRenderLinkedAccount(
PhabricatorUser $viewer,
PHUIObjectItemView $item,
PhabricatorExternalAccount $account) {
// Get a valid token, possibly refreshing it. If we're unable to refresh
// it, render a message to that effect. The user may be able to repair the
// link by manually reconnecting.
$is_invalid = false;
try {
$oauth_token = $this->getOAuthAccessToken($account);
} catch (Exception $ex) {
$oauth_token = null;
$is_invalid = true;
}
$item->addAttribute(pht('OAuth2 Account'));
if ($oauth_token) {
$oauth_expires = $account->getProperty('oauth.token.access.expires');
if ($oauth_expires) {
$item->addAttribute(
pht(
'Active OAuth Token (Expires: %s)',
phabricator_datetime($oauth_expires, $viewer)));
} else {
$item->addAttribute(
pht('Active OAuth Token'));
}
} else if ($is_invalid) {
$item->addAttribute(pht('Invalid OAuth Access Token'));
} else {
$item->addAttribute(pht('No OAuth Access Token'));
}
parent::willRenderLinkedAccount($viewer, $item, $account);
}
}

Event Timeline