-
Notifications
You must be signed in to change notification settings - Fork 22
REST Tutorial
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 :
- 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)
- A response from the Keycloak server contains :
- an access token
- a refresh token
- 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
- 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.
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.
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
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 :
- Studies : https://shanoir.irisa.fr/shanoir-ng/studies/swagger-ui.html
- Manage studies, subjects, centers, equipments, ...
- Datasets : https://shanoir.irisa.fr/shanoir-ng/datasets/swagger-ui.html
- Manage datasets, examinations, acquisitions, ...
- Import : https://shanoir.irisa.fr/shanoir-ng/import/swagger-ui.html
- Import datasets
- Preclinical : https://shanoir.irisa.fr/shanoir-ng/preclinical/swagger-ui.html
- Manage animal subject and preclinical related data
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()
usesask_access_token()
(that asks your password) andrefresh_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)