Skip to main content

An unopinionated extension to enforce role-/relation-/attribute-based access control.

Project description

SQL Alchemy Authorize

https://img.shields.io/pypi/v/sqlalchemy_authorize.svg Documentation Status Updates

An unopinionated extension to enforce field-level access control.

For Documentation: https://sqlalchemy-authorize.readthedocs.io.

How to use

Just insert the appropriate PermissionsMixin in your model definition. For now, that means the OsoPermissionsMixin (put it before db.Model / Base).

Let’s look at an easy role-based example.

In your models.py:

class User(OsoPermissionsMixin, db.Model):
    __tablename__ = 'user'

    # ``load_permissions`` is a convenience method for creating a
    # permissions dictionary of the shape:
    # {"role_1": {"action_1": ["field_1", "field_2", ...], ...}, ...}
    __permissions__ = OsoPermissionsMixin.load_permissions(
        read=["id", "username"],
        self=[
            (["create", "update"], ["username", "fullname"]),
            "read",
            "delete"
        ],
        admin=[
            (["create", "update", "read", "delete"], ("id", "username", "fullname", "is_admin")
        ]
    )

    id = sa.Column(sa.String(128), primary_key=True)
    username = sa.Column(sa.String(128), nullable=False)
    fullname = sa.Column(sa.String(128), nullable=False)
    ssn = sa.Column(sa.String(10), nullable=True)
    is_admin = sa.Column(sa.Boolean, default=False)

Then, in your polar policy, write something like:

has_role(user: User, "self": other: User) if user.id == other.id;
has_role(user: User, "admin": _resource) if user.is_admin;

# OsoPermissionsMixin provides `.role` and `.authorized_fields`
allow_field(user: User, action, resource, field) if
    role in resource.roles and
    has_role(user, role, resource) and
    (f in resource.authorized_fields(role, action) and
    (f = "*" or f = field)); # to match a wildcard

# ...

For the full example, check out rbac.polar.

Now, we can start having fun:

admin = User(id="1", username="root", is_admin=True)

# This won't work because the current user is anonymous
# and has no create permissions on `User.username`
john_doe = User(username="john_doe", check_create=True)  # oso.exceptions.ForbiddenError

with user_set(app, admin): # A helper context that sets `flask.g.user`
    john_doe = User(username="john_doe", check_create=True)
    john_doe.id = "2"

john_doe.username, john_doe.id  # ('john_doe', '2')

with user_set(app, john_doe):
    john_doe.username = "doe_john"

    # This won't work because John only has update permissions on `username` and `fullname`
    john_doe.id = "3"   # oso.exceptions.ForbiddenError

john_doe.username, john_doe.id # ('doe_john', '2')

For more details and options, check out BasePermissionsMixin and OsoPermissionsMixin. Rationale ———

sqlalchemy_authorize is a sqlalchemy extension designed to complement sqlalchemy-oso. Where sqlalchemy-oso provides authorization at the row level in the data-access layer (it modifies your queries so you pull only authorized entries from your database), sqlalchemy_authorize operates at the field level in the ORM layer (it checks whether users have permission before invoking __setattr__, __getattribute__, and __delattr__ on your models).

The use I originally had in mind was to separate authorization from graphql in Graphene-SQLAlchemy: to make it easier to create graphql-queryable models without substantial authorization boilerplate in the resolvers (which is not recommended).

Really though, the use is broader than both Graphene-SQLAlchemy and sqlalchemy-oso. Yes, there a bunch of other libraries for enforcing authorization with SQLAlchemy (and you should take a look at them before deciding to use this):

Still, I decided to go ahead and throw together this library because:

  • These options are Flask-specific and check permissions via decorators. I wanted an option that isn’t opt-in but opt-out, i.e., authorization by default.

  • Many of these options assume you’ll be authorizing at the row level, and (especially for the graphql use case) I needed field-level permissions.

  • Many of the solutions are pretty opinionated about how you should be authorizing (and assume role-based access control). I wanted a less opinionated “real-world” solution that lets me pick and choose from role-, relation- and attribute-based access control.

If any of that resonates with you, glad you’re here.

This is still a very early-stage library, and I discourage you from using it in production until I’ve tested in more thoroughly. Let me modify that: you’re more than welcome to use it, since, if there is one thing you should be testing anyway, it’s authorization.

Go ahead, just be very careful.

Misc

Timeline

  • [ ] More testing.

  • [ ] Flesh out the oso example.

  • [ ] Implement a non-oso role-based extension.

  • [ ] Check row-level create/delete permissions. (This is currently only on the field level).

Credits

This package was created with Cookiecutter and the audreyr/cookiecutter-pypackage project template.

History

0.1.0 (2022-03-16)

  • First release on PyPI.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sqlalchemy_authorize-0.1.0.tar.gz (26.6 kB view details)

Uploaded Source

Built Distribution

sqlalchemy_authorize-0.1.0-py2.py3-none-any.whl (14.4 kB view details)

Uploaded Python 2 Python 3

File details

Details for the file sqlalchemy_authorize-0.1.0.tar.gz.

File metadata

  • Download URL: sqlalchemy_authorize-0.1.0.tar.gz
  • Upload date:
  • Size: 26.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.14.0 pkginfo/1.8.2 requests/2.27.1 setuptools/60.8.2 requests-toolbelt/0.9.1 tqdm/4.63.0 CPython/3.9.1

File hashes

Hashes for sqlalchemy_authorize-0.1.0.tar.gz
Algorithm Hash digest
SHA256 e3c5549591b4a2036bcee0225b19fa62ebc02d8507a856a615d021c4afa78683
MD5 61c91c16b2483b044b2307f26949fe01
BLAKE2b-256 267b0ef941d0dbcb2580a877492b0e8eafce3029c3f88542bf4e4a40e5b5f7a4

See more details on using hashes here.

File details

Details for the file sqlalchemy_authorize-0.1.0-py2.py3-none-any.whl.

File metadata

  • Download URL: sqlalchemy_authorize-0.1.0-py2.py3-none-any.whl
  • Upload date:
  • Size: 14.4 kB
  • Tags: Python 2, Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.14.0 pkginfo/1.8.2 requests/2.27.1 setuptools/60.8.2 requests-toolbelt/0.9.1 tqdm/4.63.0 CPython/3.9.1

File hashes

Hashes for sqlalchemy_authorize-0.1.0-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 ccd9577e8bd5898c5f425ce65111b1e49348a95d3cf54e7ede987b670a1ff1c7
MD5 c96c0b7217eee8c332495f0403d6d41c
BLAKE2b-256 9a2a2da188ac970f2867a18f0204d8de6536f056b06248f911a8429a7c376bfa

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page