Skip to content
Snippets Groups Projects
Commit 22ee7557 authored by Gunnar Schaefer's avatar Gunnar Schaefer
Browse files

Merge branch 'gsfr-current-user'

parents 3c8d33bd 65fa9198
No related branches found
No related tags found
No related merge requests found
# @author: Gunnar Schaefer
import re
import json
import webapp2
import bson.json_util
import logging
log = logging.getLogger('nimsapi')
import bson.json_util
import nimsapiutil
......@@ -48,7 +45,9 @@ class Collections(nimsapiutil.NIMSRequestHandler):
def count(self):
"""Return the number of Collections."""
self.response.write(json.dumps(self.app.db.collections.count()))
if self.request.method == 'OPTIONS':
return self.options()
self.response.write(self.app.db.collections.count())
def post(self):
"""Create a new Collection."""
......@@ -63,8 +62,7 @@ class Collections(nimsapiutil.NIMSRequestHandler):
"""Return the list of Collections."""
query = {'permissions.uid': self.uid} if not self.user_is_superuser else None
projection = {'curator': 1, 'name': 1, 'notes': 1, 'permissions': {'$elemMatch': {'uid': self.uid}}}
collections = list(self.app.db.collections.find(query, projection))
self.response.write(json.dumps(collections, default=bson.json_util.default))
return list(self.app.db.collections.find(query, projection))
def put(self):
"""Update many Collections."""
......@@ -114,8 +112,7 @@ class Collection(nimsapiutil.NIMSRequestHandler):
def get(self, cid):
"""Return one Collection, conditionally with details."""
cid = bson.ObjectId(cid)
collection = self.get_collection(cid)
self.response.write(json.dumps(collection, default=bson.json_util.default))
return self.get_collection(cid)
def put(self, cid):
"""Update an existing Collection."""
......@@ -171,7 +168,9 @@ class Sessions(nimsapiutil.NIMSRequestHandler):
def count(self):
"""Return the number of Sessions."""
self.response.write(json.dumps(self.app.db.sessions.count()))
if self.request.method == 'OPTIONS':
return self.options()
self.response.write(self.app.db.sessions.count())
def post(self):
"""Create a new Session"""
......@@ -190,7 +189,7 @@ class Sessions(nimsapiutil.NIMSRequestHandler):
sessions = list(self.app.db.sessions.find(query, projection))
for sess in sessions:
sess['site'] = self.app.config['site_id']
self.response.write(json.dumps(sessions, default=bson.json_util.default))
return sessions
def put(self):
"""Update many Sessions."""
......@@ -230,7 +229,9 @@ class Epochs(nimsapiutil.NIMSRequestHandler):
def count(self):
"""Return the number of Epochs."""
self.response.write(json.dumps(self.app.db.epochs.count()))
if self.request.method == 'OPTIONS':
return self.options()
self.response.write(self.app.db.epochs.count())
def post(self):
"""Create a new Epoch."""
......@@ -247,9 +248,7 @@ class Epochs(nimsapiutil.NIMSRequestHandler):
elif sid != '':
self.abort(400, sid + ' is not a valid ObjectId')
projection = ['name', 'description', 'datatype', 'notes']
print query
epochs = list(self.app.db.epochs.find(query, projection))
self.response.write(json.dumps(epochs, default=bson.json_util.default))
return list(self.app.db.epochs.find(query, projection))
def put(self):
"""Update many Epochs."""
......
# @author: Gunnar Schaefer
import json
import webapp2
import bson.json_util
import logging
log = logging.getLogger('nimsapi')
import bson.json_util
import nimsdata
import nimsapiutil
......@@ -51,7 +49,9 @@ class Experiments(nimsapiutil.NIMSRequestHandler):
def count(self):
"""Return the number of Experiments."""
self.response.write(json.dumps(self.app.db.experiments.count()))
if self.request.method == 'OPTIONS':
return self.options()
self.response.write(self.app.db.experiments.count())
def post(self):
"""Create a new Experiment."""
......@@ -64,7 +64,7 @@ class Experiments(nimsapiutil.NIMSRequestHandler):
experiments = list(self.app.db.experiments.find(query, projection))
for exp in experiments:
exp['site'] = self.app.config['site_id']
self.response.write(json.dumps(experiments, default=bson.json_util.default))
return experiments
def put(self):
"""Update many Experiments."""
......@@ -117,8 +117,7 @@ class Experiment(nimsapiutil.NIMSRequestHandler):
def get(self, xid):
"""Return one Experiment, conditionally with details."""
xid = bson.ObjectId(xid)
experiment = self.get_experiment(xid)
self.response.write(json.dumps(experiment, default=bson.json_util.default))
return self.get_experiment(xid)
def put(self, xid):
"""Update an existing Experiment."""
......@@ -168,7 +167,9 @@ class Sessions(nimsapiutil.NIMSRequestHandler):
def count(self):
"""Return the number of Sessions."""
self.response.write(json.dumps(self.app.db.sessions.count()))
if self.request.method == 'OPTIONS':
return self.options()
self.response.write(self.app.db.sessions.count())
def post(self):
"""Create a new Session"""
......@@ -180,8 +181,7 @@ class Sessions(nimsapiutil.NIMSRequestHandler):
self.get_experiment(xid) # ensure permissions
query = {'experiment': xid}
projection = ['name', 'subject', 'notes']
sessions = list(self.app.db.sessions.find(query, projection))
self.response.write(json.dumps(sessions, default=bson.json_util.default))
return list(self.app.db.sessions.find(query, projection))
def put(self):
"""Update many Sessions."""
......@@ -222,16 +222,17 @@ class Session(nimsapiutil.NIMSRequestHandler):
}
def schema(self, *args, **kwargs):
if self.request.method == 'OPTIONS':
return self.options()
import copy
json_schema = copy.deepcopy(self.json_schema)
json_schema['properties'].update(nimsdata.NIMSData.session_properties)
self.response.write(json.dumps(json_schema, default=bson.json_util.default))
return json_schema
def get(self, sid):
"""Return one Session, conditionally with details."""
sid = bson.ObjectId(sid)
session = self.get_session(sid)
self.response.write(json.dumps(session, default=bson.json_util.default))
return self.get_session(sid)
def put(self, sid):
"""Update an existing Session."""
......@@ -247,15 +248,6 @@ class Session(nimsapiutil.NIMSRequestHandler):
"""Delete a Session."""
self.abort(501)
def move(self, sid):
"""
Move a Session to another Experiment.
Usage:
/nimsapi/sessions/123/move?dest=456
"""
self.response.write('session %s move, %s\n' % (sid, self.request.params))
class Epochs(nimsapiutil.NIMSRequestHandler):
......@@ -290,7 +282,9 @@ class Epochs(nimsapiutil.NIMSRequestHandler):
def count(self):
"""Return the number of Epochs."""
self.response.write(json.dumps(self.app.db.epochs.count()))
if self.request.method == 'OPTIONS':
return self.options()
self.response.write(self.app.db.epochs.count())
def post(self):
"""Create a new Epoch."""
......@@ -302,8 +296,7 @@ class Epochs(nimsapiutil.NIMSRequestHandler):
self.get_session(sid) # ensure permissions
query = {'session': sid}
projection = ['name', 'description', 'datatype', 'notes']
epochs = list(self.app.db.epochs.find(query, projection))
self.response.write(json.dumps(epochs, default=bson.json_util.default))
return list(self.app.db.epochs.find(query, projection))
def put(self):
"""Update many Epochs."""
......@@ -340,16 +333,17 @@ class Epoch(nimsapiutil.NIMSRequestHandler):
}
def schema(self, *args, **kwargs):
if self.request.method == 'OPTIONS':
return self.options()
import copy
json_schema = copy.deepcopy(self.json_schema)
json_schema['properties'].update(nimsdata.nimsdicom.NIMSDicom.epoch_properties)
self.response.write(json.dumps(json_schema, default=bson.json_util.default))
return json_schema
def get(self, eid):
"""Return one Epoch, conditionally with details."""
eid = bson.ObjectId(eid)
epoch = self.get_epoch(eid)
self.response.write(json.dumps(epoch, default=bson.json_util.default))
return self.get_epoch(eid)
def put(self, eid):
"""Update an existing Epoch."""
......
......@@ -2,6 +2,12 @@
#
# @author: Gunnar Schaefer, Kevin S. Hahn
import logging
import logging.config
log = logging.getLogger('internims')
logging.getLogger('requests').setLevel(logging.WARNING)
import re
import json
import base64
import datetime
......@@ -10,15 +16,10 @@ import Crypto.Hash.SHA
import Crypto.PublicKey.RSA
import Crypto.Signature.PKCS1_v1_5
import logging
import logging.config
log = logging.getLogger('internims')
logging.getLogger('requests').setLevel(logging.WARNING)
def update(db, api_uri, site_id, privkey, internims_url):
"""sends is-alive signal to internims central."""
db.remotes.ensure_index('UTC', expireAfterSeconds=120)
db.remotes.ensure_index('timestamp', expireAfterSeconds=120)
exp_userlist = [e['permissions'] for e in db.experiments.find(None, {'_id': False, 'permissions.uid': True})]
col_userlist = [c['permissions'] for c in db.collections.find(None, {'_id': False, 'permissions.uid': True})]
......@@ -29,25 +30,27 @@ def update(db, api_uri, site_id, privkey, internims_url):
signature = Crypto.Signature.PKCS1_v1_5.new(privkey).sign(h)
headers = {'Authorization': base64.b64encode(signature)}
r = requests.post(url=internims_url, data=payload, headers=headers, verify=True)
r = requests.post(internims_url, data=payload, headers=headers)
if r.status_code == 200:
response = (json.loads(r.content))
# update remotes entries
for site in response['sites']:
site['UTC'] = datetime.datetime.strptime(site['timestamp'], '%Y-%m-%dT%H:%M:%S.%f')
db.remotes.find_and_modify({'_id': site['_id']}, update=site, upsert=True)
log.debug('upserting remote: ' + site['_id'])
# update, add remotes to users
new_remotes = response['users']
log.debug('users w/ remotes: ' + str(new_remotes))
for user in response['users']:
db.users.update({'uid': user}, {'$set': {'remotes': new_remotes.get(user, [])}})
# cannot use new_remotes.viewkeys(). leads to 'bson.errors.InvalidDocument: Cannot encode object: dict_keys([])'
db.users.update({'remotes': {'$exists':True}, 'uid': {'$nin': new_remotes.keys()}}, {'$unset': {'remotes': ''}}, multi=True)
site['timestamp'] = datetime.datetime.strptime(site['timestamp'], '%Y-%m-%dT%H:%M:%S.%fZ')
db.remotes.update({'_id': site['_id']}, site, upsert=True)
log.debug('updating remotes: ' + ', '.join((r['_id'] for r in response['sites'])))
# delete remotes from users, who no longer have remotes
db.users.update({'remotes': {'$exists':True}, '_id': {'$nin': response['users'].keys()}}, {'$unset': {'remotes': ''}}, multi=True)
# add remotes to users
log.debug('users w/ remotes: ' + ', '.join(response['users']))
for uid, remotes in response['users'].iteritems():
db.users.update({'_id': uid}, {'$set': {'remotes': remotes}})
else:
log.warning((r.status_code, r.reason))
# r.reason contains generic description for the specific error code
# need the part of the error response body that contains the detailed explanation
reason = re.search('<br /><br />\n(.*)\n\n\n </body>\n</html>', r.content)
log.warning((r.status_code, reason.group(1)))
if __name__ == '__main__':
......
This diff is collapsed.
......@@ -48,6 +48,7 @@ else:
site_id = config.get('nims', 'site_id')
application = nimsapi.app
application.config['stage_path'] = config.get('nims', 'stage_path')
application.config['log_path'] = config.get('nims', 'log_path')
application.config['site_id'] = site_id
application.config['ssl_key'] = privkey
application.config['oauth2_id_endpoint'] = config.get('oauth2', 'id_endpoint')
......
# @author: Gunnar Schaefer, Kevin S. Hahn
import logging
log = logging.getLogger('nimsapi')
logging.getLogger('requests').setLevel(logging.WARNING) # silence Requests library logging
import json
import base64
import webapp2
......@@ -10,10 +14,6 @@ import Crypto.Hash.SHA
import Crypto.PublicKey.RSA
import Crypto.Signature.PKCS1_v1_5
import logging
log = logging.getLogger('nimsapi')
logging.getLogger('requests').setLevel(logging.WARNING) # silence Requests library logging
INTEGER_ROLES = {
'anon-read': 0,
'read-only': 1,
......@@ -57,174 +57,175 @@ class NIMSRequestHandler(webapp2.RequestHandler):
def __init__(self, request=None, response=None):
self.initialize(request, response)
self.uid = '@public' # @public is default user
self.target_id = self.request.get('iid', None)
self.access_token = self.request.headers.get('Authorization', None)
log.debug('accesstoken: ' + str(self.access_token))
# CORS header
if 'Origin' in self.request.headers and self.request.headers['Origin'].startswith('https://'):
self.response.headers['Access-Control-Allow-Origin'] = self.request.headers['Origin']
if self.access_token and self.app.config['oauth2_id_endpoint']:
r = requests.request(method='GET', url = self.app.config['oauth2_id_endpoint'], headers={'Authorization': 'Bearer ' + self.access_token})
r = requests.get(self.app.config['oauth2_id_endpoint'], headers={'Authorization': 'Bearer ' + self.access_token})
if r.status_code == 200:
oauth_user = json.loads(r.content)
self.uid = oauth_user['email']
log.debug('oauth user: ' + oauth_user['email'])
self.uid = json.loads(r.content)['email']
else:
#TODO: add handlers for bad tokens.
log.debug('ERR: ' + str(r.status_code) + ' bad token')
# TODO: add handlers for bad tokens
# inform app of expired token, app will try to get new token, or ask user to log in again
log.debug('ERR: ' + str(r.status_code) + r.reason + ' bad token')
elif self.app.config['insecure'] and 'X-Requested-With' not in self.request.headers and self.request.get('user', None):
self.uid = self.request.get('user')
else:
self.uid = '@public'
self.user_is_superuser = False
if self.uid != '@public':
user = self.app.db.users.find_one({'_id': self.uid}, ['superuser'])
if user:
self.user_is_superuser = user.get('superuser', None)
else:
self.abort(403, 'user ' + self.uid + ' does not exist')
self.user = self.app.db.users.find_one({'uid': self.uid})
self.user_is_superuser = self.user.get('superuser', None) if self.user else False
if self.target_id not in [None, self.app.config['site_id']]:
self.rtype = 'to_remote'
# p2p request
self.target_id = self.request.get('iid', None)
self.p2p_user = self.request.headers.get('X-From', None)
self.site_id = self.app.config['site_id']
self.ssl_key = self.app.config['ssl_key']
log.debug('X-From: ' + str(self.p2p_user))
if not self.app.config['site_id']:
self.abort(500, 'api site_id is not configured')
if not self.app.config['ssl_key']:
self.abort(500, 'api ssl_key is not configured')
# CORS bare minimum
self.response.headers.add('Access-Control-Allow-Origin', self.request.headers.get('origin', '*'))
target = self.app.db.remotes.find_one({'_id': self.target_id}, {'_id': False, 'api_uri': True})
if not target:
self.abort(402, 'remote host ' + self.target_id + ' is not an authorized remote')
if not self.request.path.endswith('/nimsapi/log'):
log.info(self.request.method + ' ' + self.request.path + ' ' + str(self.request.params.mixed()))
# adjust headers
self.headers = self.request.headers
self.headers['User-Agent'] = 'NIMS Instance ' + self.app.config['site_id']
self.headers['X-From'] = (self.uid + '#' + self.app.config['site_id']) if self.uid != '@public' else self.uid
self.headers['Content-Length'] = len(self.request.body)
self.headers['Date'] = str(datetime.datetime.now().strftime('%a, %d %b %Y %H:%M:%S')) # Nonce for msg
del self.headers['Host']
if self.headers.get('Authorization'): del self.headers['Authorization']
# adjust params
self.params = self.request.params.mixed()
if self.params.get('user'): del self.params['user']
del self.params['iid']
# assemble msg, hash, and signature
msg = self.request.method + self.request.path + str(self.params) + self.request.body + self.headers.get('Date')
signature = Crypto.Signature.PKCS1_v1_5.new(self.app.config['ssl_key']).sign(Crypto.Hash.SHA.new(msg))
self.headers['X-Signature'] = base64.b64encode(signature)
# prepare delegated request URI
self.target_uri = target['api_uri'] + self.request.path.split('/nimsapi')[1]
elif self.request.user_agent.startswith('NIMS Instance'):
self.rtype = 'from_remote'
self.uid = self.request.headers.get('X-From')
self.user_is_superuser = False
remote_instance = self.request.user_agent.replace('NIMS Instance', '').strip()
requester = self.app.db.remotes.find_one({'_id': remote_instance})
if not requester:
self.abort(402, remote_instance + ' is not authorized')
# assemble msg, hash, and verify received signature
signature = base64.b64decode(self.request.headers.get('X-Signature'))
msg = self.request.method + self.request.path + str(self.request.params.mixed()) + self.request.body + self.request.headers.get('Date')
verifier = Crypto.Signature.PKCS1_v1_5.new(Crypto.PublicKey.RSA.importKey(requester['pubkey']))
if not verifier.verify(Crypto.Hash.SHA.new(msg), signature):
self.abort(402, 'remote message/signature is not authentic')
else:
self.rtype = 'local'
def dispatch(self):
"""dispatching and request forwarding"""
# dispatch to local instance
if self.target_id in [None, self.site_id]:
# request originates from remote instance
if self.request.user_agent.startswith('NIMS Instance'):
# is the requester an authorized remote site
requester = self.request.user_agent.replace('NIMS Instance', '').strip()
target = self.app.db.remotes.find_one({'_id':requester})
if not target:
log.debug('remote host ' + requester + ' not in auth list. DENIED')
self.abort(403, requester + ' is not authorized')
log.debug('request from ' + self.request.user_agent + ', interNIMS p2p initiated')
# verify signature
self.signature = base64.b64decode(self.request.headers.get('X-Signature'))
# assemble msg to be hased
msg = self.request.method + self.request.path + str(dict(self.request.params)) + self.request.body + self.request.headers.get('Date')
key = Crypto.PublicKey.RSA.importKey(target['pubkey'])
h = Crypto.Hash.SHA.new(msg)
verifier = Crypto.Signature.PKCS1_v1_5.new(key)
if verifier.verify(h, self.signature):
super(NIMSRequestHandler, self).dispatch()
else:
log.debug('message/signature is not authentic')
self.abort(403, 'authentication failed')
# request originates from self
else:
super(NIMSRequestHandler, self).dispatch()
# dispatch to remote instance
elif self.ssl_key is not None and self.site_id is not None:
log.debug('dispatching to remote ' + self.target_id)
# is target registered?
target = self.app.db.remotes.find_one({'_id': self.target_id}, {'_id':False, 'api_uri':True})
if not target:
log.debug('remote host ' + self.target_id + ' not in auth list. DENIED')
self.abort(403, self.target_id + 'is not authorized')
# adjust headers
headers = self.request.headers
headers['User-Agent'] = 'NIMS Instance ' + self.site_id
headers['X-From'] = self.uid
headers['Content-Length'] = len(self.request.body)
del headers['Host'] # delete old host destination
try:
del headers['Authorization'] # delete access_token
except KeyError:
pass # not all requests will have access_token
# assemble msg to be hashed
nonce = str(datetime.datetime.now().strftime('%a, %d %b %Y %H:%M:%S'))
headers['Date'] = nonce
msg = self.request.method + self.request.path + str(dict(self.request.params)) + self.request.body + nonce
# create a signature
h = Crypto.Hash.SHA.new(msg)
signature = Crypto.Signature.PKCS1_v1_5.new(self.ssl_key).sign(h)
headers['X-Signature'] = base64.b64encode(signature)
# construct outgoing request
target_api = 'https://' + target['api_uri'] + self.request.path.split('/nimsapi')[1]
r = requests.request(method=self.request.method, data=self.request.body, url=target_api, params=self.request.params, headers=headers, verify=False)
# return response content
# TODO: think about: are the headers even useful?
log.debug(self.rtype + ' ' + self.uid + ' ' + self.request.method + ' ' + self.request.path + ' ' + str(self.request.params.mixed()))
if self.rtype in ['local', 'from_remote']:
return super(NIMSRequestHandler, self).dispatch()
else:
if self.request.method == 'OPTIONS':
return self.options()
r = requests.request(self.request.method, self.target_uri, params=self.params, data=self.request.body, headers=self.headers, verify=False)
if r.status_code != 200:
self.abort(r.status_code, 'internims p2p err: ' + r.reason)
self.response.write(r.content)
elif self.ssl_key is None or self.site_id is None:
log.debug('ssl key or site id undefined, cannot dispatch to remote')
def abort(self, code, *args, **kwargs):
log.warning(str(code) + ' ' + '; '.join(args))
if 'Access-Control-Allow-Origin' in self.response.headers:
headers = kwargs.setdefault('headers', {})
headers['Access-Control-Allow-Origin'] = self.response.headers['Access-Control-Allow-Origin']
webapp2.abort(code, *args, **kwargs)
def options(self, *args, **kwargs):
self.response.headers['Access-Control-Allow-Methods'] = 'GET, HEAD, POST, PUT, DELETE, OPTIONS'
self.response.headers['Access-Control-Allow-Headers'] = 'Authorization'
self.response.headers['Access-Control-Max-Age'] = '151200'
def schema(self, *args, **kwargs):
self.response.write(json.dumps(self.json_schema, default=bson.json_util.default))
def schema(self):
if self.request.method == 'OPTIONS':
return self.options()
return self.json_schema
def get_collection(self, cid, min_role='anon-read'):
def get_collection(self, cid, min_role=None):
collection = self.app.db.collections.find_one({'_id': cid})
if not collection:
self.abort(404)
self.abort(404, 'no such Collection')
if not self.user_is_superuser:
for perm in collection['permissions']:
if perm['uid'] == self.uid:
break
else:
self.abort(403, self.uid + ' does not have permission to this Collection')
if INTEGER_ROLES[perm['role']] < INTEGER_ROLES[min_role]:
self.abort(403, self.uid + ' does not have at least ' + min_role + ' on this Collection')
if perm['role'] != 'admin': # if not admin, mask all other permissions
collection['permissions'] = [{'uid': self.uid, 'role': perm['role']}]
coll = self.app.db.collections.find_one({'_id': cid, 'permissions.uid': self.uid}, ['permissions.$'])
if not coll:
self.abort(403, self.uid + ' does not have permissions on this Collection')
if min_role and INTEGER_ROLES[coll['permissions'][0]['role']] < INTEGER_ROLES[min_role]:
self.abort(403, self.uid + ' does not have at least ' + min_role + ' permissions on this Collection')
if coll['permissions'][0]['role'] != 'admin': # if not admin, mask permissions of other users
collection['permissions'] = coll['permissions']
return collection
def get_experiment(self, xid, min_role='anon-read'):
def get_experiment(self, xid, min_role=None):
experiment = self.app.db.experiments.find_one({'_id': xid})
if not experiment:
self.abort(404)
self.abort(404, 'no such Experiment')
if not self.user_is_superuser:
for perm in experiment['permissions']:
if perm['uid'] == self.uid:
break
else:
self.abort(403, self.uid + ' does not have permission to this Experiment')
if INTEGER_ROLES[perm['role']] < INTEGER_ROLES[min_role]:
self.abort(403, self.uid + ' does not have at least ' + min_role + ' on this Experiment')
if perm['role'] != 'admin': # if not admin, mask all other permissions
experiment['permissions'] = [{'uid': self.uid, 'role': perm['role']}]
exp = self.app.db.experiments.find_one({'_id': xid, 'permissions.uid': self.uid}, ['permissions.$'])
if not exp:
self.abort(403, self.uid + ' does not have permissions on this Experiment')
if min_role and INTEGER_ROLES[exp['permissions'][0]['role']] < INTEGER_ROLES[min_role]:
self.abort(403, self.uid + ' does not have at least ' + min_role + ' permissions on this Experiment')
if exp['permissions'][0]['role'] != 'admin': # if not admin, mask permissions of other users
experiment['permissions'] = exp['permissions']
return experiment
def get_session(self, sid, min_role='anon-read'):
#FIXME: implement min_role logic
def get_session(self, sid, min_role=None):
session = self.app.db.sessions.find_one({'_id': sid})
if not session:
self.abort(404)
experiment = self.app.db.experiments.find_one({'_id': session['experiment']})
if not experiment:
self.abort(500)
self.abort(404, 'no such Session')
if not self.user_is_superuser:
for perm in experiment['permissions']:
if perm['uid'] == self.uid:
break
else:
self.abort(403, 'user does not have permission to this Session')
experiment = self.app.db.experiments.find_one({'_id': session['experiment'], 'permissions.uid': self.uid}, ['permissions.$'])
if not experiment:
if not self.app.db.experiments.find_one({'_id': session['experiment']}, []):
self.abort(500)
else:
self.abort(403, self.uid + ' does not have permissions to this Session')
if min_role and INTEGER_ROLES[experiment['permissions'][0]['role']] < INTEGER_ROLES[min_role]:
self.abort(403, self.uid + ' does not have at least ' + min_role + ' permissions on this Session')
return session
def get_epoch(self, eid, min_role='anon-read'):
#FIXME: implement min_role logic
def get_epoch(self, eid, min_role=None):
epoch = self.app.db.epochs.find_one({'_id': eid})
if not epoch:
self.abort(404)
session = self.app.db.sessions.find_one({'_id': epoch['session']})
if not session:
self.abort(500)
experiment = self.app.db.experiments.find_one({'_id': session['experiment']})
if not experiment:
self.abort(500)
self.abort(404, 'no such Epoch')
if not self.user_is_superuser:
for perm in experiment['permissions']:
if perm['uid'] == self.uid:
break
else:
self.abort(403, 'user does not have permission to this Epoch')
session = self.app.db.sessions.find_one({'_id': epoch['session']}, ['experiment'])
if not session:
self.abort(500)
experiment = self.app.db.experiments.find_one({'_id': session['experiment'], 'permissions.uid': self.uid}, ['permissions.$'])
if not experiment:
if not self.app.db.experiments.find_one({'_id': session['experiment']}, []):
self.abort(500)
else:
self.abort(403, self.uid + ' does not have permissions on this Epoch')
if min_role and INTEGER_ROLES[experiment['permissions'][0]['role']] < INTEGER_ROLES[min_role]:
self.abort(403, self.uid + ' does not have at least ' + min_role + ' permissions on this Epoch')
return epoch
users.py 0 → 100644
# @author: Gunnar Schaefer
import logging
log = logging.getLogger('nimsapi')
import bson.json_util
import nimsapiutil
class Users(nimsapiutil.NIMSRequestHandler):
"""/nimsapi/users """
json_schema = {
'$schema': 'http://json-schema.org/draft-04/schema#',
'title': 'User List',
'type': 'array',
'items': {
'title': 'User',
'type': 'object',
'properties': {
'_id': {
'title': 'Database ID',
'type': 'string',
},
'firstname': {
'title': 'First Name',
'type': 'string',
'default': '',
},
'lastname': {
'title': 'Last Name',
'type': 'string',
'default': '',
},
'email': {
'title': 'Email',
'type': 'string',
'format': 'email',
'default': '',
},
'email_hash': {
'type': 'string',
'default': '',
},
}
}
}
def count(self):
"""Return the number of Users."""
if self.request.method == 'OPTIONS':
return self.options()
self.response.write(self.app.db.users.count())
def post(self):
"""Create a new User"""
self.response.write('users post\n')
def get(self):
"""Return the list of Users."""
return list(self.app.db.users.find({}, ['firstname', 'lastname', 'email_hash']))
def put(self):
"""Update many Users."""
self.response.write('users put\n')
class User(nimsapiutil.NIMSRequestHandler):
"""/nimsapi/users/<uid> """
json_schema = {
'$schema': 'http://json-schema.org/draft-04/schema#',
'title': 'User',
'type': 'object',
'properties': {
'_id': {
'title': 'Database ID',
'type': 'string',
},
'firstname': {
'title': 'First Name',
'type': 'string',
'default': '',
},
'lastname': {
'title': 'Last Name',
'type': 'string',
'default': '',
},
'email': {
'title': 'Email',
'type': 'string',
'format': 'email',
'default': '',
},
'email_hash': {
'type': 'string',
'default': '',
},
'superuser': {
'title': 'Superuser',
'type': 'boolean',
},
},
'required': ['_id'],
}
def get(self, uid):
"""Return User details."""
projection = []
if self.request.get('remotes') in ('1', 'true'):
projection += ['remotes']
if self.request.get('status') in ('1', 'true'):
projection += ['status']
user = self.app.db.users.find_one({'_id': uid}, projection or None)
if not user:
self.abort(404, 'no such User')
return user
def put(self, uid):
"""Update an existing User."""
user = self.app.db.users.find_one({'_id': uid})
if not user:
self.abort(404)
if uid == self.uid or self.user_is_superuser: # users can only update their own info
updates = {'$set': {}, '$unset': {}}
for k, v in self.request.params.iteritems():
if k != 'superuser' and k in []:#user_fields:
updates['$set'][k] = v # FIXME: do appropriate type conversion
elif k == 'superuser' and uid == self.uid and self.user_is_superuser is not None: # toggle superuser for requesting user
updates['$set'][k] = v.lower() in ('1', 'true')
elif k == 'superuser' and uid != self.uid and self.user_is_superuser: # enable/disable superuser for other user
if v.lower() in ('1', 'true') and user.get('superuser') is None:
updates['$set'][k] = False # superuser is tri-state: False indicates granted, but disabled, superuser privileges
elif v.lower() not in ('1', 'true'):
updates['$unset'][k] = ''
self.app.db.users.update({'_id': uid}, updates)
else:
self.abort(403)
def delete(self, uid):
"""Delete an User."""
self.response.write('user %s delete, %s\n' % (uid, self.request.params))
class Groups(nimsapiutil.NIMSRequestHandler):
"""/nimsapi/groups """
json_schema = {
'$schema': 'http://json-schema.org/draft-04/schema#',
'title': 'Group List',
'type': 'array',
'items': {
'title': 'Group',
'type': 'object',
'properties': {
'_id': {
'title': 'Database ID',
'type': 'string',
},
}
}
}
def count(self):
"""Return the number of Groups."""
if self.request.method == 'OPTIONS':
return self.options()
self.response.write(self.app.db.groups.count())
def post(self):
"""Create a new Group"""
self.response.write('groups post\n')
def get(self):
"""Return the list of Groups."""
return list(self.app.db.groups.find({}, []))
def put(self):
"""Update many Groups."""
self.response.write('groups put\n')
class Group(nimsapiutil.NIMSRequestHandler):
"""/nimsapi/groups/<gid>"""
json_schema = {
'$schema': 'http://json-schema.org/draft-04/schema#',
'title': 'Group',
'type': 'object',
'properties': {
'_id': {
'title': 'Database ID',
'type': 'string',
},
'name': {
'title': 'Name',
'type': 'string',
'maxLength': 32,
},
'roles': {
'title': 'Roles',
'type': 'array',
'default': [],
'items': {
'type': 'object',
'properties': {
'uid': {
'type': 'string',
},
'role': {
'type': 'string',
'enum': [k for k, v in sorted(nimsapiutil.INTEGER_ROLES.iteritems(), key=lambda (k, v): v)],
},
},
},
'uniqueItems': True,
},
},
'required': ['_id'],
}
def get(self, gid):
"""Return Group details."""
group = self.app.db.groups.find_one({'_id': gid})
if not group:
self.abort(404, 'no such Group')
return group
def put(self, gid):
"""Update an existing Group."""
self.response.write('group %s put, %s\n' % (gid, self.request.params))
def delete(self, gid):
"""Delete an Group."""
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment