Adds configuration example in migrate_example_advanced and support for basic/digest auth to http fetcher. We leverage Guzzle's support for this.

Comments

moshe weitzman created an issue. See original summary.

mikeryan’s picture

Title: Enhance http fetcher for basic and digest auth » Enhance http fetcher for basic, digest, and OAuth authentication
Component: Examples » Plugins

My current project needs OAuth support, so let's throw that in too...

mikeryan’s picture

See #2762499: Rearrange Url plugin configuration - this is the point (adding fetcher-specific configuration) that the idea of rearranging the configuration that's been rattling around the back of my mind reaches the front of my mind... So, if we took that approach, it might look like

source:
  plugin: url
  fetcher:
    plugin: http
    authentication:
      # Recognized types are basic and digest.
      type: basic
      parameters:
        username: foo
        password: bar

or

  fetcher:
    plugin: http
    authentication:
      type: oauth
      parameters:
        grant_type: client_credentials
        client_id: my_client
        client_secret: abcdef123456

(each authentication type defining its particular set of parameters).

Thoughts?

mikeryan’s picture

And... Should we, instead of building each authentication type directly into the http plugin, implement them as plugins? Or is that overdesigning?

moshe weitzman’s picture

Sure, I think some nesting improves clarity. To be honest though, folks coy/paste the examples and press on.

I cant spot the difference between your two examples. The first examples specifies the url source but the second doesn't.

moshe weitzman’s picture

I can see value in not jamming everything into its the same http plugin. In my patch the key work happens in getOptions(). I would think that each auth method could extend same class and override getOptions()?

mikeryan’s picture

I'll hold off on the rearrangement for now - I played enough with it to be convinced BC will be simple enough, we can do that later.

This untested patch sets up an Authentication plugin type - there's a lot of boilerplate, but the specific authentication plugins themselves are simple enough. What I'd like to do next is actually put basic auth on the /migrate_example_advanced_position endpoint so we have a real test and demonstration of authentication, haven't quite figured that out - the basic_auth module authenticates against Drupal users, while we'd like to set a hard-coded username/password for the REST endpoint here.

The Authentication plugin has a single method, authenticate(), returning an array of options to be passed to Guzzle after merging with any other explicitly-provided headers. The basic and digest providers don't have to do anything but return the proper arrays, but oauth2 will need to deal with obtaining an access token.

mikeryan’s picture

Status: Active » Needs work
StatusFileSize
new9.86 KB

*This* untested patch, I meant...

mikeryan’s picture

As for oauth2 support, it's not builtin to Guzzle but CommerceGuys have a plugin: https://github.com/commerceguys/guzzle-oauth2-plugin.

mikeryan’s picture

Belay that - it doesn't support Guzzle 6.

mikeryan’s picture

Title: Enhance http fetcher for basic, digest, and OAuth authentication » Provide authentication plugins to HTTP fetcher

There's a fork of that plugin purportedly supporting Guzzle 6: https://packagist.org/packages/sainsburys/guzzle-oauth2-plugin. Will look into that... What are best practices for handling an external dependency for a specific plugin within a module, when you don't want the whole module dependent?

moshe weitzman’s picture

In composer, you can suggest a dependency. I think thats the closest analogue. Your other options are to break out the oauth stuff into a separate module that requires oauth package, or users of your module have the oauth package even if they dont need it.

mikeryan’s picture

Assigned: moshe weitzman » Unassigned
Status: Needs work » Needs review
StatusFileSize
new12.71 KB

This works with my OAuth2 feed, untested with basic/digest authentication.

moshe weitzman’s picture

Looks great.

  1. Performs authentication, returning any options to be added to the request.

    authenticate() doesn't actually perform authentication. I might rename to getOptions(), but thats minor. Perhaps be explicit and say: "Any option listed at http://docs.guzzlephp.org/en/latest/request-options.html is valid."

  2. Need some docs for oAuth plugin. Maybe add to a different migration?
  3. Plugin question. is the @Authentication annotation namespaced somehow? What prevents Drupal from getting confused if another module declares @Authentication annotation?
mikeryan’s picture

authenticate() doesn't actually perform authentication. I might rename to getOptions(), but thats minor. Perhaps be explicit and say: "Any option listed at http://docs.guzzlephp.org/en/latest/request-options.html is valid."

With OAuth I was thinking of the preliminary get-a-token stuff as authentication, but right, the actual authentication happens on the request. Maybe something more explicit like getAuthentiationOptions()?

