Skip to content
Snippets Groups Projects
Commit 37803c40 authored by Stephan Zeidler's avatar Stephan Zeidler Committed by Jürgen Haas
Browse files

Issue #3357098 by szeidler: Make remote authentication pluggable

parent 89499e2f
Branches 4.0.x
Tags 4.0.5
1 merge request!7Issue #3357098: Make remote authentication pluggable
......@@ -2,3 +2,6 @@ services:
drd_agent.setup:
class: Drupal\drd_agent\Setup
arguments: ['@state', '@datetime.time', '@request_stack']
plugin.manager.drd_pi_auth:
class: Drupal\drd_agent\DrdPiAuthManager
parent: default_plugin_manager
......@@ -17,6 +17,7 @@ use Drupal\drd_agent\Agent\Auth\BaseInterface as AuthBaseInterface;
use Drupal\drd_agent\Agent\Auth\Base as AuthBase;
use Drupal\drd_agent\Crypt\Base as CryptBase;
use Drupal\drd_agent\Crypt\BaseMethodInterface;
use Drupal\drd_agent\DrdPiAuthManager;
use Drupal\user\Entity\User;
use Exception;
use Psr\Log\LogLevel;
......@@ -31,10 +32,6 @@ class Base implements BaseInterface, ContainerInjectionInterface {
private $debugMode = FALSE;
private $arguments = array();
const SEC_AUTH_ACQUIA = 'Acquia';
const SEC_AUTH_PANTHEON = 'Pantheon';
const SEC_AUTH_PLATFORMSH = 'PlatformSH';
/**
* Crypt object for this DRD request.
*
......@@ -97,6 +94,11 @@ class Base implements BaseInterface, ContainerInjectionInterface {
*/
protected $time;
/**
* @var \Drupal\drd_agent\DrdPiAuthManager
*/
protected $drdPiAuthManager;
/**
* Base constructor.
*
......@@ -111,8 +113,9 @@ class Base implements BaseInterface, ContainerInjectionInterface {
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* @param \Drupal\Core\State\StateInterface $state
* @param \Drupal\Component\Datetime\Time $time
* @param \Drupal\drd_agent\DrdPiAuthManager $drd_pi_auth_manager
*/
public function __construct(ContainerInterface $container, AccountSwitcherInterface $account_switcher, ConfigFactoryInterface $config_factory, Connection $database, EntityTypeManagerInterface $entity_type_manager, FileSystemInterface $file_system, LoggerChannelFactoryInterface $logger_channel_factory, MessengerInterface $messenger, ModuleHandlerInterface $module_handler, StateInterface $state, Time $time) {
public function __construct(ContainerInterface $container, AccountSwitcherInterface $account_switcher, ConfigFactoryInterface $config_factory, Connection $database, EntityTypeManagerInterface $entity_type_manager, FileSystemInterface $file_system, LoggerChannelFactoryInterface $logger_channel_factory, MessengerInterface $messenger, ModuleHandlerInterface $module_handler, StateInterface $state, Time $time, DrdPiAuthManager $drd_pi_auth_manager) {
$this->accountSwitcher = $account_switcher;
$this->configFactory = $config_factory;
$this->container = $container;
......@@ -124,6 +127,7 @@ class Base implements BaseInterface, ContainerInjectionInterface {
$this->moduleHandler = $module_handler;
$this->state = $state;
$this->time = $time;
$this->drdPiAuthManager = $drd_pi_auth_manager;
}
/**
......@@ -141,7 +145,8 @@ class Base implements BaseInterface, ContainerInjectionInterface {
$container->get('messenger'),
$container->get('module_handler'),
$container->get('state'),
$container->get('datetime.time')
$container->get('datetime.time'),
$container->get('plugin.manager.drd_pi_auth')
);
}
......@@ -335,34 +340,10 @@ class Base implements BaseInterface, ContainerInjectionInterface {
throw new RuntimeException('Input is incomplete');
}
switch ($input['method']) {
case self::SEC_AUTH_ACQUIA:
$required = array('username', 'password');
$local = $this->getDbInfo();
break;
case self::SEC_AUTH_PANTHEON:
$required = array('PANTHEON_SITE');
$local = $_ENV;
break;
case self::SEC_AUTH_PLATFORMSH:
$required = array('PLATFORM_PROJECT');
$local = $_ENV;
break;
/** @var \Drupal\drd_agent\Plugin\DrdPiAuth\DrdPiAuthInterface $auth */
$auth = $this->drdPiAuthManager->createInstance($input['method']);
$auth->validate($input);
default:
throw new RuntimeException('Unknown method.');
}
foreach ($required as $item) {
if (!isset($local[$item])) {
throw new RuntimeException('Unsupported method.');
}
if ($local[$item] !== $input['secrets'][$item]) {
throw new RuntimeException('Invalid secret.');
}
}
$this->authorize($input['remoteSetupToken']);
}
catch (Exception $ex) {
......
<?php
namespace Drupal\drd_agent\Annotation;
use Drupal\Component\Annotation\Plugin;
/**
* Defines a DRD PI Auth annotation object.
*
* @Annotation
*/
class DrdPiAuth extends Plugin {
/**
* The plugin ID.
*
* @var string
*/
public $id;
}
<?php
namespace Drupal\drd_agent;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\drd_agent\Annotation\DrdPiAuth;
use Drupal\drd_agent\Plugin\DrdPiAuth\DrdPiAuthInterface;
use Traversable;
/**
* Provides the DRD PI Auth plugin manager.
*/
class DrdPiAuthManager extends DefaultPluginManager {
/**
* Constructor for DrdPiAuthManager objects.
*
* @param \Traversable $namespaces
* An object that implements \Traversable which contains the root paths
* keyed by the corresponding namespace to look for plugin implementations.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
* Cache backend instance to use.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler to invoke the alter hook with.
*/
public function __construct(Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
parent::__construct('Plugin/DrdPiAuth', $namespaces, $module_handler, DrdPiAuthInterface::class, DrdPiAuth::class);
$this->alterInfo('drd_agent_drd_pi_auth_info');
$this->setCacheBackend($cache_backend, 'drd_agent_drd_pi_auth_plugins');
}
}
<?php
namespace Drupal\drd_agent\Plugin\DrdPiAuth;
use Drupal\Core\Database\Connection;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides the Acquia PI Auth plugin.
*
* @DrdPiAuth(
* id = "acquia"
* )
*/
class Acquia extends DrdPiAuthBase implements ContainerFactoryPluginInterface {
/**
* @var \Drupal\Core\Database\Connection
*/
protected $database;
/**
* Constructs an Acquia DRD PI auth object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Database\Connection $database
* The database connection
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, Connection $database) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->database = $database;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('database')
);
}
/**
* {@inheritdoc}
*/
protected function getRequired(): array {
return ['username', 'password'];
}
/**
* {@inheritdoc}
*/
protected function getLocal(): array {
return $this->getDbInfo();
}
/**
* Get the database information.
*
* @return array
* The database connection details.
*/
protected function getDbInfo(): array {
return $this->database->getConnectionOptions();
}
}
<?php
namespace Drupal\drd_agent\Plugin\DrdPiAuth;
use Drupal\Component\Plugin\PluginBase;
use RuntimeException;
abstract class DrdPiAuthBase extends PluginBase implements DrdPiAuthInterface {
/**
* Get the required properties for comparison.
*
* @return array
* The required properties.
*/
abstract protected function getRequired(): array;
/**
* Return the local data for comparison.
*
* @return array
* The local data.
*/
abstract protected function getLocal(): array;
/**
* {@inheritdoc}
*/
public function validate($input): void {
$local = $this->getLocal();
$required = $this->getRequired();
foreach ($required as $item) {
if (!isset($local[$item])) {
throw new RuntimeException('Unsupported method.');
}
if ($local[$item] !== $input['secrets'][$item]) {
throw new RuntimeException('Invalid secret.');
}
}
}
}
<?php
namespace Drupal\drd_agent\Plugin\DrdPiAuth;
interface DrdPiAuthInterface {
/**
* Validate the provided input with the local secrets.
*
* Should throw exceptions if a validation failed.
*
* @param array $input
* The decoded input.
*
* @return void
*/
public function validate($input): void;
}
<?php
namespace Drupal\drd_agent\Plugin\DrdPiAuth;
/**
* Provides the Pantheon PI Auth plugin.
*
* @DrdPiAuth(
* id = "pantheon"
* )
*/
class Pantheon extends DrdPiAuthBase {
/**
* {@inheritdoc}
*/
protected function getRequired(): array {
return ['PANTHEON_SITE'];
}
/**
* {@inheritdoc}
*/
protected function getLocal(): array {
return $_ENV;
}
}
<?php
namespace Drupal\drd_agent\Plugin\DrdPiAuth;
/**
* Provides the Platform.sh PI Auth plugin.
*
* @DrdPiAuth(
* id = "platformsh"
* )
*/
class PlatformSh extends DrdPiAuthBase {
/**
* {@inheritdoc}
*/
protected function getRequired(): array {
return ['PLATFORM_PROJECT'];
}
/**
* {@inheritdoc}
*/
protected function getLocal(): array {
return $_ENV;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment