def token_to_object(request, strict=True): """ Processes the "Authorization" param in the header and returns an http response OR a user object. Requires the application's initialized JWT to work. """ # khoa's back door - chop this whole block when he gets CORS sorted out if request.method == "POST" and request.json is not None and request.json.get( 'user_id', None) is not None: logger.warn( "'user_id' key in POST body; attempting Khoa-style token-less auth..." ) try: user_oid = ObjectId(request.json['user_id']) except Exception as e: msg = "User OID '%s' does not look like an OID!" % request.json[ 'user_id'] logger.error(msg) logger.error(e) raise utils.InvalidUsage(msg, status_code=422) try: return User(_id=user_oid) except Exception as e: msg = "The OID '%s' does not belong to any known user! %s" % ( user_oid, e) logger.error(msg) raise utils.InvalidUsage(msg, status_code=401) # real auth workflow starts here # first, get the token or bail auth_token = request.headers.get("Authorization", None) if auth_token is None: msg = "'Authorization' header missing!" logger.error(msg) raise utils.InvalidUsage(msg, status_code=401) # now, try to decode the token and get a dict try: if strict: decoded = jwt.decode(auth_token, secret_key, verify=True) user_dict = dict(json.loads(decoded["identity"])) return User(_id=user_dict["_id"]["$oid"]) else: user_dict = refresh_authorization(auth_token) return User(_id=user_dict["_id"]) except jwt.DecodeError: logger.error("Incorrectly formatted token!") logger.error("Token contents: |%s|" % auth_token) except Exception as e: logger.exception(e) raise utils.InvalidUsage("Incoming JWT could not be processed!", status_code=422)
def check_request_params(self, keys=[], verbose=True, raise_exception=True): """ Checks self.params for the presence of all keys specified in 'keys' list. Returns True if they're present and False if they're not. Set 'verbose' to True if you want to log validation failures as errors. """ for k in keys: if k not in self.params.keys(): if verbose: self.logger.error( "Request JSON is missing required parameter '%s'!" % k) if raise_exception: curframe = inspect.currentframe() calframe = inspect.getouterframes(curframe, 2) caller_function = calframe[1][3] msg = "Insufficient request parameters for this route! The %s() method requires values for the following keys: %s." % ( caller_function, utils.list_to_pretty_string(keys)) self.logger.exception(msg) self.logger.error("Bad request params were: %s" % self.params) raise utils.InvalidUsage(msg, status_code=400) else: return False return True
def get_asset(self, handle=None, backoff_to_name=False, raise_exception_if_not_found=True): """ Return an asset dict based on a handle. Return None if the handle cannot be retrieved. """ asset = copy(self.assets.get( handle, None)) # return a copy, so we don't modify the actual def # implement backoff logic if asset is None and backoff_to_name: asset = copy(self.get_asset_from_name(handle)) # if the asset is still None, see if we want to raise an expception if asset is None and raise_exception_if_not_found: if not backoff_to_name: msg = "The handle '%s' is not in %s and could not be retrieved! " % ( handle, self.get_handles()) self.logger.error(msg) elif backoff_to_name: msg = "After backoff to name lookup, asset handle '%s' is not in %s and could not be retrieved." % ( handle, self.get_names()) self.logger.error(msg) raise utils.InvalidUsage(msg) # finally, return the asset (or the NoneType) return asset
def get_random_names(self, count=100): """ Returns 'count' random names for each sex. This is meant primarily as a front-end helper, so it returns a JSON-like dict. It also raises a big, wordy error if you ask it for too many names. """ m = copy(names.male) f = copy(names.female) m.extend(copy(names.neuter)) f.extend(copy(names.neuter)) male = set() female = set() for l in m, f: if count > len(l): raise utils.InvalidUsage( 'Cannot return more than %s random names!' % len(l)) for i in [(male, m), (female, f)]: l, s = (i) while len(l) < count: l.add(random.choice(s)) return {'M': sorted(list(male)), 'F': sorted(list(female))}
def set_preferences(self): """ Expects a request context and will not work without one. Iterates thru a list of preference handles and sets them. """ self.check_request_params(['preferences']) pref_list = self.params['preferences'] if type(pref_list) != list: raise utils.InvalidUsage("set_preferences() endpoint requires 'preferences' to be a list!") for pref_dict in pref_list: handle = pref_dict.get('handle',None) value = pref_dict.get('value',None) for p in [handle, value]: if p is None: raise utils.InvalidUsage("Nah, bro: individual preference hashes/dicts should follow this syntax: {handle: 'preference_handle', value: true}") self.user['preferences'][handle] = value # self.logger.info("%s Set '%s' = %s'" % (request.User.login, handle, value)) self.save()
def new(self): """ Creates a new user based on request.json values. Like all UserAsset 'new()' methods, this one returns the new user's MDB _id when it's done. """ self.logger.info("Creating new user...") self.check_request_params(['username','password']) # clean up the incoming values so that they conform to our data model username = self.params["username"].strip().lower() password = self.params["password"].strip() # do some minimalistic validation (i.e. rely on front-end for good data) # and barf if it fails #separationOfConcerns msg = "The email address '%s' does not appear to be a valid email address!" % username if not '@' in username: raise utils.InvalidUsage(msg) elif not '.' in username: raise utils.InvalidUsage(msg) # make sure the new user doesn't already exist msg = "The email address '%s' is already in use by another user!" % username if utils.mdb.users.find_one({'login': username}) is not None: raise utils.InvalidUsage(msg) # now do it self.user = { 'created_on': datetime.now(), 'login': username, # 'password': md5(password).hexdigest(), 'password': generate_password_hash(password), 'preferences': {}, 'collection': {"expansions": []}, 'notifications': {}, } self._id = utils.mdb.users.insert(self.user) self.load() logger.info("New user '%s' created!" % username) return self.user["_id"]
def __init__(self, img_id): try: img_oid = ObjectId(img_id) except: raise utils.InvalidUsage( 'Invalid OID! Image OIDs must be 12-byte input or 24-character hex string!', status_code=400) try: self.img = gridfs.GridFS(utils.mdb).get(img_oid) except gridfs.errors.NoFile: self.img = None
def __init__(self, collection=None, _id=None, normalize_on_init=True, new_asset_attribs={}, Settlement=None): # initialize basic vars self.logger = utils.get_logger() self.normalize_on_init = normalize_on_init self.new_asset_attribs = new_asset_attribs if collection is not None: self.collection = collection elif hasattr(self, "collection"): pass else: err_msg = "User assets (settlements, users, etc.) may not be initialized without specifying a collection!" self.logger.error(err_msg) raise AssetInitError(err_msg) # use attribs to determine whether the object has been loaded self.loaded = False if _id is None: self.get_request_params() self.new() _id = self._id # if we're initializing with a settlement object already in memory, use it # if this object IS a Settlement, the load() call below will overwrite this self.Settlement = Settlement # now do load() stuff try: try: self._id = ObjectId(_id) except Exception as e: self.logger.error(e) raise utils.InvalidUsage( "The asset OID '%s' does not appear to be a valid object ID! %s" % (_id, e), status_code=422) self.load() self.loaded = True except Exception as e: self.logger.error("Could not load _id '%s' from %s!" % (_id, self.collection)) self.logger.exception(e) raise
def get_admin_data(resource=None): """ Retrieves admin panel data. """ try: if resource == 'user_data': return panel.get_user_data() if resource == 'settlement_data': return panel.get_settlement_data() elif resource == 'logs': return panel.serialize_system_logs() except Exception as e: logger.error("Unable to return '%s' admin data!" % resource) logger.error(e) raise utils.InvalidUsage(e, status_code=500) return utils.http_500
def new(self): """ Creates a new webapp alert; initializes. """ self.alert = request.json # sanity check the incoming for req_var in ['body', 'title']: if self.alert[req_var] is None: raise utils.InvalidUsage( "Webapp Alert key '%s' cannot be None type!" % req_var) self.alert['sub_type'] = self.alert['type'] self.alert['type'] = 'webapp_alert' self.alert['created_on'] = datetime.now() self.alert['created_by'] = ObjectId(self.alert['created_by']) self.alert['expired'] = False self.alert['release'] = settings.get('api', 'version') self.alert['remote_ip'] = request.remote_addr # finally, save it and return it self.alert['_id'] = utils.mdb.notifications.insert(self.alert)
def load(self): self.alert = utils.mdb.notifications.find_one({'_id': self._id}) if self.alert is None: raise utils.InvalidUsage('Notification %s does not exist!' % (self._id), status_code=404)