Automating User Management in Drupal For Apigee DevPortal

Unix is user friendly. It is just picky about who its friends are.

-- Anon

We can easily adopt the famous quotation about Unix being friendly to Drupal CMF (Content Management Framework).

The learning curve to master Drupal is steep. It is even harder when you need to use Drupal occasionally. I should know. I am an occasional self-proclaimed Apigee DevPortal administrator and a DevOps engineer. And that is Drupal. The stack feels alien, the conventions are peculiar, the UX is bizarre, the community is spartan in looks and laconic in expressions... Yet the game is worth the candle for all the knowledge, power, and flexibility you acquire.

The Drupal community did an incredible job reinventing the product from Drupal-7-in-crisis state into an architecturally-solid secure product in versions 8/9/10 by Decoupling Drupal. Incorporating Symfony elements, introducing JSON:API and JSON-RPC, refactoring REST API across the whole code base and module ecosystem not only made the task of usage of Drupal easier, it also became a showcase and a case study of many standards and approaches that are useful for every API Management expert in the making.

Today we are going to concentrate on administering automation tasks around user provisioning between DevPortal and Apigee X/Hybrid/Edge/On-prem.

In terms of API calls, we've got plenty of choices and lots of cryptic documentation pages, which are hard to navigate without a bit of guidance that gives an ability to put a page in question into a context of an API call style.

Contemporary drupal [ie, the decoupled drupal] supports different styles of calls:

There is a healthy overlap of entity- and entity properties- related operations executed in every style. You can choose a style which best fits your skill set and/or your company coding standards. 

The Drupal Way du jour prefers JSON:API for entity manipulations and Core REST API for anything, which is not pure RESTful,  like reset password or user registration

(https://www.drupal.org/docs/core-modules-and-themes/core-modules/jsonapi-module/what-jsonapi-doesnt-...).

JSON:API in Drupal and DevPortal

The JSON:API module is a fully compliant implementation of the JSON:API Specification, which is an implementation of HATEOAS, which is a constraint of the REST application architecture.

That's quite a mouthful. Yet the message is positive: by mastering Drupal's JSON:API, you build your RESTful API Design karma. By the way, github exposes JSON:API.

JSON:API module is enabled by default when you install Apigee Developer Portal Kickstart distribution.

If you already set up your CI/CD pipeline to push your API's OpenAPI specification document to the DevPortal, you either used Apigee Smartdocs maven plugin or directly called Apigee API Catalog module via JSON:API,  https://www.drupal.org/docs/contributed-modules/apigee-api-catalog/expose-rest-apis-to-interact-with....

 

Filter User Role

Here's our one-request introduction to JSON:API. Let's send a JSON:API request to filter an id of the "Team API Devs" role.

 

curl -g -s -X GET   "https://$DP_HOST/jsonapi/user_role/user_role?filter[drupal_internal__id]=team_api_devs" \
   -H 'Content-Type: application/vnd.api+json' \
   -H "Authorization: Basic $ADS_CREDS"

 

The output contains the id of the role we need to assign it to a DevPortal developer we are going to register.

NOTE: -g/--globoff This curl option switches off the "URL globbing parser". When you set this option, you can specify URLs that contain the letters {}[] without having them being interpreted by curl itself. Note that these letters are not normal legal URL contents but they should be encoded according to the URI standard.

 

yuriyl_0-1644089899976.png

 

We are going to pipe the result to ` |jq -r ".data[]|.id"` a jq query to extract the id only and assign it to a temp env variable:

 

$ export ROLE_ID=$(curl -g -s -X GET  "https://$DP_HOST/jsonapi/user_role/user_role?filter[drupal_internal__id]=team_api_devs"   -H 'Content-Type: application/vnd.api+json'   -H "Authorization:  $ADS_CREDS" |jq -r ".data[]|.id")
$ echo $ROLE_ID
846f4a43-41fb-498d-a020-6ed81f273d71

 

Now you shall be able to recognize the main elements of a JSON:API request. 

 

List Users with Pagination

A useful request is to return a list of all registered users. It can be long. Pagination is a first-class citizen in JSON:API standard and its Drupal implementation.

 

curl -g "https://$DP_HOST/jsonapi/user/user?page[limit]=3&page[offset]=3" \
  -H 'Accept: application/vnd.api+json' \
  -H "Authorization: Basic $ADS_CREDS"




yuriyl_1-1644089899983.png

Developer Provisioning

An easy way to automate this operation is to use a simple script that loops through your developer information and executes a JSON:API call to Drupal's User entity, user--user resource. 

Assuming that each iteration populates the following environment variables, the following request will create a User account in Drupal for authentication purposes. It will also assign an $ROLE_ID for the newly created user. The developer account will be automatically provisioned in your connected Apigee organization.

 

export NAME=<developer-name>
export FNAME=<developer-first-name>
export LNAME=<developer-last-name>
export EMAIL=<developer-email>
export PASS=<generate nice random-secure-password>


curl -X POST \
  https://$DP_HOST/jsonapi/user/user \
  -H 'Content-Type: application/vnd.api+json' \
  -H 'Accept: application/vnd.api+json' \
  -H "Authorization: Basic $ADS_CREDS" \
  --data-binary @- << EOF
{
  "data": {
    "type": "user--user",
    "attributes": {
      "status": true,
      "name": "$NAME",
      "first_name": "$FNAME",
      "last_name": "$LNAME",
      "pass": "$PASS",
      "mail": "$EMAIL"
     },
     "relationships": {
       "roles": {
         "data": {
           "type": "user_role--user_role",
           "id": "$ROLE_ID"
         }
       }
     }
   }
}
EOF

 

Upon successful completion, a new account will be created.

User Password Reset

As we are going to generate a developer's password, it would be a good idea for the developer to reset it. We can optimize developer's experience and automatically generate an email with a password reset link in it as a convenience.

This request uses an unauthenticated Drupal Core REST API endpoint /user/password.

 

curl -v -X POST \
  https://$DP_HOST/user/password?_format=json \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  --data-binary @- << EOF
{
  "name": "$NAME"
}
EOF

 

An example email with a reset link:

yuriyl_2-1644089899980.png

 

Final Notes, Recommendations, and Best Practices

It depends on your particular use case how to manage and automate developer provisioning. 

If you already have developer accounts in the Apigee Organization, you have to execute a Developer Sync operation to replicate developer accounts and update their information. See this link for additional details. https://www.drupal.org/docs/contributed-modules/apigee-edge/synchronize-developers-with-apigee-edge.

Apigee DevPortal allows a developer to self-onboard herself. Sometimes you need to on-board multiple developers and of course, automation is our preferred way to do this.

Even without previous Drupal 8/9 experience, it doesn't take long to master the basics of interaction with your Apigee DevPortal instance to automate many useful operations. If you already know the JSON:API standard, you can immediately apply your knowledge in Drupal. If this is your first encounter with the standard, use it as a learning opportunity to master it.

Another good Best Practice is to manage authentication of your API developers by integrating your corporate Single Sign-On (SSO) services via Drupal SAML module, https://www.drupal.org/docs/contributed-modules/apigee-developer-portal-kickstart/integrate-simplesa...


Big thanks to @gitesh  for guidance and support in writing this article and on my Drupal journey.

Contributors
Version history
Last update:
‎02-05-2022 11:52 AM
Updated by: