Skip to content
Snippets Groups Projects
Commit c846d102 authored by Martin Fraser's avatar Martin Fraser Committed by Aaron Bauman
Browse files

Issue #3102133 by AaronBauman, darksnow: Refresh identity whenever token is refreshed

parents 7e07b8b2 bdf17e50
No related branches found
No related tags found
No related merge requests found
......@@ -177,6 +177,7 @@ class SalesforceJWTPlugin extends SalesforceAuthProviderPluginBase {
$response = $this->httpClient->retrieveResponse(new Uri($this->getLoginUrl() . static::AUTH_TOKEN_PATH), $data, ['Content-Type' => 'application/x-www-form-urlencoded']);
$token = $this->parseAccessTokenResponse($response);
$this->storage->storeAccessToken($this->service(), $token);
$this->refreshIdentity($token);
return $token;
}
......@@ -184,7 +185,9 @@ class SalesforceJWTPlugin extends SalesforceAuthProviderPluginBase {
* {@inheritDoc}
*/
public function refreshAccessToken(TokenInterface $token) {
return $this->requestAccessToken($this->generateAssertion());
$token = $this->requestAccessToken($this->generateAssertion());
$this->refreshIdentity($token);
return $token;
}
/**
......
<?php
namespace Drupal\salesforce;
/**
* Class IdentityNotFoundException extends Runtime Exception.
*
* Thrown when an auth provider does not have a properly initialized identity.
*/
class IdentityNotFoundException extends \RuntimeException {
}
\ No newline at end of file
......@@ -8,6 +8,7 @@ use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\State\StateInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\salesforce\IdentityNotFoundException;
use Drupal\salesforce\SalesforceAuthProviderPluginManagerInterface;
use Drupal\salesforce\SelectQueryInterface;
use Drupal\salesforce\SFID;
......@@ -364,19 +365,20 @@ class RestClient implements RestClientInterface {
if (!$this->authProvider) {
return [];
}
$id = $this->authProvider->getIdentity();
if (!empty($id)) {
$url = str_replace('v{version}/', '', $id['urls']['rest']);
$response = new RestResponse($this->httpRequest($url));
foreach ($response->data as $version) {
$versions[$version['version']] = $version;
}
$this->cache->set('salesforce:versions', $versions, $this->getRequestTime() + self::LONGTERM_CACHE_LIFETIME, ['salesforce']);
return $versions;
try {
$id = $this->authProvider->getIdentity();
}
else {
catch (IdentityNotFoundException $e) {
return [];
}
$url = str_replace('v{version}/', '', $id->getUrl('rest'));
$response = new RestResponse($this->httpRequest($url));
foreach ($response->data as $version) {
$versions[$version['version']] = $version;
}
$this->cache->set('salesforce:versions', $versions, $this->getRequestTime() + self::LONGTERM_CACHE_LIFETIME, ['salesforce']);
return $versions;
}
/**
......
<?php
namespace Drupal\salesforce\Rest;
use OAuth\Common\Http\Exception\TokenResponseException;
class SalesforceIdentity implements SalesforceIdentityInterface {
protected $data;
/**
* Handle the identity response from Salesforce.
*
* @param string $responseBody
* JSON identity response from Salesforce.
*
* @throws \OAuth\Common\Http\Exception\TokenResponseException
* If responseBody cannot be parsed, or contains an error.
*/
public function __construct($responseBody) {
$data = json_decode($responseBody, TRUE);
if (NULL === $data || !is_array($data)) {
throw new TokenResponseException('Unable to parse response.');
}
elseif (isset($data['error'])) {
throw new TokenResponseException('Error in retrieving token: "' . $data['error'] . '"');
}
$this->data = $data;
}
/**
* Static creation method.
*
* @param array $data
* Data array.
*
* @return \Drupal\salesforce\Rest\SalesforceIdentity
* New identity.
*
* @throws \OAuth\Common\Http\Exception\TokenResponseException
*/
public static function create(array $data) {
return new static(json_encode($data));
}
/**
* {@inheritdoc}
*/
public function getUrl($api_type, $api_version = NULL) {
if (empty($this->data['urls'][$api_type])) {
return '';
}
$url = $this->data['urls'][$api_type];
return $api_version ? str_replace('{version}', $api_version, $url) : $url;
}
}
\ No newline at end of file
<?php
namespace Drupal\salesforce\Rest;
interface SalesforceIdentityInterface {
/**
* Given API type and optional API version, return the API url.
*
* @param string $api_type
* The api type, e.g. rest, partner, meta.
* @param string $api_version
* If given, replace {version} placeholder. Otherwise, return the raw URL.
*
* @return string
* The API url.
*/
public function getUrl($api_type, $api_version = NULL);
}
\ No newline at end of file
......@@ -38,6 +38,8 @@ interface SalesforceAuthProviderInterface extends ServiceInterface, PluginFormIn
/**
* Perform a refresh of the given token.
*
* NB: This method should also refresh any associated identity.
*
* @param \OAuth\Common\Token\TokenInterface $token
* The token.
*
......@@ -49,6 +51,20 @@ interface SalesforceAuthProviderInterface extends ServiceInterface, PluginFormIn
*/
public function refreshAccessToken(TokenInterface $token);
/**
* Given a token, fetch the SF identity.
*
* @param \OAuth\Common\Token\TokenInterface $token
* The token.
*
* @return \Drupal\salesforce\Rest\SalesforceIdentityInterface
* The refreshed identity.
*
* @throws \OAuth\OAuth2\Service\Exception\MissingRefreshTokenException
* Comment.
*/
public function refreshIdentity(TokenInterface $token);
/**
* Return the credentials configured for this auth provider instance.
*
......@@ -72,8 +88,11 @@ interface SalesforceAuthProviderInterface extends ServiceInterface, PluginFormIn
/**
* Identify for this connection.
*
* @return array
* @return \Drupal\salesforce\Rest\SalesforceIdentityInterface
* Identity for this connection.
*
* @throws \Drupal\salesforce\IdentityNotFoundException
* If there is no identity.
*/
public function getIdentity();
......
......@@ -7,10 +7,11 @@ use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Messenger\MessengerTrait;
use Drupal\Core\Routing\TrustedRedirectResponse;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\salesforce\Rest\SalesforceIdentity;
use Drupal\salesforce\Storage\SalesforceAuthTokenStorageInterface;
use OAuth\Common\Http\Client\ClientInterface;
use OAuth\Common\Http\Exception\TokenResponseException;
use OAuth\Common\Http\Uri\Uri;
use OAuth\Common\Token\TokenInterface;
use OAuth\OAuth2\Service\Salesforce;
use Symfony\Component\DependencyInjection\ContainerInterface;
......@@ -184,24 +185,51 @@ abstract class SalesforceAuthProviderPluginBase extends Salesforce implements Sa
return TRUE;
}
$token = $this->getAccessToken();
$headers = [
'Authorization' => 'OAuth ' . $token->getAccessToken(),
'Content-type' => 'application/json',
];
$data = $token->getExtraParams();
try {
$response = $this->httpClient->retrieveResponse(new Uri($data['id']), [], $headers);
$this->refreshIdentity($token);
}
catch (\Exception $e) {
watchdog_exception('salesforce', $e);
$this->messenger()->addError($e->getMessage());
$form_state->disableRedirect();
return FALSE;
}
$identity = $this->parseIdentityResponse($response);
$this->storage->storeIdentity($this->service(), $identity);
return TRUE;
}
/**
* {@inheritdoc}
*/
public function requestAccessToken($code, $state = NULL) {
$token = parent::requestAccessToken($code, $state);
$this->refreshIdentity($token);
return $token;
}
/**
* {@inheritdoc}
*/
public function refreshAccessToken(TokenInterface $token) {
$token = parent::refreshAccessToken($token);
$this->refreshIdentity($token);
return $token;
}
/**
* {@inheritdoc}
*/
public function refreshIdentity(TokenInterface $token) {
$headers = [
'Authorization' => 'OAuth ' . $token->getAccessToken(),
'Content-type' => 'application/json',
];
$data = $token->getExtraParams();
$response = $this->httpClient->retrieveResponse(new Uri($data['id']), [], $headers);
$identity = new SalesforceIdentity($response);
$this->storage->storeIdentity($this->service(), $identity);
return $identity;
}
/**
* {@inheritdoc}
*/
......@@ -259,21 +287,11 @@ abstract class SalesforceAuthProviderPluginBase extends Salesforce implements Sa
* {@inheritdoc}
*/
public function getApiEndpoint($api_type = 'rest') {
$url = &drupal_static(self::CLASS . __FUNCTION__ . $api_type);
if (!isset($url)) {
$identity = $this->getIdentity();
if (empty($identity)) {
return FALSE;
}
if (is_string($identity)) {
$url = $identity;
}
elseif (isset($identity['urls'][$api_type])) {
$url = $identity['urls'][$api_type];
}
$url = str_replace('{version}', $this->getApiVersion(), $url);
$identity = $this->getIdentity();
if (empty($identity)) {
throw new IdentityNotFoundException();
}
return $url;
return $identity->getUrl($api_type, $this->getApiVersion());
}
/**
......@@ -291,7 +309,11 @@ abstract class SalesforceAuthProviderPluginBase extends Salesforce implements Sa
* {@inheritdoc}
*/
public function getIdentity() {
return $this->storage->retrieveIdentity($this->id());
$identity = $this->storage->retrieveIdentity($this->id());
if (empty($identity)) {
throw new IdentityNotFoundException();
}
return $identity;
}
/**
......@@ -301,29 +323,6 @@ abstract class SalesforceAuthProviderPluginBase extends Salesforce implements Sa
return $this->id();
}
/**
* Handle the identity response from Salesforce.
*
* @param string $responseBody
* JSON identity response from Salesforce.
*
* @return array
* The identity.
*
* @throws \OAuth\Common\Http\Exception\TokenResponseException
*/
protected function parseIdentityResponse($responseBody) {
$data = json_decode($responseBody, TRUE);
if (NULL === $data || !is_array($data)) {
throw new TokenResponseException('Unable to parse response.');
}
elseif (isset($data['error'])) {
throw new TokenResponseException('Error in retrieving token: "' . $data['error'] . '"');
}
return $data;
}
/**
* Accessor to the storage adapter to be able to retrieve tokens.
*
......
......@@ -3,6 +3,8 @@
namespace Drupal\salesforce\Storage;
use Drupal\Core\State\StateInterface;
use Drupal\salesforce\Rest\SalesforceIdentity;
use Drupal\salesforce\Rest\SalesforceIdentityInterface;
use OAuth\Common\Storage\Exception\TokenNotFoundException;
use OAuth\Common\Token\TokenInterface;
......@@ -157,7 +159,7 @@ class SalesforceAuthTokenStorage implements SalesforceAuthTokenStorageInterface
/**
* {@inheritdoc}
*/
public function storeIdentity($service, $identity) {
public function storeIdentity($service, SalesforceIdentityInterface $identity) {
$this->state->set(static::getIdentityStorageId($service), $identity);
return $this;
}
......@@ -173,7 +175,12 @@ class SalesforceAuthTokenStorage implements SalesforceAuthTokenStorageInterface
* {@inheritdoc}
*/
public function retrieveIdentity($service) {
return $this->state->get(static::getIdentityStorageId($service));
// Backwards compatibility in case someone missed the hook_update.
$identity = $this->state->get(static::getIdentityStorageId($service));
if (is_array($identity)) {
$identity = SalesforceIdentity::create($identity);
}
return $identity;
}
/**
......
......@@ -2,6 +2,7 @@
namespace Drupal\salesforce\Storage;
use Drupal\salesforce\Rest\SalesforceIdentityInterface;
use OAuth\Common\Storage\TokenStorageInterface;
/**
......@@ -16,7 +17,7 @@ interface SalesforceAuthTokenStorageInterface extends TokenStorageInterface {
*
* @return $this
*/
public function storeIdentity($service, $identity);
public function storeIdentity($service, SalesforceIdentityInterface $identity);
/**
* Return boolean indicating whether this service has an identity.
......@@ -29,7 +30,7 @@ interface SalesforceAuthTokenStorageInterface extends TokenStorageInterface {
/**
* Identity for the given service.
*
* @return array
* @return \Drupal\salesforce\Rest\SalesforceIdentityInterface
* Identity.
*/
public function retrieveIdentity($service);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment