Skip to content

REST Tutorial

Julien Louis edited this page Jan 14, 2021 · 4 revisions

Authentication

Shanoir relies on Keycloak for the authentication management. The authentication protocol is OpenID which is a layer on top of OAuth 2.0.

In our case, the authentication flow is quite simple :

  1. A request is sent to the Shanoir Keycloak server, providing :
    • a username
    • a password
    • a client id (which is fixed to 'shanoir-uploader' for now)
  2. A response from the Keycloak server contains :
    • an access token
    • a refresh token
  3. Now we can use the Shanoir REST API provinding in any request :
    • a http header called 'Authorization' with 'Bearer ' + the access token as a value
  4. Access tokens lifespan are set to 5 minutes. So if your script execution exceed this time, the next call to the Shanoir REST API might fail with a 401 code. In this case the access token can be refreshed using the refresh token.

About passwords security and refresh token

One reason security protocols use refresh token instead of a login/password authentication every 5 minutes is because we don't want to type our password every 5 minutes. Of course you could have your password written in clear text inside your script but it would be against basic IT security principles. Please avoid doing that.

The Shanoir REST API

REST

REST is a standard for http communications with web services. What you might want to know is that Shanoir uses http methods like the following :

  • GET : Read
  • POST : Create
  • PUT : Update
  • DELETE : Delete

Shanoir's

The Shanoir backend application is based on a microservice architecture. It means that the REST endpoints are distributed among several servers. Documentation pages (automatically generated with Springfox) are available for each microservice. Those pages list the REST endpoints in a user-friendly interface that even provide the possibility to directly request them (you would have to deal with the keyloak token manually though).

Here are the documentation pages for every microservice :

Exemple script

Here is a Python 3 script that can connect to Shanoir, deal with the token refreshing, display Shanoir data and download a dataset. The aim is to help you to use the Shanoir REST API not only with Python but also with any other technology that you use.

  • Please set your username at the indicated location at the beginning of the script
  • The exemples are at the end and use the rest_get() function to query Shanoir
  • The given exemples only read data, so we use http GET. If you need to save data on Shanoir you might want to use POST or PUT.
  • rest_get() uses ask_access_token() (that asks your password) and refresh_access_token() if needed
import requests
import json
import getpass
import re
from http.client import responses


server_domain = 'shanoir.irisa.fr'
username = 'unset' # your Shanoir username !!!


access_token = None
refresh_token = None

# using user's password, get the first access token and the refresh token
def ask_access_token():
    try:
        password = getpass.getpass(prompt='Password for Shanoir user ' + username + ': ', stream=None) 
    except:
        exit(0)
    url = 'https://' + server_domain + '/auth/realms/shanoir-ng/protocol/openid-connect/token'
    payload = {
        'client_id' : 'shanoir-uploader', 
        'grant_type' : 'password', 
        'username' : username, 
        'password' : password,
        'scope' : 'offline_access'
    }
    headers = {'content-type': 'application/x-www-form-urlencoded'}
    print('get keycloak token...', end=' ')
    response = requests.post(url, data=payload, headers=headers)
    print('response status :', response.status_code, responses[response.status_code])
    response_json = json.loads(response.text)
    if 'error_description' in response_json and response_json['error_description'] == 'Invalid user credentials':
        print('bad username or password')
        exit(1)
    global refresh_token 
    refresh_token = response_json['refresh_token']
    return response_json['access_token']

# get a new acess token using the refresh token
def refresh_access_token():
    url = 'https://' + server_domain + '/auth/realms/shanoir-ng/protocol/openid-connect/token'
    payload = {
        'grant_type' : 'refresh_token',
        'refresh_token' : refresh_token,
        'client_id' : 'shanoir-uploader'
    }
    headers = {'content-type': 'application/x-www-form-urlencoded'}
    print('refresh keycloak token...', end=' ')
    response = requests.post(url, data=payload, headers=headers)
    print('response status :', response.status_code, responses[response.status_code])
    response_json = json.loads(response.text)
    return response_json['access_token']

# perform a GET request on the given url, asks for a new access token if the current one is outdated
def rest_get(url) :
    global access_token
    if access_token is None:
        access_token = ask_access_token()
    headers = { 
        'Authorization' : 'Bearer ' + access_token,
        'content-type' : 'application/json'
    }
    response = requests.get(url, headers=headers)
    # if token is outdated, refresh it and try again
    if response.status_code == 401:
        access_token = refresh_access_token()
        headers['Authorization'] = 'Bearer ' + access_token
        response = requests.get(url, headers=headers)
    if 'Content-Type' in response.headers and 'application/json' in response.headers['Content-Type']:
        # if the response is json data we return the body parsed
        return json.loads(response.text)
    else:
        # else just return the whole response
        return response



################
### EXEMPLES ###
################

# get every acquisition equipment from shanoir
url = 'https://' + server_domain + '/shanoir-ng/studies/acquisitionequipments'
print(json.dumps(rest_get(url), indent=4, sort_keys=True))

# get one acquisition equipment
url = 'https://' + server_domain + '/shanoir-ng/studies/acquisitionequipments/244'
print(json.dumps(rest_get(url), indent=4, sort_keys=True))

# download a given dataset as nifti into the current folder
# !!! You might not have the right to download this dataset, change 100 to a dataset id that you can download
url = 'https://' + server_domain + '/shanoir-ng/datasets/datasets/download/100?format=nii' # ?format=dcm for dicom
dl_request = rest_get(url)
filename = re.findall('filename=(.+)', dl_request.headers.get('Content-Disposition'))[0]
open(filename, 'wb').write(dl_request.content)
Clone this wiki locally