Hi all!
Here is another project I’ve been working on: a capability based object permission system (used for my project Oxylus).
Github: GitHub - oxylus-tech/django-caps: Capability based object permission system for Django · GitHub
Soooo what’s this?
Here is what we provide:
- Capability based object permissions system: objects can be shared with specific permissions to user/group. The object is then accessed by this shared object rather than directly (except for its owner).
- Access sharing: Objects’ accesses can be shared with granular control on permissions.
- Integration: authentication/permission backend is provided both for Django and Django Rest Framework. Views, viewsets and serializers too.
- Agents: users can act under different profiles, as a user or group. The accesses always target other agents.
- This avoids ACL usage, which can are prone to ambient privileges (for more on this subject, look for the paper on the project page, it actually is kinda interesting)
Among other things:
- Database id obfuscation: object internal id are never exposed to the outside world. Instead uuid are used to reference them in API and urls. This mitigate attacks on predictive id.
How it looks like
So what you do is:
- Create a model subclassing the
Ownedclass; - Provide which permissions can be assigned and shared on it;
- Use it in views;
# models.py
from django.db import models
from django.utils.translation import gettext_lazy as _
from caps.models import Owned
__all__ = ("Post",)
# Create our example model.
class Post(Owned):
title = models.CharField(_("Title"), max_length=64)
content = models.TextField(_("Content"))
# ... other fields
# Allowed permissions with allowed reshare depth
root_grants = {
"app.view_post": 2, # can be shared then reshared
"app.change_post": 1, # can be shared once
"app.delete_post": 0, # can not be shared
}
Example of programmatic usage:
from datetime import timedelta
from django.contrib.auth.models import User, Permission
from django.utils import timezone as tz
from caps.models import Agent
from .models import Post
# User has 1-1 relation with an agent
user = User.objects.all()[0]
user_1 = User.objects.all()[1]
# Create the post
post = Post.objects.create(owner=user.agent, title="Some title", content="Some content")
# Share the post to agent 1 with default grants
access = post.share(user_1.agent)
assert access.grants == {"app.view_post": 1, "app.change_post": 0}
# Get objects for user_1
objs = Post.objects.available(user_1.agent)
Example URL:
from django.urls import path
from caps import views
from . import models
urlpatterns = [
path("/post/", views.OwnedListView.as_view(model=models.Post), name="post-list"),
path("/post/<uuid:uuid>/", views.OwnedDetailView.as_view(model=models.Post), name="post-detail"),
path("/post/create/", views.OwnedCreateView.as_view(model=models.Post), name="post-create"),
path(
"/post/update/<uuid:uuid>",
views.OwnedUpdateView.as_view(model=models.Post),
name="post-update",
),
]
Notes
Here are different notes:
- We avoid database storage deduplication: capabilities are not stored in db as model but as a field;
- I’m currently restructuring different projects, idea would be to reuse part of thoses to add other features, as JWT support among other things. This will be done when I need it in other projects.
- Seamless integration with existing Django stack: views, middleware and automation for agent creation, etc.
What we don’t provide (yet?):
- Form/frontend integration to edit/share capabilities.
- Support for JWT: it awaits some restructuration among different project and that the need is required by people.