Skip to main content

django-keeper

Authorization library for Django, not depends on models.

  • Won't depend on models
  • Won't depend on datastores
  • Won't depend on Django's permission system
  • Won't depend on Django's User model

Supported versions:

  • Python 3.8
  • Python 3.9
  • Python 3.10
  • Python 3.11
  • Python 3.12
  • Python 3.13
  • Django 2.2
  • Django 3.0
  • Django 3.1
  • Django 3.2
  • Django 4.1
  • Django 4.2
  • Django 5.0
  • Django 5.1
  • Django 5.2

Install

$ pip install django-keeper

And add to INSTALLED_APPS

INSTALLED_APPS = [
    ...
    'keeper',
]

At A Glance

Declarative permission mapping for models.

from django.conf import settings
from keeper.security import Allow
from keeper.operators import Everyone, Authenticated, IsUser


class Issue(models.Model):
    author = models.ForeignKey(settings.AUTH_USER_MODEL)
    ...

    def __acl__(self):
        return [
            (Allow, Everyone, 'view'),
            (Allow, Authenticated, 'add_comment'),
            (Allow, IsUser(self.author), 'edit'),
        ]

Instances of model allow:

  • Every requests to view
  • Autheticated requests to add comments
  • it's author to edit

Then, apply @keeper for views.

from keeper.views import keeper


# Model Permissions
@keeper(
    'view',
    model=Issue,
    mapper=lambda request, issue_id: {'id': issue_id},
)
def issue_detail(request, issue_id):
    """ View requires 'view' permission of Issue model

    * An issue object will be retrieved
    * keeper will check whether the rquests has 'view' permission for the issue

    The third argument function can return keyword argument to retrieve the issue object.
    """
    request.k_context  # Will be instance of the issue object
    ...



@keeper(
    'add_comment',
    model=Issue,
    mapper=lambda request, issue_id: {'id': issue_id},
)
def add_comment(request, issue_id):
    ...

Global Permission

Not just for model permissions django-keeper can handle global permissions.

First, write class having __acl__ method in models.py.

class Root:
    def __acl__(self):
        return [
            (Allow, Authenticated, 'view_dashboard'),
            (Allow, Authenticated, 'add_issue'),
        ]

It's not necessary to put it in models.py, but easy to understand.

And specify it in settings.

KEEPER_GLOBAL_CONTEXT = 'myapp.models.Root'

Then you can use global permission in views. Simply just apply @keeper and permission names.

@keeper('add_issue')
def issue_list(request):
    """ View requires 'add_issue' permission of Root Context
    """

Operators

Operators is just Callable[[HttpRequest], bool]. By default django-keeper has these operators:

  • keeper.operators.Everyone
  • keeper.operators.Authenticated
  • keeper.operators.IsUser
  • keeper.operators.Staff

Also you can create your own operators easily.

from keeper.operators import Authenticated, Operator


class IsIP(Operator):
    def __init__(self, ip):
        self.ip = ip
        
    def __call__(self, request):
        return request.META.get('REMOTE_ADDR') == self.ip


class BelongsTeam(Authenticated):
    def __init__(self, team, role):
        self.team = team

    def __call__(self, request):
        if not super().__call__(request):
            return False
        return  request.user.team == self.team

Use it in ACL

class Article(models.Model):
    team = models.ForeignKey(Team)
    
    def __acl__(self):
        return [
            (Allow, Everyone, 'view'),
            (Allow, BelongsTeam(self.team), 'edit'),
            (Allow, IsIP(settings.COMPANY_IP_ADDRESS), 'edit'),
        ]

Combining operators

You can use bitwise operators to combine multiple "Operators".

class Article(models.Model):
    def __acl__(self):
        return [
            (Allow, Authenticated() & IsIP(settings.COMPANY_IP_ADDRESS), 'view'),
        ]

There operators can be used

  • a & b
  • a | b
  • a ^ b
  • ~a

On Fail Actions

You can change actions when requests can't pass ACLs.

from keeper.views import keeper, login_required

@keeper(
    'view_articles',
    on_fail=login_required(),
)
def dashboard(request):
    ...

This view will behave just like @login_required decorator of Django when requests don't have 'view' permission.

Also you can use other actions.

  • keeper.views.login_required
  • keeper.views.permission_denied
  • keeper.views.not_found
  • keeper.views.redirect

Use in template

Handling permissions in templates is also supported.

{% load keeper %}

{% has_permission issue 'edit' as can_edit %}

{% if can_edit %}
    <a href="...">Edit</a>
{% endif %}

When checking global permission, use has_global_permission.

{% load keeper %}

{% has_global_permission 'add_issue' as can_add_issue %}

{% if can_add_issue %}
    <a href="...">New Issue</a>
{% endif %}

With Django Core

Add the authentication backend:

AUTHENTICATION_BACKENDS = (
    'keeper.permissions.ObjectPermissionBackend',
    'django.contrib.auth.backends.ModelBackend',
)

Now User.has_perm method will consider permissions of django-keeper.

Alternative

  • django-guardian
    • It depends on databases
    • Not way to handle global permissions, not just for a model
  • django-rules
    • Basiaclly, rules is used with Django's permissions, but keeper isn't (basiacally).
    • rules doesn't support view decorators or mixins for it's "rules" system (for Django's permission, yes).

FAQ

  • Can I filter models by using ACL?
    • Not supported

Metadata

Release files for django-keeper 0.6.0

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

Source distribution (sdist)

Source distribution for django-keeper 0.6.0
File Size Uploaded
django_keeper-0.6.0.tar.gz 7.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for django-keeper 0.6.0
File Interpreter ABI Platform
django_keeper-0.6.0-py3-none-any.whl Python 3 none any Details

Total release size: 15.8 kB

Release files / django_keeper-0.6.0.tar.gz

Download URL django_keeper-0.6.0.tar.gz
Size 7.9 kB
Tags Source
SHA-256 checksum
How to use checksums
9aba96fc845b0a36083df2e3265e5710125f518bf3f3c388eff69c2ab35c884f
BLAKE2b-256 checksum
How to use checksums
f5f7b2a819dc979c98263e8e2365da689026919d772633f9c3a5c3f2f3fc9f38
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.12.6

Release files / django_keeper-0.6.0-py3-none-any.whl

Download URL django_keeper-0.6.0-py3-none-any.whl
Size 7.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
781d11a31c2d9b32d13cae45d829846ddf3bf15b0e343e463cbf0b3af918f1cd
BLAKE2b-256 checksum
How to use checksums
d4ab4a310765fe238afd2e728d2c8b511de402ff3ba59f337d21f4e2185ba128
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.12.6

Release history Release notifications | RSS feed

This release

0.6.0 This release

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.13

2 release files

0.1.9

2 release files

0.1.8

2 release files

0.1.7

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

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