This API is meant to be a not too bad documented gate to SafeNet while enabling fast and easy adaption to network changes
Connection settings:
Apps can make HTTP requests to http://localhost:8100. This is a local REST server that is automatically started when the user opens SAFE Launcher. Introduction to the Safe Launcher
import requests
import json
import urllib
import hashlib
import sys
from os.path import expanduser
class SafeException(Exception):
def __init__(self, response):
self._raw_text = response.text
s = '<[%d] %s>' % (response.status_code, response.text)
super(SafeException, self).__init__(s)
def json(self):
return json.loads(self._raw_text)
class Safe:
def __init__(self,
name,
version,
vendor,
id,
addr='http://localhost',
port=8100):
self.name = name
self.version = version
self.vendor = vendor
self.id = id
self.url = "%s:%d/" % (addr, port)
self.token = ""
def _get_url(self, location):
return self.url + location
Safe._get_url = _get_url
del(_get_url)
(the token is obtained via authenticaton as described later on)
requests look like this:
GET /immutable-data/:handleId
http://localhost:8100/immutable-data/2
Field | Description |
---|---|
Authorization | The authorization token obtained from SAFE Launcher. |
Range | Partial content of the immutable data can be obtained by specifying the range. The start and end offset can be passed in the range header (e.g bytes=0-1000). This is an optional field, if not specified the entire content of the immutable data is streamed to the client. |
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpZCI6Im5RT1poRFJ2VUFLRlVZMzNiRTlnQ25VbVVJSkV0Q2lmYk4zYjE1dXZ2TlU9In0.OTKcHQ9VUKYzBXH_MqeWR4UcHFJV-xlllR68UM9l0b4
On success, if a Range header was specified in the request, then the HTTP status code in the response header is 206 (Partial Content). Otherwise, it's 200 (OK).
Property | Description |
---|---|
Content-Range | Range being streamed / total size. |
Content-Length | Original (pre-encryption) size of the immutable data. |
Content-Range: bytes 0-25607/25607
Content-Length: 25607
The binary data of the immutable data.
def _get(self, path):
headers = {
'Content-Type': 'application/json'
}
if self.token:
headers['Authorization'] = 'Bearer %s' % self.token
url = self._get_url(path)
r = requests.get(url,
headers=headers)
return r
Safe._get = _get
del(_get)
def _post(self, path, payload):
return self._request('POST', path, payload)
Safe._post = _post
del(_post)
def _request(self, request, path, payload):
headers = {
'Content-Type': 'application/json'
}
if self.token:
headers['Authorization'] = 'Bearer %s' % self.token
url = self._get_url(path)
payload = json.dumps(payload)
r = requests.request(request,
url,
data=payload,
headers=headers)
return r
Safe._request = _request
del(_request)
def _post_file(self, path, payload, content):
headers = {
'Content-Type': 'application/json',
'Content-Length': sys.getsizeof(content)
}
if self.token:
headers['Authorization'] = 'Bearer %s' % self.token
url = self._get_url(path)
payload = json.dumps(payload)
r = requests.post(url,
data=content,
headers=headers)
return r
Safe._post_file = _post_file
del(_post_file)
def authenticate(self, permissions=[]): #TODO check is needs to = None
# map is required as /auth returns unicode
self.permissions = map(unicode, permissions)
if self._get_saved_token():
return True
payload = {
'app': {
'name': self.name,
'version': self.version,
'vendor': self.vendor,
'id': self.id
},
'permissions': permissions
}
r = self._post('auth', payload)
if r.status_code == 200:
responseJson = r.json()
self.token = responseJson['token']
self.permissions = responseJson['permissions']
self._save_token()
return True
else:
return False
Safe.authenticate = authenticate
del(authenticate)
def _get_saved_token(self):
try:
with open(expanduser('~/.safe_store'), 'r') as f:
appHash = self._get_app_hash()
tokens = json.loads(f.read())
self.token = tokens[appHash]
if self.is_authenticated():
return True
else:
self.token = ''
return False
except (IOError, ValueError, KeyError):
self.token = ''
return False
Safe._get_saved_token = _get_saved_token
del(_get_saved_token)
def _save_token(self):
try:
with open(expanduser('~/.safe_store'), 'r+') as f:
appHash = self._get_app_hash()
try:
tokens = json.loads(f.read())
except ValueError:
tokens = {}
tokens[appHash] = self.token
f.seek(0)
f.write(json.dumps(tokens))
f.truncate()
except IOError:
pass
Safe._save_token = _save_token
del(_save_token)
def _get_app_hash(self):
# Tokens will be in plain text - md5 will suffice
m = hashlib.md5()
m.update(self.name)
m.update(self.version)
m.update(self.vendor)
m.update(self.id)
m.update(str(self.permissions))
return m.hexdigest()
Safe._get_app_hash = _get_app_hash
del(_get_app_hash)
def is_authenticated(self):
try: # If not token saved definitely not authenticated
self.token
except AttributeError:
return False
r = self._get('auth')
if r.status_code == 200:
return True
else:
return False
Safe.is_authenticated = is_authenticated
del(is_authenticated)
def mkdirSafe(self, rootPath, dirPath, metadata=None):
payload = {
'metadata': metadata,
}
url = 'nfs/directory/%s/%s' % (rootPath, dirPath)
r = self._post(url, payload)
if r.status_code == 200:
return True
else:
raise SafeException(r)
Safe.mkdir = mkdirSafe
del(mkdirSafe)
def get_dir(self, rootPath, dirPath):
url = 'nfs/directory/%s/%s' % (rootPath, dirPath)
r = self._get(url)
if r.status_code == 200:
return json.loads(r.text)
elif r.status_code == 401:
raise SafeException(r)
else:
return None
Safe.get_dir = get_dir
del(get_dir)
def update_dir(self, rootPath, dirPath, newPath, metadata=None):
payload = {
'name': newPath,
'metadata': metadata
}
url = 'nfs/directory/%s/%s' % (rootPath, dirPath)
r = self._request('put', url, payload)
if r.status_code == 200:
return True
else:
raise SafeException(r)
Safe.update_dir = update_dir
del(update_dir)
def move_dir(self, srcRootPath, srcPath,
destRootPath, destPath, action='move'):
payload = {
'srcRootPath': srcRootPath,
'srcPath': srcPath,
'destRootPath': destRootPath,
'destPath': destPath,
'action': action
}
url = 'nfs/movedir'
r = self._post(url, payload)
if r.status_code == 200:
return True
else:
raise SafeException(r)
Safe.move_dir = move_dir
del(move_dir)
def delete_dir(self, rootPath, dirPath):
url = 'nfs/directory/%s/%s' % (rootPath, dirPath)
r = self._request('delete', url, None)
if r.status_code == 200:
return True
else:
raise SafeException(r)
Safe.delete_dir = delete_dir
del(delete_dir)
def create_file(self, rootPath, filePath, content, metadata=None):
payload = {
'metadata': metadata,
}
url = 'nfs/file/%s/%s' % (rootPath, filePath)
if content:
r = self._post_file(url, payload, content)
else:
r = self._post(url, payload)
if r.status_code == 200:
return True
else:
raise SafeException(r)
Safe.create_file = create_file
del(create_file)
def read_file(self, rootPath, dirPath):
path = 'nfs/file/%s/%s' % (rootPath, dirPath)
r = self._get(path)
if r.status_code == 200:
return r.text
elif r.status_code == 401:
raise SafeException(r)
else:
return None
Safe.read_file = read_file
del(read_file)
def delete_file(self, rootPath, filePath):
path = 'nfs/file/%s/%s' % (rootPath, filePath)
r = self._request('DELETE', path, None)
if r.status_code == 200:
return True
else:
raise SafeException(r)
Safe.delete_file = delete_file
del(delete_file)
def create_long_name(self, longname):
url = 'dns/%s/' % longname
r = self._post(url, None)
if r.status_code == 200:
return True
else:
raise SafeException(r)
Safe.create_long_name = create_long_name
del(create_long_name)
def register_dns(self, rootPath, longName, serviceName, serviceHomeDirPath):
payload = {
'rootPath': rootPath,
'longName': longName,
'serviceName': serviceName,
'serviceHomeDirPath': serviceHomeDirPath,
}
r = self._post('dns', payload)
if r.status_code == 200:
return True
else:
raise SafeException(r)
Safe.register_dns = register_dns
del(register_dns)
def add_service(self, longName, serviceName, rootPath, serviceHomeDirPath):
payload = {
'longName': longName,
'serviceName': serviceName,
'rootPath': rootPath,
'serviceHomeDirPath': serviceHomeDirPath
}
r = self._request('PUT', 'dns', payload)
if r.status_code == 200:
return True
else:
raise SafeException(r)
Safe.add_service = add_service
del(add_service)
def get_long_names(self):
url = 'dns/'
r = self._get(url)
if r.status_code == 200:
return r.json()
else:
raise SafeException(r)
Safe.get_long_names = get_long_names
del(get_long_names)
def get_dns(self, longName):
url = 'dns/%s' % longName
r = self._get(url)
if r.status_code == 200:
return json.loads(r.text)
elif r.status_code == 401:
raise SafeException("Unauthorised")
else:
return None
Safe.get_dns = get_dns
del(get_dns)
def get_service_home_directory(self, serviceName, longName):
url = 'dns/%s/%s' % (serviceName, longName)
r = self._get(url)
if r.status_code == 200:
return r.json()
else:
raise SafeException(r)
Safe.get_service_home_directory = get_service_home_directory
del(get_service_home_directory)
def delete_service_from_long_name(self, serviceName, longName):
url = 'dns/%s/%s' % (serviceName, longName)
r = self._request('DELETE', url, None)
if r.status_code == 200:
return True
else:
raise SafeException(r)
Safe.delete_service_from_long_name = delete_service_from_long_name
del(delete_service_from_long_name)
def delete_long_name(self, longName):
url = 'dns/%s' % longName
r = self._request('DELETE', url, None)
if r.status_code == 200:
return True
else:
raise SafeException(r)
Safe.delete_long_name = delete_long_name
del(delete_long_name)