Skip to content

Commit

Permalink
Adding the lib folder from before.
Browse files Browse the repository at this point in the history
  • Loading branch information
dalefwillis committed Jun 22, 2015
1 parent 12cc46f commit e3a54e7
Show file tree
Hide file tree
Showing 9 changed files with 805 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ dist/
downloads/
eggs/
.eggs/
lib/
#lib/
lib64/
parts/
sdist/
Expand Down
Empty file.
Empty file.
74 changes: 74 additions & 0 deletions paradrop/paradrop/lib/api/pdapi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import random, math


class PDAPIError(Exception):
"""
Exception class related to ParaDrop API calls.
"""
def __init__(self, etype, msg):
self.etype = etype
self.msg = msg

def __str__(self):
return "PDAPIError %s: %s" % (self.etype, self.msg)


def isPDError(code):
"""Checks all Paradrop API error codes, if the HTTP code is in our set it is assumed a PDAPI error."""
if(code in RESP_MSG.keys()):
return True
else:
return False

OK = 200
ERR_BADPARAM = 400
ERR_TOKEXPIRE = 401
ERR_BADFORMAT = 402
ERR_BADAUTH = 403
ERR_THRESHOLD = 404
ERR_BADMETHOD = 405
ERR_BADIO=406
ERR_BADPATH=407
ERR_CONTACTPD = 501
ERR_DBISSUE = 502
ERR_STATECHANGE = 503
ERR_CHUTESTATE = 504
ERR_UPDATEPENDING = 505
ERR_NOSTATUS = 506
ERR_RESETPENDING = 507
ERR_CHUTEINVALID = 508
ERR_UNIMPLEMENTED = 599

RESP_MSG = {
OK: None,
ERR_BADPARAM: "Bad parameter: %s",
ERR_TOKEXPIRE: "Token expired",
ERR_BADFORMAT: "Bad format",
ERR_BADAUTH: "Bad authorization",
ERR_THRESHOLD: "Access limited due to threshold",
ERR_BADMETHOD: "Bad method type",
ERR_BADIO: "Bad IO",
ERR_BADPATH: "Path not found",
ERR_CONTACTPD: "Contact Paradrop, ERRORTOKEN: %s",
ERR_DBISSUE: "Issue with database, please try again",
ERR_STATECHANGE: "Bad state transition",
ERR_CHUTESTATE: "Cannot make change with chute in its current state",
ERR_UPDATEPENDING: "Action already pending for Chute",
ERR_NOSTATUS: "No status data available, either none exists or bad authorization",
ERR_RESETPENDING: "Reset already pending for AP",
ERR_CHUTEINVALID: "Cannot make change, chute would become invalid",
ERR_UNIMPLEMENTED: "Function unimplemented yet",
}


def getResponse(code, *args):
"""Designed to be called to provide the arguments for the Request.setResponseCode()"""
if(len(args) == 0):
return code, RESP_MSG[code]
else:
return code, RESP_MSG[code] % (args)

def getErrorToken():
"""Generates a random string which is used to match client issues with log output."""
return '%010d' % int(random.getrandbits(32))

231 changes: 231 additions & 0 deletions paradrop/paradrop/lib/api/pdrest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@

# TODO add original licensing for this, don't remember where it came from

import re
from itertools import ifilter
from functools import wraps
from twisted.web.resource import Resource
from twisted.web.resource import NoResource
#DFW: I think this is old: from twisted.web.error import NoResource
from zope.interface.advice import addClassAdvisor

def method_factory_factory(method):
def factory(regex):
_f = {}
def decorator(f):
_f[f.__name__] = f
return f
def advisor(cls):
def wrapped(f):
def __init__(self, *args, **kwargs):
f(self, *args, **kwargs)
for func_name in _f:
orig = _f[func_name]
func = getattr(self, func_name)
if func.im_func==orig:
self.register(method, regex, func)
return __init__
cls.__init__ = wrapped(cls.__init__)
return cls
addClassAdvisor(advisor)
return decorator
return factory