Need some docs for oAuth plugin. Maybe add to a different migration?

Oh, yeah - definitely need to point to the guzzle plugin info since that determines the available parameters (unfortunately, that is under-documented).

"add to a different migration"?

Plugin question. is the @Authentication annotation namespaced somehow? What prevents Drupal from getting confused if another module declares @Authentication annotation?

This is handled through the plugin manager, which is constructed with the annotation namespace (Drupal\migrate_plus\Annotation\Authentication) and the namespace where plugins using that annotation may be discovered (Plugin/migrate_plus/authentication). So, a plugin manager for another "Authentication" plugin type won't see these plugins, and our plugin manager won't see theirs. I get your point though - "Authentication" is broad enough to be likely used for other plugins, which may be confusing to humans if not the plugin managers...

mikeryan’s picture

StatusFileSize
new13.04 KB
new3.42 KB

Updated patch.

mikeryan’s picture

StatusFileSize
new12.19 KB
new662 bytes

Removing the example until/unless it works.

  • mikeryan committed bb296e3 on 8.x-2.x
    Issue #2761489 by mikeryan, moshe weitzman: Provide authentication...
mikeryan’s picture

Status: Needs review » Fixed
geerlingguy’s picture

Note that the above patch didn't make it into 8.x-2.0-beta2 (it seems like a few of the patches were committed just after that tag was created?). Had me puzzled since I thought it was :)

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

merilainen’s picture

Anyone have any idea how to use this?

mrkdboyd’s picture

This worked for me. Make sure you composer require sainsburys/guzzle-oauth2-plugin:^3.0.

source:
  plugin: url
  data_fetcher_plugin: http
  authentication:
    plugin: oauth2
    base_uri: YOUR_BASE_URI (e.g. http://www.someapi.com)
    token_url: YOUR_TOKEN_PATH (e.g. /token/oauth2 - which is the default)
    grant_type: YOUR_GRANT_TYPE (see http://cgit.drupalcode.org/migrate_plus/tree/src/Plugin/migrate_plus/authentication/OAuth2.php?h=8.x-3.x#n40)
    client_id: YOUR_CLIENT_ID
    client_secret: YOUR_CLIENT_SECRET
  data_parser_plugin: json
hazong’s picture

Hey community,
I’m trying to migrate some data from a remote rest/api (son). Here is my current migration iml file.
I also enabled the migrate_plus and migrate_tools

# Migration configuration for products.
id: product
label: 'Product'
migration_group: Products
# Here we define the migration source
source:
  plugin: url
  track_changes: true
  cache_counts: true
  data_fetcher_plugin: http
  data_parser_plugin: json
  urls: 'https://base_url/endpoint’
  item_selector: data/
  fields:
    -
      name: uuid
      label: 'uuid'
      selector: uuid

    -
     name: title
     label: 'title'
     selector: name

  ids:
    uuid:
      type: string


  authentication:
    plugin: oauth2
    grant_type: client_credentials
    base_uri: ‘base_url’
    token_url: '/oauth/token/'
    client_id: my_id
    client_secret: ‘my_client_secret’
  headers:
    Accept: 'application/json'
    Content-Type: 'application/json'

  destination:
    plugin: 'entity:node'

  process:
    type:
      plugin: default_value
      default_value: produkt

    title: name
    field_uuid: uuid
    
    sticky:
      plugin: default_value
      default_value: 0
    uid:
      plugin: default_value
      default_value: 0

  migration_dependencies:
    required: {}
    optional: {}

  dependencies:
    enforced:
      module:
        - migrate_products

Running the command ‘drush ms’ provides the following error:

‘Class 'Sainsburys\Guzzle\Oauth2\GrantType\ClientCredentials' not found in /usr/share/nginx/html/modules/contrib/migrate_plus/src/Plugin/migrate_plus/authentication/OAuth2.php’

Could some one help me try to understand what is happening or how I can handle this issue?

Thank you in advance

hazong’s picture

Version: 8.x-2.x-dev » 8.x-4.0
Category: Feature request » Bug report
johnle’s picture

@hazong,
You need to do a
composer require sainsburys/guzzle-oauth2-plugin

I am not sure of this oauth2 plugin is still maintain or being abandon.

johnle’s picture

Oops accidentally hit submit.

heddn’s picture

Category: Bug report » Feature request

Please open a new issue. Don't reopen a feature issue from 2 years.