Skip to main content

Simple access-scopes utility package.

Project description

Access scopes

Utility package for access scopes definition and checking.

Installation

pip install px-access-scopes

Usage

Defining access scopes:

access_scopes.py

from px_access_scopes import ScopeRegistry, ScopeDomain, auto, raw

# Creating root access scopes registry.
root = ScopeRegistry.create_root(ScopeDomain('TOKENS'))

# Tokens can be any Enum or even a simple object.
@root.nest('TOKENS')
class Tokens(Enum):
  AUTO1 = auto
  AUTO2 = auto
  RAW = raw('RAW')
  FIXED = 'SOME'

@Tokens.nest('NESTED')
class Nested:
  AUTO = auto
  SOME = 'OTHER'

Defining scopes aggregates(roles):

access_aggregates.py

from px_access_scopes import Aggregates

from .access_scopes import Tokens, Nested


class Roles(Aggregates):
  Simple = Aggregate('Simple')
  First = Aggregate('First')

Roles.Simple.add(Tokens.RAW)
Roles.First.add(Tokens.RAW)
Roles.First.add(Nested.SOME)

Run checkers whenever you need:

from px_access_scopes import (
  ScopesCheckRunner, scopes_checker, aggregates_checker, HierarchyChecker,
  domain_path_hierarchy_lookup,
)
from .access_scopes import Tokens, Nested
from .access_aggregates import Roles

# Defining a checker. The result is just a simple callable.
# ScopesCheckRunner receives special checker runners list and evaluates them,
# until it founds a match.
checker = ScopesCheckRunner((
  HierarchyChecker((
    scopes_checker,
    aggregates_checker,
  ), hierarchy_lookup=domain_path_hierarchy_lookup),
))

USER1 = {'scopes': [Nested.AUTO]}
USER2 = {'scopes': [Nested.AUTO], 'aggregates': [Roles.Simple]}
USER3 = {'scopes': [Nested.AUTO], 'aggregates': [Roles.First]}

# It receives scopes list and kwargs, that internal checkers need to make decision.
checker((Nested.AUTO,), **USER1) # > True
checker((Nested.SOME,), **USER1) # > False

checker((Tokens.RAW,), **USER2) # > True
checker((Tokens.RAW,), **USER3) # > True

checker((Nested.AUTO,), **USER3) # > True
checker((Nested.SOME,), **USER3) # > True

Django

Registering scopes registries and access tokens aggregates.

On every manage.py migrate auth Permissions and Groups will be autogenerated based on registered definitions.

settings.py

PX_ACCESS_TOKENS_REGISTRIES = [
  'access_scopes.staff_root',
]
PX_ACCESS_TOKENS_AGGREGATES = [
  'access_scopes.Roles',
]

access_scopes.py

from django.utils.translation import pgettext_lazy
from django.db.models.enums import TextChoices

# Has it's own, a little bit improved for django implementations:
from px_access_scopes.contrib.django import Aggregate, Aggregates, ScopeRegistry


class Roles(Aggregates):
  # First parameter for any django aggregate is group identifier.
  # Second is a key name, and the third one is an aggregate's verbose_name.
  Admin = Aggregate(5000, 'Admin', pgettext_lazy('staff', 'Admin'))
  Owner = Aggregate(4900, 'Owner', pgettext_lazy('staff', 'Owner'))
  Reader = Aggregate(4800, 'Reader', pgettext_lazy('staff', 'Reader'))


staff_root = ScopeRegistry.create_root('STAFF', pgettext_lazy('staff', 'Staff'))


# TextChoices is also a Enum, but with labels so better use a django-specific registry.
@staff_root.nest('USERS', pgettext_lazy('staff', 'Users'))
class Users(TextChoices):
  VIEW = 'VIEW', pgettext_lazy('staff', 'View')
  CHANGE = 'CHANGE', pgettext_lazy('staff', 'Change')
  CHANGE_OWN = 'CHANGE_OWN', pgettext_lazy('staff', 'Change own')
  DISABLE = 'DISABLE', pgettext_lazy('staff', 'Disable')


SHARED = {Users.CHANGE_OWN}


Roles.Admin.update(
  SHARED
  | {Users.CHANGE, Users.VIEW, Users.DISABLE}
)
Roles.Owner.update(
  SHARED
  | {Users.CHANGE_OWN, Users.VIEW}
)
Roles.Reader.update(
  SHARED
)

And so now you may run a checker for any user. Internally django checker will use user's .has_perm. So this way administrators cay manage access for any user.

from px_access_scopes import (
  ScopesChecker, HierarchyChecker, ScopesCheckRunner,
  MultiregistryHierarchyLookup
)
from px_access_scopes.contrib.django import ScopeDomain, user_checker
# All registries, that you've registered in config
from px_access_scopes.contrib.django.globals import registries

from .access_scopes import Users


# Simple checker here.
checker: ScopesChecker = ScopesCheckRunner((
  HierarchyChecker(
    # Django-specific checker that calls `user.has_perm`.
    (user_checker,),
    hierarchy_lookup=MultiregistryHierarchyLookup(
      # This might be any registries, not default ones.
      registries=registries
    )
  ),
))


def my_view(request):
  can = checker(
    # `.permission` - is the django's permission string.
    (User.CHANGE.permission,),
    # User kwarg is required for `user_checker`.
    user=request.user,
  )

  if not can:
    raise ...
  ...

For an easier usage scope on frontend there is an export mechanics:

from px_access_scopes.contrib.django.export import export_scopes

Function export_scopes exports all scopes from all registered registries. It has two modes: shorter one export_scopes(as_leaves=True) and more verbose and full export_scopes(as_leaves=False). It's for you to decide which one is preferable.

DRF

For django rest framework there are ready-to use permission classes.

They use the same checker mechanics as described above in django checking section.

some_views.py

from rest_framework.permissions import IsAuthenticated

from .access_scopes import Users


class UserUpdateDestroyAPIView:
  permission_classes = (
    IsAuthenticated
    &
    (
      # You may pass multiple scopes here.
      # Checker will be evaluated only for passed methods.
      # `methods` keyword is optional as by default it will check permission
      # for every possible method.
      ScopePermission.from_scopes(Users.VIEW, methods=('GET',))
      |
      ScopePermission.from_scopes(Users.CHANGE, methods=('PUT', 'PATCH))
      |
      ScopePermission.from_scopes(Users.CHANGE_OWN, methods=('PUT', 'PATCH))
      |
      ScopePermission.from_scopes(Users.DISABLE, methods=('DELETE',))
    ),
  )
  serializer_class = UserSerializer

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[Unreleased]

[0.1.7]

Added

  • Django 4.x support.

[0.1.0]

Initial version.

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

px-access-scopes-0.1.7.tar.gz (18.8 kB view hashes)

Uploaded Source

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