Commit bb296e33 authored by mikeryan's avatar mikeryan Committed by mikeryan

Issue #2761489 by mikeryan, moshe weitzman: Provide authentication plugins to HTTP fetcher

parent 70315894
......@@ -17,5 +17,8 @@
"source": "https://cgit.drupalcode.org/migrate_plus"
},
"minimum-stability": "dev",
"require": {}
"require": {},
"suggest": {
"sainsburys/guzzle-oauth2-plugin": "3.0 required for the OAuth2 authentication plugin"
}
}
services:
plugin.manager.migrate_plus.authentication:
class: Drupal\migrate_plus\AuthenticationPluginManager
parent: default_plugin_manager
plugin.manager.migrate_plus.data_fetcher:
class: Drupal\migrate_plus\DataFetcherPluginManager
parent: default_plugin_manager
......
<?php
namespace Drupal\migrate_plus\Annotation;
use Drupal\Component\Annotation\Plugin;
/**
* Defines an authentication annotation object.
*
* Plugin Namespace: Plugin\migrate_plus\authentication
*
* @see \Drupal\migrate_plus\AuthenticationPluginBase
* @see \Drupal\migrate_plus\AuthenticationPluginInterface
* @see \Drupal\migrate_plus\AuthenticationPluginManager
* @see plugin_api
*
* @Annotation
*/
class Authentication extends Plugin {
/**
* The plugin ID.
*
* @var string
*/
public $id;
/**
* The title of the plugin.
*
* @var \Drupal\Core\Annotation\Translation
*
* @ingroup plugin_translatable
*/
public $title;
}
<?php
namespace Drupal\migrate_plus;
use Drupal\Core\Plugin\PluginBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Defines a base authentication implementation.
*
* @see \Drupal\migrate_plus\Annotation\Authentication
* @see \Drupal\migrate_plus\AuthenticationPluginInterface
* @see \Drupal\migrate_plus\AuthenticationPluginManager
* @see plugin_api
*/
abstract class AuthenticationPluginBase extends PluginBase implements AuthenticationPluginInterface {
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition);
}
}
<?php
namespace Drupal\migrate_plus;
/**
* Defines an interface for authenticaion handlers.
*
* @see \Drupal\migrate_plus\Annotation\Authentication
* @see \Drupal\migrate_plus\AuthenticationPluginBase
* @see \Drupal\migrate_plus\AuthenticationPluginManager
* @see plugin_api
*/
interface AuthenticationPluginInterface {
/**
* Performs authentication, returning any options to be added to the request.
*
* @return array
* Options (such as Authentication headers) to be added to the request.
*
* @link http://docs.guzzlephp.org/en/latest/request-options.html
*/
public function getAuthenticationOptions();
}
<?php
namespace Drupal\migrate_plus;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
/**
* Provides a plugin manager for authentication handlers.
*
* @see \Drupal\migrate_plus\Annotation\DataFetcher
* @see \Drupal\migrate_plus\DataFetcherPluginBase
* @see \Drupal\migrate_plus\DataFetcherPluginInterface
* @see plugin_api
*/
class AuthenticationPluginManager extends DefaultPluginManager {
/**
* Constructs a new AuthenticationPluginManager.
*
* @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/migrate_plus/authentication', $namespaces, $module_handler, 'Drupal\migrate_plus\AuthenticationPluginInterface', 'Drupal\migrate_plus\Annotation\Authentication');
$this->alterInfo('authentication_info');
$this->setCacheBackend($cache_backend, 'migrate_plus_plugins_authentication');
}
}
......@@ -73,7 +73,6 @@ abstract class DataParserPluginBase extends PluginBase implements DataParserPlug
return new static($configuration, $plugin_id, $plugin_definition);
}
/**
* Returns the initialized data fetcher plugin.
*
......
<?php
namespace Drupal\migrate_plus\Plugin\migrate_plus\authentication;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\migrate_plus\AuthenticationPluginBase;
/**
* Provides basic authentication for the HTTP resource.
*
* @Authentication(
* id = "basic",
* title = @Translation("Basic")
* )
*/
class Basic extends AuthenticationPluginBase implements ContainerFactoryPluginInterface {
/**
* {@inheritdoc}
*/
public function getAuthenticationOptions() {
return [
'auth' => [
$this->configuration['username'],
$this->configuration['password'],
],
];
}
}
<?php
namespace Drupal\migrate_plus\Plugin\migrate_plus\authentication;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\migrate_plus\AuthenticationPluginBase;
/**
* Provides digest authentication for the HTTP resource.
*
* @Authentication(
* id = "digest",
* title = @Translation("Digest")
* )
*/
class Digest extends AuthenticationPluginBase implements ContainerFactoryPluginInterface {
/**
* {@inheritdoc}
*/
public function getAuthenticationOptions() {
return [
'auth' => [
$this->configuration['username'],
$this->configuration['password'],
'digest',
],
];
}
}
<?php
namespace Drupal\migrate_plus\Plugin\migrate_plus\authentication;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\migrate\MigrateException;
use Drupal\migrate_plus\AuthenticationPluginBase;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use Sainsburys\Guzzle\Oauth2\GrantType\AuthorizationCode;
use Sainsburys\Guzzle\Oauth2\GrantType\ClientCredentials;
use Sainsburys\Guzzle\Oauth2\GrantType\JwtBearer;
use Sainsburys\Guzzle\Oauth2\GrantType\PasswordCredentials;
use Sainsburys\Guzzle\Oauth2\GrantType\RefreshToken;
use Sainsburys\Guzzle\Oauth2\Middleware\OAuthMiddleware;
/**
* Provides OAuth2 authentication for the HTTP resource.
*
* @link https://packagist.org/packages/sainsburys/guzzle-oauth2-plugin
*
* @Authentication(
* id = "oauth2",
* title = @Translation("OAuth2")
* )
*/
class OAuth2 extends AuthenticationPluginBase implements ContainerFactoryPluginInterface {
/**
* {@inheritdoc}
*/
public function getAuthenticationOptions() {
$handlerStack = HandlerStack::create();
$client = new Client([
'handler'=> $handlerStack,
'base_uri' => $this->configuration['base_uri'],
'auth' => 'oauth2',
]);
switch ($this->configuration['grant_type']) {
case 'authorization_code':
$grant_type = new AuthorizationCode($client, $this->configuration);
break;
case 'client_credentials':
$grant_type = new ClientCredentials($client, $this->configuration);
break;
case 'urn:ietf:params:oauth:grant-type:jwt-bearer':
$grant_type = new JwtBearer($client, $this->configuration);
break;
case 'password':
$grant_type = new PasswordCredentials($client, $this->configuration);
break;
case 'refresh_token':
$grant_type = new RefreshToken($client, $this->configuration);
break;
default:
throw new MigrateException("Unrecognized grant_type {$this->configuration['grant_type']}.");
break;
}
$middleware = new OAuthMiddleware($client, $grant_type);
return [
'headers' => [
'Authorization' => 'Bearer ' . $middleware->getAccessToken()->getToken(),
],
];
}
}
......@@ -31,6 +31,13 @@ class Http extends DataFetcherPluginBase implements ContainerFactoryPluginInterf
*/
protected $headers = [];
/**
* The data retrieval client.
*
* @var \Drupal\migrate_plus\AuthenticationPluginInterface
*/
protected $authenticationPlugin;
/**
* {@inheritdoc}
*/
......@@ -39,6 +46,19 @@ class Http extends DataFetcherPluginBase implements ContainerFactoryPluginInterf
$this->httpClient = \Drupal::httpClient();
}
/**
* Returns the initialized authentication plugin.
*
* @return \Drupal\migrate_plus\AuthenticationPluginInterface
* The authentication plugin.
*/
public function getAuthenticationPlugin() {
if (!isset($this->authenticationPlugin)) {
$this->authenticationPlugin = \Drupal::service('plugin.manager.migrate_plus.authentication')->createInstance($this->configuration['authentication']['plugin'], $this->configuration['authentication']);
}
return $this->authenticationPlugin;
}
/**
* {@inheritdoc}
*/
......@@ -50,7 +70,7 @@ class Http extends DataFetcherPluginBase implements ContainerFactoryPluginInterf
* {@inheritdoc}
*/
public function getRequestHeaders() {
return !empty($this->headers) ? $this->headers : array();
return !empty($this->headers) ? $this->headers : [];
}
/**
......@@ -58,11 +78,11 @@ class Http extends DataFetcherPluginBase implements ContainerFactoryPluginInterf
*/
public function getResponse($url) {
try {
$response = $this->httpClient->get($url, array(
'headers' => $this->getRequestHeaders(),
// Uncomment the following to debug the request.
//'debug' => true,
));
$options = ['headers' => $this->getRequestHeaders()];
if (!empty($this->configuration['authentication'])) {
$options = array_merge($options, $this->getAuthenticationPlugin()->getAuthenticationOptions());
}
$response = $this->httpClient->get($url, $options);
if (empty($response)) {
throw new MigrateException('No response at ' . $url . '.');
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment