From 74955ab1d5403d11f83725e13547c28a03875c97 Mon Sep 17 00:00:00 2001 From: Aaron Bauman <aaron@messageagency.com> Date: Fri, 15 Mar 2019 12:10:43 -0400 Subject: [PATCH] Update new auth plugins with dependencies, refactor with annotation definition and super-class helpers, fix confusion between plugin definition and configuration --- config/schema/salesforce.schema.yml | 2 +- .../src/Consumer/JWTCredentials.php | 20 ++++ .../SalesforceJWTPlugin.php | 70 +++++------ .../salesforce_mapping_ui.module | 2 +- .../Consumer/SalesforceOAuthCredentials.php | 20 ++++ .../SalesforceOAuthPlugin.php | 69 ++--------- .../src/Plugin/QueueWorker/PullBase.php | 2 +- modules/salesforce_push/src/PushQueue.php | 2 +- salesforce.install | 2 +- src/Annotation/SalesforceAuthProvider.php | 33 +++++ src/Consumer/SalesforceCredentials.php | 19 ++- .../SalesforceCredentialsInterface.php | 18 +++ src/Controller/SalesforceAuthListBuilder.php | 4 +- src/Entity/SalesforceAuthConfig.php | 42 ++++++- src/Form/SalesforceAuthForm.php | 2 +- src/Plugin/SalesforceAuthProvider/Broken.php | 62 ++++++++++ src/SalesforceAuthProviderInterface.php | 8 -- src/SalesforceAuthProviderPluginBase.php | 113 +++++++++++++----- src/SalesforceAuthProviderPluginManager.php | 7 ++ ...orceAuthProviderPluginManagerInterface.php | 3 +- 20 files changed, 353 insertions(+), 147 deletions(-) create mode 100644 src/Annotation/SalesforceAuthProvider.php create mode 100644 src/Plugin/SalesforceAuthProvider/Broken.php diff --git a/config/schema/salesforce.schema.yml b/config/schema/salesforce.schema.yml index a05925dc..6d0d17ec 100644 --- a/config/schema/salesforce.schema.yml +++ b/config/schema/salesforce.schema.yml @@ -69,7 +69,7 @@ salesforce.salesforce_auth.*: label: 'Label' translatable: true provider: - type: string + type: salesforce.auth_provider_settings.[id] label: 'Provider Plugin' provider_settings: type: salesforce.auth_provider_settings.[%parent.provider] diff --git a/modules/salesforce_jwt/src/Consumer/JWTCredentials.php b/modules/salesforce_jwt/src/Consumer/JWTCredentials.php index e5f516f6..978be30a 100644 --- a/modules/salesforce_jwt/src/Consumer/JWTCredentials.php +++ b/modules/salesforce_jwt/src/Consumer/JWTCredentials.php @@ -34,6 +34,19 @@ class JWTCredentials extends SalesforceCredentials { $this->keyId = $keyId; } + /** + * Constructor helper. + * + * @param array $configuration + * Plugin configuration. + * + * @return \Drupal\salesforce_jwt\Consumer\JWTCredentials + * Credentials, valid or not. + */ + public static function create(array $configuration) { + return new static($configuration['consumer_key'], $configuration['login_url'], $configuration['login_user'], $configuration['encrypt_key']); + } + /** * Login user getter. * @@ -54,4 +67,11 @@ class JWTCredentials extends SalesforceCredentials { return $this->keyId; } + /** + * {@inheritdoc} + */ + public function isValid() { + return !empty($this->loginUser) && !empty($this->consumerId) && !empty($this->keyId); + } + } diff --git a/modules/salesforce_jwt/src/Plugin/SalesforceAuthProvider/SalesforceJWTPlugin.php b/modules/salesforce_jwt/src/Plugin/SalesforceAuthProvider/SalesforceJWTPlugin.php index c2a6f6d2..51ad878c 100644 --- a/modules/salesforce_jwt/src/Plugin/SalesforceAuthProvider/SalesforceJWTPlugin.php +++ b/modules/salesforce_jwt/src/Plugin/SalesforceAuthProvider/SalesforceJWTPlugin.php @@ -5,7 +5,6 @@ namespace Drupal\salesforce_jwt\Plugin\SalesforceAuthProvider; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Url; use Drupal\key\KeyRepositoryInterface; -use Drupal\salesforce_jwt\Consumer\JWTCredentials; use Drupal\salesforce\SalesforceAuthProviderPluginBase; use OAuth\Common\Http\Uri\Uri; use Drupal\salesforce\Storage\SalesforceAuthTokenStorageInterface; @@ -19,7 +18,8 @@ use Firebase\JWT\JWT; * * @Plugin( * id = "jwt", - * label = @Translation("Salesforce JWT OAuth") + * label = @Translation("Salesforce JWT OAuth"), + * credentials_class = "\Drupal\salesforce_jwt\Consumer\JWTCredentials" * ) */ class SalesforceJWTPlugin extends SalesforceAuthProviderPluginBase { @@ -38,23 +38,15 @@ class SalesforceJWTPlugin extends SalesforceAuthProviderPluginBase { */ protected $keyRepository; - /** - * {@inheritdoc} - */ - const SERVICE_TYPE = 'jwt'; - - /** - * {@inheritdoc} - */ - const LABEL = 'JWT'; - /** * SalesforceAuthServiceBase constructor. * - * @param string $id - * The plugin / auth config id. - * @param \Drupal\salesforce_jwt\Consumer\JWTCredentials $credentials - * The credentials. + * @param array $configuration + * Configuration. + * @param string $plugin_id + * Plugin id. + * @param mixed $plugin_definition + * Plugin definition. * @param \OAuth\Common\Http\Client\ClientInterface $httpClient * Http client wrapper. * @param \Drupal\salesforce\Storage\SalesforceAuthTokenStorageInterface $storage @@ -65,10 +57,9 @@ class SalesforceJWTPlugin extends SalesforceAuthProviderPluginBase { * @throws \OAuth\OAuth2\Service\Exception\InvalidScopeException * On error. */ - public function __construct($id, JWTCredentials $credentials, ClientInterface $httpClient, SalesforceAuthTokenStorageInterface $storage, KeyRepositoryInterface $keyRepository) { - parent::__construct($credentials, $httpClient, $storage, [], new Uri($credentials->getLoginUrl())); - $this->id = $id; + public function __construct(array $configuration, $plugin_id, $plugin_definition, ClientInterface $httpClient, SalesforceAuthTokenStorageInterface $storage, KeyRepositoryInterface $keyRepository) { $this->keyRepository = $keyRepository; + parent::__construct($configuration, $plugin_id, $plugin_definition, $httpClient, $storage); } /** @@ -76,8 +67,7 @@ class SalesforceJWTPlugin extends SalesforceAuthProviderPluginBase { */ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { $configuration = array_merge(self::defaultConfiguration(), $configuration); - $cred = new JWTCredentials($configuration['consumer_key'], $configuration['login_url'], $configuration['login_user'], $configuration['encrypt_key']); - return new static($configuration['id'], $cred, $container->get('salesforce.http_client_wrapper'), $container->get('salesforce.auth_token_storage'), $container->get('key.repository')); + return new static($configuration, $plugin_id, $plugin_definition, $container->get('salesforce.http_client_wrapper'), $container->get('salesforce.auth_token_storage'), $container->get('key.repository')); } /** @@ -95,7 +85,7 @@ class SalesforceJWTPlugin extends SalesforceAuthProviderPluginBase { * {@inheritdoc} */ public function getLoginUrl() { - return $this->credentials->getLoginUrl(); + return $this->getCredentials()->getLoginUrl(); } /** @@ -111,7 +101,7 @@ class SalesforceJWTPlugin extends SalesforceAuthProviderPluginBase { '#type' => 'textfield', '#description' => t('Consumer key of the Salesforce remote application you want to grant access to'), '#required' => TRUE, - '#default_value' => $this->credentials->getConsumerKey(), + '#default_value' => $this->getCredentials()->getConsumerKey(), ]; $form['login_user'] = [ @@ -119,13 +109,13 @@ class SalesforceJWTPlugin extends SalesforceAuthProviderPluginBase { '#type' => 'textfield', '#description' => $this->t('User account to issue token to'), '#required' => TRUE, - '#default_value' => $this->credentials->getLoginUser(), + '#default_value' => $this->getCredentials()->getLoginUser(), ]; $form['login_url'] = [ '#title' => t('Login URL'), '#type' => 'textfield', - '#default_value' => $this->credentials->getLoginUrl(), + '#default_value' => $this->getCredentials()->getLoginUrl(), '#description' => t('Enter a login URL, either https://login.salesforce.com or https://test.salesforce.com.'), '#required' => TRUE, ]; @@ -137,7 +127,7 @@ class SalesforceJWTPlugin extends SalesforceAuthProviderPluginBase { '#type' => 'select', '#options' => $this->keyRepository->getKeyNamesAsOptions(['type' => 'authentication']), '#required' => TRUE, - '#default_value' => $this->credentials->getKeyId(), + '#default_value' => $this->getCredentials()->getKeyId(), ]; return $form; @@ -148,12 +138,10 @@ class SalesforceJWTPlugin extends SalesforceAuthProviderPluginBase { */ public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { parent::validateConfigurationForm($form, $form_state); - $this->setConfiguration($form_state->getValues()); + $this->setConfiguration($form_state->getValue('provider_settings')); try { - $values = $form_state->getValues(); - $this->id = $values['id']; - $settings = $values['provider_settings']; - $this->credentials = new JWTCredentials($settings['consumer_key'], $settings['login_url'], $settings['login_user'], $settings['encrypt_key']); + // Bootstrap here by setting ID to provide a key to token storage. + $this->id = $form_state->getValue('id'); $this->requestAccessToken($this->generateAssertion()); } catch (\Exception $e) { @@ -210,7 +198,7 @@ class SalesforceJWTPlugin extends SalesforceAuthProviderPluginBase { * JWT Assertion. */ protected function generateAssertion() { - $key = $this->keyRepository->getKey($this->credentials->getKeyId())->getKeyValue(); + $key = $this->keyRepository->getKey($this->getCredentials()->getKeyId())->getKeyValue(); $token = $this->generateAssertionClaim(); return JWT::encode($token, $key, 'RS256'); } @@ -222,12 +210,24 @@ class SalesforceJWTPlugin extends SalesforceAuthProviderPluginBase { * The claim array. */ protected function generateAssertionClaim() { + $cred = $this->getCredentials(); return [ - 'iss' => $this->credentials->getConsumerKey(), - 'sub' => $this->credentials->getLoginUser(), - 'aud' => $this->credentials->getLoginUrl(), + 'iss' => $cred->getConsumerKey(), + 'sub' => $cred->getLoginUser(), + 'aud' => $cred->getLoginUrl(), 'exp' => \Drupal::time()->getCurrentTime() + 60, ]; } + /** + * {@inheritdoc} + */ + public function getPluginDefinition() { + $definition = parent::getPluginDefinition(); + if ($this->configuration['encrypt_key'] && $key = $this->keyRepository->getKey($this->configuration['encrypt_key'])) { + $definition['config_dependencies']['config'][] = $key->getConfigDependencyName(); + } + return $definition; + } + } diff --git a/modules/salesforce_mapping_ui/salesforce_mapping_ui.module b/modules/salesforce_mapping_ui/salesforce_mapping_ui.module index 96393f82..30f252a1 100644 --- a/modules/salesforce_mapping_ui/salesforce_mapping_ui.module +++ b/modules/salesforce_mapping_ui/salesforce_mapping_ui.module @@ -29,7 +29,7 @@ function salesforce_mapping_ui_entity_type_alter(array &$entity_types) { $entity_type->setLinkTemplate('salesforce', "/$entity_type_id/{{$entity_type_id}}/salesforce"); } } - // Set our UI classes for SalesforceMappingEntity + // Set our UI classes for SalesforceMappingEntity. $entity_types['salesforce_mapping']->setHandlerClass('list_builder', SalesforceMappingList::class); $entity_types['salesforce_mapping']->setFormClass('add', SalesforceMappingAddForm::class); $entity_types['salesforce_mapping']->setFormClass('edit', SalesforceMappingEditForm::class); diff --git a/modules/salesforce_oauth/src/Consumer/SalesforceOAuthCredentials.php b/modules/salesforce_oauth/src/Consumer/SalesforceOAuthCredentials.php index e3a93f81..0649af9a 100644 --- a/modules/salesforce_oauth/src/Consumer/SalesforceOAuthCredentials.php +++ b/modules/salesforce_oauth/src/Consumer/SalesforceOAuthCredentials.php @@ -19,6 +19,19 @@ class SalesforceOAuthCredentials extends SalesforceCredentials { $this->loginUrl = $loginUrl; } + /** + * Constructor helper. + * + * @param array $configuration + * Plugin configuration. + * + * @return \Drupal\salesforce_oauth\Consumer\SalesforceOAuthCredentials + * Credentials, valid or not. + */ + public static function create(array $configuration) { + return new static($configuration['consumer_key'], $configuration['consumer_secret'], $configuration['login_url']); + } + /** * {@inheritdoc} */ @@ -39,4 +52,11 @@ class SalesforceOAuthCredentials extends SalesforceCredentials { ])->toString(); } + /** + * {@inheritdoc} + */ + public function isValid() { + return !empty($this->loginUrl) && !empty($this->consumerSecret) && !empty($this->consumerId); + } + } diff --git a/modules/salesforce_oauth/src/Plugin/SalesforceAuthProvider/SalesforceOAuthPlugin.php b/modules/salesforce_oauth/src/Plugin/SalesforceAuthProvider/SalesforceOAuthPlugin.php index 97cdc7b5..4411b3ca 100644 --- a/modules/salesforce_oauth/src/Plugin/SalesforceAuthProvider/SalesforceOAuthPlugin.php +++ b/modules/salesforce_oauth/src/Plugin/SalesforceAuthProvider/SalesforceOAuthPlugin.php @@ -3,72 +3,28 @@ namespace Drupal\salesforce_oauth\Plugin\SalesforceAuthProvider; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Routing\TrustedRedirectResponse; use Drupal\Core\Url; -use Drupal\salesforce\Consumer\SalesforceCredentials; -use Drupal\salesforce\SalesforceAuthProviderInterface; use Drupal\salesforce\SalesforceAuthProviderPluginBase; -use Drupal\salesforce\Storage\SalesforceAuthTokenStorageInterface; -use Drupal\salesforce_oauth\Consumer\SalesforceOAuthCredentials; -use OAuth\Common\Http\Client\ClientInterface; -use OAuth\Common\Http\Uri\Uri; -use Symfony\Component\DependencyInjection\ContainerInterface; /** * Salesforce OAuth user-agent flow auth provider plugin. * * @Plugin( * id = "oauth", - * label = @Translation("Salesforce OAuth User-Agent") + * label = @Translation("Salesforce OAuth User-Agent"), + * credentials_class = "\Drupal\salesforce_oauth\Consumer\SalesforceOAuthCredentials" * ) */ -class SalesforceOAuthPlugin extends SalesforceAuthProviderPluginBase implements SalesforceAuthProviderInterface { +class SalesforceOAuthPlugin extends SalesforceAuthProviderPluginBase { /** * Credentials. * - * @var \Drupal\salesforce\Consumer\SalesforceCredentials + * @var \Drupal\salesforce_oauth\Consumer\SalesforceOAuthCredentials */ protected $credentials; - /** - * {@inheritdoc} - */ - const SERVICE_TYPE = 'oauth'; - - /** - * {@inheritdoc} - */ - const LABEL = 'OAuth'; - - /** - * SalesforceOAuthPlugin constructor. - * - * @param string $id - * The plugin id. - * @param \Drupal\salesforce\Consumer\SalesforceCredentials $credentials - * The credentials. - * @param \OAuth\Common\Http\Client\ClientInterface $httpClient - * The oauth http client. - * @param \Drupal\salesforce\Storage\SalesforceAuthTokenStorageInterface $storage - * Auth token storage service. - * - * @throws \OAuth\OAuth2\Service\Exception\InvalidScopeException - * Comment. - */ - public function __construct($id, SalesforceCredentials $credentials, ClientInterface $httpClient, SalesforceAuthTokenStorageInterface $storage) { - parent::__construct($credentials, $httpClient, $storage, [], new Uri($credentials->getLoginUrl())); - $this->id = $id; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - $configuration = array_merge(self::defaultConfiguration(), $configuration); - $cred = new SalesforceOAuthCredentials($configuration['consumer_key'], $configuration['consumer_secret'], $configuration['login_url']); - return new static($configuration['id'], $cred, $container->get('salesforce.http_client_wrapper'), $container->get('salesforce.auth_token_storage')); - } - /** * {@inheritdoc} */ @@ -88,7 +44,7 @@ class SalesforceOAuthPlugin extends SalesforceAuthProviderPluginBase implements '#type' => 'textfield', '#description' => t('Consumer key of the Salesforce remote application you want to grant access to'), '#required' => TRUE, - '#default_value' => $this->credentials->getConsumerKey(), + '#default_value' => $this->getCredentials()->getConsumerKey(), ]; $form['consumer_secret'] = [ @@ -96,13 +52,13 @@ class SalesforceOAuthPlugin extends SalesforceAuthProviderPluginBase implements '#type' => 'textfield', '#description' => $this->t('Consumer secret of the Salesforce remote application.'), '#required' => TRUE, - '#default_value' => $this->credentials->getConsumerSecret(), + '#default_value' => $this->getCredentials()->getConsumerSecret(), ]; $form['login_url'] = [ '#title' => t('Login URL'), '#type' => 'textfield', - '#default_value' => $this->credentials->getLoginUrl(), + '#default_value' => $this->getCredentials()->getLoginUrl(), '#description' => t('Enter a login URL, either https://login.salesforce.com or https://test.salesforce.com.'), '#required' => TRUE, ]; @@ -114,7 +70,6 @@ class SalesforceOAuthPlugin extends SalesforceAuthProviderPluginBase implements */ public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { parent::submitConfigurationForm($form, $form_state); - $settings = $form_state->getValue('provider_settings'); // Write the config id to private temp store, so that we can use the same // callback URL for all OAuth applications in Salesforce. @@ -125,16 +80,16 @@ class SalesforceOAuthPlugin extends SalesforceAuthProviderPluginBase implements try { $path = $this->getAuthorizationEndpoint(); $query = [ - 'redirect_uri' => $this->credentials->getCallbackUrl(), + 'redirect_uri' => $this->getCredentials()->getCallbackUrl(), 'response_type' => 'code', - 'client_id' => $settings['consumer_key'], + 'client_id' => $this->getCredentials()->getConsumerKey(), ]; // 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. - $form_state->setRedirectUrl(Url::fromUri($path . '?' . http_build_query($query))); + $form_state->setResponse(new TrustedRedirectResponse(Url::fromUri($path . '?' . http_build_query($query))->toString())); } catch (\Exception $e) { $form_state->setError($form, $this->t("Error during authorization: %message", ['%message' => $e->getMessage()])); @@ -145,7 +100,7 @@ class SalesforceOAuthPlugin extends SalesforceAuthProviderPluginBase implements * {@inheritdoc} */ public function getConsumerSecret() { - return $this->credentials->getConsumerSecret(); + return $this->getCredentials()->getConsumerSecret(); } } diff --git a/modules/salesforce_pull/src/Plugin/QueueWorker/PullBase.php b/modules/salesforce_pull/src/Plugin/QueueWorker/PullBase.php index eb28daff..ec0dfe00 100644 --- a/modules/salesforce_pull/src/Plugin/QueueWorker/PullBase.php +++ b/modules/salesforce_pull/src/Plugin/QueueWorker/PullBase.php @@ -107,7 +107,7 @@ abstract class PullBase extends QueueWorkerBase implements ContainerFactoryPlugi * * @throws \Exception */ - public function processItem($item) { + public function processItem($item) { // @codingStandardsIgnoreLine $sf_object = $item->getSobject(); $mapping = $this->mappingStorage->load($item->getMappingId()); if (!$mapping) { diff --git a/modules/salesforce_push/src/PushQueue.php b/modules/salesforce_push/src/PushQueue.php index 146791ca..6a070588 100644 --- a/modules/salesforce_push/src/PushQueue.php +++ b/modules/salesforce_push/src/PushQueue.php @@ -229,7 +229,7 @@ class PushQueue extends DatabaseQueue implements PushQueueInterface { * * @TODO convert $data to a proper class and make sure that's what we get for this argument. */ - protected function doCreateItem($data) { + protected function doCreateItem($data) { // @codingStandardsIgnoreLine if (empty($data['name']) || empty($data['entity_id']) || empty($data['op'])) { diff --git a/salesforce.install b/salesforce.install index 6e232c30..1f8ca954 100644 --- a/salesforce.install +++ b/salesforce.install @@ -398,7 +398,7 @@ function salesforce_update_8402() { return; } $credentials = $authConfig->getPlugin()->getCredentials(); - if (!$credentials instanceof \Drupal\salesforce_oauth\Consumer\SalesforceOAuthCredentials) { + if (!$credentials instanceof \Drupal\salesforce_oauth\Consumer\SalesforceOAuthCredentials) { // @codingStandardsIgnoreLine // If we're not using OAuth, we are done. return; } diff --git a/src/Annotation/SalesforceAuthProvider.php b/src/Annotation/SalesforceAuthProvider.php new file mode 100644 index 00000000..cb8fe8b3 --- /dev/null +++ b/src/Annotation/SalesforceAuthProvider.php @@ -0,0 +1,33 @@ +<?php + +namespace Drupal\salesforce\Annotation; + +use Drupal\Component\Annotation\Plugin; + +/** + * SalesforceAuthProvider annotation definition. + */ +class SalesforceAuthProvider extends Plugin { + + /** + * The plugin ID of the auth provider. + * + * @var string + */ + public $id; + + /** + * The human-readable name of the key provider. + * + * @var \Drupal\Core\Annotation\Translation + */ + public $label; + + /** + * The credentials class specific to this provider. + * + * @var string + */ + public $credentials_class; // @codingStandardsIgnoreLine + +} diff --git a/src/Consumer/SalesforceCredentials.php b/src/Consumer/SalesforceCredentials.php index 533a9871..5973870e 100644 --- a/src/Consumer/SalesforceCredentials.php +++ b/src/Consumer/SalesforceCredentials.php @@ -5,9 +5,9 @@ namespace Drupal\salesforce\Consumer; use OAuth\Common\Consumer\Credentials; /** - * Class SalesforceCredentials. + * Stub class SalesforceCredentials. Used for broken / fallback plugin only. */ -abstract class SalesforceCredentials extends Credentials implements SalesforceCredentialsInterface { +class SalesforceCredentials extends Credentials implements SalesforceCredentialsInterface { /** * Login URL e.g. https://test.salesforce.com or https://login.salesforce.com. @@ -37,4 +37,19 @@ abstract class SalesforceCredentials extends Credentials implements SalesforceCr return $this->loginUrl; } + /** + * {@inheritdoc} + */ + public function isValid() { + // This class is a stub. + return FALSE; + } + + /** + * {@inheritdoc} + */ + public static function create(array $configuration) { + return new static($configuration['consumer_key'], $configuration['consumer_secret'], NULL); + } + } diff --git a/src/Consumer/SalesforceCredentialsInterface.php b/src/Consumer/SalesforceCredentialsInterface.php index 000e1d10..914d44b6 100644 --- a/src/Consumer/SalesforceCredentialsInterface.php +++ b/src/Consumer/SalesforceCredentialsInterface.php @@ -23,4 +23,22 @@ interface SalesforceCredentialsInterface { */ public function getLoginUrl(); + /** + * Sanity check for credentials validity. + * + * @return bool + * TRUE if credentials are set properly. Otherwise false. + */ + public function isValid(); + + /** + * Create helper. + * + * @param array $configuration + * Plugin configuration. + * + * @return static + */ + public static function create(array $configuration); + } diff --git a/src/Controller/SalesforceAuthListBuilder.php b/src/Controller/SalesforceAuthListBuilder.php index 4c189d7e..002d138f 100644 --- a/src/Controller/SalesforceAuthListBuilder.php +++ b/src/Controller/SalesforceAuthListBuilder.php @@ -18,8 +18,8 @@ class SalesforceAuthListBuilder extends ConfigEntityListBuilder { /** @var \Drupal\salesforce\SalesforceAuthProviderInterface $plugin */ $plugin = $entity->getPlugin(); $row['default'] = $entity->authManager() - ->getProvider() && $entity->authManager() - ->getProvider() + ->getConfig() && $entity->authManager() + ->getConfig() ->id() == $entity->id() ? $this->t('Default') : ''; $row['label'] = $entity->label(); diff --git a/src/Entity/SalesforceAuthConfig.php b/src/Entity/SalesforceAuthConfig.php index 2d96212c..06e589e5 100644 --- a/src/Entity/SalesforceAuthConfig.php +++ b/src/Entity/SalesforceAuthConfig.php @@ -3,7 +3,8 @@ namespace Drupal\salesforce\Entity; use Drupal\Core\Config\Entity\ConfigEntityBase; -use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Entity\EntityWithPluginCollectionInterface; +use Drupal\Core\Plugin\DefaultSingleLazyPluginCollection; /** * Defines a Salesforce Auth entity. @@ -33,7 +34,7 @@ use Drupal\Core\Entity\EntityInterface; * admin_permission = "authorize salesforce", * ) */ -class SalesforceAuthConfig extends ConfigEntityBase implements EntityInterface { +class SalesforceAuthConfig extends ConfigEntityBase implements EntityWithPluginCollectionInterface { /** * Auth id. e.g. "oauth_full_sandbox". @@ -70,6 +71,13 @@ class SalesforceAuthConfig extends ConfigEntityBase implements EntityInterface { */ protected $manager; + /** + * The plugin provider. + * + * @var \Drupal\salesforce\SalesforceAuthProviderInterface + */ + protected $plugin; + /** * Id getter. */ @@ -93,9 +101,20 @@ class SalesforceAuthConfig extends ConfigEntityBase implements EntityInterface { * @throws \Drupal\Component\Plugin\Exception\PluginException */ public function getPlugin() { - $settings = $this->provider_settings ?: []; - $settings += ['id' => $this->id()]; - return $this->provider ? $this->authManager()->createInstance($this->provider, $settings) : NULL; + if (!$this->plugin) { + $this->plugin = $this->provider ? $this->authManager()->createInstance($this->provider, $this->getProviderSettings()) : NULL; + } + return $this->plugin; + } + + /** + * Wrapper for provider settings to inject instance id, from auth config. + * + * @return array + * Provider settings. + */ + public function getProviderSettings() { + return $this->provider_settings + ['id' => $this->id()]; } /** @@ -141,6 +160,10 @@ class SalesforceAuthConfig extends ConfigEntityBase implements EntityInterface { */ public function getPluginsAsOptions() { foreach ($this->authManager()->getDefinitions() as $id => $definition) { + if ($id == 'broken') { + // Do not add the fallback provider. + continue; + } $options[$id] = ($definition['label']); } if (!empty($options)) { @@ -149,4 +172,13 @@ class SalesforceAuthConfig extends ConfigEntityBase implements EntityInterface { return []; } + /** + * {@inheritdoc} + */ + public function getPluginCollections() { + return [ + 'auth_provider' => new DefaultSingleLazyPluginCollection($this->authManager(), $this->provider, $this->getProviderSettings()), + ]; + } + } diff --git a/src/Form/SalesforceAuthForm.php b/src/Form/SalesforceAuthForm.php index 648669c5..cddca11b 100644 --- a/src/Form/SalesforceAuthForm.php +++ b/src/Form/SalesforceAuthForm.php @@ -88,7 +88,7 @@ class SalesforceAuthForm extends EntityForm { $form['save_default'] = [ '#type' => 'checkbox', '#title' => 'Save and set default', - '#default_value' => $this->entity->isNew() || ($this->entity->authManager()->getProvider() && $this->entity->authManager()->getProvider()->id() == $this->entity->id()) + '#default_value' => $this->entity->isNew() || ($this->entity->authManager()->getProvider() && $this->entity->authManager()->getProvider()->id() == $this->entity->id()), ]; return parent::form($form, $form_state); } diff --git a/src/Plugin/SalesforceAuthProvider/Broken.php b/src/Plugin/SalesforceAuthProvider/Broken.php new file mode 100644 index 00000000..9782fa75 --- /dev/null +++ b/src/Plugin/SalesforceAuthProvider/Broken.php @@ -0,0 +1,62 @@ +<?php + +namespace Drupal\salesforce\Plugin\SalesforceAuthProvider; + +use Drupal\Core\Form\FormStateInterface; +use Drupal\salesforce\Consumer\SalesforceCredentials; +use Drupal\salesforce\SalesforceAuthProviderPluginBase; +use OAuth\Common\Token\TokenInterface; +use OAuth\OAuth2\Service\Exception\MissingRefreshTokenException; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Fallback for broken / missing plugin. + * + * @Plugin( + * id = "broken", + * label = @Translation("Broken or missing provider"), + * credentials_class = "Drupal\salesforce\Consumer\SalesforceCredentials" + * ) + */ +class Broken extends SalesforceAuthProviderPluginBase { + + /** + * Broken constructor. + * + * @param array $configuration + * Configuration. + * @param string $plugin_id + * Plugin id. + * @param mixed $plugin_definition + * Plugin definition. + */ + public function __construct(array $configuration, $plugin_id, $plugin_definition) { + $this->configuration = $configuration; + $this->pluginDefinition = $plugin_definition; + $this->id = $plugin_id; + $this->credentials = new SalesforceCredentials('', '', ''); + } + + /** + * {@inheritdoc} + */ + public function refreshAccessToken(TokenInterface $token) { + throw new MissingRefreshTokenException(); + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static($configuration, $plugin_id, $plugin_definition); + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $this->messenger()->addError($this->t('Auth provider for %id is missing or broken.', ['%id' => $this->id])); + return $form; + } + +} diff --git a/src/SalesforceAuthProviderInterface.php b/src/SalesforceAuthProviderInterface.php index f149b5d1..776e3cee 100644 --- a/src/SalesforceAuthProviderInterface.php +++ b/src/SalesforceAuthProviderInterface.php @@ -35,14 +35,6 @@ interface SalesforceAuthProviderInterface extends ServiceInterface, PluginFormIn */ public function label(); - /** - * Auth type id for this service, e.g. oauth, jwt, etc. - * - * @return string - * Provider type for this auth provider. - */ - public function type(); - /** * Perform a refresh of the given token. * diff --git a/src/SalesforceAuthProviderPluginBase.php b/src/SalesforceAuthProviderPluginBase.php index 9a7b0f7e..b5aaac02 100644 --- a/src/SalesforceAuthProviderPluginBase.php +++ b/src/SalesforceAuthProviderPluginBase.php @@ -6,9 +6,12 @@ use Drupal\Core\DependencyInjection\DependencySerializationTrait; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Messenger\MessengerTrait; use Drupal\Core\StringTranslation\StringTranslationTrait; +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\OAuth2\Service\Salesforce; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Shared methods for auth providers. @@ -41,12 +44,60 @@ abstract class SalesforceAuthProviderPluginBase extends Salesforce implements Sa protected $storage; /** - * Machine name identifier. + * Provider id, e.g. jwt, oauth. + * + * @var string + */ + protected $pluginId; + + /** + * Plugin definition. + * + * @var array + */ + protected $pluginDefinition; + + /** + * Instance id, e.g. "sandbox1" or "production". * * @var string */ protected $id; + /** + * SalesforceOAuthPlugin constructor. + * + * @param array $configuration + * Plugin configuration. + * @param string $plugin_id + * Plugin id. + * @param mixed $plugin_definition + * Plugin definition. + * @param \OAuth\Common\Http\Client\ClientInterface $httpClient + * The oauth http client. + * @param \Drupal\salesforce\Storage\SalesforceAuthTokenStorageInterface $storage + * Auth token storage service. + * + * @throws \OAuth\OAuth2\Service\Exception\InvalidScopeException + * Comment. + */ + public function __construct(array $configuration, $plugin_id, $plugin_definition, ClientInterface $httpClient, SalesforceAuthTokenStorageInterface $storage) { + $this->id = $configuration['id']; + $this->configuration = $configuration; + $this->pluginDefinition = $plugin_definition; + $this->pluginId = $plugin_id; + $this->credentials = $this->getCredentials(); + parent::__construct($this->getCredentials(), $httpClient, $storage, [], new Uri($this->getCredentials()->getLoginUrl())); + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + $configuration = array_merge(self::defaultConfiguration(), $configuration); + return new static($configuration, $plugin_id, $plugin_definition, $container->get('salesforce.http_client_wrapper'), $container->get('salesforce.auth_token_storage')); + } + /** * {@inheritdoc} */ @@ -57,18 +108,32 @@ abstract class SalesforceAuthProviderPluginBase extends Salesforce implements Sa ]; } + /** + * {@inheritdoc} + */ + public function label() { + return $this->getPluginDefinition()['label']; + } + + /** + * {@inheritdoc} + */ + public function id() { + return $this->id; + } + /** * {@inheritdoc} */ public function getPluginId() { - return $this->getConfiguration('id'); + return $this->pluginId; } /** * {@inheritdoc} */ public function getPluginDefinition() { - return $this->getConfiguration(); + return $this->pluginDefinition; } /** @@ -81,13 +146,6 @@ abstract class SalesforceAuthProviderPluginBase extends Salesforce implements Sa return $this->configuration; } - /** - * {@inheritdoc} - */ - public function getCredentials() { - return $this->credentials; - } - /** * {@inheritdoc} */ @@ -106,14 +164,17 @@ abstract class SalesforceAuthProviderPluginBase extends Salesforce implements Sa * {@inheritdoc} */ public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { - $this->setConfiguration($form_state->getValues()); + $this->setConfiguration($form_state->getValue('provider_settings')); } /** * {@inheritdoc} */ public function save(array $form, FormStateInterface $form_state) { - // Initialize identity. + // Initialize identity if token is available. + if (!$this->hasAccessToken()) { + return TRUE; + } $token = $this->getAccessToken(); $headers = [ 'Authorization' => 'OAuth ' . $token->getAccessToken(), @@ -129,43 +190,33 @@ abstract class SalesforceAuthProviderPluginBase extends Salesforce implements Sa /** * {@inheritdoc} */ - public function id() { - return $this->id; - } - - /** - * {@inheritdoc} - */ - public function type() { - return static::SERVICE_TYPE; - } - - /** - * {@inheritdoc} - */ - public function label() { - return static::LABEL; + public function getCredentials() { + if (!$this->credentials || !$this->credentials->isValid()) { + $pluginDefinition = $this->getPluginDefinition(); + $this->credentials = $pluginDefinition['credentials_class']::create($this->configuration); + } + return $this->credentials; } /** * {@inheritdoc} */ public function getAuthorizationEndpoint() { - return new Uri($this->credentials->getLoginUrl() . static::AUTH_ENDPOINT_PATH); + return new Uri($this->getCredentials()->getLoginUrl() . static::AUTH_ENDPOINT_PATH); } /** * {@inheritdoc} */ public function getAccessTokenEndpoint() { - return new Uri($this->credentials->getLoginUrl() . static::AUTH_TOKEN_PATH); + return new Uri($this->getCredentials()->getLoginUrl() . static::AUTH_TOKEN_PATH); } /** * {@inheritdoc} */ public function hasAccessToken() { - return $this->storage->hasAccessToken($this->id()); + return $this->storage ? $this->storage->hasAccessToken($this->id()) : FALSE; } /** diff --git a/src/SalesforceAuthProviderPluginManager.php b/src/SalesforceAuthProviderPluginManager.php index a139e474..1d409c2e 100644 --- a/src/SalesforceAuthProviderPluginManager.php +++ b/src/SalesforceAuthProviderPluginManager.php @@ -162,4 +162,11 @@ class SalesforceAuthProviderPluginManager extends DefaultPluginManager implement return $this->config; } + /** + * {@inheritdoc} + */ + public function getFallbackPluginId($plugin_id, array $configuration = []) { + return 'broken'; + } + } diff --git a/src/SalesforceAuthProviderPluginManagerInterface.php b/src/SalesforceAuthProviderPluginManagerInterface.php index 25b11425..695975a4 100644 --- a/src/SalesforceAuthProviderPluginManagerInterface.php +++ b/src/SalesforceAuthProviderPluginManagerInterface.php @@ -3,13 +3,14 @@ namespace Drupal\salesforce; use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface; +use Drupal\Component\Plugin\FallbackPluginManagerInterface; use Drupal\Component\Plugin\PluginManagerInterface; use Drupal\Core\Cache\CacheableDependencyInterface; /** * Auth provider plugin manager interface. */ -interface SalesforceAuthProviderPluginManagerInterface extends PluginManagerInterface, CachedDiscoveryInterface, CacheableDependencyInterface { +interface SalesforceAuthProviderPluginManagerInterface extends PluginManagerInterface, CachedDiscoveryInterface, CacheableDependencyInterface, FallbackPluginManagerInterface { /** * All Salesforce auth providers. -- GitLab