Skip to content

Access Control

Thomas Pollet edited this page Jul 3, 2023 · 20 revisions

Overview

The complexity of implementing proper API access control depends on the granularity of required the access control:

Application-Wide Access Control

With application-level access control, all authenticated users can retrieve all API data. You can implement application-wide access control using the flask before_request decorator.

Restrict HTTP Methods

The HTTP methods that are allowed on a class, are defined by the SAFRSBase.http_methods hybrid property. By default, all api-related HTTP methods are allowed ( ["GET", "POST", "PATCH", "DELETE", "PUT", "HEAD", "OPTIONS"] ). If you want to restrict the allowed methods, you can override this property on the class- or relationship level, for example:

class User(SAFRSBase, db.Model):
    """
        description: User description
    """
    __tablename__ = "Users"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String)
    email = db.Column(db.String)
    
    http_methods = ["GET"]

or, to restrict the allowed HTTP Methods on a relationship:

class User(SAFRSBase, db.Model):
    """
        description: User description
    """
    __tablename__ = "Users"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String)
    email = db.Column(db.String)
    others= db.relationship('Others')
    others.http_methods = ["GET"]

You can also use SQLAlchemy hybrid properties for even more flexible control of the allowed methods. (Check the safrs base.py to see how the http_methods hybrid property is implemented).

Class-Level and Relationship Level Security Controls

Access control can be enforced on the API endpoints by means of function decorators that will be applied to the API endpoints of the class where the decorators are declared. This is demonstrated in this example. In this example we add the flask-httpauth login_required decorator to the decorators list of the User class:

class User(SAFRSBase, db.Model):
    """
        description: Protected user resource
    """

    __tablename__ = "users"
    id = db.Column(db.String(32), primary_key=True)
    username = db.Column(db.String(32))
    # The custom_decorators will be applied to all API endpoints
    decorators = [auth.login_required]

We create a simplistic verify_password function to verify the username and password sent in the Authorization header

@auth.verify_password
def verify_password(username_or_token, password):
    # Implement your authentication here
    if username_or_token == "user" and password == "pass":
        return True

    return False

The decorators can be customized, this example shows a custom decorator where authentication is only required for specific HTTP methods. These decorators are also applied to the relationships exposed by the class objects.

HTTP Method decorators are also documented here

Access Control for Attributes and Relationships

More granular access control for attributes can be implemented

  • by overriding the _s_check_perm method (cfr. ex_16.py)
  • by overriding the _s_post and _s_patch methods (HTTP methods)
  • in the class __init__ constructror for post requests
  • in the orm.reconstructor for patch requests
  • in the to_dict json serialization method
  • using a custom SQLAlchemy column type

Access control for relationships can be implemented

  • in the relationship target classes.
  • by overriding _s_relationships
  • by overriding _s_get_related

Database-Level Access Control

It is possible to use LogicBank to implement authorization rules that will be checked using sqlalchemy commit hooks. An example with logicbank can be found here: https://github.com/thomaxxl/safrs/blob/master/examples/mini_examples/ex07_logicbank.py