See blog Menu
Reporting to See Which User Mappings will Apply to a User

kevin.creason | December 4th, 2020


OneLogin administrators often wish to know an easy way to see which mappings will apply to a user. Until now, the only way was to use your mental brain power: memorize the user’s attributes and scan the shorthand conditions in your list of user mappings. It was very tedious at a minimum and impossible for mere mortals if you have dozens of user fields and one hundred mappings or more.

This is not a report you can build in the OneLogin UI. This is a report that we will be building using the OneLogin API endpoints. It will be run from a system you control in a simple shell script; this could be run from a Mac that you already have or a Linux machine. It will mostly run with tools likely already installed on your machine, but there is one dependent tool that will require administrative privilege to install so that we can parse JSON data from the API endpoints.

A powershell version may follow, let us know if you are interested.

Script Sample Output

The script will simply output a listing of the mappings for your organization with the position number, the text name, and the actual rule identifier in case you use other APIs and script to manipulate your mappings. This can be adjusted to print out exactly what you want, for instance, into a CSV if you wish.

Sample list of user mappings

But this simple no/YES output will serve you well initially.

Sound good? Let’s proceed.

The API Endpoint

The API is straightforward, so let’s get that out of the way first. That may be all that you came here for.

curl -x POST 'https://<subdomain>.onelogin.com/api/2/mappings/<mapping ID>/dryrun' --data-raw '[userid]'

The data output from the endpoint is true or false.

That’s it.

Prerequisites

  1. Manage Users API credentials
    In Create new API credential, select Manage users
  2. A Mac or Linux machine. This initial version will tackle using scripting tools available on these platforms. A powershell version may follow.
  3. “Curl” command line tool to call URLs for API
  4. Local admin rights on your machine, so that you can install JQ to parse the JSON data.

That last point… I found a utility, JQ, that will be a lifesaver when dealing with APIs that provide output in JSON format. This utility will parse JSON in ways that trying to pipe command line tools to do is impossible. As a bonus you can use it to colorize and space output so that it is more human readable.

Installing JQ: Mac

The package you need to install is “jq”. This tool is worth the price of admission into the “brew” or homebrew Mac tools— the admission is “free” except for your time. So it is worth your time to install.

Just consider how this JSON blob:

Unformatted JSON

And then this readable version:

Beautified JQ after Brew

So go ahead, install “brew” and then install JQ.

In the script that follows we will use JQ to extract out the elements we need from the JSON data returned.

Script Sections

We’ll break this script down into sections so that it can be easier to learn and digest. This script will run in the Bash shell on most symptoms. Begin editing your file with any text editor and call it “check_mapping.sh”. Make sure it does not have a text file extension.

Before we really get started, locate your bash interpreter from your command line interface (Terminal, etc) by using the “which bash” command.

Which bash command

Use this path for the first line in your script in the following format. This is commonly referred to as the “shebang line”.

Shebang line

We’ll create the file with this first line using a simple echo command and a redirect.

echo '#!/bin/bash' >check_mappings.sh

Now set the execute permission so that only you can execute the script

chmod 500 check_mappings.sh

Now you are ready to edit the file with your favorite text editor.

Parse Command Options

The first thing your script should do is not waste any additional processing power if it wasn’t passed the correct command line options. Obviously we need some way to know which user we need to analyze. The plan is that you will execute the script from your shell window, such as Terminal, and pass the user’s email address in as the first argument.

Passing the user's email address

So this section of the script simply checks to make sure that if something was passed to the command line after the execution we will treat that as the user identifier. If there is nothing in the first variable position then we will prompt below when we prompt for administrator input:

if [ "$#" -gt 0 ]
then
 EMAIL=$1
fi

Tokens

The second thing your API scripts need to do is handle the tokens. The API credentials consist of a client identifier and a client secret. Think of them as the username and the password. But we don’t want to be passing the username and password all the time, they are time consuming, unwieldy, and ancient means of authentication. So we get an access token that will slip us through the door more easily.

Many people will record the ID and secret to the file but this is poor security for a script to be stored on your system with an ID and password in it. This script will prompt you for input at the time you run it:

read -p "Enter your API Client ID: " ID
read -sp "Enter your API Client Secret: " SSSH
echo

The dangling “echo” provides a line break from the hidden input of the API Secret. Next, the script will prompt you for the user identifier (usually email address) if it was not passed in as a command line argument:

if [[ "$EMAIL" == "" ]]
then
 read -p "Enter the email address of user: " EMAIL
fi

Now that we have the API ID, Secret, and the user identifier the script will execute “curl” to do the HTTP request to get an access token. We send the data returned to JQ to parse out just the access token that we need. This is stored in a new variable we are calling TK.

# Get the Bearer Token
TK=$(curl https://<subdomain>.onelogin.com/auth/oauth2/v2/token \
 -H "Authorization: client_id:$ID, client_secret:$SSSH" \
 -H "Content-type: application/json" \
 -d '{ "grant_type":"client_credentials" }' 2>/dev/null \
 | jq -r '.access_token' )

You should now have an access token stored in variable TK.

Process the User Input

The first thing that needs to happen with the fresh access token is we need to make sure that a real user was provided and that we can get the user id number in the OneLogin system.

It’s pretty straightforward. We are using this API endpoint to search by the email address and it will return all information regarding the user. So if you wanted, you could tell JQ to pull out the user’s first name, but all we are really interested in is the user number stored in the ID field.

