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 details)

Uploaded Source

File details

Details for the file px-access-scopes-0.1.7.tar.gz.

File metadata

  • Download URL: px-access-scopes-0.1.7.tar.gz
  • Upload date:
  • Size: 18.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.11.4

File hashes

Hashes for px-access-scopes-0.1.7.tar.gz
Algorithm Hash digest
SHA256 d59610ec3115c85820569796598f03101c5d8740e234cdbb10c4cacc1330b12c
MD5 9205f5b9c35b2c93b81bdff383ee44f2
BLAKE2b-256 71993dd7ef5b381f791dfd9b5926fbdfd38af5cb88eef97430da2fea823cb81a

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