Skip to content
Snippets Groups Projects
Commit 9a44f06c authored by Kevin S. Hahn's avatar Kevin S. Hahn
Browse files

nimsapi, remote instance id is urlencoded

parent d6950114
No related branches found
No related tags found
No related merge requests found
......@@ -9,6 +9,8 @@ import nimsapiutil
class Epochs(nimsapiutil.NIMSRequestHandler):
"""/nimsapi/epochs """
json_schema = {
'$schema': 'http://json-schema.org/draft-04/schema#',
'title': 'Epoch List',
......@@ -43,15 +45,15 @@ class Epochs(nimsapiutil.NIMSRequestHandler):
}
}
def count(self, iid):
def count(self):
"""Return the number of Epochs."""
self.response.write(json.dumps(self.app.db.epochs.count()))
def post(self, iid):
def post(self):
"""Create a new Epoch."""
self.response.write('epochs post\n')
def get(self, iid, sid):
def get(self, sid):
"""Return the list of Session Epochs."""
session = self.app.db.sessions.find_one({'_id': bson.objectid.ObjectId(sid)})
if not session:
......@@ -66,13 +68,15 @@ class Epochs(nimsapiutil.NIMSRequestHandler):
epochs = list(self.app.db.epochs.find(query, projection))
self.response.write(json.dumps(epochs, default=bson.json_util.default))
def put(self, iid):
def put(self):
"""Update many Epochs."""
self.response.write('epochs put\n')
class Epoch(nimsapiutil.NIMSRequestHandler):
"""/nimsapi/epochs/<eid> """
json_schema = {
'$schema': 'http://json-schema.org/draft-04/schema#',
'title': 'Epoch',
......@@ -209,7 +213,7 @@ class Epoch(nimsapiutil.NIMSRequestHandler):
'required': ['_id'],
}
def get(self, iid, eid):
def get(self, eid):
"""Return one Epoch, conditionally with details."""
epoch = self.app.db.epochs.find_one({'_id': bson.objectid.ObjectId(eid)})
if not epoch:
......@@ -224,10 +228,10 @@ class Epoch(nimsapiutil.NIMSRequestHandler):
self.abort(403)
self.response.write(json.dumps(epoch, default=bson.json_util.default))
def put(self, iid, eid):
def put(self, eid):
"""Update an existing Epoch."""
self.response.write('epoch %s put, %s\n' % (epoch_id, self.request.params))
def delete(self, iid, eid):
def delete(self, eid):
"""Delete an Epoch."""
self.response.write('epoch %s delete, %s\n' % (epoch_id, self.request.params))
......@@ -9,6 +9,8 @@ import nimsapiutil
class Experiments(nimsapiutil.NIMSRequestHandler):
"""/experiments """
json_schema = {
'$schema': 'http://json-schema.org/draft-04/schema#',
'title': 'Experiment List',
......@@ -35,15 +37,15 @@ class Experiments(nimsapiutil.NIMSRequestHandler):
}
}
def count(self, iid):
def count(self):
"""Return the number of Experiments."""
self.response.write(json.dumps(self.app.db.experiments.count()))
def post(self, iid):
def post(self):
"""Create a new Experiment."""
self.response.write('experiments post\n')
def get(self, iid):
def get(self):
"""Return the list of Experiments."""
query = {'permissions.' + self.userid: {'$exists': 'true'}} if not self.user_is_superuser else None
projection = ['group', 'name', 'permissions.'+self.userid]
......@@ -57,13 +59,15 @@ class Experiments(nimsapiutil.NIMSRequestHandler):
exp['timestamp'] = timestamps[exp['_id']]
self.response.write(json.dumps(experiments, default=bson.json_util.default))
def put(self, iid):
def put(self):
"""Update many Experiments."""
self.response.write('experiments put\n')
class Experiment(nimsapiutil.NIMSRequestHandler):
"""/experiments/<xid> """
json_schema = {
'$schema': 'http://json-schema.org/draft-04/schema#',
'title': 'Experiment',
......@@ -99,7 +103,7 @@ class Experiment(nimsapiutil.NIMSRequestHandler):
'required': ['_id', 'group', 'name'],
}
def get(self, iid, xid):
def get(self, xid):
"""Return one Experiment, conditionally with details."""
experiment = self.app.db.experiments.find_one({'_id': bson.objectid.ObjectId(xid)})
if not experiment:
......@@ -115,10 +119,10 @@ class Experiment(nimsapiutil.NIMSRequestHandler):
experiment['permissions'] = {self.userid: experiment['permissions'][self.userid]}
self.response.write(json.dumps(experiment, default=bson.json_util.default))
def put(self, iid, xid):
def put(self, xid):
"""Update an existing Experiment."""
self.response.write('experiment %s put, %s\n' % (exp_id, self.request.params))
def delete(self, iid, xid):
def delete(self, xid):
"""Delete an Experiment."""
self.response.write('experiment %s delete, %s\n' % (exp_id, self.request.params))
......@@ -28,6 +28,8 @@ log = logging.getLogger('nimsapi')
class NIMSAPI(nimsapiutil.NIMSRequestHandler):
"""/nimsapi """
def head(self):
"""Return 200 OK."""
self.response.set_status(200)
......@@ -63,7 +65,7 @@ class NIMSAPI(nimsapiutil.NIMSRequestHandler):
self.response.write('</style>\n')
self.response.write('</head>\n')
self.response.write('<body>\n')
self.response.write('<p>nimsapi - ' + self.app.config['site_id'] + '</p>\n')
self.response.write('<h1>nimsapi - ' + self.app.config['site_id'] + '</h1>\n')
self.response.write('<table class="tftable" border="1">\n')
self.response.write('<tr><th>Resource</th><th>Description</th></tr>\n')
for r, d in resources:
......@@ -108,6 +110,8 @@ class NIMSAPI(nimsapiutil.NIMSRequestHandler):
class Users(nimsapiutil.NIMSRequestHandler):
"""/nimsapi/users """
json_schema = {
'$schema': 'http://json-schema.org/draft-04/schema#',
'title': 'User List',
......@@ -144,27 +148,29 @@ class Users(nimsapiutil.NIMSRequestHandler):
}
}
def count(self, iid):
def count(self):
"""Return the number of Users."""
self.response.write('%d users\n' % self.app.db.users.count())
def post(self, iid):
def post(self):
"""Create a new User"""
self.response.write('users post\n')
def get(self, iid):
def get(self):
"""Return the list of Users."""
projection = ['firstname', 'lastname', 'email_hash']
users = list(self.app.db.users.find({}, projection))
self.response.write(json.dumps(users, default=bson.json_util.default))
def put(self, iid):
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',
......@@ -202,12 +208,12 @@ class User(nimsapiutil.NIMSRequestHandler):
'required': ['_id'],
}
def get(self, iid, uid):
def get(self, uid):
"""Return User details."""
user = self.app.db.users.find_one({'_id': uid})
self.response.write(json.dumps(user, default=bson.json_util.default))
def put(self, iid, uid):
def put(self, uid):
"""Update an existing User."""
user = self.app.db.users.find_one({'_id': uid})
if not user:
......@@ -229,13 +235,15 @@ class User(nimsapiutil.NIMSRequestHandler):
self.abort(403)
self.response.write(json.dumps(user, default=bson.json_util.default) + '\n')
def delete(self, iid, uid):
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',
......@@ -252,27 +260,29 @@ class Groups(nimsapiutil.NIMSRequestHandler):
}
}
def count(self, iid):
def count(self):
"""Return the number of Groups."""
self.response.write('%d groups\n' % self.app.db.groups.count())
def post(self, iid):
def post(self):
"""Create a new Group"""
self.response.write('groups post\n')
def get(self, iid):
def get(self):
"""Return the list of Groups."""
projection = ['_id']
groups = list(self.app.db.groups.find({}, projection))
self.response.write(json.dumps(groups, default=bson.json_util.default))
def put(self, iid):
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',
......@@ -313,16 +323,16 @@ class Group(nimsapiutil.NIMSRequestHandler):
'required': ['_id'],
}
def get(self, iid, gid):
def get(self, gid):
"""Return Group details."""
group = self.app.db.groups.find_one({'_id': gid})
self.response.write(json.dumps(group, default=bson.json_util.default))
def put(self, iid, gid):
def put(self, gid):
"""Update an existing Group."""
self.response.write('group %s put, %s\n' % (gid, self.request.params))
def delete(self, iid, gid):
def delete(self, gid):
"""Delete an Group."""
......@@ -385,8 +395,6 @@ routes = [
webapp2.Route(r'/dump', NIMSAPI, handler_method='dump', methods=['GET']),
webapp2.Route(r'/upload', NIMSAPI, handler_method='upload', methods=['PUT']),
webapp2.Route(r'/remotes', Remotes),
]),
webapp2_extras.routes.PathPrefixRoute(r'/nimsapi/<iid:[^/]+>', [
webapp2.Route(r'/users', Users),
webapp2.Route(r'/users/count', Users, handler_method='count', methods=['GET']),
webapp2.Route(r'/users/listschema', Users, handler_method='schema', methods=['GET']),
......@@ -422,6 +430,7 @@ app = webapp2.WSGIApplication(routes, debug=True)
if __name__ == '__main__':
args = ArgumentParser().parse_args()
nimsutil.configure_log(args.logfile, not args.quiet, args.loglevel)
# TODO: if pubkey not specified, throw log.warning
from paste import httpserver
app.config = dict(stage_path=args.stage_path, site_id=args.uid, pubkey=args.pubkey)
......
......@@ -8,11 +8,12 @@ import webapp2
import datetime
import requests
import bson.json_util
from Crypto.Hash import HMAC
from Crypto.Random import random
import Crypto.Hash.HMAC
import Crypto.Random.random
log = logging.getLogger('nimsapi')
requests_log = logging.getLogger('requests') # configure Requests logging
requests_log.setLevel(logging.WARNING) # set level to WARNING (default is INFO)
class NIMSRequestHandler(webapp2.RequestHandler):
......@@ -62,16 +63,12 @@ class NIMSRequestHandler(webapp2.RequestHandler):
self.user = self.app.db.users.find_one({'_id': self.userid})
self.user_is_superuser = self.user.get('superuser')
self.response.headers['Content-Type'] = 'application/json'
try:
self.target_id = request.route_kwargs['iid'] # for what site is the request meant
except KeyError:
self.target_id = 'local' # change to site_id?
self.site_id = self.app.config['site_id'] # what is THIS site
self.pubkey = open(self.app.config['pubkey']).read() if self.app.config['pubkey'] is not None else None
self.target_id = self.request.get('iid', None)
self.site_id = self.app.config['site_id']
# requests coming from another NIMS instance are dealt with differently
if self.request.user_agent.startswith('NIMS Instance'):
log.info("request from '{0}', interNIMS p2p initiated".format(self.request.user_agent))
log.debug("request from '{0}', interNIMS p2p initiated".format(self.request.user_agent))
try:
authinfo = self.request.headers['authorization']
challenge_id, digest = base64.b64decode(authinfo).split()
......@@ -85,36 +82,44 @@ class NIMSRequestHandler(webapp2.RequestHandler):
# purge challenge (challenges are single use)
self.app.db.challenges.remove({'_id': challenge_id})
# verify
h = HMAC.new(remote_pubkey, challenge)
h = Crypto.Hash.HMAC.new(remote_pubkey, challenge)
self.expected = base64.b64encode('%s %s' % (challenge_id, h.hexdigest()))
log.debug('recieved: %s' % authinfo)
log.debug('expected: %s' % self.expected)
if self.expected == authinfo:
log.info('CRAM successful')
log.debug('CRAM response accepted - %s authenticated' % challenge_id)
else:
self.abort(403, 'Not Authorized: cram failed')
except KeyError, e:
except KeyError as e:
# send a 401 with a fresh challenge
cid = self.request.get('cid')
if not cid: self.abort(403, 'challenge_id not in payload')
if not cid: self.abort(403, 'cid, challenge_id, required')
challenge = {'_id': cid,
'challenge': str(random.getrandbits(128)),
'challenge': str(Crypto.Random.random.getrandbits(128)),
'timestamp': datetime.datetime.now()}
# upsert challenge with time of creation
spam = self.app.db.challenges.find_and_modify(query={'_id': cid}, update=challenge, upsert=True, new=True)
self.app.db.challenges.find_and_modify(query={'_id': cid}, update=challenge, upsert=True, new=True)
# send 401 + challenge in 'www-authenticate' header
self.response.headers['www-authenticate'] = base64.b64encode(challenge['challenge'])
self.response.set_status(401)
log.debug('issued challenge to %s; %s' % (cid, challenge['challenge']))
def dispatch(self):
"""dispatching and request forwarding"""
if self.target_id in ['local', self.site_id]:
log.info('{0} delegating to local {1}'.format(socket.gethostname(), self.request.url))
if self.target_id in [None, self.site_id]:
log.debug('{0} delegating to local {1}'.format(socket.gethostname(), self.request.url))
super(NIMSRequestHandler, self).dispatch()
else:
log.info('{0} delegating to remote {1}'.format(socket.gethostname(), self.target_id))
# WORK ON THIS SPOT
# capture error, and log.
self.pubkey = open(self.app.config['pubkey']).read()
# check if pubkey specified, throw error
log.debug('{0} delegating to remote {1}'.format(socket.gethostname(), self.target_id))
# is target registered?
target = self.app.db.remotes.find_one({'_id': self.target_id}, {'_id':False, 'hostname':True})
if not target:
log.info('remote host {0} not in auth list. DENIED'.format(self.target_id))
log.debug('remote host {0} not in auth list. DENIED'.format(self.target_id))
self.abort(403, 'forbidden: site is not registered with interNIMS')
self.cid = self.userid + ':' + self.site_id
reqheaders = dict(self.request.headers)
......@@ -125,16 +130,17 @@ class NIMSRequestHandler(webapp2.RequestHandler):
target_api = 'http://{0}{1}?{2}'.format(target['hostname'], self.request.path, self.request.query_string)
reqparams = {'cid': self.cid}
# TODO: error handling for host-down/host-unreachable
# first attempt, expect 401, send as little as possible...
r = requests.request(method=self.request.method, url=target_api, params=reqparams, headers=reqheaders, cookies=self.request.cookies)
r = requests.request(method=self.request.method, url=target_api, params=reqparams, headers=reqheaders)
if r.status_code == 401:
challenge = base64.b64decode(r.headers['www-authenticate'])
# log.info('challenge {0} recieved'.format(challenge))
h = HMAC.new(self.pubkey, challenge)
log.debug('Authorization requested - challenge: %s' % challenge)
h = Crypto.Hash.HMAC.new(self.pubkey, challenge)
response = base64.b64encode('%s %s' % (self.cid, h.hexdigest()))
# log.info('sending: {0} {1}'.format(self.cid, h.hexdigest()))
#adjust the request and try again
log.debug('response: %s %s' % (self.cid, h.hexdigest()))
log.debug('b4encoded: %s' % response)
reqheaders['authorization'] = response
r = requests.request(method=self.request.method, url=target_api, params=reqparams, data=self.request.body, headers=reqheaders, cookies=self.request.cookies)
......
......@@ -9,6 +9,8 @@ import nimsapiutil
class Sessions(nimsapiutil.NIMSRequestHandler):
"""/sessions """
json_schema = {
'$schema': 'http://json-schema.org/draft-04/schema#',
'title': 'Session List',
......@@ -31,15 +33,15 @@ class Sessions(nimsapiutil.NIMSRequestHandler):
}
}
def count(self, iid):
def count(self):
"""Return the number of Sessions."""
self.response.write(json.dumps(self.app.db.sessions.count()))
def post(self, iid):
def post(self):
"""Create a new Session"""
self.response.write('sessions post\n')
def get(self, iid, xid):
def get(self, xid):
"""Return the list of Experiment Sessions."""
experiment = self.app.db.experiments.find_one({'_id': bson.objectid.ObjectId(xid)})
if not experiment:
......@@ -51,13 +53,15 @@ class Sessions(nimsapiutil.NIMSRequestHandler):
sessions = list(self.app.db.sessions.find(query, projection))
self.response.write(json.dumps(sessions, default=bson.json_util.default))
def put(self, iid):
def put(self):
"""Update many Sessions."""
self.response.write('sessions put\n')
class Session(nimsapiutil.NIMSRequestHandler):
"""/sessions/<sid> """
json_schema = {
'$schema': 'http://json-schema.org/draft-04/schema#',
'title': 'Session',
......@@ -106,7 +110,7 @@ class Session(nimsapiutil.NIMSRequestHandler):
'required': ['_id', 'experiment', 'uid', 'patient_id', 'subject'],
}
def get(self, iid, sid):
def get(self, sid):
"""Return one Session, conditionally with details."""
session = self.app.db.sessions.find_one({'_id': bson.objectid.ObjectId(sid)})
if not session:
......@@ -118,15 +122,15 @@ class Session(nimsapiutil.NIMSRequestHandler):
self.abort(403)
self.response.write(json.dumps(session, default=bson.json_util.default))
def put(self, iid, sid):
def put(self, sid):
"""Update an existing Session."""
self.response.write('session %s put, %s\n' % (sid, self.request.params))
def delete(self, iid, sid):
def delete(self, sid):
"""Delete an Session."""
self.response.write('session %s delete, %s\n' % (sid, self.request.params))
def move(self, iid, sid):
def move(self, sid):
"""
Move a Session to another Experiment.
......
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