Skip to Content
Technical Articles

SAC: Export user list by REST API

What?

To get more familiar with Python and also polish my somewhat rusty REST know how I wanted to write some code. Additionally it would be nice if SAP Analytic Cloud is somehow involved.

So I decided to develop a small script to export SAC users with their roles and teams. And yes, I am aware of the option to export users to csv from the UI.

But clicking on a button is not the same as learning how to do it ;o)

Note: We will not produce production quality code, this is only for demonstration purposes.

Prerequisites

  • Access to a SAC tenant and rights to view and change System > Administration > App Integration
  • Basic unterstanding of Python, REST or at least some skills to google for it
  • An editor and python executables – it’s up to your choice. What I used see below.

My environment

I use VS Code as it is lean and fast. I prefer to start it up via a batch file where I can set explicitely the PATH without bothering anything else on my PC.

So this looks like that – VSCode.bat:

set PATH=%PATH%;C:\Dev\VSCode;C:\Dev\PortableGit\bin;C:\Dev\Python;C:\Dev\Python\Scripts
cd C:\Dev\Projects
start code .
exit

Please note everything is in subfolders of C:\Dev: VSCode, Git, Python and my projects directory.

I also added Python scripts to my PATH as there is the pip utility. Python is just a portable version.

Reference documentation

https://help.sap.com/viewer/product/SAP_ANALYTICS_CLOUD/release/en-US

There is a chapter Development with the necessary infos:

VS Code extensions

In order to use Python I added some extensions to VS Code and configured them. For an easy start look at https://code.visualstudio.com/docs/python/python-tutorial

My extensions look like that:

Setup in SAC

Log on to your tenant and choose from the menu System > Administration > App Integration, then select “Add a new OAuth client”. We need to define a kind of technical user to access the system from our program.

Fill out the dialog:

Select “User Provisioning”, and also set a password aka secret.

This results in:

Required URLs

We will need two URLs one – Token URL – can be found right here in App Integration:

The other one is derived from your SAC tenant URL:

https://<your-SAC-system>.<region>.sapanalytics.cloud/api/vi/scim

This endpoint provides access to the user and team API.

SCIM means System for Cross-domain Identity Management which is a specification for managing user identities.

Coding

Additional library

After some research I used the requests module, please have a look at their web site: https://pypi.org/project/requests/2.7.0/

Steps

  1. Request an OAuth access token via POST to token URL and fetch the result
  2. Use this access token to get a x-csrf-token via GET to your tenant and store the result
  3. Use the x-csrf-token for every request, in this case getting a user list

Program structure

To keep things simple I put everything in one file, for better understanding I show it one part after the other.

We need some modules, then the base URLs and logon data are defined in variables.

import json
import requests
import base64

api_oauth_client_id = 'My_OAuth_Client'
api_secrect = 'stefan'
# derived from your SAC tenant URL
api_url_base = 'https://<your tenant>.eu1.sapbusinessobjects.cloud/api/v1/scim/'
# This is from System > Administration > App Integration
api_token_url = 'https://oauthasservices-<your oauth-service>.hana.ondemand.com/oauth2/api/v1/token'

I put step 1 in the function get_api_token():

def get_api_token():
    # combine client id and secret according to API
    logon = '{0}:{1}'.format(api_oauth_client_id, api_secrect)

    # prepare the authorization header, please note the character encoding
    token_headers = {'Authorization': 'Basic {0}'.format(
        base64.b64encode(logon.encode('utf-8')).decode('utf-8'))}

    # prepare POST call parameters
    params = {'grant_type': 'client_credentials'}
    
    # call the OAuth API
    response = requests.post(api_token_url, headers=token_headers, params=params)

    # if success then get the result as json else cry out for help
    if response.status_code == 200:
        data = json.loads(response.content.decode('utf-8'))
    else:
        data = None
        print('Problem!')

    # return json for further processing
    return data

Hopefully the inline documentation explains it. After all we do a http post with required headers and get the result as json.

Step 2 is also packaged into a function:

def get_crsf_token():
    # maybe ugly but I prepare the http headers outside this function
    # please note now we call another URL
    response = requests.get('{0}Groups'.format(api_url_base), headers=headers)

    # if success then we get a x-crsf-token ...
    if response.status_code == 200:
        crsf_token = response.headers['x-csrf-token']
    else:
        crsf_token = None
        print('Problem!')

    # ... which can be returned
    return crsf_token

Now let’s put it together and call both functions in a sequence to get the desired x-csrf-token:

# get API token
return_data = get_api_token()

# extract token from json return
api_token = return_data['access_token']

# get crsf token and update headers
headers = {'Authorization': 'Bearer {0}'.format(api_token),
           'x-sap-sac-custom-auth': 'true',
           'x-csrf-token': 'fetch'}

# get the x-csrf-token and put in into the header for the next request           
headers['x-csrf-token'] = get_crsf_token()

If you believe or not, now we are ready to start the real work! Just in case you get lost: We just wanted to export a user list from SAC.

# now lets to do the real work
response = requests.get('{0}Users?count=500'.format(
    api_url_base), headers=headers)

# get response as json
if response.status_code == 200:
    data = json.loads(response.content.decode('utf-8'))
else:
    print('Problem!')

I follow a shortcut here because I extend the page size to a high value so every user in my system is fetched in one request by adding count=500 the the URL. Please see the documention if it’s not appropriate to your requirement.

To keep it simple I print the result to stdout by looping over the users and concatenating teams and roles so one line shows a user:

# print(data)
users = data['Resources']
for usr in users:
    name = usr['userName']
    groups = ''
    group_objs = usr['groups']
    for grp in group_objs:
        groups = groups + grp['display'] + ','
    roles = usr['roles']
    print('{0} - {1} - {2}'.format(name, groups.rstrip(','), ','.join(roles)))

If you want to run this program in your environment, copy all parts of the demo code into one file, prepare and adapt your SAC tenant and it should work.

Thanks for reading and happy coding!

Be the first to leave a comment
You must be Logged on to comment or reply to a post.