-
-
Notifications
You must be signed in to change notification settings - Fork 874
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
move rbac stuff to separate django app
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
- Loading branch information
Showing
33 changed files
with
3,257 additions
and
3,196 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
authentik/core/migrations/0032_alter_group_options_group_roles.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# Generated by Django 4.2.6 on 2023-10-09 15:19 | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
dependencies = [ | ||
("authentik_rbac", "__first__"), | ||
("authentik_core", "0031_alter_user_type"), | ||
] | ||
|
||
operations = [ | ||
migrations.AlterModelOptions( | ||
name="group", | ||
options={"verbose_name": "Group", "verbose_name_plural": "Groups"}, | ||
), | ||
migrations.AddField( | ||
model_name="group", | ||
name="roles", | ||
field=models.ManyToManyField( | ||
blank=True, related_name="ak_groups", to="authentik_rbac.role" | ||
), | ||
), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
"""authentik rbac app config""" | ||
from authentik.blueprints.apps import ManagedAppConfig | ||
|
||
|
||
class AuthentikRBACConfig(ManagedAppConfig): | ||
"""authentik rbac app config""" | ||
|
||
name = "authentik.rbac" | ||
label = "authentik_rbac" | ||
verbose_name = "authentik RBAC" | ||
default = True | ||
|
||
def reconcile_load_rbac_signals(self): | ||
"""Load rbac signals""" | ||
self.import_module("authentik.rbac.signals") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
from uuid import uuid4 | ||
|
||
from django.db import models | ||
from django.utils.translation import gettext_lazy as _ | ||
|
||
|
||
class Role(models.Model): | ||
"""RBAC role, which can have different permissions (both global and per-object) attached | ||
to it.""" | ||
|
||
uuid = models.UUIDField(default=uuid4, editable=False, unique=True, primary_key=True) | ||
# Due to the way django and django-guardian work, this is somewhat of a hack. | ||
# Django and django-guardian allow for setting permissions on users and groups, but they | ||
# only allow for a custom user object, not a custom group object, which is why | ||
# we have both authentik and django groups. With this model, we use the inbuilt group system | ||
# for RBAC. This means that every Role needs a single django group that its assigned to | ||
# which will hold all of the actual permissions | ||
# The main advantage of that is that all the permission checking just works out of the box, | ||
# as these permissions are checked by default by django and most other libraries that build | ||
# on top of django | ||
group = models.OneToOneField("auth.Group", on_delete=models.CASCADE) | ||
|
||
# name field has the same constraints as the group model | ||
name = models.TextField(max_length=150, unique=True) | ||
|
||
def __str__(self) -> str: | ||
return f"Role {self.name}" | ||
|
||
class Meta: | ||
verbose_name = _("Role") | ||
verbose_name_plural = _("Roles") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
"""rbac signals""" | ||
from django.contrib.auth.models import Group as DjangoGroup | ||
from django.db.models.signals import m2m_changed, pre_save | ||
from django.db.transaction import atomic | ||
from django.dispatch import receiver | ||
from structlog.stdlib import get_logger | ||
|
||
from authentik.core.models import Group | ||
from authentik.rbac.models import Role | ||
|
||
LOGGER = get_logger() | ||
|
||
|
||
@receiver(pre_save, sender=Role) | ||
def rbac_role_pre_save(sender: type[Role], instance: Role, **_): | ||
"""Ensure role has a group object created for it""" | ||
if hasattr(instance, "group"): | ||
return | ||
group, _ = DjangoGroup.objects.get_or_create(name=instance.name) | ||
instance.group = group | ||
|
||
|
||
@receiver(m2m_changed, sender=Group.roles.through) | ||
def rbac_group_role_m2m(sender: type[Group], action: str, instance: Group, reverse: bool, **_): | ||
"""RBAC: Sync group members into roles when roles are assigned""" | ||
if action not in ["post_add", "post_remove", "post_clear"]: | ||
return | ||
with atomic(): | ||
group_users = list( | ||
instance.children_recursive() | ||
.exclude(users__isnull=True) | ||
.values_list("users", flat=True) | ||
) | ||
if not group_users: | ||
return | ||
for role in instance.roles.all(): | ||
role: Role | ||
role.group.user_set.set(group_users) | ||
LOGGER.debug("Updated users in group", group=instance) | ||
|
||
|
||
@receiver(m2m_changed, sender=Group.users.through) | ||
def rbac_group_users_m2m( | ||
sender: type[Group], action: str, instance: Group, pk_set: set, reverse: bool, **_ | ||
): | ||
if action not in ["post_add", "post_remove"]: | ||
return | ||
# reverse: instance is a Group, pk_set is a list of user pks | ||
# non-reverse: instance is a User, pk_set is a list of groups | ||
with atomic(): | ||
if reverse: | ||
for role in instance.roles.all(): | ||
role: Role | ||
if action == "post_add": | ||
role.group.user_set.add(*pk_set) | ||
elif action == "post_remove": | ||
role.group.user_set.remove(*pk_set) | ||
else: | ||
for group in Group.objects.filter(pk__in=pk_set): | ||
for role in group.roles.all(): | ||
role: Role | ||
if action == "post_add": | ||
role.group.user_set.add(instance) | ||
elif action == "post_remove": | ||
role.group.user_set.remove(instance) | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
from authentik.rbac.api.rbac import RBACPermissionViewSet | ||
from authentik.rbac.api.rbac_roles import RoleAssignedPermissionViewSet | ||
from authentik.rbac.api.rbac_users import UserAssignedPermissionViewSet | ||
from authentik.rbac.api.roles import RoleViewSet | ||
|
||
api_urlpatterns = [ | ||
("rbac/permissions", RBACPermissionViewSet), | ||
("rbac/assigned_users", UserAssignedPermissionViewSet), | ||
("rbac/assigned_roles", RoleAssignedPermissionViewSet), | ||
("rbac/roles", RoleViewSet), | ||
] |
Oops, something went wrong.