# resolve the user's email address to id number
uid=$(curl --location --request GET https://<subdomain>.onelogin.com/api/2/users?email=$1 --header "Authorization: Bearer $TK" 2>/dev/null |jq -r '.[] | .id')

There’s an “echo” statement to print out user information if you like:

echo "  The user ID is $uid"

Now that we have made this call we do a quick check in the following IF statement with a “then”. This statement is not checking the validity of the UID number but making sure that a user was found with the input provided and we can move on to the next step.

if [[ "$uid" != "" ]]
then

The IF statement closes out in the final three lines at the bottom of the script with an “else”. All the remaining section of code only executes if the user exists and the API has returned an identifier.

Get the Mappings and Loop

Inside this IF statement we create a loop around each mapping that exists in your OneLogin tenant. The loop will process each mapping individually against the user we resolved earlier.

We have to do one little thing before we start this loop: we have to do a little shell magic. This next variable is very important, it tells the system to separate fields with a return code or line feed instead of a space. We do this with the JQ option “—compact-output” so that each mapping is contained to a line.

IFS=$'\n'

With that all squared away we setup our loop around the curl command that lists all mappings:

for map in $(curl --location --request \
GET https://<subdomain>.onelogin.com/api/2/mappings \
--header "Authorization: Bearer $TK" 2>/dev/null | \
jq -r --compact-output '.[] |{id: .id,name: .name,position: .position}' )
 do

The last line, the“do”, starts an inner loop on the “map” variables extracted from each line of output from the previous curl-jq juggernaut.

This curl-jq piped command grabs all mappings and parses out just their id (number), their name, and the position (line number).

Now that we have the name, rank, and serial number of every mapping and we are prepared to examine them one at a time, inside this “do” statement.

Parse the Mapping Information

The first item is to parse out the mapping information from JQ into variables that we can address individually. We start my playing with the magic field separator, but the field separator we don’t want to be permanent, we want it only to be active long enough to parse the JQ data. So we place the IFS option on the same line where we split the JQ data into three temporary variables, v1, v2, and v3:

IFS="," read v1 v2 v3 <<< "$map"

Unfortunately, the JQ data has a label and data. I use the a “cut” command to blow away the chaff and save only the seed, putting the mapping identifier number into variable “mid” and “nm”:

mid=$(cut -f 2- -d":" <<< "$v1" )
  nm=$( cut -f 2- -d":" <<< $v2 )

Next, I use the “tr” command again to strip away an end curly brace combined with the cut command when grabbing the position number. The final “echo” statement just prints the information message to the screen.

pos=$(cut -f 2- -d":" <<< $v3 | tr -d "}" )
  echo "  Checking the mapping #$pos, name '$nm' (id:$mid)"

We are now ready to run the API to evaluate the user and API to see if it will apply.

Check the Uuser and the Mapping

We have one more use of JQ. It is called into action when we call the dry run mapping so that we can see if the evaluation is “true” or “false”. The actual API endpoint called is in the mappings section, it is called ‘dryrun’. We are required to pass in the user’s identifier number as the data for the POST request.

# Check to see if mapping applies to user
x=$(curl --header 'Content-Type: application/json' \
 --header "Authorization: Bearer $TK" --location --request POST\
 https://<subdomain>.onelogin.com/api/2/mappings/$mid/dryrun \
 --data-raw "[$uid]" 2>/dev/null |jq '.[] |{x: .mapped}' )

The data results will echo back the user information which we would already have, so we use JQ to grab the true/false value in the “mapped” portion.

Anticlimactic Ending: the Results

Now that we have the true/false data in the “x” variable the ending is pretty obvious. We print some sort of statement to indicate the results.

# Output the results of the test
  if [[ "$x" =~ "true" ]]
   then
     echo "     YES"
  elif [[ "$x" =~ "false" ]]
   then
     echo "     no"
  fi

Wrap Up and Close the Loops

The results are on the screen but we have to close one loop with a “done” statement. We also should print out a message in an else statement to indicate that a real user could not be found.

done
else
 echo "Could not resolve user $1"
fi

The End

That’s it! It’s not a complex script but there is a lot of processing packed in there to make human readable output thanks to the power of the JQ utility.

Hopefully this blog and this script is useful for you, made your life better, and you learned something in the process.

The Full Script

#!/bin/bash

if [ "$#" -gt 0 ]
then
 EMAIL=$1
fi

read -p "Enter your API Client ID: " ID
read -sp "Enter your API Client Secret: " SSSH
echo
if [[ "$EMAIL" == "" ]]
then
 read -p "Enter the email address of user: " EMAIL
fi

# Get the Bearer Token
TK=$(curl https://<subdomain>.onelogin.com/auth/oauth2/v2/token -H "Authorization: client_id:$ID, client_secret:$SSSH" -H "Content-type: application/json" -d '{ "grant_type":"client_credentials" }' 2>/dev/null  | jq -r '.access_token' )

# resolve the user's email address to id number
uid=$(curl --location --request GET https://<subdomain>.onelogin.com/api/2/users?email=$1 --header "Authorization: Bearer $TK" 2>/dev/null |jq -r '.[] | .id')
 echo "  The user ID is $uid"

if [[ "$uid" != "" ]]
then 
 # Get The Mapping list
 IFS=$'\n'
 for i in $(curl --location --request GET https://<subdomain>.onelogin.com/api/2/mappings --header "Authorization: Bearer $TK" 2>/dev/null | jq -r --compact-output '.[] |{id: .id,name: .name,position: .position}' )
 do
  IFS="," read v1 v2 v3 <<< "$i"
  mid=$(cut -f 2- -d":" <<< "$v1" )
  nm=$( cut -f 2- -d":" <<< $v2 )
  pos=$(cut -f 2- -d":" <<< $v3 | tr -d "}" )
  echo "  Checking the mapping #'$pos', name $nm (id:'$mid')"

   # Check to see if mapping applies to user
   x=$(curl --header 'Content-Type: application/json' --header "Authorization: Bearer $TK" --location --request POST https://<subdomain>.onelogin.com/api/2/mappings/$mid/dryrun  --data-raw "[$uid]" 2>/dev/null |jq '.[] |{x: .mapped}' )
 
  # Output the results of the test
  if [[ "$x" =~ "true" ]]
   then
     echo "     YES"
  elif [[ "$x" =~ "false" ]]
   then
     echo "     no"
  fi
 done
else
 echo "Could not resolve user $1"
fi

Blog author, Kevin Creason

Kevin Creason is one of our Senior Implementation Consultants and is also known for his coffee problem. The phrase “coffee snob” has been used but mostly he just loves roasting his own beans and using one of his 13 different brewing devices.