def __build_filter_exp(query, model): """ Recursively build SQLAlchemy filter expression from user-provided query. Args: query: An array whose first element is the name of the filter. Subsequent elements in this array are the parameters of this filter. Parameters can be a single value or another query array. e.g. ["and", ["eq", "field1", true], ["or", ["ne", "field2", "hi"], ["gte", "field3.nested", 10]]] model: Data model on which fields in the filters can be found. Returns: A corresponding SQLAlchemy filter expression. Raises: APIError: When unknown query operator occurs. """ # Comparison filters comp_builder = __comp_filters.get(query[0]) if comp_builder: field = getattr_keypath(model, query[1]) return comp_builder(field, query[2]) # Logical filters logical_builder = __logical_filters.get(query[0]) if logical_builder: nested_exp_list = [ __build_filter_exp(nested_query, model) for nested_query in query[1:] ] return logical_builder(*nested_exp_list) # Unknown filter raise APIError(400, "unknown_query_oper", operator=query[0])
def load_data(schema, data, load_args={}, **kwargs): """ Load data through schema. Args: schema: Schema instance or class used for serialization. obj: Data to be deserialized. load_args: Arguments for "load" method in serialization process. Only valid if schema is a class derived from "Schema". kwargs: Arguments for class constructor if schema is class, or for "load" method if schema is instance. """ # Schema instance if isinstance(schema, Schema): load_args = kwargs # Schema class elif issubclass(schema, Schema): schema = schema(**kwargs) else: raise TypeError( "'schema' must be a derived class or a instance of Schema class.") # Parse with error handling obj, error = schema.load(data, **load_args) if error: raise APIError(400, "arg_fmt", errors=error) return obj
def logout(self): """ Log user out. """ # Remove current session with map_error(APIError(400, "bad_token")): api_session = get_by(Session, b64decode(request.headers[AUTH_TOKEN_HEADER])) db.session.delete(api_session) db.session.commit() # Success return jsonify(**SUCCESS_RESP)
def handle_prog_error(e): """ Handle PG8000 programming error. """ # Error arguments and numbers error_args = e.orig.args errno = int(error_args[2]) # Unique constraint violation if errno == 23505: key, value = unique_msg_rx.search(error_args[4]).groups() return APIError(400, "unique_violation", key=key, value=value) return e
def check_perm(*rules, **kwargs): """ Check operation permission. Args: rules: Authorization rules. Each rule is handled by a corresponding rule handler. The rule handler will be looked up for in "__perm_rule_map" by its type. Use "tuple" type to describe "or" relationship, and "list" for "and" relationship. auth_kwargs: Additional context used by authentication rules. Returns: Whether the user is authorized or not. """ throw = kwargs.get("throw", True) # Not logged in if not g.user and throw: raise APIError(401, "login_required") # Execute rules authorized = __handle_perm_rule(rules, g.user, **kwargs) if not authorized and throw: raise APIError(403, "perm_denied") return authorized
def get_pk(model, pk, allow_null=False, error=APIError(404, "not_found")): """ Get element by primary key. Args: model: Model class to operate. pk: Primary key of the element. allow_null: Return null when nothing is found. Will throw APIError otherwise. error: Custom error instance to be thrown when nothing is found. Returns: Model instance. Raises: APIError: When nothing is found and allow_null is set to false. """ result = model.query.get(pk) if not (allow_null or result): raise error return result
def get_by(model, allow_null=False, error=APIError(404, "not_found"), **kwargs): """ Get element by given condition. Args: model: Model class to operate. allow_null: Return null when nothing is found. Will throw APIError otherwise. error: Custom error instance to be thrown when nothing is found. kwargs: Keyword arguments to be passed to filter_by function. Returns: Model instance. Raises: APIError: When nothing is found and allow_null is set to false. """ result = model.query.filter_by(**kwargs).first() if not (allow_null or result): raise error return result
def login(self): """ Log user in. """ import sys print(g, file=sys.stderr) assert g.params!=None # Find user user = get_by( User, username=g.params["username"], password=g.params["password"], error=APIError(401, "incorrect_credential") ) # Log user in session = Session(token=os.urandom(TOKEN_LEN), user=user) db.session.add(session) db.session.commit() # Token token = b64encode(session.token).decode() # Success return jsonify( **SUCCESS_RESP, user=dump_data(UserSchema, user), token=token )