ALL = method_factory_factory('ALL')
GET = method_factory_factory('GET')
POST = method_factory_factory('POST')
PUT = method_factory_factory('PUT')
DELETE = method_factory_factory('DELETE')

class _FakeResource(Resource):
_result = ''
isLeaf = True
def __init__(self, result):
Resource.__init__(self)
self._result = result
def render(self, request):
return self._result


def maybeResource(f):
@wraps(f)
def inner(*args, **kwargs):
result = f(*args, **kwargs)
if not isinstance(result, Resource):
result = _FakeResource(result)
return result
return inner


class APIResource(Resource):

_registry = None

def __init__(self, *args, **kwargs):
Resource.__init__(self, *args, **kwargs)
self._registry = []

def _get_callback(self, request):
filterf = lambda t:t[0] in (request.method, 'ALL')
for m, r, cb in ifilter(filterf, self._registry):
result = r.search(request.path)
if result:
return cb, result.groupdict()
return None, None

def register(self, method, regex, callback):
self._registry.append((method, re.compile(regex), callback))

def unregister(self, method=None, regex=None, callback=None):
if regex is not None: regex = re.compile(regex)
for m, r, cb in self._registry[:]:
if not method or (method and m==method):
if not regex or (regex and r==regex):
if not callback or (callback and cb==callback):
self._registry.remove((m, r, cb))

def getChild(self, name, request):
r = self.children.get(name, None)
if r is None:
# Go into the thing
callback, args = self._get_callback(request)
if callback is None:
return NoResource()
else:
return maybeResource(callback)(request, **args)
else:
return r

def APIDecorator(admin=False, permission=None, requiredArgs=[], optionalArgs=[]):
"""
The decorator for the API functions to make the API functions focus on their job.
This decorator do the following things:
* Set HTTP header
* Get some common values like ip, tictoc
* Do the preprocess
* Extract arguments from HTTP body and put them into APIPackage
* Get devid if token is shown and put devid into APIPackage
* Check admin authorization if devid is shown and admin argument is set to be true
* Do the failprocess if fails. Do the postProcess if success
This decorator will pass an APIPackage to an API function and the API function is supposed to put return value into the API package
Arguments:
* admin: if the function needs admin authority to call
* requiredArgs: the required arguments for this API, this wrapper will parse the required args from HTTP body and check if they exist in the body.
* optionalArgs: the optional arguments for this API, the args will be parsed from HTTP body
* permission: choose from None, "AP Owner", "Chute Owner"
TODO:
1.Permission
* multiple permission/multiple level of permission??
* More permissions: such as Vnet Owner, Group Owner
"""
def realDecorator(func):
def wrapper(theSelf, request, **args):
tictoc = theSelf.perf.tic()
ip = getIP(request)
out.verbose('-- %s HTTP request from IP %s\n' % (logPrefix(), ip))
request.setHeader('Access-Control-Allow-Origin', settings.DBAPI_HEADER_VALUE)


apiPackage = APIPackage()
# Extract required arguments
if(requiredArgs or optionalArgs):
body = str2json(request.content.read())
if(requiredArgs):
required = pdutils.explode(body, *requiredArgs)
for idx, arg in enumerate(requiredArgs):
# Check if required arg exist
if(required[idx] is None):
return theSelf.rest.failprocess(ip, request, (ip, theSelf.rest.clientFailures), "Malformed Body: %s", (tictoc, None), pdapi.ERR_BADPARAM)
# If exist put it into the apiPackage
apiPackage.inputArgs[arg] = required[idx]

# Extract optional arguments
if(optionalArgs):
optional = pdutils.explode(body, *optionalArgs)
for idx, arg in enumerate(optionalArgs):
if(optional[idx]):
apiPackage.inputArgs[arg] = optional[idx]

