Skip to content
Snippets Groups Projects
Commit e6669089 authored by Shawn Duncan's avatar Shawn Duncan
Browse files

Merge branch '3351251-provide-base-classes' into '4.0.x'

Issue #3351251: Provide base classes or traits for the state and tempstore access token methods

See merge request !15
parents 7d6e4cdc 5126bb01
No related branches found
No related tags found
No related merge requests found
Pipeline #15036 passed
Showing with 139 additions and 228 deletions
......@@ -7,11 +7,9 @@ namespace Drupal\oauth2_client_example_plugins\Plugin\Oauth2Client;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Access\AccessResultInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\TempStore\PrivateTempStore;
use Drupal\oauth2_client\Plugin\Oauth2Client\Oauth2ClientPluginAccessInterface;
use Drupal\oauth2_client\Plugin\Oauth2Client\Oauth2ClientPluginBase;
use League\OAuth2\Client\Token\AccessTokenInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\oauth2_client\Plugin\Oauth2Client\TempStoreTokenStorage;
/**
* Auth code with access example.
......@@ -27,56 +25,18 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
*/
class AuthCodeAccessExample extends Oauth2ClientPluginBase implements Oauth2ClientPluginAccessInterface {
/**
* Access Token storage implementation.
*/
private PrivateTempStore $tempStore;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, $configuration, $plugin_id, $plugin_definition): AuthCodeAccessExample {
$instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
$instance->tempStore = $container->get('tempstore.private')->get('authcode_private_temp_store_example');
return $instance;
}
/**
* {@inheritdoc}
*/
public function codeRouteAccess(AccountInterface $account): AccessResultInterface {
return AccessResult::allowedIfHasPermissions($account, ['access content']);
}
/*
* This example assumes that a user is authenticating against a third-party
* service to retrieve a token that Drupal can use to access resources on
* that user's behalf.
*
*/
/**
* {@inheritdoc}
*/
public function storeAccessToken(AccessTokenInterface $accessToken): void {
$key = 'oauth2_client_access_token-' . $this->getId();
$this->tempStore->set($key, $accessToken);
}
/**
* {@inheritdoc}
*/
public function retrieveAccessToken(): ?AccessTokenInterface {
$key = 'oauth2_client_access_token-' . $this->getId();
return $this->tempStore->get($key);
}
use TempStoreTokenStorage;
/**
* {@inheritdoc}
*/
public function clearAccessToken(): void {
$key = 'oauth2_client_access_token-' . $this->getId();
$this->tempStore->delete($key);
public function codeRouteAccess(AccountInterface $account): AccessResultInterface {
return AccessResult::allowedIfHasPermissions($account, ['access content']);
}
}
......@@ -5,7 +5,7 @@ declare(strict_types = 1);
namespace Drupal\oauth2_client_example_plugins\Plugin\Oauth2Client;
use Drupal\oauth2_client\Plugin\Oauth2Client\Oauth2ClientPluginBase;
use League\OAuth2\Client\Token\AccessTokenInterface;
use Drupal\oauth2_client\Plugin\Oauth2Client\StateTokenStorage;
/**
* Auth code example.
......@@ -29,30 +29,6 @@ class AuthCodeExample extends Oauth2ClientPluginBase {
* external resource for ALL users of this plugin.
*/
/**
* {@inheritdoc}
*/
public function storeAccessToken(AccessTokenInterface $accessToken): void {
$this->state->set('oauth2_client_access_token-' . $this->getId(), $accessToken);
if ($this->displaySuccessMessage()) {
$this->messenger->addStatus(
$this->t('OAuth token stored.')
);
}
}
/**
* {@inheritdoc}
*/
public function retrieveAccessToken(): ?AccessTokenInterface {
return $this->state->get('oauth2_client_access_token-' . $this->getId());
}
/**
* {@inheritdoc}
*/
public function clearAccessToken(): void {
$this->state->delete('oauth2_client_access_token-' . $this->getId());
}
use StateTokenStorage;
}
......@@ -4,12 +4,10 @@ declare(strict_types = 1);
namespace Drupal\oauth2_client_example_plugins\Plugin\Oauth2Client;
use Drupal\Core\TempStore\PrivateTempStore;
use Drupal\Core\Url;
use Drupal\oauth2_client\Plugin\Oauth2Client\Oauth2ClientPluginBase;
use Drupal\oauth2_client\Plugin\Oauth2Client\Oauth2ClientPluginRedirectInterface;
use League\OAuth2\Client\Token\AccessTokenInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\oauth2_client\Plugin\Oauth2Client\TempStoreTokenStorage;
use Symfony\Component\HttpFoundation\RedirectResponse;
/**
......@@ -26,59 +24,20 @@ use Symfony\Component\HttpFoundation\RedirectResponse;
*/
class AuthCodeRedirectExample extends Oauth2ClientPluginBase implements Oauth2ClientPluginRedirectInterface {
/**
* Access Token storage implementation.
*/
private PrivateTempStore $tempStore;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
$instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
$instance->tempStore = $container->get('tempstore.private')
->get('authcode_private_temp_store_example');
return $instance;
}
/**
* {@inheritdoc}
*/
public function getPostCaptureRedirect(): RedirectResponse {
// After capturing the token, go to the site homepage.
$url = Url::fromRoute('<front>');
return new RedirectResponse($url->toString(TRUE)->getGeneratedUrl());
}
/*
* This example assumes that a user is authenticating against a third-party
* service to retrieve a token that Drupal can use to access resources on
* that user's behalf.
*
*/
/**
* {@inheritdoc}
*/
public function storeAccessToken(AccessTokenInterface $accessToken): void {
$key = 'oauth2_client_access_token-' . $this->getId();
$this->tempStore->set($key, $accessToken);
}
/**
* {@inheritdoc}
*/
public function retrieveAccessToken(): ?AccessTokenInterface {
$key = 'oauth2_client_access_token-' . $this->getId();
return $this->tempStore->get($key);
}
use TempStoreTokenStorage;
/**
* {@inheritdoc}
*/
public function clearAccessToken(): void {
$key = 'oauth2_client_access_token-' . $this->getId();
$this->tempStore->delete($key);
public function getPostCaptureRedirect(): RedirectResponse {
// After capturing the token, go to the site homepage.
$url = Url::fromRoute('<front>');
return new RedirectResponse($url->toString(TRUE)->getGeneratedUrl());
}
}
......@@ -5,7 +5,7 @@ declare(strict_types = 1);
namespace Drupal\oauth2_client_example_plugins\Plugin\Oauth2Client;
use Drupal\oauth2_client\Plugin\Oauth2Client\Oauth2ClientPluginBase;
use League\OAuth2\Client\Token\AccessTokenInterface;
use Drupal\oauth2_client\Plugin\Oauth2Client\StateTokenStorage;
/**
* Resource Owner example plugin.
......@@ -28,25 +28,6 @@ class ResourceOwnerExample extends Oauth2ClientPluginBase {
* external resource for ALL users of this plugin.
*/
/**
* {@inheritdoc}
*/
public function storeAccessToken(AccessTokenInterface $accessToken): void {
$this->state->set('oauth2_client_access_token-' . $this->getId(), $accessToken);
}
/**
* {@inheritdoc}
*/
public function retrieveAccessToken(): ?AccessTokenInterface {
return $this->state->get('oauth2_client_access_token-' . $this->getId());
}
/**
* {@inheritdoc}
*/
public function clearAccessToken(): void {
$this->state->delete('oauth2_client_access_token-' . $this->getId());
}
use StateTokenStorage;
}
<?php
namespace Drupal\oauth2_client\Plugin\Oauth2Client;
use League\OAuth2\Client\Token\AccessTokenInterface;
/**
* Implements token storage using State API.
*
* This trait implements storage appropriate for a Drupal site is using a
* shared resource from a third-party service that provides a service to all
* users of the site. Storing a single AccessToken in state for the plugin
* shares access to the external resource for ALL users of this plugin.
*/
trait StateTokenStorage {
/**
* Stores access tokens obtained by the client.
*
* @param \League\OAuth2\Client\Token\AccessTokenInterface $accessToken
* The token to store.
*/
public function storeAccessToken(AccessTokenInterface $accessToken): void {
$this->state->set('oauth2_client_access_token-' . $this->getId(), $accessToken);
if ($this->displaySuccessMessage()) {
$this->messenger->addStatus(
$this->t('OAuth token stored.')
);
}
}
/**
* Retrieve the access token from storage.
*
* @return \League\OAuth2\Client\Token\AccessTokenInterface|null
* The stored token, or NULL if no value exists.
*/
public function retrieveAccessToken(): ?AccessTokenInterface {
return $this->state->get('oauth2_client_access_token-' . $this->getId());
}
/**
* Clears the access token from storage.
*/
public function clearAccessToken(): void {
$this->state->delete('oauth2_client_access_token-' . $this->getId());
}
}
<?php
namespace Drupal\oauth2_client\Plugin\Oauth2Client;
use Drupal\Core\TempStore\PrivateTempStore;
use League\OAuth2\Client\Token\AccessTokenInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Implements token storage using PrivateTempStore.
*
* This trait implements storage appropriate for a Drupal site in which
* a user is authenticating against a third-party service to retrieve
* a token that Drupal can use to access resources on that user's behalf.
* Storing an AccessToken in PrivateTempStore for the plugin is
* ensured to be only for a particular user and users can never share data.
*/
trait TempStoreTokenStorage {
/**
* Per-user storage service.
*/
private PrivateTempStore $tempStore;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, $configuration, $plugin_id, $plugin_definition): Oauth2ClientPluginInterface {
$instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
$instance->tempStore = $container->get('tempstore.private')
->get('authcode_private_temp_store_example');
return $instance;
}
/**
* {@inheritdoc}
*/
public function storeAccessToken(AccessTokenInterface $accessToken): void {
$key = 'oauth2_client_access_token-' . $this->getId();
$this->tempStore->set($key, $accessToken);
}
/**
* {@inheritdoc}
*/
public function retrieveAccessToken(): ?AccessTokenInterface {
$key = 'oauth2_client_access_token-' . $this->getId();
return $this->tempStore->get($key);
}
/**
* {@inheritdoc}
*/
public function clearAccessToken(): void {
$key = 'oauth2_client_access_token-' . $this->getId();
$this->tempStore->delete($key);
}
}
......@@ -7,12 +7,10 @@ namespace Drupal\oauth2_client_test_plugins\Plugin\Oauth2Client;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Access\AccessResultInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\TempStore\PrivateTempStore;
use Drupal\oauth2_client\Plugin\Oauth2Client\Oauth2ClientPluginAccessInterface;
use Drupal\oauth2_client\Plugin\Oauth2Client\Oauth2ClientPluginBase;
use Drupal\oauth2_client\Plugin\Oauth2Client\StateTokenStorage;
use League\OAuth2\Client\Provider\GenericProvider;
use League\OAuth2\Client\Token\AccessTokenInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Auth code with access example.
......@@ -28,20 +26,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
*/
class AuthCodeTest extends Oauth2ClientPluginBase implements Oauth2ClientPluginAccessInterface {
use MockClientTrait;
/**
* Access Token storage implementation.
*/
private PrivateTempStore $tempStore;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, $configuration, $plugin_id, $plugin_definition): self {
$instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
$instance->tempStore = $container->get('tempstore.private')->get('authcode_private_temp_store_example');
return $instance;
}
use StateTokenStorage;
/**
* {@inheritdoc}
......@@ -50,37 +35,6 @@ class AuthCodeTest extends Oauth2ClientPluginBase implements Oauth2ClientPluginA
return AccessResult::allowedIfHasPermissions($account, ['access content']);
}
/*
* This example assumes that a user is authenticating against a third-party
* service to retrieve a token that Drupal can use to access resources on
* that user's behalf.
*
*/
/**
* {@inheritdoc}
*/
public function storeAccessToken(AccessTokenInterface $accessToken): void {
$key = 'oauth2_client_access_token-' . $this->getId();
$this->tempStore->set($key, $accessToken);
}
/**
* {@inheritdoc}
*/
public function retrieveAccessToken(): ?AccessTokenInterface {
$key = 'oauth2_client_access_token-' . $this->getId();
return $this->tempStore->get($key);
}
/**
* {@inheritdoc}
*/
public function clearAccessToken(): void {
$key = 'oauth2_client_access_token-' . $this->getId();
$this->tempStore->delete($key);
}
/**
* {@inheritdoc}
*/
......
......
......@@ -4,11 +4,9 @@ declare(strict_types = 1);
namespace Drupal\oauth2_client_test_plugins\Plugin\Oauth2Client;
use Drupal\Core\TempStore\PrivateTempStore;
use Drupal\oauth2_client\Plugin\Oauth2Client\Oauth2ClientPluginBase;
use Drupal\oauth2_client\Plugin\Oauth2Client\TempStoreTokenStorage;
use League\OAuth2\Client\Provider\GenericProvider;
use League\OAuth2\Client\Token\AccessTokenInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Auth code with access example.
......@@ -25,6 +23,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
*/
class ClientCredTest extends Oauth2ClientPluginBase {
use MockClientTrait;
use TempStoreTokenStorage;
/**
* Array to hold response history.
......@@ -33,44 +32,6 @@ class ClientCredTest extends Oauth2ClientPluginBase {
*/
protected array $responses = [];
/**
* Access Token storage implementation.
*/
private PrivateTempStore $tempStore;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, $configuration, $plugin_id, $plugin_definition): self {
$instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
$instance->tempStore = $container->get('tempstore.private')->get('authcode_private_temp_store_example');
return $instance;
}
/**
* {@inheritdoc}
*/
public function storeAccessToken(AccessTokenInterface $accessToken): void {
$key = 'oauth2_client_access_token-' . $this->getId();
$this->tempStore->set($key, $accessToken);
}
/**
* {@inheritdoc}
*/
public function retrieveAccessToken(): ?AccessTokenInterface {
$key = 'oauth2_client_access_token-' . $this->getId();
return $this->tempStore->get($key);
}
/**
* {@inheritdoc}
*/
public function clearAccessToken(): void {
$key = 'oauth2_client_access_token-' . $this->getId();
$this->tempStore->delete($key);
}
/**
* {@inheritdoc}
*/
......
......
......@@ -96,6 +96,9 @@ class Oauth2ClientEntityKernelTest extends Oauth2ClientKernelTestBase {
/**
* @covers \Drupal\oauth2_client\Plugin\Oauth2GrantType\AuthorizationCode::requestAccessToken
* @covers \Drupal\oauth2_client\Plugin\Oauth2Client\StateTokenStorage::storeAccessToken
* @covers \Drupal\oauth2_client\Plugin\Oauth2Client\StateTokenStorage::retrieveAccessToken
* @covers \Drupal\oauth2_client\Plugin\Oauth2Client\StateTokenStorage::clearAccessToken
*/
public function testAuthCodeRequestToken(): void {
$app = $this->getApp('authcode_access_test');
......@@ -106,6 +109,9 @@ class Oauth2ClientEntityKernelTest extends Oauth2ClientKernelTestBase {
$this->assertTrue($authCodePlugin->requestAccessToken($plugin, 'test-code'));
$token = $plugin->retrieveAccessToken();
$this->assertInstanceOf(AccessTokenInterface::class, $token);
$plugin->clearAccessToken();
$token = $plugin->retrieveAccessToken();
$this->assertNull($token);
}
/**
......@@ -123,6 +129,9 @@ class Oauth2ClientEntityKernelTest extends Oauth2ClientKernelTestBase {
/**
* @covers \Drupal\oauth2_client\Plugin\Oauth2GrantType\ClientCredentials::getAccessToken
* @covers \Drupal\oauth2_client\Plugin\Oauth2Client\TempStoreTokenStorage::storeAccessToken
* @covers \Drupal\oauth2_client\Plugin\Oauth2Client\TempStoreTokenStorage::retrieveAccessToken
* @covers \Drupal\oauth2_client\Plugin\Oauth2Client\TempStoreTokenStorage::clearAccessToken
*/
public function testClientCredGetToken(): void {
$app = $this->getApp('client_cred_test');
......@@ -132,6 +141,9 @@ class Oauth2ClientEntityKernelTest extends Oauth2ClientKernelTestBase {
$clientCodePlugin = $this->grantPluginManager->createInstance($plugin->getGrantType());
$token = $clientCodePlugin->getAccessToken($plugin);
$this->assertInstanceOf(AccessTokenInterface::class, $token);
$plugin->clearAccessToken();
$token = $plugin->retrieveAccessToken();
$this->assertNull($token);
$history = $plugin->getHistory();
$roundTrip = reset($history);
$request = $roundTrip['request'] ?? NULL;
......
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment