예제 #1
0
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])
예제 #2
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
예제 #3
0
 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)
예제 #4
0
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
예제 #5
0
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
예제 #6
0
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
예제 #7
0
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
예제 #8
0
 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
     )