# Determine which failureDict to use based on the function name
# Kaichen: Here we have an assumption that the signin function in Auth module is the only
# function that use uname as key and use the signinFailures as the failure dict.
# Other APIs use ip as key and use clientFailure as the failure dict.
if(func.__name__ == "POST_signin"):
failureKey = apiPackage.inputArgs.get("username")
failureDict = theSelf.signinFailures
else:
failureKey = ip
failureDict = theSelf.rest.clientFailures

token = apiPackage.inputArgs.get("sessionToken", None)
# Do the preprocess
res = theSelf.rest.preprocess(request, (ip, token, failureKey, failureDict), tictoc)
if(res):
return res

# Get the devid and put it into apiPackage if token exist
devid = None

if(token):
devid = theSelf.rest.userCache.getDevidFromToken(token)
if(devid is None):
return theSelf.rest.failprocess(ip, request, (failureKey, failureDict), 'Bad Token: %s', (tictoc, None), pdapi.ERR_BADAUTH)
apiPackage.inputArgs["devid"] = devid

# Find the real apid if the apid is an ap name
apid = apiPackage.inputArgs.get("apid", None)
if(apid and not pdutils.isGuid(apid)):
apid = pddb.getApidByName(theSelf.dbh, apid)
if(apid is None):
out.err("!! %s Bad apid: %s\n" % (logPrefix(ip), apid))
return theSelf.rest.failprocess(ip, request, (ip, theSelf.rest.clientFailures), 'Bad apid: %s', (tictoc, None),pdapi.getResponse(pdapi.ERR_BADAUTH))
else:
apiPackage.inputArgs['apid'] = apid

# Check admin
if(admin):
user = self.rest.userCache.getUserFromToken(token)
if(user['admin'] != 1):
return self.rest.failprocess(ip, request, (failureKey, failureDict), "Unauthorized: %s", (tictoc, devid), pdapi.ERR_BADAUTH)

# Bypass permission check for the testingAdmin user
if(devid != settings.DBAPI_ADMIN_DEVID):
# Check permission
if(permission):
chuteid = apiPackage.inputArgs.get("chuteid", None)
apid = apiPackage.inputArgs.get("apid", None)
# AP owner
if(permission == "AP Owner" and not pddb.checkAPDevPermissions(theSelf.dbh, devid, apid)):
out.err("!! %s Developer %s doesn't permission to AP: %s\n" % (logPrefix(ip), devid, apid))
return theSelf.rest.failprocess(ip, request, (ip, theSelf.rest.clientFailures), "Developer has no permission to AP:%s" % apid + " %s", (tictoc, None), pdapi.ERR_BADAUTH)
# chute owner
elif(permission == "Chute Owner" and not pddb.isDevChuteOwner(theSelf.dbh, devid, chuteid)):
out.err("!! %s Developer %s has no permission on chute %s\n" % (logPrefix(ip), devid, chuteid))
return theSelf.rest.failprocess(ip, request, (ip, theSelf.rest.clientFailures), "Developer has no permission on chute" + chuteid+ ": %s", (tictoc, devid), pdapi.ERR_BADAUTH)
elif(permission != "Chute Owner" and permission != "AP Owner"):
out.err("!! Bad usage of api decorator, invalid permission\n")

# Call the real API function
apiPackage.inputArgs['request'] = request
func(theSelf, apiPackage, **args)
# NOT_DONE_YET
if(apiPackage.result is None):
return NOT_DONE_YET
# SUCCESS
elif(apiPackage.result):
theSelf.rest.postprocess(request, failureKey, failureDict, (tictoc, ip, devid))
return apiPackage.returnVal
# FAILURE
else:
errMsg = apiPackage.errMsg
errType = apiPackage.errType
if(apiPackage.countFailure):
return theSelf.rest.failprocess(ip, request, (failureKey, failureDict), errMsg, (tictoc, devid), errType)
else:
return theSelf.rest.failprocess(ip, request, None, errMsg, (tictoc, devid), errType)

return wrapper
return realDecorator



0 comments on commit e3a54e7

Please sign in to comment.