clean install

This commit is contained in:
2024-12-10 15:08:16 +01:00
commit e14eb2d8fd
31193 changed files with 3555714 additions and 0 deletions

View File

@@ -0,0 +1,73 @@
<?php
namespace Drupal\simple_oauth\Repositories;
use Drupal\simple_oauth\Entities\AccessTokenEntity;
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
/**
* The access token repository.
*/
class AccessTokenRepository implements AccessTokenRepositoryInterface {
use RevocableTokenRepositoryTrait;
/**
* The bundle ID.
*
* @var string
*/
protected static string $bundleId = 'access_token';
/**
* The OAuth2 entity class name.
*
* @var string
*/
protected static string $entityClass = 'Drupal\simple_oauth\Entities\AccessTokenEntity';
/**
* The OAuth2 entity interface name.
*
* @var string
*/
protected static string $entityInterface = 'League\OAuth2\Server\Entities\AccessTokenEntityInterface';
/**
* {@inheritdoc}
*/
public function persistNewAccessToken(AccessTokenEntityInterface $access_token_entity) {
$this->persistNew($access_token_entity);
}
/**
* {@inheritdoc}
*/
public function revokeAccessToken($token_id) {
$this->revoke($token_id);
}
/**
* {@inheritdoc}
*/
public function isAccessTokenRevoked($token_id) {
return $this->isRevoked($token_id);
}
/**
* {@inheritdoc}
*/
public function getNewToken(ClientEntityInterface $client_entity, array $scopes, $user_identifier = NULL) {
$access_token = new AccessTokenEntity();
$access_token->setClient($client_entity);
foreach ($scopes as $scope) {
$access_token->addScope($scope);
}
$access_token->setUserIdentifier($user_identifier);
return $access_token;
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace Drupal\simple_oauth\Repositories;
use League\OAuth2\Server\Entities\AuthCodeEntityInterface;
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
/**
* The repository for the Auth Code grant.
*/
class AuthCodeRepository implements AuthCodeRepositoryInterface {
use RevocableTokenRepositoryTrait;
/**
* The bundle ID.
*
* @var string
*/
protected static $bundleId = 'auth_code';
/**
* The OAuth2 entity class name.
*
* @var string
*/
protected static $entityClass = 'Drupal\simple_oauth\Entities\AuthCodeEntity';
/**
* The OAuth2 entity interface name.
*
* @var string
*/
protected static $entityInterface = 'League\OAuth2\Server\Entities\AuthCodeEntityInterface';
/**
* {@inheritdoc}
*/
public function getNewAuthCode() {
return $this->getNew();
}
/**
* {@inheritdoc}
*/
public function persistNewAuthCode(AuthCodeEntityInterface $auth_code_entity) {
$this->persistNew($auth_code_entity);
}
/**
* {@inheritdoc}
*/
public function revokeAuthCode($code_id) {
$this->revoke($code_id);
}
/**
* {@inheritdoc}
*/
public function isAuthCodeRevoked($code_id) {
return $this->isRevoked($code_id);
}
}

View File

@@ -0,0 +1,98 @@
<?php
namespace Drupal\simple_oauth\Repositories;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Password\PasswordInterface;
use Drupal\simple_oauth\Entities\ClientEntity;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
/**
* The client repository.
*/
class ClientRepository implements ClientRepositoryInterface {
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected EntityTypeManagerInterface $entityTypeManager;
/**
* The password hashing service.
*
* @var \Drupal\Core\Password\PasswordInterface
*/
protected PasswordInterface $passwordChecker;
/**
* Constructs a ClientRepository object.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, PasswordInterface $password_checker) {
$this->entityTypeManager = $entity_type_manager;
$this->passwordChecker = $password_checker;
}
/**
* {@inheritdoc}
*/
public function getClientEntity($clientIdentifier) {
$client_drupal_entities = $this->entityTypeManager
->getStorage('consumer')
->loadByProperties(['client_id' => $clientIdentifier]);
// Check if the client is registered.
if (empty($client_drupal_entities)) {
return NULL;
}
$client_drupal_entity = reset($client_drupal_entities);
return new ClientEntity($client_drupal_entity);
}
/**
* {@inheritdoc}
*/
public function validateClient($clientIdentifier, $clientSecret, $grantType) {
$client_entity = $this->getClientEntity($clientIdentifier);
if (!$client_entity) {
return FALSE;
}
$client_drupal_entity = $client_entity->getDrupalEntity();
// For the client credentials grant type a default user is required.
if ($grantType === 'client_credentials' && !$client_drupal_entity->get('user_id')->entity) {
throw OAuthServerException::serverError('Invalid default user for client.');
}
// Determine if a client secret is configured. The client may omit the
// parameter if the configured secret is NULL or if the value of the
// secret is the hash of an empty string.
// @see https://datatracker.ietf.org/doc/html/rfc6749#section-2.3.1
$secret_field = $client_drupal_entity->get('secret');
$secret_field_is_empty = $secret_field->isEmpty() || $this->passwordChecker->check('', $secret_field->value);
// The client_credentials grant is specifically special-cased, the
// client credentials grant type MUST only be used by confidential clients.
// @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.4
if ($grantType === 'client_credentials' && $secret_field_is_empty) {
return FALSE;
}
// Validate a client without a client secret if the client is explicitly
// configured to be non-confidential. Note that if a client secret is
// provided it should be validated, even if the client is non-confidential.
if (!$client_drupal_entity->get('confidential')->value &&
$secret_field_is_empty &&
empty($clientSecret)) {
return TRUE;
}
// Check if a secret has been provided for this client and validate it.
// @see https://datatracker.ietf.org/doc/html/rfc6749#section-3.2.1
return $clientSecret && $this->passwordChecker->check($clientSecret, $secret_field->value);
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace Drupal\simple_oauth\Repositories;
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
/**
* The optional refresh token repository interface.
*/
interface OptionalRefreshTokenRepositoryInterface extends RefreshTokenRepositoryInterface {
/**
* Disable the refresh token.
*/
public function disableRefreshToken(): void;
}

View File

@@ -0,0 +1,77 @@
<?php
namespace Drupal\simple_oauth\Repositories;
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
/**
* The refresh token repository.
*/
class RefreshTokenRepository implements OptionalRefreshTokenRepositoryInterface {
use RevocableTokenRepositoryTrait;
/**
* The bundle ID.
*
* @var string
*/
protected static string $bundleId = 'refresh_token';
/**
* The OAuth2 entity class name.
*
* @var string
*/
protected static string $entityClass = 'Drupal\simple_oauth\Entities\RefreshTokenEntity';
/**
* The OAuth2 entity interface name.
*
* @var string
*/
protected static string $entityInterface = 'League\OAuth2\Server\Entities\RefreshTokenEntityInterface';
/**
* Boolean indicating if the refresh token is enabled.
*
* @var bool
*/
protected bool $refreshTokenEnabled = TRUE;
/**
* {@inheritdoc}
*/
public function getNewRefreshToken() {
return $this->refreshTokenEnabled ? $this->getNew() : NULL;
}
/**
* {@inheritdoc}
*/
public function persistNewRefreshToken(RefreshTokenEntityInterface $refresh_token_entity) {
$this->persistNew($refresh_token_entity);
}
/**
* {@inheritdoc}
*/
public function revokeRefreshToken($token_id) {
$this->revoke($token_id);
}
/**
* {@inheritdoc}
*/
public function isRefreshTokenRevoked($token_id) {
return $this->isRevoked($token_id);
}
/**
* {@inheritdoc}
*/
public function disableRefreshToken(): void {
$this->refreshTokenEnabled = FALSE;
}
}

View File

@@ -0,0 +1,134 @@
<?php
namespace Drupal\simple_oauth\Repositories;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
use Symfony\Component\Serializer\SerializerInterface;
/**
* Common methods for token repositories on different grants.
*/
trait RevocableTokenRepositoryTrait {
/**
* The entity type ID.
*
* @var string
*/
protected static string $entityTypeId = 'oauth2_token';
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected EntityTypeManagerInterface $entityTypeManager;
/**
* The serializer.
*
* @var \Symfony\Component\Serializer\SerializerInterface
*/
protected SerializerInterface $serializer;
/**
* Construct a revocable token.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Symfony\Component\Serializer\SerializerInterface $serializer
* The normalizer for tokens.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, SerializerInterface $serializer) {
$this->entityTypeManager = $entity_type_manager;
$this->serializer = $serializer;
}
/**
* Persists a new access token to permanent storage.
*
* @param mixed $token_entity
* The token entity.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
* @throws \Drupal\Core\Entity\EntityStorageException
* @throws \Symfony\Component\Serializer\Exception\ExceptionInterface
*/
public function persistNew($token_entity): void {
if (!is_a($token_entity, static::$entityInterface)) {
throw new \InvalidArgumentException(sprintf('%s does not implement %s.', get_class($token_entity), static::$entityInterface));
}
$values = $this->serializer->normalize($token_entity);
$values['bundle'] = static::$bundleId;
$new_token = $this->entityTypeManager->getStorage(static::$entityTypeId)->create($values);
if ($token_entity instanceof RefreshTokenEntityInterface) {
$access_token = $token_entity->getAccessToken();
if (!empty($access_token->getUserIdentifier())) {
$new_token->set('auth_user_id', $access_token->getUserIdentifier());
}
}
$new_token->save();
}
/**
* Revoke an access token.
*
* @param string $token_id
* The token id.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
* @throws \Drupal\Core\Entity\EntityStorageException
*/
public function revoke(string $token_id): void {
$tokens = $this
->entityTypeManager
->getStorage(static::$entityTypeId)
->loadByProperties(['value' => $token_id]);
if ($tokens) {
/** @var \Drupal\simple_oauth\Entity\Oauth2TokenInterface $token */
$token = reset($tokens);
$token->revoke();
$token->save();
}
}
/**
* Check if the token has been revoked.
*
* @param string $token_id
* The token id.
*
* @return bool
* Return true if this token has been revoked.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
public function isRevoked(string $token_id): bool {
$tokens = $this
->entityTypeManager
->getStorage(static::$entityTypeId)
->loadByProperties(['value' => $token_id]);
/** @var \Drupal\simple_oauth\Entity\Oauth2TokenInterface|null $token */
$token = $tokens ? reset($tokens) : NULL;
return !$token || $token->isRevoked();
}
/**
* Create a new token.
*
* @return mixed
* Returns a new token entity.
*/
public function getNew() {
$class = static::$entityClass;
return new $class();
}
}

View File

@@ -0,0 +1,102 @@
<?php
namespace Drupal\simple_oauth\Repositories;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\simple_oauth\Entities\ScopeEntity;
use Drupal\simple_oauth\Oauth2ScopeInterface;
use Drupal\simple_oauth\Oauth2ScopeProviderInterface;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
/**
* The repository for scopes.
*/
class ScopeRepository implements ScopeRepositoryInterface {
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected EntityTypeManagerInterface $entityTypeManager;
/**
* The scope provider.
*
* @var \Drupal\simple_oauth\Oauth2ScopeProviderInterface
*/
protected Oauth2ScopeProviderInterface $scopeProvider;
/**
* ScopeRepository constructor.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\simple_oauth\Oauth2ScopeProviderInterface $scope_provider
* The scope provider.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, Oauth2ScopeProviderInterface $scope_provider) {
$this->entityTypeManager = $entity_type_manager;
$this->scopeProvider = $scope_provider;
}
/**
* {@inheritdoc}
*/
public function getScopeEntityByIdentifier($identifier) {
$scope = $this->scopeProvider->loadByName($identifier);
return $scope ? $this->scopeFactory($scope) : NULL;
}
/**
* {@inheritdoc}
*/
public function finalizeScopes(array $scopes, $grant_type, ClientEntityInterface $client_entity, $user_identifier = NULL) {
$default_user = NULL;
if (!$client_entity->getDrupalEntity()->get('user_id')->isEmpty()) {
$default_user = $client_entity->getDrupalEntity()->get('user_id')->entity;
}
/** @var \Drupal\user\UserInterface $user */
$user = $user_identifier
? $this->entityTypeManager->getStorage('user')->load($user_identifier)
: $default_user;
if (!$user) {
return [];
}
$default_scopes = [];
$client_drupal_entity = $client_entity->getDrupalEntity();
if (!$client_drupal_entity->get('scopes')->isEmpty()) {
$default_scopes = array_map(function (Oauth2ScopeInterface $scope) {
return $this->scopeFactory($scope);
}, $client_drupal_entity->get('scopes')->getScopes());
}
$finalized_scopes = !empty($scopes) ? $scopes : $default_scopes;
// Validate scopes if the associated grant type is enabled.
foreach ($finalized_scopes as $finalized_scope) {
if ($finalized_scope instanceof ScopeEntity && !$finalized_scope->getScopeObject()->isGrantTypeEnabled($grant_type)) {
throw OAuthServerException::invalidScope($finalized_scope->getIdentifier());
}
}
return $finalized_scopes;
}
/**
* Build a scope entity.
*
* @param \Drupal\simple_oauth\Oauth2ScopeInterface $scope
* The associated scope.
*
* @return \League\OAuth2\Server\Entities\ScopeEntityInterface
* The initialized scope entity.
*/
protected function scopeFactory(Oauth2ScopeInterface $scope) {
return new ScopeEntity($scope);
}
}