Skip to content
Snippets Groups Projects
Commit b8a939c9 authored by Aaron Bauman's avatar Aaron Bauman
Browse files

WIP 4.x branch

parent d766c8b8
No related branches found
No related tags found
No related merge requests found
Showing
with 588 additions and 756 deletions
...@@ -74,17 +74,3 @@ salesforce.salesforce_auth.*: ...@@ -74,17 +74,3 @@ salesforce.salesforce_auth.*:
provider_settings: provider_settings:
type: salesforce.auth_provider_settings.[%parent.provider] type: salesforce.auth_provider_settings.[%parent.provider]
label: 'Provider Plugin Settings' label: 'Provider Plugin Settings'
salesforce.auth_provider_settings.oauth:
type: mapping
label: 'Salesforce OAuth Provider Settings'
mapping:
consumer_key:
type: string
label: 'Consumer Key'
consumer_secret:
type: string
label: 'Consumer Secret'
login_url:
type: uri
label: 'Login URL'
salesforce.auth_provider_settings.oauth_encrypt:
type: mapping
label: 'Salesforce OAuth, Encrypted, Provider Settings'
mapping:
consumer_key:
type: string
label: 'Consumer Key'
consumer_secret:
type: string
label: 'Consumer Secret'
login_url:
type: uri
label: 'Login URL'
encryption_profile:
type: encrypt.profile.[%key]
label: 'Encryption Profile ID'
name: Salesforce Encrypted Keys name: Salesforce Encrypted Keys
type: module type: module
description: Supplants Salesforce RestClient service to provide encrypted stateful data. description: Adds encryption support for auth providers.
package: Salesforce package: Salesforce
core: 8.x core: 8.x
dependencies: dependencies:
......
<?php
/**
* @file
* Requirements and uninstall hooks.
*/
use Drupal\Core\Url;
use Drupal\salesforce\EntityNotFoundException;
/**
* Throw a runtime error if Salesforce encryption profile is not selected.
*
* Implements hook_requirements().
*/
function salesforce_encrypt_requirements($phase) {
$requirements = [];
if ($phase == 'runtime') {
$profile_id = NULL;
try {
$profile = \Drupal::service('salesforce.client')->getEncryptionProfile();
}
catch (EntityNotFoundException $e) {
// Noop.
}
$requirements['salesforce_encrypt'] = [
'title' => t('Salesforce Encrypt'),
'value' => t('Encryption Profile'),
];
if (empty($profile)) {
$requirements['salesforce_encrypt'] += [
'severity' => REQUIREMENT_ERROR,
'description' => t('You need to <a href="@url">select an encryption profile</a> in order to fully enable Salesforce Encrypt and protect sensitive information.', ['@url' => Url::fromRoute('salesforce_encrypt.settings')->toString()]),
];
}
else {
$requirements['salesforce_encrypt'] += [
'severity' => REQUIREMENT_OK,
'description' => t('Profile id: <a href=":url">%profile</a>', ['%profile' => $profile->id(), ':url' => $profile->url()]),
];
}
}
return $requirements;
}
/**
* Implements hook_uninstall().
*
* Decrypt and purge our data.
*/
function salesforce_encrypt_uninstall() {
\Drupal::service('salesforce.client')->disableEncryption();
\Drupal::state()->delete('salesforce_encrypt.profile');
}
salesforce_encrypt.settings:
route_name: salesforce_encrypt.settings
parent: salesforce.admin_config_salesforce
title: Salesforce Encrypt
description: 'Encrypt sensitive Salesforce OAuth credentials and identity.'
weight: 10
salesforce_encrypt.settings:
path: '/admin/config/salesforce/encrypt'
defaults:
_form: '\Drupal\salesforce_encrypt\Form\SettingsForm'
_title: 'Salesforce Encryption'
_description: 'Encrypt sensitive Salesforce OAuth credentials and identity.'
requirements:
_permission: 'administer salesforce encryption'
<?php
namespace Drupal\salesforce_encrypt\Consumer;
use Drupal\salesforce\Consumer\SalesforceCredentials;
/**
* OAuth encrypted creds.
*/
class OAuthEncryptedCredentials extends SalesforceCredentials {
/**
* Encryption profile id.
*
* @var string
*/
protected $encryptionProfileId;
/**
* {@inheritdoc}
*/
public function __construct($consumerKey, $loginUrl, $consumerSecret, $encryptionProfileId) {
parent::__construct($consumerKey, $loginUrl, $consumerSecret);
$this->encryptionProfileId = $encryptionProfileId;
}
/**
* Getter.
*
* @return string
* The encryption profile id.
*/
public function getEncryptionProfileId() {
return $this->encryptionProfileId;
}
}
<?php
namespace Drupal\salesforce_encrypt\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\State\StateInterface;
use Drupal\encrypt\EncryptionProfileManagerInterface;
use Drupal\salesforce\EntityNotFoundException;
use Drupal\salesforce_encrypt\Rest\EncryptedRestClientInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Url;
/**
* Base form for key add and edit forms.
*
* @deprecated will be removed in 8.x-4.0 release.
*/
class SettingsForm extends FormBase {
/**
* Profile manager.
*
* @var \Drupal\encrypt\EncryptionProfileManagerInterface
*/
protected $encryptionProfileManager;
/**
* SettingsForm constructor.
*
* @param \Drupal\Core\State\StateInterface $state
* State service.
* @param \Drupal\encrypt\EncryptionProfileManagerInterface $encryptionProfileManager
* Encryption profile manager service.
* @param \Drupal\salesforce_encrypt\Rest\EncryptedRestClientInterface $client
* Rest client service.
*/
public function __construct(StateInterface $state, EncryptionProfileManagerInterface $encryptionProfileManager, EncryptedRestClientInterface $client) {
$this->encryptionProfileManager = $encryptionProfileManager;
$this->state = $state;
$this->client = $client;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('state'),
$container->get('encrypt.encryption_profile.manager'),
$container->get('salesforce.client')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'salesforce_encrypt_config';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$options = $this
->encryptionProfileManager
->getEncryptionProfileNamesAsOptions();
$default = NULL;
try {
$profile = $this->client->getEncryptionProfile();
if (!empty($profile)) {
$default = $profile->id();
}
}
catch (EntityNotFoundException $e) {
drupal_set_message($e->getFormattableMessage(), 'error');
drupal_set_message($this->t('Error while loading encryption profile. You will need to <a href=":encrypt">assign a new encryption profile</a>, then <a href=":oauth">re-authenticate to Salesforce</a>.', [':encrypt' => Url::fromRoute('salesforce_encrypt.settings')->toString(), ':oauth' => Url::fromRoute('salesforce.authorize')->toString()]), 'error');
}
$form['profile'] = [
'#type' => 'select',
'#title' => $this->t('Encryption Profile'),
'#description' => $this->t('Choose an encryption profile with which to encrypt Salesforce information.'),
'#options' => $options,
'#default_value' => $default,
'#empty_option' => $this->t('Do not use encryption'),
];
$form['actions']['#type'] = 'actions';
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Save configuration'),
'#button_type' => 'primary',
];
// By default, render the form using system-config-form.html.twig.
$form['#theme'] = 'system_config_form';
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$old_profile_id = $this->state->get('salesforce_encrypt.profile');
$profile_id = $form_state->getValue('profile');
if ($old_profile_id == $profile_id) {
// No change to encryption profile. Do nothing.
return;
}
$profile = $this
->encryptionProfileManager
->getEncryptionProfile($profile_id);
if (empty($profile_id)) {
// New profile id empty: disable encryption.
$this->client->disableEncryption();
}
elseif (empty($old_profile_id)) {
// Old profile id empty: enable encryption anew.
$this->client->enableEncryption($profile);
}
else {
// Changing encryption profiles: disable, then re-enable.
$this->client->disableEncryption();
$this->client->enableEncryption($profile);
}
$this->state->resetCache();
drupal_set_message($this->t('The configuration options have been saved.'));
}
}
<?php
namespace Drupal\salesforce_encrypt\Plugin\SalesforceAuthProvider;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Routing\TrustedRedirectResponse;
use Drupal\Core\Url;
use Drupal\encrypt\EncryptionProfileInterface;
use Drupal\encrypt\EncryptionProfileManagerInterface;
use Drupal\encrypt\EncryptServiceInterface;
use Drupal\salesforce\EntityNotFoundException;
use Drupal\salesforce\SalesforceAuthProviderPluginBase;
use Drupal\salesforce\SalesforceAuthProviderInterface;
use Drupal\salesforce_encrypt\Consumer\OAuthEncryptedCredentials;
use Drupal\salesforce_encrypt\SalesforceEncryptedAuthTokenStorageInterface;
use OAuth\Common\Http\Client\ClientInterface;
use OAuth\Common\Http\Uri\Uri;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* OAuth provider with encrypted credentials.
*
* @Plugin(
* id = "oauth_encrypted",
* label = @Translation("Salesforce OAuth User-Agent, Encrypted")
* )
*/
class SalesforceEncryptedOAuthPlugin extends SalesforceAuthProviderPluginBase {
/**
* OAuth credentials.
*
* @var \Drupal\salesforce\Consumer\SalesforceCredentials
*/
protected $credentials;
/**
* Encryption profile manager.
*
* @var \Drupal\encrypt\EncryptionProfileManagerInterface
*/
protected $encryptionProfileManager;
/**
* Encryption service.
*
* @var \Drupal\encrypt\EncryptServiceInterface
*/
protected $encryption;
/**
* Encryption profile.
*
* @var \Drupal\encrypt\EncryptionProfileInterface
*/
protected $encryptionProfile;
/**
* Encryption profile id.
*
* @var string
*/
protected $encryptionProfileId;
/**
* {@inheritdoc}
*/
const SERVICE_TYPE = 'oauth_encrypted';
/**
* {@inheritdoc}
*/
const LABEL = 'OAuth Encrypted';
/**
* Token storage;.
*
* @var \Drupal\salesforce_encrypt\SalesforceEncryptedAuthTokenStorageInterface
*/
protected $storage;
/**
* {@inheritdoc}
*/
public function __construct($id, OAuthEncryptedCredentials $credentials, ClientInterface $httpClient, SalesforceEncryptedAuthTokenStorageInterface $storage, EncryptionProfileManagerInterface $encryptionProfileManager, EncryptServiceInterface $encrypt) {
parent::__construct($credentials, $httpClient, $storage, [], new Uri($credentials->getLoginUrl()));
$this->id = $id;
$this->encryptionProfileManager = $encryptionProfileManager;
$this->encryption = $encrypt;
$this->encryptionProfileId = $credentials->getEncryptionProfileId();
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
$configuration = array_merge(self::defaultConfiguration(), $configuration);
$storage = $container->get('salesforce.auth_token_storage');
/** @var \Drupal\encrypt\EncryptServiceInterface $encrypt */
$encrypt = $container->get('encryption');
$encryptProfileMan = $container->get('encrypt.encryption_profile.manager');
if ($configuration['encryption_profile']) {
try {
$profile = $encryptProfileMan->getEncryptionProfile($configuration['encryption_profile']);
$configuration['consumer_key'] = $encrypt->decrypt($configuration['consumer_key'], $profile);
$configuration['consumer_secret'] = $encrypt->decrypt($configuration['consumer_secret'], $profile);
}
catch (\Exception $e) {
// Any exception here may cause WSOD, don't allow that to happen.
watchdog_exception('SFOAuthEncrypted', $e);
}
}
$cred = new OAuthEncryptedCredentials($configuration['consumer_key'], $configuration['login_url'], $configuration['consumer_secret'], $configuration['encryption_profile']);
return new static($configuration['id'], $cred, $container->get('salesforce.http_client_wrapper'), $storage, $encryptProfileMan, $encrypt);
}
/**
* {@inheritdoc}
*/
public static function defaultConfiguration() {
$defaults = parent::defaultConfiguration();
return array_merge($defaults, [
'encryption_profile' => NULL,
]);
}
/**
* {@inheritdoc}
*/
public function hookEncryptionProfileDelete(EncryptionProfileInterface $profile) {
if ($this->encryptionProfile()->id() == $profile->id()) {
// @todo decrypt identity, access token, refresh token, consumer secret, consumer key and re-save
}
}
/**
* {@inheritdoc}
*/
public function encryptionProfile() {
if ($this->encryptionProfile) {
return $this->encryptionProfile;
}
elseif (empty($this->encryptionProfileId)) {
return NULL;
}
else {
$this->encryptionProfile = $this->encryptionProfileManager
->getEncryptionProfile($this->encryptionProfileId);
if (empty($this->encryptionProfile)) {
throw new EntityNotFoundException(['id' => $this->encryptionProfileId], 'encryption_profile');
}
return $this->encryptionProfile;
}
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$options = $this
->encryptionProfileManager
->getEncryptionProfileNamesAsOptions();
$default = NULL;
try {
$profile = $this->encryptionProfile();
if (!empty($profile)) {
$default = $profile->id();
}
}
catch (EntityNotFoundException $e) {
$this->messenger()->addError($e->getFormattableMessage());
$this->messenger()->addError($this->t('Error while loading encryption profile. You will need to assign a new encryption profile and re-authenticate to Salesforce.'));
}
if (empty($options)) {
$this->messenger()->addError($this->t('Please <a href="@href">create an encryption profile</a> before adding an OAuth Encrypted provider.', ['@href' => Url::fromRoute('entity.encryption_profile.add_form')->toString()]));
}
$form['consumer_key'] = [
'#title' => t('Salesforce consumer key'),
'#type' => 'textfield',
'#description' => t('Consumer key of the Salesforce remote application you want to grant access to. VALUE WILL BE ENCRYPTED ON FORM SUBMISSION.'),
'#required' => TRUE,
'#default_value' => $this->credentials->getConsumerKey(),
];
$form['consumer_secret'] = [
'#title' => $this->t('Salesforce consumer secret'),
'#type' => 'textfield',
'#description' => $this->t('Consumer secret of the Salesforce remote application. VALUE WILL BE ENCRYPTED ON FORM SUBMISSION.'),
'#required' => TRUE,
'#default_value' => $this->credentials->getConsumerSecret(),
];
$form['login_url'] = [
'#title' => t('Login URL'),
'#type' => 'textfield',
'#default_value' => $this->credentials->getLoginUrl(),
'#description' => t('Enter a login URL, either https://login.salesforce.com or https://test.salesforce.com.'),
'#required' => TRUE,
];
$form['encryption_profile'] = [
'#type' => 'select',
'#title' => $this->t('Encryption Profile'),
'#description' => $this->t('Choose an encryption profile with which to encrypt Salesforce information.'),
'#options' => $options,
'#default_value' => $default,
'#required' => TRUE,
];
return $form;
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$this->setConfiguration($form_state->getValues());
$settings = $form_state->getValue('provider_settings');
$this->encryptionProfileId = $settings['encryption_profile'];
$consumer_key = $settings['consumer_key'];
$settings['consumer_key'] = $this->encrypt($settings['consumer_key']);
$settings['consumer_secret'] = $this->encrypt($settings['consumer_secret']);
$form_state->setValue('provider_settings', $settings);
parent::submitConfigurationForm($form, $form_state);
// Write the config id to private temp store, so that we can use the same
// callback URL for all OAuth applications in Salesforce.
/** @var \Drupal\Core\TempStore\PrivateTempStore $tempstore */
$tempstore = \Drupal::service('user.private_tempstore')->get('salesforce_oauth');
$tempstore->set('config_id', $form_state->getValue('id'));
try {
$path = $this->getAuthorizationEndpoint();
$query = [
'redirect_uri' => $this->credentials->getCallbackUrl(),
'response_type' => 'code',
'client_id' => $consumer_key,
];
// Send the user along to the Salesforce OAuth login form. If successful,
// the user will be redirected to {redirect_uri} to complete the OAuth
// handshake, and thence to the entity listing. Upon failure, the user
// redirect URI will send the user back to the edit form.
$response = new TrustedRedirectResponse($path . '?' . http_build_query($query), 302);
$response->send();
return;
}
catch (\Exception $e) {
$this->messenger()->addError(t("Error during authorization: %message", ['%message' => $e->getMessage()]));
}
}
/**
* {@inheritdoc}
*/
public function decrypt($value) {
return $this->encryption->decrypt($value, $this->encryptionProfile());
}
/**
* {@inheritdoc}
*/
public function encrypt($value) {
return $this->encryption->encrypt($value, $this->encryptionProfile());
}
/**
* {@inheritdoc}
*/
public function getConsumerSecret() {
return $this->credentials->getConsumerSecret();
}
/**
* {@inheritdoc}
*/
public function finalizeOauth() {
$this->requestAccessToken(\Drupal::request()->get('code'));
$token = $this->getAccessToken();
// Initialize identity.
$headers = [
'Authorization' => 'OAuth ' . $token->getAccessToken(),
'Content-type' => 'application/json',
];
$data = $token->getExtraParams();
$response = $this->httpClient->retrieveResponse(new Uri($data['id']), [], $headers);
$identity = $this->parseIdentityResponse($response);
$this->storage->storeIdentity($this->service(), $identity);
return TRUE;
}
}
<?php
namespace Drupal\salesforce_encrypt\Rest;
use Drupal\encrypt\EncryptionProfileInterface;
use Drupal\salesforce\Rest\RestClientInterface;
/**
* Objects, properties, and methods to communicate with the Salesforce REST API.
*
* @deprecated will be removed in 8.x-4.0 release.
*/
interface EncryptedRestClientInterface extends RestClientInterface {
/**
* Encrypts all sensitive salesforce config values.
*
* @param \Drupal\encrypt\EncryptionProfileInterface $profile
* Id of the Encrypt Profile to use for encryption.
*
* @return bool
* TRUE if encryption was enabled or FALSE if it is already enabled
*
* @throws RuntimeException
* If Salesforce encryption profile hasn't been selected.
*/
public function enableEncryption(EncryptionProfileInterface $profile);
/**
* Decrypt and re-save sensitive salesforce config values.
*
* Inverse of ::enableEncryption.
*
* @return bool
* TRUE if encryption was disabled or FALSE if it is already disabled
*
* @throws RuntimeException
* If Salesforce encryption profile hasn't been selected.
*/
public function disableEncryption();
/**
* Returns the EncryptionProfileInterface assigned to Salesforce Encrypt.
*
* @return \Drupal\encrypt\EncryptionProfileInterface|null
* The assigned profile, or null if none has been assigned.
*
* @throws \Drupal\salesforce\EntityNotFoundException
* If a profile is assigned, but cannot be loaded.
*/
public function getEncryptionProfile();
/**
* If the given profile is our active one, disable encryption.
*
* Since we rely on a specific encryption profile, we need to respond in case
* it gets deleted. Check to see if the profile being deleted is the one
* assigned for encryption; if so, decrypt our config and disable encryption.
*
* @param \Drupal\encrypt\EncryptionProfileInterface $profile
* The encryption profile being deleted.
*/
public function hookEncryptionProfileDelete(EncryptionProfileInterface $profile);
/**
* Encrypts a value using the active encryption profile, or return plaintext.
*
* @param string $value
* The value to encrypt.
*
* @return string
* The encrypted value, or plaintext if no active profile.
*/
public function encrypt($value);
/**
* Decrypts a value using active encryption profile, or return the same value.
*
* @param string $value
* The value to decrypt.
*
* @return string
* The decrypted value, or the unchanged value if no active profile.
*/
public function decrypt($value);
}
<?php
namespace Drupal\salesforce_encrypt\Rest;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Lock\LockBackendInterface;
use Drupal\Core\State\StateInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
use Drupal\encrypt\EncryptServiceInterface;
use Drupal\encrypt\EncryptionProfileInterface;
use Drupal\encrypt\EncryptionProfileManagerInterface;
use Drupal\salesforce\EntityNotFoundException;
use Drupal\salesforce\Rest\RestClient as SalesforceRestClient;
use GuzzleHttp\ClientInterface;
use Drupal\Component\Datetime\TimeInterface;
/**
* Objects, properties, and methods to communicate with the Salesforce REST API.
*
* @deprecated will be removed in 8.x-4.0 release.
*/
class RestClient extends SalesforceRestClient implements EncryptedRestClientInterface {
use StringTranslationTrait;
/**
* Encryption service.
*
* @var \Drupal\encrypt\EncryptServiceInterface
*/
protected $encryption;
/**
* Encryption profile manager.
*
* @var \Drupal\encrypt\EncryptionProfileManagerInterface
*/
protected $encryptionProfileManager;
/**
* The active encryption profile id.
*
* @var string
*/
protected $encryptionProfileId;
/**
* The encryption profile to use when encrypting and decrypting data.
*
* @var \Drupal\encrypt\EncryptionProfileInterface
*/
protected $encryptionProfile;
/**
* Construct a new Encrypted Rest Client.
*
* @param \GuzzleHttp\ClientInterface $http_client
* The GuzzleHttp Client.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory service.
* @param \Drupal\Core\State\StateInterface $state
* The state service.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache
* The cache service.
* @param \Drupal\Component\Serialization\Json $json
* The JSON serializer service.
* @param \Drupal\Component\Datetime\TimeInterface $time
* The Time service.
* @param \Drupal\encrypt\EncryptServiceInterface $encryption
* The encryption service.
* @param \Drupal\encrypt\EncryptionProfileManagerInterface $encryptionProfileManager
* The Encryption profile manager service.
* @param \Drupal\Core\Lock\LockBackendInterface $lock
* The lock backend service.
*/
public function __construct(ClientInterface $http_client, ConfigFactoryInterface $config_factory, StateInterface $state, CacheBackendInterface $cache, Json $json, TimeInterface $time, EncryptServiceInterface $encryption, EncryptionProfileManagerInterface $encryptionProfileManager, LockBackendInterface $lock) {
parent::__construct($http_client, $config_factory, $state, $cache, $json, $time);
$this->encryption = $encryption;
$this->encryptionProfileId = $this->state->get('salesforce_encrypt.profile');
$this->encryptionProfileManager = $encryptionProfileManager;
$this->encryptionProfile = NULL;
$this->lock = $lock;
}
/**
* {@inheritdoc}
*/
public function enableEncryption(EncryptionProfileInterface $profile) {
if ($ret = $this->setEncryption($profile)) {
$this->state->resetCache();
}
return $ret;
}
/**
* {@inheritdoc}
*/
public function disableEncryption() {
if ($ret = $this->setEncryption()) {
$this->state->resetCache();
}
return $ret;
}
/**
* {@inheritdoc}
*/
public function hookEncryptionProfileDelete(EncryptionProfileInterface $profile) {
if ($this->encryptionProfileId == $profile->id()) {
$this->disableEncryption();
}
}
/**
* Set the given encryption profile as active.
*
* If given profile is null, decrypt and disable encryption.
*
* @param \Drupal\encrypt\EncryptionProfileInterface|null $profile
* The encryption profile. If null, encryption will be disabled.
*/
protected function setEncryption(EncryptionProfileInterface $profile = NULL) {
if (!$this->lock->acquire('salesforce_encrypt')) {
throw new \RuntimeException('Unable to acquire lock.');
}
$access_token = $this->getAccessToken();
$refresh_token = $this->getRefreshToken();
$identity = $this->getIdentity();
$consumerKey = $this->getConsumerKey();
$consumerSecret = $this->getConsumerSecret();
$this->encryptionProfileId = $profile == NULL ? NULL : $profile->id();
$this->encryptionProfile = $profile;
$this->state->set('salesforce_encrypt.profile', $this->encryptionProfileId);
$this->setAccessToken($access_token);
$this->setRefreshToken($refresh_token);
$this->setIdentity($identity);
$this->setConsumerKey($consumerKey);
$this->setConsumerSecret($consumerSecret);
$this->lock->release('salesforce_encrypt');
}
/**
* {@inheritdoc}
*/
public function getEncryptionProfile() {
if ($this->encryptionProfile) {
return $this->encryptionProfile;
}
elseif (empty($this->encryptionProfileId)) {
return NULL;
}
else {
$this->encryptionProfile = $this->encryptionProfileManager
->getEncryptionProfile($this->encryptionProfileId);
if (empty($this->encryptionProfile)) {
throw new EntityNotFoundException(['id' => $this->encryptionProfileId], 'encryption_profile');
}
return $this->encryptionProfile;
}
}
/**
* Deprecated, use doGetEncryptionProfile.
*
* @deprecated use ::doGetEncryptionProfile().
*/
protected function _getEncryptionProfile() {
return $this->doGetEncryptionProfile();
}
/**
* Exception-handling wrapper around getEncryptionProfile().
*
* GetEncryptionProfile() will throw an EntityNotFoundException exception
* if it has an encryption profile ID but cannot load it. In this wrapper
* we handle that exception by setting a helpful error message and allow
* execution to proceed.
*
* @return \Drupal\encrypt\EncryptionProfileInterface|null
* The encryption profile if it can be loaded, otherwise NULL.
*/
protected function doGetEncryptionProfile() {
try {
$profile = $this->getEncryptionProfile();
}
catch (EntityNotFoundException $e) {
drupal_set_message($this->t('Error while loading encryption profile. You will need to <a href=":encrypt">assign a new encryption profile</a>, then <a href=":oauth">re-authenticate to Salesforce</a>.', [':encrypt' => Url::fromRoute('salesforce_encrypt.settings')->toString(), ':oauth' => Url::fromRoute('salesforce.authorize')->toString()]), 'error');
}
return $profile;
}
/**
* {@inheritdoc}
*/
public function encrypt($value) {
if (empty($this->doGetEncryptionProfile())) {
return $value;
}
else {
return $this->encryption->encrypt($value, $this->doGetEncryptionProfile());
}
}
/**
* {@inheritdoc}
*/
public function decrypt($value) {
if (empty($this->doGetEncryptionProfile()) || empty($value) || mb_strlen($value) === 0) {
return $value;
}
else {
return $this->encryption->decrypt($value, $this->doGetEncryptionProfile());
}
}
/**
* {@inheritdoc}
*/
public function getAccessToken() {
return $this->decrypt(parent::getAccessToken());
}
/**
* {@inheritdoc}
*/
public function setAccessToken($token) {
return parent::setAccessToken($this->encrypt($token));
}
/**
* {@inheritdoc}
*/
public function getRefreshToken() {
return $this->decrypt(parent::getRefreshToken());
}
/**
* {@inheritdoc}
*/
public function setRefreshToken($token) {
return parent::setRefreshToken($this->encrypt($token));
}
/**
* {@inheritdoc}
*/
public function setIdentity($data) {
if (is_array($data)) {
$data = serialize($data);
}
return parent::setIdentity($this->encrypt($data));
}
/**
* {@inheritdoc}
*/
public function getIdentity() {
$data = $this->decrypt(parent::getIdentity());
if (!empty($data) && !is_array($data)) {
$data = unserialize($data);
}
return $data;
}
/**
* {@inheritdoc}
*/
public function getConsumerKey() {
return $this->decrypt(parent::getConsumerKey());
}
/**
* {@inheritdoc}
*/
public function setConsumerKey($value) {
return parent::setConsumerKey($this->encrypt($value));
}
/**
* {@inheritdoc}
*/
public function getConsumerSecret() {
return $this->decrypt(parent::getConsumerSecret());
}
/**
* {@inheritdoc}
*/
public function setConsumerSecret($value) {
return parent::setConsumerSecret($this->encrypt($value));
}
}
<?php
namespace Drupal\salesforce_encrypt;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ServiceProviderBase;
use Drupal\salesforce_encrypt\Rest\RestClient;
use Symfony\Component\DependencyInjection\Reference;
/**
* Modifies the salesforce client service.
*
* @deprecated will be removed in 8.x-4.0 release.
*/
class SalesforceEncryptServiceProvider extends ServiceProviderBase {
/**
* {@inheritdoc}
*/
public function alter(ContainerBuilder $container) {
// Overrides salesforce.client class with our EncryptedRestClientInterface.
$container->getDefinition('salesforce.client')
->setClass(RestClient::class)
->addArgument(new Reference('encryption'))
->addArgument(new Reference('encrypt.encryption_profile.manager'))
->addArgument(new Reference('lock'));
}
}
<?php
namespace Drupal\salesforce_encrypt;
use Drupal\salesforce\Entity\SalesforceAuthConfig;
use Drupal\salesforce\Storage\SalesforceAuthTokenStorage;
use Drupal\salesforce_encrypt\Plugin\SalesforceAuthProvider\SalesforceEncryptedOAuthPlugin;
use OAuth\Common\Token\TokenInterface;
/**
* Auth token storage, using encryption.
*/
class SalesforceEncryptedAuthTokenStorage extends SalesforceAuthTokenStorage implements SalesforceEncryptedAuthTokenStorageInterface {
/**
* Auth plugin manager.
*
* @var \Drupal\salesforce\SalesforceAuthProviderPluginManager
*/
protected $authPluginManager;
/**
* Given a service id, return the instantiated auth provider plugin.
*
* @param string $service_id
* The service id.
*
* @return \Drupal\salesforce\SalesforceAuthProviderInterface
* The plugin.
*/
protected function service($service_id) {
if (!$this->authPluginManager) {
$this->authPluginManager = \Drupal::service('plugin.manager.salesforce.auth_providers');
}
$auth = SalesforceAuthConfig::load($service_id);
return $auth->getPlugin();
}
/**
* {@inheritdoc}
*/
public function retrieveAccessToken($service_id) {
$token = parent::retrieveAccessToken($service_id);
if ($token instanceof TokenInterface || !$this->service($service_id) instanceof SalesforceEncryptedOAuthPlugin) {
return $token;
}
$token = unserialize($this->service($service_id)->decrypt($token));
return $token;
}
/**
* {@inheritdoc}
*/
public function storeAccessToken($service_id, TokenInterface $token) {
if ($this->service($service_id) instanceof SalesforceEncryptedOAuthPlugin) {
$token = $this->service($service_id)->encrypt(serialize($token));
}
$this->state->set(self::getTokenStorageId($service_id), $token);
return $this;
}
/**
* {@inheritdoc}
*/
public function storeIdentity($service_id, $identity) {
if ($this->service($service_id) instanceof SalesforceEncryptedOAuthPlugin) {
if (is_array($identity)) {
$identity = serialize($identity);
}
$identity = $this->service($service_id)->encrypt($identity);
}
$this->state->set(self::getIdentityStorageId($service_id), $identity);
return $this;
}
/**
* {@inheritdoc}
*/
public function retrieveIdentity($service_id) {
$identity = parent::retrieveIdentity($service_id);
if (!$this->service($service_id) instanceof SalesforceEncryptedOAuthPlugin) {
return $identity;
}
$identity = $this->service($service_id)->decrypt($identity);
if (!empty($identity) && !is_array($identity)) {
$identity = unserialize($identity);
}
return $identity;
}
}
<?php
namespace Drupal\salesforce_encrypt;
use Drupal\salesforce\Storage\SalesforceAuthTokenStorageInterface;
/**
* Encrypted token storage interface.
*/
interface SalesforceEncryptedAuthTokenStorageInterface extends SalesforceAuthTokenStorageInterface {
}
<?php
namespace Drupal\salesforce_encrypt;
use Drupal\encrypt\EncryptionProfileInterface;
use Drupal\salesforce\SalesforceOAuthPluginInterface;
/**
* Encrypted oauth provider interface.
*/
interface SalesforceEncryptedOAuthPluginInterface extends SalesforceOAuthPluginInterface {
/**
* Callback for hook_encryption_profile_predelete().
*
* @param \Drupal\encrypt\EncryptionProfileInterface $profile
* The encryption profile being deleted.
*/
public function hookEncryptionProfileDelete(EncryptionProfileInterface $profile);
/**
* Get the encryption profile assigned to this auth plugin.
*
* @return \Drupal\encrypt\EncryptionProfileInterface|null
* Profile.
*/
public function encryptionProfile();
/**
* Decrypt a given value, using the assigned encryption profile.
*
* @param string $value
* The encrypted value.
*
* @return string
* The plain text value.
*
* @throws \Drupal\encrypt\Exception\EncryptException
* On decryption error.
*/
public function decrypt($value);
/**
* Encrypt a value, using the assigned encryption profile.
*
* @param string $value
* The plain text value.
*
* @return string
* The encrypted value.
*
* @throws \Drupal\encrypt\Exception\EncryptException
* On error.
*/
public function encrypt($value);
}
<?php
namespace Drupal\Tests\salesforce_encrypt\Unit;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Config\ConfigFactory;
use Drupal\Core\Lock\LockBackendInterface;
use Drupal\Core\State\State;
use Drupal\Tests\UnitTestCase;
use Drupal\encrypt\EncryptServiceInterface;
use Drupal\encrypt\EncryptionProfileInterface;
use Drupal\encrypt\EncryptionProfileManagerInterface;
use Drupal\salesforce_encrypt\Rest\RestClient;
use GuzzleHttp\Client;
use Drupal\Component\Datetime\TimeInterface;
/**
* @coversDefaultClass \Drupal\salesforce_encrypt\Rest\RestClient
* @group salesforce
*
* @deprecated will be removed in 8.x-4.0 release.
*/
class RestClientTest extends UnitTestCase {
static public $modules = [
'key',
'encrypt',
'salesforce',
'salesforce_encrypt',
];
protected $httpClient;
protected $configFactory;
protected $state;
protected $cache;
protected $json;
protected $time;
protected $encryption;
protected $profileManager;
protected $lock;
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
$this->accessToken = 'foo';
$this->refreshToken = 'bar';
$this->identity = ['zee' => 'bang'];
$this->httpClient = $this->getMock(Client::CLASS);
$this->configFactory =
$this->getMockBuilder(ConfigFactory::CLASS)
->disableOriginalConstructor()
->getMock();
$this->state =
$this->getMockBuilder(State::CLASS)
->disableOriginalConstructor()
->getMock();
$this->cache = $this->createMock(CacheBackendInterface::CLASS);
$this->json = $this->createMock('Drupal\Component\Serialization\Json');
$this->encryption = $this->createMock(EncryptServiceInterface::CLASS);
$this->profileManager = $this->createMock(EncryptionProfileManagerInterface::CLASS);
$this->lock = $this->createMock(LockBackendInterface::CLASS);
$this->encryptionProfile = $this->createMock(EncryptionProfileInterface::CLASS);
$this->json = $this->createMock(Json::CLASS);
$this->time = $this->createMock(TimeInterface::CLASS);
$this->client = $this->getMockBuilder(RestClient::CLASS)
->setMethods(['doGetEncryptionProfile'])
->setConstructorArgs([
$this->httpClient,
$this->configFactory,
$this->state,
$this->cache,
$this->json,
$this->time,
$this->encryption,
$this->profileManager,
$this->lock,
])
->getMock();
}
/**
* @covers ::encrypt
*
* encrypt is protected, so we get at it through ::getAccessToken
* This test covers the case where access token is NULL.
*/
public function testEncryptNull() {
// Test unencrypted.
$this->state->expects($this->any())
->method('get')
->willReturn(NULL);
$this->client->expects($this->any())
->method('doGetEncryptionProfile')
->willReturn(NULL);
$this->assertFalse($this->client->getAccessToken());
}
/**
* @covers ::encrypt
*
* This test covers the case where access token is not NULL.
*/
public function testEncryptNotNull() {
// Test unencrypted.
$this->state->expects($this->any())
->method('get')
->willReturn('not null');
$this->client->expects($this->any())
->method('doGetEncryptionProfile')
->willReturn($this->encryptionProfile);
$this->encryption->expects($this->any())
->method('decrypt')
->willReturn($this->accessToken);
$this->assertEquals($this->accessToken, $this->client->getAccessToken());
}
}
salesforce.auth_provider_settings.jwt:
type: mapping
label: 'Salesforce JWT Provider Settings'
mapping:
consumer_key:
type: string
label: 'Consumer Key'
login_user:
type: string
label: 'Login User'
login_url:
type: uri
label: 'Login URL'
encrypt_key:
type: key.key.[%key]
label: 'Encryption Key ID'
name: Salesforce JWT Auth Provider
type: module
description: Provides key-based Salesforce authentication.
core: 8.x
package: Salesforce
dependencies:
- salesforce
- key
<?php
namespace Drupal\salesforce_jwt\Consumer;
use Drupal\salesforce\Consumer\SalesforceCredentials;
/**
* JWT credentials.
*/
class JWTCredentials extends SalesforceCredentials {
/**
* Pre-authorized login user for JWT OAuth authentication.
*
* @var string
*/
protected $loginUser;
/**
* Id of authorization key for this JWT Credential.
*
* @var string
*/
protected $keyId;
/**
* {@inheritdoc}
*/
public function __construct($consumerKey, $loginUrl, $loginUser, $keyId) {
parent::__construct($consumerKey, $loginUrl);
$this->loginUser = $loginUser;
$this->keyId = $keyId;
}
/**
* Login user getter.
*
* @return string
* The login user.
*/
public function getLoginUser() {
return $this->loginUser;
}
/**
* Authorization key getter.
*
* @return string
* The key id.
*/
public function getKeyId() {
return $this->keyId;
}
}
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