Skip to main content

Django combinable groups

Adds hierarchy to django permission groups.

Installation

pip install px-django-combinable-groups

Depends on: px-django-tree.

In settings.py:

INSTALLED_APPS += [
  # ...
  'django.contrib.auth',
  # ...
  # Dependent on:
  'pxd_tree.adjacency_list',
  # Should be added after auth package, or the one that defines `Group` model.
  'pxd_combinable_groups',
]

AUTHENTICATION_BACKENDS = [
  # Extended backend to check permissions in nested groups.
  'pxd_combinable_groups.backends.ModelBackend',
]

PXD_COMBINABLE_GROUPS = {
  # Use local cache in permissions resolver for key->id, id->key conversion.
  'USE_LOCAL_CACHE': True,
  # Use django's cache in permissions resolver for key->id, id->key conversion.
  'USE_DJANGO_CACHE': True,

  # Django cache key.
  'CACHE_KEY': 'default',
  # Cache timeout.
  'CACHE_TIMEOUT': 7 * 24 * 60 * 60,

  # Group model.
  'GROUP_MODEL': 'django.contrib.auth.models.Group',
  # Permission model.
  'PERMISSION_MODEL': 'django.contrib.auth.models.Permission',
}

Usage

Package adds additional model, to construct auth groups oriented graph. So for each group you may define dependencies to additionally collect permissions from.

There are two services available for groups tree management:

  • pxd_combinable_groups.services.permissions_collector - Permissions management methods.
  • pxd_combinable_groups.services.tree_collector - Groups tree manager.
from pxd_combinable_groups.services import permissions_collector


# Mapping

# Permissions collector mostly works with permission identifiers, not
# key-strings.
# To make your life easier there are two methods that converts one to another:
keys = ('auth.view_group', 'auth.change_group')
ids = permissions_collector.keys_to_ids(keys)
# > [1, 2]
permissions_collector.ids_to_keys(ids)
# > ['auth.view_group', 'auth.change_group']

# Methods preserve order so you may do things like that:
key_to_id_map = dict(zip(keys, permissions_collector.keys_to_ids(keys)))
# > {'auth.view_group': 1, 'auth.change_group': 2}


# Group management

# In case you need to know what permissions is in the certain groups you may
# use those methods:

# To get all permissions from any amount of groups:
permission_ids = permissions_collector.collect_all([
  group_id_1, group_id_2, group_id_3, group_id_4, # ... and so on.
])
# > [1,2,3,4,5,6,7,8,9]

# When you need to get permissions for each of groups separately:
groups = permissions_collector.collect([group_id_1, group_id_2])
# Result will be a list of `(group_id, permission_ids[])` for each passed group.
# Method `collect` preserves groups order.
# > [
#   (group_id_1, {1,2,3,4,5}),
#   (group_id_2, {2,3,4,5,6}),
# ]

# In case when you need to get permission for sets of groups:
sets = permissions_collector.collect_sets([
  (group_id_1, group_id_2),
  (group_id_3, group_id_4),
  (group_id_2, group_id_5),
])
# For each group identifier lists there will be a set of permission ids.
# Result will preserve initial sets order.
# > [
#   {1,2,3,4,5,6},
#   {3,4,5,6},
#   {1,2,5,6,7,8},
# ]


# User permissions resolver

# There is also a permissions resolvers for users.

# If you need to get permissions for multiple users at once:
permissions = permissions_collector.for_users([user_id_1, user_id_2])
# For each user identifier you'll receive a set of permission identifiers in
# a preserved order.
# > [
#   {2,3,7,9,23},
#   {2,3,7,9,23},
# ]

# And for one user.
# This method also will store cached response in user object, so for second
# call and further - there wouldn't be any db queries.
permissions = permissions_collector.for_user(user_object)
# > {2,3,7,9,23}

All those methods are pretty efficient in terms of database querying, so it will be better to prepare incoming permission/group/user ids/keys and then collect all of them once, than run them n times for each case.

from pxd_combinable_groups.services import tree_collector


# This is a simple tree resolver for combinable groups.
# Technically it's not a tree, but an oriented graph, but you know,
# naming issues...

# Only two methods: `get_ancestors` and `get_descendants`.
# All those methods are done in one db call. They'r not very efficient but
# we're assuming there wouldn't be a lot of groups.
# Method is circular-graph proved. So any circular dependencies will result
# in graph gathering no matter what method will be called.

# Getting descendants is simple for any amount of groups you'll need:
descendants = tree_collector.get_descendants([group_id_1, group_id_2])
# Result will preserve order:
# > [
#   (group_id_1, [nested_group_id_1, nested_group_id_2, ...],
#   (group_id_2, [nested_group_id_4, nested_group_id_5, ...],
# ]

# Getting ancestors is same as for descendants:
descendants = tree_collector.get_descendants([group_id_1, group_id_2])
# Result will preserve order too.
# For tree-like structures where there is only one parent ancestors will be
# in straight [parent, parent's_parent, parent's_parent's_parent, ...] order.
# > [
#   (group_id_1, [ancestor_group_id_1, ancestor_group_id_2, ...],
#   (group_id_2, [ancestor_group_id_4, ancestor_group_id_5, ...],
# ]

Admin

In administrative panel you may configure your groups with an additional grouper_to_source inliner in Group model editing interface.

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.1]

Added

  • Extended authorization backend.

[0.1.0]

Initial version.

Metadata

Release files for px-django-combinable-groups 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for px-django-combinable-groups 0.1.1
File Size Uploaded Provenance
px-django-combinable-groups-0.1.1.tar.gz 10.7 kB Without provenance Details

Release files / px-django-combinable-groups-0.1.1.tar.gz

Download URL px-django-combinable-groups-0.1.1.tar.gz
Size 10.7 kB
Tags Source
SHA-256 checksum
How to use checksums
6159be73479435a37a2d9c15afd98e4cb4a2095ddcced8ca38075f94b0e4f0a3
BLAKE2b-256 checksum
How to use checksums
cbaa80b93d430b2a07bc4ba78a6882e32a03e912b52aef814f37d09dc961e69e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.1 CPython/3.9.12

Security for px-django-combinable-groups 0.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.1 This release

1 release file No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page