rbac: rework API for terraform, add blueprint support (#10698)
* rbac: rework API slightly to improve terraform compatibility Signed-off-by: Jens Langhammer <jens@goauthentik.io> * sigh https://www.django-rest-framework.org/api-guide/filtering/#filtering-and-object-lookups Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add permission support for users global permissions Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add role support to blueprints Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix yaml tags Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add generated read-only role Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix web Signed-off-by: Jens Langhammer <jens@goauthentik.io> * make permissions optional Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add docs Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add object permission support to blueprints Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix tests kinda Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add more tests and fix bugs Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
15
.vscode/settings.json
vendored
15
.vscode/settings.json
vendored
@ -23,15 +23,16 @@
|
||||
"todo-tree.tree.showCountsInTree": true,
|
||||
"todo-tree.tree.showBadges": true,
|
||||
"yaml.customTags": [
|
||||
"!Find sequence",
|
||||
"!KeyOf scalar",
|
||||
"!Context scalar",
|
||||
"!Context sequence",
|
||||
"!Format sequence",
|
||||
"!Condition sequence",
|
||||
"!Env sequence",
|
||||
"!Context scalar",
|
||||
"!Enumerate sequence",
|
||||
"!Env scalar",
|
||||
"!If sequence"
|
||||
"!Find sequence",
|
||||
"!Format sequence",
|
||||
"!If sequence",
|
||||
"!Index scalar",
|
||||
"!KeyOf scalar",
|
||||
"!Value scalar"
|
||||
],
|
||||
"typescript.preferences.importModuleSpecifier": "non-relative",
|
||||
"typescript.preferences.importModuleSpecifierEnding": "index",
|
||||
|
@ -113,16 +113,19 @@ class Command(BaseCommand):
|
||||
)
|
||||
model_path = f"{model._meta.app_label}.{model._meta.model_name}"
|
||||
self.schema["properties"]["entries"]["items"]["oneOf"].append(
|
||||
self.template_entry(model_path, serializer)
|
||||
self.template_entry(model_path, model, serializer)
|
||||
)
|
||||
|
||||
def template_entry(self, model_path: str, serializer: Serializer) -> dict:
|
||||
def template_entry(self, model_path: str, model: type[Model], serializer: Serializer) -> dict:
|
||||
"""Template entry for a single model"""
|
||||
model_schema = self.to_jsonschema(serializer)
|
||||
model_schema["required"] = []
|
||||
def_name = f"model_{model_path}"
|
||||
def_path = f"#/$defs/{def_name}"
|
||||
self.schema["$defs"][def_name] = model_schema
|
||||
def_name_perm = f"model_{model_path}_permissions"
|
||||
def_path_perm = f"#/$defs/{def_name_perm}"
|
||||
self.schema["$defs"][def_name_perm] = self.model_permissions(model)
|
||||
return {
|
||||
"type": "object",
|
||||
"required": ["model", "identifiers"],
|
||||
@ -135,6 +138,7 @@ class Command(BaseCommand):
|
||||
"default": "present",
|
||||
},
|
||||
"conditions": {"type": "array", "items": {"type": "boolean"}},
|
||||
"permissions": {"$ref": def_path_perm},
|
||||
"attrs": {"$ref": def_path},
|
||||
"identifiers": {"$ref": def_path},
|
||||
},
|
||||
@ -185,3 +189,20 @@ class Command(BaseCommand):
|
||||
if required:
|
||||
result["required"] = required
|
||||
return result
|
||||
|
||||
def model_permissions(self, model: type[Model]) -> dict:
|
||||
perms = [x[0] for x in model._meta.permissions]
|
||||
for action in model._meta.default_permissions:
|
||||
perms.append(f"{action}_{model._meta.model_name}")
|
||||
return {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["permission"],
|
||||
"properties": {
|
||||
"permission": {"type": "string", "enum": perms},
|
||||
"user": {"type": "integer"},
|
||||
"role": {"type": "string"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
24
authentik/blueprints/tests/fixtures/rbac_object.yaml
vendored
Normal file
24
authentik/blueprints/tests/fixtures/rbac_object.yaml
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
version: 1
|
||||
entries:
|
||||
- model: authentik_core.user
|
||||
id: user
|
||||
identifiers:
|
||||
username: "%(id)s"
|
||||
attrs:
|
||||
name: "%(id)s"
|
||||
- model: authentik_rbac.role
|
||||
id: role
|
||||
identifiers:
|
||||
name: "%(id)s"
|
||||
- model: authentik_flows.flow
|
||||
identifiers:
|
||||
slug: "%(id)s"
|
||||
attrs:
|
||||
designation: authentication
|
||||
name: foo
|
||||
title: foo
|
||||
permissions:
|
||||
- permission: view_flow
|
||||
user: !KeyOf user
|
||||
- permission: view_flow
|
||||
role: !KeyOf role
|
8
authentik/blueprints/tests/fixtures/rbac_role.yaml
vendored
Normal file
8
authentik/blueprints/tests/fixtures/rbac_role.yaml
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
version: 1
|
||||
entries:
|
||||
- model: authentik_rbac.role
|
||||
identifiers:
|
||||
name: "%(id)s"
|
||||
attrs:
|
||||
permissions:
|
||||
- authentik_blueprints.view_blueprintinstance
|
9
authentik/blueprints/tests/fixtures/rbac_user.yaml
vendored
Normal file
9
authentik/blueprints/tests/fixtures/rbac_user.yaml
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
version: 1
|
||||
entries:
|
||||
- model: authentik_core.user
|
||||
identifiers:
|
||||
username: "%(id)s"
|
||||
attrs:
|
||||
name: "%(id)s"
|
||||
permissions:
|
||||
- authentik_blueprints.view_blueprintinstance
|
57
authentik/blueprints/tests/test_v1_rbac.py
Normal file
57
authentik/blueprints/tests/test_v1_rbac.py
Normal file
@ -0,0 +1,57 @@
|
||||
"""Test blueprints v1"""
|
||||
|
||||
from django.test import TransactionTestCase
|
||||
from guardian.shortcuts import get_perms
|
||||
|
||||
from authentik.blueprints.v1.importer import Importer
|
||||
from authentik.core.models import User
|
||||
from authentik.flows.models import Flow
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.lib.tests.utils import load_fixture
|
||||
from authentik.rbac.models import Role
|
||||
|
||||
|
||||
class TestBlueprintsV1RBAC(TransactionTestCase):
|
||||
"""Test Blueprints rbac attribute"""
|
||||
|
||||
def test_user_permission(self):
|
||||
"""Test permissions"""
|
||||
uid = generate_id()
|
||||
import_yaml = load_fixture("fixtures/rbac_user.yaml", id=uid)
|
||||
|
||||
importer = Importer.from_string(import_yaml)
|
||||
self.assertTrue(importer.validate()[0])
|
||||
self.assertTrue(importer.apply())
|
||||
user = User.objects.filter(username=uid).first()
|
||||
self.assertIsNotNone(user)
|
||||
self.assertTrue(user.has_perms(["authentik_blueprints.view_blueprintinstance"]))
|
||||
|
||||
def test_role_permission(self):
|
||||
"""Test permissions"""
|
||||
uid = generate_id()
|
||||
import_yaml = load_fixture("fixtures/rbac_role.yaml", id=uid)
|
||||
|
||||
importer = Importer.from_string(import_yaml)
|
||||
self.assertTrue(importer.validate()[0])
|
||||
self.assertTrue(importer.apply())
|
||||
role = Role.objects.filter(name=uid).first()
|
||||
self.assertIsNotNone(role)
|
||||
self.assertEqual(
|
||||
list(role.group.permissions.all().values_list("codename", flat=True)),
|
||||
["view_blueprintinstance"],
|
||||
)
|
||||
|
||||
def test_object_permission(self):
|
||||
"""Test permissions"""
|
||||
uid = generate_id()
|
||||
import_yaml = load_fixture("fixtures/rbac_object.yaml", id=uid)
|
||||
|
||||
importer = Importer.from_string(import_yaml)
|
||||
self.assertTrue(importer.validate()[0])
|
||||
self.assertTrue(importer.apply())
|
||||
flow = Flow.objects.filter(slug=uid).first()
|
||||
user = User.objects.filter(username=uid).first()
|
||||
role = Role.objects.filter(name=uid).first()
|
||||
self.assertIsNotNone(flow)
|
||||
self.assertEqual(get_perms(user, flow), ["view_flow"])
|
||||
self.assertEqual(get_perms(role.group, flow), ["view_flow"])
|
@ -1,7 +1,7 @@
|
||||
"""transfer common classes"""
|
||||
|
||||
from collections import OrderedDict
|
||||
from collections.abc import Iterable, Mapping
|
||||
from collections.abc import Generator, Iterable, Mapping
|
||||
from copy import copy
|
||||
from dataclasses import asdict, dataclass, field, is_dataclass
|
||||
from enum import Enum
|
||||
@ -58,6 +58,15 @@ class BlueprintEntryDesiredState(Enum):
|
||||
MUST_CREATED = "must_created"
|
||||
|
||||
|
||||
@dataclass
|
||||
class BlueprintEntryPermission:
|
||||
"""Describe object-level permissions"""
|
||||
|
||||
permission: Union[str, "YAMLTag"]
|
||||
user: Union[int, "YAMLTag", None] = field(default=None)
|
||||
role: Union[str, "YAMLTag", None] = field(default=None)
|
||||
|
||||
|
||||
@dataclass
|
||||
class BlueprintEntry:
|
||||
"""Single entry of a blueprint"""
|
||||
@ -69,6 +78,7 @@ class BlueprintEntry:
|
||||
conditions: list[Any] = field(default_factory=list)
|
||||
identifiers: dict[str, Any] = field(default_factory=dict)
|
||||
attrs: dict[str, Any] | None = field(default_factory=dict)
|
||||
permissions: list[BlueprintEntryPermission] = field(default_factory=list)
|
||||
|
||||
id: str | None = None
|
||||
|
||||
@ -150,6 +160,17 @@ class BlueprintEntry:
|
||||
"""Get the blueprint model, with yaml tags resolved if present"""
|
||||
return str(self.tag_resolver(self.model, blueprint))
|
||||
|
||||
def get_permissions(
|
||||
self, blueprint: "Blueprint"
|
||||
) -> Generator[BlueprintEntryPermission, None, None]:
|
||||
"""Get permissions of this entry, with all yaml tags resolved"""
|
||||
for perm in self.permissions:
|
||||
yield BlueprintEntryPermission(
|
||||
permission=self.tag_resolver(perm.permission, blueprint),
|
||||
user=self.tag_resolver(perm.user, blueprint),
|
||||
role=self.tag_resolver(perm.role, blueprint),
|
||||
)
|
||||
|
||||
def check_all_conditions_match(self, blueprint: "Blueprint") -> bool:
|
||||
"""Check all conditions of this entry match (evaluate to True)"""
|
||||
return all(self.tag_resolver(self.conditions, blueprint))
|
||||
|
@ -16,6 +16,7 @@ from django.db.models.query_utils import Q
|
||||
from django.db.transaction import atomic
|
||||
from django.db.utils import IntegrityError
|
||||
from guardian.models import UserObjectPermission
|
||||
from guardian.shortcuts import assign_perm
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.serializers import BaseSerializer, Serializer
|
||||
from structlog.stdlib import BoundLogger, get_logger
|
||||
@ -35,6 +36,7 @@ from authentik.core.models import (
|
||||
PropertyMapping,
|
||||
Provider,
|
||||
Source,
|
||||
User,
|
||||
UserSourceConnection,
|
||||
)
|
||||
from authentik.enterprise.license import LicenseKey
|
||||
@ -54,11 +56,13 @@ from authentik.events.utils import cleanse_dict
|
||||
from authentik.flows.models import FlowToken, Stage
|
||||
from authentik.lib.models import SerializerModel
|
||||
from authentik.lib.sentry import SentryIgnoredException
|
||||
from authentik.lib.utils.reflection import get_apps
|
||||
from authentik.outposts.models import OutpostServiceConnection
|
||||
from authentik.policies.models import Policy, PolicyBindingModel
|
||||
from authentik.policies.reputation.models import Reputation
|
||||
from authentik.providers.oauth2.models import AccessToken, AuthorizationCode, RefreshToken
|
||||
from authentik.providers.scim.models import SCIMProviderGroup, SCIMProviderUser
|
||||
from authentik.rbac.models import Role
|
||||
from authentik.sources.scim.models import SCIMSourceGroup, SCIMSourceUser
|
||||
from authentik.stages.authenticator_webauthn.models import WebAuthnDeviceType
|
||||
from authentik.tenants.models import Tenant
|
||||
@ -136,6 +140,16 @@ def transaction_rollback():
|
||||
pass
|
||||
|
||||
|
||||
def rbac_models() -> dict:
|
||||
models = {}
|
||||
for app in get_apps():
|
||||
for model in app.get_models():
|
||||
if not is_model_allowed(model):
|
||||
continue
|
||||
models[model._meta.model_name] = app.label
|
||||
return models
|
||||
|
||||
|
||||
class Importer:
|
||||
"""Import Blueprint from raw dict or YAML/JSON"""
|
||||
|
||||
@ -154,7 +168,10 @@ class Importer:
|
||||
|
||||
def default_context(self):
|
||||
"""Default context"""
|
||||
return {"goauthentik.io/enterprise/licensed": LicenseKey.get_total().is_valid()}
|
||||
return {
|
||||
"goauthentik.io/enterprise/licensed": LicenseKey.get_total().is_valid(),
|
||||
"goauthentik.io/rbac/models": rbac_models(),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def from_string(yaml_input: str, context: dict | None = None) -> "Importer":
|
||||
@ -320,6 +337,15 @@ class Importer:
|
||||
) from exc
|
||||
return serializer
|
||||
|
||||
def _apply_permissions(self, instance: Model, entry: BlueprintEntry):
|
||||
"""Apply object-level permissions for an entry"""
|
||||
for perm in entry.get_permissions(self._import):
|
||||
if perm.user is not None:
|
||||
assign_perm(perm.permission, User.objects.get(pk=perm.user), instance)
|
||||
if perm.role is not None:
|
||||
role = Role.objects.get(pk=perm.role)
|
||||
role.assign_permission(perm.permission, obj=instance)
|
||||
|
||||
def apply(self) -> bool:
|
||||
"""Apply (create/update) models yaml, in database transaction"""
|
||||
try:
|
||||
@ -384,6 +410,7 @@ class Importer:
|
||||
if "pk" in entry.identifiers:
|
||||
self.__pk_map[entry.identifiers["pk"]] = instance.pk
|
||||
entry._state = BlueprintEntryState(instance)
|
||||
self._apply_permissions(instance, entry)
|
||||
elif state == BlueprintEntryDesiredState.ABSENT:
|
||||
instance: Model | None = serializer.instance
|
||||
if instance.pk:
|
||||
|
@ -5,6 +5,7 @@ from json import loads
|
||||
from typing import Any
|
||||
|
||||
from django.contrib.auth import update_session_auth_hash
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.contrib.sessions.backends.cache import KEY_PREFIX
|
||||
from django.core.cache import cache
|
||||
from django.db.models.functions import ExtractHour
|
||||
@ -33,15 +34,21 @@ from drf_spectacular.utils import (
|
||||
)
|
||||
from guardian.shortcuts import get_objects_for_user
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.fields import CharField, IntegerField, ListField, SerializerMethodField
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.fields import (
|
||||
BooleanField,
|
||||
CharField,
|
||||
ChoiceField,
|
||||
DateTimeField,
|
||||
IntegerField,
|
||||
ListField,
|
||||
SerializerMethodField,
|
||||
)
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.serializers import (
|
||||
BooleanField,
|
||||
DateTimeField,
|
||||
ListSerializer,
|
||||
PrimaryKeyRelatedField,
|
||||
ValidationError,
|
||||
)
|
||||
from rest_framework.validators import UniqueValidator
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
@ -78,6 +85,7 @@ from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlanner
|
||||
from authentik.flows.views.executor import QS_KEY_TOKEN
|
||||
from authentik.lib.avatars import get_avatar
|
||||
from authentik.rbac.decorators import permission_required
|
||||
from authentik.rbac.models import get_permission_choices
|
||||
from authentik.stages.email.models import EmailStage
|
||||
from authentik.stages.email.tasks import send_mails
|
||||
from authentik.stages.email.utils import TemplateEmailMessage
|
||||
@ -141,12 +149,19 @@ class UserSerializer(ModelSerializer):
|
||||
super().__init__(*args, **kwargs)
|
||||
if SERIALIZER_CONTEXT_BLUEPRINT in self.context:
|
||||
self.fields["password"] = CharField(required=False, allow_null=True)
|
||||
self.fields["permissions"] = ListField(
|
||||
required=False, child=ChoiceField(choices=get_permission_choices())
|
||||
)
|
||||
|
||||
def create(self, validated_data: dict) -> User:
|
||||
"""If this serializer is used in the blueprint context, we allow for
|
||||
directly setting a password. However should be done via the `set_password`
|
||||
method instead of directly setting it like rest_framework."""
|
||||
password = validated_data.pop("password", None)
|
||||
permissions = Permission.objects.filter(
|
||||
codename__in=[x.split(".")[1] for x in validated_data.pop("permissions", [])]
|
||||
)
|
||||
validated_data["user_permissions"] = permissions
|
||||
instance: User = super().create(validated_data)
|
||||
self._set_password(instance, password)
|
||||
return instance
|
||||
@ -155,6 +170,10 @@ class UserSerializer(ModelSerializer):
|
||||
"""Same as `create` above, set the password directly if we're in a blueprint
|
||||
context"""
|
||||
password = validated_data.pop("password", None)
|
||||
permissions = Permission.objects.filter(
|
||||
codename__in=[x.split(".")[1] for x in validated_data.pop("permissions", [])]
|
||||
)
|
||||
validated_data["user_permissions"] = permissions
|
||||
instance = super().update(instance, validated_data)
|
||||
self._set_password(instance, password)
|
||||
return instance
|
||||
|
@ -23,6 +23,7 @@ class SAMLPropertyMappingFilter(PropertyMappingFilterSet):
|
||||
|
||||
class Meta(PropertyMappingFilterSet.Meta):
|
||||
model = SAMLPropertyMapping
|
||||
fields = PropertyMappingFilterSet.Meta.fields + ["saml_name", "friendly_name"]
|
||||
|
||||
|
||||
class SAMLPropertyMappingViewSet(UsedByMixin, ModelViewSet):
|
||||
|
@ -59,6 +59,12 @@ class PermissionSerializer(ModelSerializer):
|
||||
]
|
||||
|
||||
|
||||
class PermissionAssignResultSerializer(PassiveSerializer):
|
||||
"""Result from assigning permissions to a user/role"""
|
||||
|
||||
id = CharField()
|
||||
|
||||
|
||||
class PermissionFilter(FilterSet):
|
||||
"""Filter permissions"""
|
||||
|
||||
|
@ -16,7 +16,7 @@ from rest_framework.viewsets import GenericViewSet
|
||||
|
||||
from authentik.core.api.utils import ModelSerializer, PassiveSerializer
|
||||
from authentik.policies.event_matcher.models import model_choices
|
||||
from authentik.rbac.api.rbac import PermissionAssignSerializer
|
||||
from authentik.rbac.api.rbac import PermissionAssignResultSerializer, PermissionAssignSerializer
|
||||
from authentik.rbac.decorators import permission_required
|
||||
from authentik.rbac.models import Role
|
||||
|
||||
@ -28,7 +28,7 @@ class RoleObjectPermissionSerializer(ModelSerializer):
|
||||
model = ReadOnlyField(source="content_type.model")
|
||||
codename = ReadOnlyField(source="permission.codename")
|
||||
name = ReadOnlyField(source="permission.name")
|
||||
object_pk = ReadOnlyField()
|
||||
object_pk = CharField()
|
||||
|
||||
class Meta:
|
||||
model = GroupObjectPermission
|
||||
@ -88,8 +88,9 @@ class RoleAssignedPermissionViewSet(ListModelMixin, GenericViewSet):
|
||||
@extend_schema(
|
||||
request=PermissionAssignSerializer(),
|
||||
responses={
|
||||
204: OpenApiResponse(description="Successfully assigned"),
|
||||
200: PermissionAssignResultSerializer(many=True),
|
||||
},
|
||||
operation_id="rbac_permissions_assigned_by_roles_assign",
|
||||
)
|
||||
@action(methods=["POST"], detail=True, pagination_class=None, filter_backends=[])
|
||||
def assign(self, request: Request, *args, **kwargs) -> Response:
|
||||
@ -98,10 +99,12 @@ class RoleAssignedPermissionViewSet(ListModelMixin, GenericViewSet):
|
||||
role: Role = self.get_object()
|
||||
data = PermissionAssignSerializer(data=request.data)
|
||||
data.is_valid(raise_exception=True)
|
||||
ids = []
|
||||
with atomic():
|
||||
for perm in data.validated_data["permissions"]:
|
||||
assign_perm(perm, role.group, data.validated_data["model_instance"])
|
||||
return Response(status=204)
|
||||
assigned_perm = assign_perm(perm, role.group, data.validated_data["model_instance"])
|
||||
ids.append(PermissionAssignResultSerializer(instance={"id": assigned_perm.pk}).data)
|
||||
return Response(ids, status=200)
|
||||
|
||||
@permission_required("authentik_rbac.unassign_role_permissions")
|
||||
@extend_schema(
|
||||
|
@ -9,7 +9,7 @@ from guardian.models import UserObjectPermission
|
||||
from guardian.shortcuts import assign_perm, remove_perm
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.fields import BooleanField, ReadOnlyField
|
||||
from rest_framework.fields import BooleanField, CharField, ReadOnlyField
|
||||
from rest_framework.mixins import ListModelMixin
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
@ -19,7 +19,7 @@ from authentik.core.api.groups import GroupMemberSerializer
|
||||
from authentik.core.api.utils import ModelSerializer
|
||||
from authentik.core.models import User, UserTypes
|
||||
from authentik.policies.event_matcher.models import model_choices
|
||||
from authentik.rbac.api.rbac import PermissionAssignSerializer
|
||||
from authentik.rbac.api.rbac import PermissionAssignResultSerializer, PermissionAssignSerializer
|
||||
from authentik.rbac.decorators import permission_required
|
||||
|
||||
|
||||
@ -30,7 +30,7 @@ class UserObjectPermissionSerializer(ModelSerializer):
|
||||
model = ReadOnlyField(source="content_type.model")
|
||||
codename = ReadOnlyField(source="permission.codename")
|
||||
name = ReadOnlyField(source="permission.name")
|
||||
object_pk = ReadOnlyField()
|
||||
object_pk = CharField()
|
||||
|
||||
class Meta:
|
||||
model = UserObjectPermission
|
||||
@ -90,8 +90,9 @@ class UserAssignedPermissionViewSet(ListModelMixin, GenericViewSet):
|
||||
@extend_schema(
|
||||
request=PermissionAssignSerializer(),
|
||||
responses={
|
||||
204: OpenApiResponse(description="Successfully assigned"),
|
||||
200: PermissionAssignResultSerializer(many=True),
|
||||
},
|
||||
operation_id="rbac_permissions_assigned_by_users_assign",
|
||||
)
|
||||
@action(methods=["POST"], detail=True, pagination_class=None, filter_backends=[])
|
||||
def assign(self, request: Request, *args, **kwargs) -> Response:
|
||||
@ -101,10 +102,12 @@ class UserAssignedPermissionViewSet(ListModelMixin, GenericViewSet):
|
||||
raise ValidationError("Permissions cannot be assigned to an internal service account.")
|
||||
data = PermissionAssignSerializer(data=request.data)
|
||||
data.is_valid(raise_exception=True)
|
||||
ids = []
|
||||
with atomic():
|
||||
for perm in data.validated_data["permissions"]:
|
||||
assign_perm(perm, user, data.validated_data["model_instance"])
|
||||
return Response(status=204)
|
||||
assigned_perm = assign_perm(perm, user, data.validated_data["model_instance"])
|
||||
ids.append(PermissionAssignResultSerializer(instance={"id": assigned_perm.pk}).data)
|
||||
return Response(ids, status=200)
|
||||
|
||||
@permission_required("authentik_core.unassign_user_permissions")
|
||||
@extend_schema(
|
||||
|
@ -6,7 +6,12 @@ from django_filters.filterset import FilterSet
|
||||
from guardian.models import GroupObjectPermission
|
||||
from guardian.shortcuts import get_objects_for_group
|
||||
from rest_framework.fields import SerializerMethodField
|
||||
from rest_framework.mixins import ListModelMixin
|
||||
from rest_framework.mixins import (
|
||||
DestroyModelMixin,
|
||||
ListModelMixin,
|
||||
RetrieveModelMixin,
|
||||
UpdateModelMixin,
|
||||
)
|
||||
from rest_framework.viewsets import GenericViewSet
|
||||
|
||||
from authentik.api.pagination import SmallerPagination
|
||||
@ -64,10 +69,12 @@ class ExtraRoleObjectPermissionSerializer(RoleObjectPermissionSerializer):
|
||||
class RolePermissionFilter(FilterSet):
|
||||
"""Role permission filter"""
|
||||
|
||||
uuid = UUIDFilter("group__role__uuid", required=True)
|
||||
uuid = UUIDFilter("group__role__uuid")
|
||||
|
||||
|
||||
class RolePermissionViewSet(ListModelMixin, GenericViewSet):
|
||||
class RolePermissionViewSet(
|
||||
ListModelMixin, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin, GenericViewSet
|
||||
):
|
||||
"""Get a role's assigned object permissions"""
|
||||
|
||||
serializer_class = ExtraRoleObjectPermissionSerializer
|
||||
|
@ -6,7 +6,12 @@ from django_filters.filterset import FilterSet
|
||||
from guardian.models import UserObjectPermission
|
||||
from guardian.shortcuts import get_objects_for_user
|
||||
from rest_framework.fields import SerializerMethodField
|
||||
from rest_framework.mixins import ListModelMixin
|
||||
from rest_framework.mixins import (
|
||||
DestroyModelMixin,
|
||||
ListModelMixin,
|
||||
RetrieveModelMixin,
|
||||
UpdateModelMixin,
|
||||
)
|
||||
from rest_framework.viewsets import GenericViewSet
|
||||
|
||||
from authentik.api.pagination import SmallerPagination
|
||||
@ -64,10 +69,12 @@ class ExtraUserObjectPermissionSerializer(UserObjectPermissionSerializer):
|
||||
class UserPermissionFilter(FilterSet):
|
||||
"""User-assigned permission filter"""
|
||||
|
||||
user_id = NumberFilter("user__id", required=True)
|
||||
user_id = NumberFilter("user__id")
|
||||
|
||||
|
||||
class UserPermissionViewSet(ListModelMixin, GenericViewSet):
|
||||
class UserPermissionViewSet(
|
||||
ListModelMixin, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin, GenericViewSet
|
||||
):
|
||||
"""Get a users's assigned object permissions"""
|
||||
|
||||
serializer_class = ExtraUserObjectPermissionSerializer
|
||||
|
@ -1,15 +1,44 @@
|
||||
"""RBAC Roles"""
|
||||
|
||||
from django.contrib.auth.models import Permission
|
||||
from rest_framework.fields import (
|
||||
ChoiceField,
|
||||
ListField,
|
||||
)
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.core.api.utils import ModelSerializer
|
||||
from authentik.rbac.models import Role
|
||||
from authentik.rbac.models import Role, get_permission_choices
|
||||
|
||||
|
||||
class RoleSerializer(ModelSerializer):
|
||||
"""Role serializer"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if SERIALIZER_CONTEXT_BLUEPRINT in self.context:
|
||||
self.fields["permissions"] = ListField(
|
||||
required=False, child=ChoiceField(choices=get_permission_choices())
|
||||
)
|
||||
|
||||
def create(self, validated_data: dict) -> Role:
|
||||
permissions = Permission.objects.filter(
|
||||
codename__in=[x.split(".")[1] for x in validated_data.pop("permissions", [])]
|
||||
)
|
||||
instance: Role = super().create(validated_data)
|
||||
instance.group.permissions.set(permissions)
|
||||
return instance
|
||||
|
||||
def update(self, instance: Role, validated_data: dict) -> Role:
|
||||
permissions = Permission.objects.filter(
|
||||
codename__in=[x.split(".")[1] for x in validated_data.pop("permissions", [])]
|
||||
)
|
||||
instance: Role = super().update(instance, validated_data)
|
||||
instance.group.permissions.set(permissions)
|
||||
return instance
|
||||
|
||||
class Meta:
|
||||
model = Role
|
||||
fields = ["pk", "name"]
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
from uuid import uuid4
|
||||
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.db import models
|
||||
from django.db.transaction import atomic
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
@ -11,6 +12,26 @@ from rest_framework.serializers import BaseSerializer
|
||||
from authentik.lib.models import SerializerModel
|
||||
|
||||
|
||||
def get_permissions():
|
||||
return (
|
||||
Permission.objects.all()
|
||||
.select_related("content_type")
|
||||
.filter(
|
||||
content_type__app_label__startswith="authentik",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def get_permission_choices() -> list[tuple[str, str]]:
|
||||
return [
|
||||
(
|
||||
f"{x.content_type.app_label}.{x.codename}",
|
||||
f"{x.content_type.app_label}.{x.codename}",
|
||||
)
|
||||
for x in get_permissions()
|
||||
]
|
||||
|
||||
|
||||
class Role(SerializerModel):
|
||||
"""RBAC role, which can have different permissions (both global and per-object) attached
|
||||
to it."""
|
||||
|
@ -73,7 +73,7 @@ class TestRBACRoleAPI(APITestCase):
|
||||
"permissions": ["authentik_stages_invitation.view_invitation"],
|
||||
},
|
||||
)
|
||||
self.assertEqual(res.status_code, 204)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertTrue(self.user.has_perm("authentik_stages_invitation.view_invitation"))
|
||||
|
||||
def test_assign_object(self):
|
||||
@ -96,7 +96,7 @@ class TestRBACRoleAPI(APITestCase):
|
||||
"object_pk": str(inv.pk),
|
||||
},
|
||||
)
|
||||
self.assertEqual(res.status_code, 204)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertTrue(
|
||||
self.user.has_perm(
|
||||
"authentik_stages_invitation.view_invitation",
|
||||
|
@ -79,7 +79,7 @@ class TestRBACUserAPI(APITestCase):
|
||||
"permissions": ["authentik_stages_invitation.view_invitation"],
|
||||
},
|
||||
)
|
||||
self.assertEqual(res.status_code, 204)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertTrue(self.user.has_perm("authentik_stages_invitation.view_invitation"))
|
||||
|
||||
def test_assign_global_internal_sa(self):
|
||||
@ -121,7 +121,7 @@ class TestRBACUserAPI(APITestCase):
|
||||
"object_pk": str(inv.pk),
|
||||
},
|
||||
)
|
||||
self.assertEqual(res.status_code, 204)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertTrue(
|
||||
self.user.has_perm(
|
||||
"authentik_stages_invitation.view_invitation",
|
||||
|
@ -32,7 +32,7 @@ class TestRBACPermissionRoles(APITestCase):
|
||||
)
|
||||
self.role.assign_permission("authentik_stages_invitation.view_invitation", obj=inv)
|
||||
res = self.client.get(reverse("authentik_api:permissions-roles-list"))
|
||||
self.assertEqual(res.status_code, 400)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_list_role(self):
|
||||
"""Test list of all permissions"""
|
||||
|
@ -33,7 +33,7 @@ class TestRBACPermissionUsers(APITestCase):
|
||||
)
|
||||
assign_perm("authentik_stages_invitation.view_invitation", self.user, inv)
|
||||
res = self.client.get(reverse("authentik_api:permissions-users-list"))
|
||||
self.assertEqual(res.status_code, 400)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_list_role(self):
|
||||
"""Test list of all permissions"""
|
||||
|
28
blueprints/default/rbac-role-read-only.yaml
Normal file
28
blueprints/default/rbac-role-read-only.yaml
Normal file
@ -0,0 +1,28 @@
|
||||
metadata:
|
||||
name: Default - RBAC - Read-only
|
||||
version: 1
|
||||
entries:
|
||||
- model: authentik_rbac.role
|
||||
identifiers:
|
||||
name: authentik Read-only
|
||||
id: role
|
||||
attrs:
|
||||
permissions: !Enumerate [
|
||||
!Context goauthentik.io/rbac/models,
|
||||
SEQ,
|
||||
!Format [
|
||||
"%s.view_%s",
|
||||
!Value 0,
|
||||
!Index 0,
|
||||
],
|
||||
]
|
||||
- model: authentik_core.group
|
||||
identifiers:
|
||||
name: authentik Read-only
|
||||
attrs:
|
||||
roles:
|
||||
- !KeyOf role
|
||||
is_superuser: false
|
||||
attributes:
|
||||
notes: |
|
||||
An group with an auto-generated role that allows read-only permissions on all objects.
|
File diff suppressed because it is too large
Load Diff
375
schema.yml
375
schema.yml
@ -14825,6 +14825,10 @@ paths:
|
||||
operationId: propertymappings_saml_list
|
||||
description: SAMLPropertyMapping Viewset
|
||||
parameters:
|
||||
- in: query
|
||||
name: friendly_name
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: managed
|
||||
schema:
|
||||
@ -14859,6 +14863,10 @@ paths:
|
||||
description: Number of results to return per page.
|
||||
schema:
|
||||
type: integer
|
||||
- in: query
|
||||
name: saml_name
|
||||
schema:
|
||||
type: string
|
||||
- name: search
|
||||
required: false
|
||||
in: query
|
||||
@ -15953,10 +15961,6 @@ paths:
|
||||
operationId: propertymappings_source_scim_list
|
||||
description: SCIMSourcePropertyMapping Viewset
|
||||
parameters:
|
||||
- in: query
|
||||
name: expression
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: managed
|
||||
schema:
|
||||
@ -15965,6 +15969,10 @@ paths:
|
||||
type: string
|
||||
explode: true
|
||||
style: form
|
||||
- in: query
|
||||
name: managed__isnull
|
||||
schema:
|
||||
type: boolean
|
||||
- in: query
|
||||
name: name
|
||||
schema:
|
||||
@ -15987,11 +15995,6 @@ paths:
|
||||
description: Number of results to return per page.
|
||||
schema:
|
||||
type: integer
|
||||
- in: query
|
||||
name: pm_uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
- name: search
|
||||
required: false
|
||||
in: query
|
||||
@ -21393,7 +21396,7 @@ paths:
|
||||
description: ''
|
||||
/rbac/permissions/assigned_by_roles/{uuid}/assign/:
|
||||
post:
|
||||
operationId: rbac_permissions_assigned_by_roles_assign_create
|
||||
operationId: rbac_permissions_assigned_by_roles_assign
|
||||
description: |-
|
||||
Assign permission(s) to role. When `object_pk` is set, the permissions
|
||||
are only assigned to the specific object, otherwise they are assigned globally.
|
||||
@ -21416,8 +21419,14 @@ paths:
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'204':
|
||||
description: Successfully assigned
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/PermissionAssignResult'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
@ -21614,7 +21623,7 @@ paths:
|
||||
description: ''
|
||||
/rbac/permissions/assigned_by_users/{id}/assign/:
|
||||
post:
|
||||
operationId: rbac_permissions_assigned_by_users_assign_create
|
||||
operationId: rbac_permissions_assigned_by_users_assign
|
||||
description: Assign permission(s) to user
|
||||
parameters:
|
||||
- in: path
|
||||
@ -21634,8 +21643,14 @@ paths:
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'204':
|
||||
description: Successfully assigned
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/PermissionAssignResult'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
@ -21719,7 +21734,6 @@ paths:
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
required: true
|
||||
tags:
|
||||
- rbac
|
||||
security:
|
||||
@ -21743,6 +21757,146 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/rbac/permissions/roles/{id}/:
|
||||
get:
|
||||
operationId: rbac_permissions_roles_retrieve
|
||||
description: Get a role's assigned object permissions
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: integer
|
||||
description: A unique integer value identifying this group object permission.
|
||||
required: true
|
||||
tags:
|
||||
- rbac
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ExtraRoleObjectPermission'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
put:
|
||||
operationId: rbac_permissions_roles_update
|
||||
description: Get a role's assigned object permissions
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: integer
|
||||
description: A unique integer value identifying this group object permission.
|
||||
required: true
|
||||
tags:
|
||||
- rbac
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ExtraRoleObjectPermissionRequest'
|
||||
required: true
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ExtraRoleObjectPermission'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
patch:
|
||||
operationId: rbac_permissions_roles_partial_update
|
||||
description: Get a role's assigned object permissions
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: integer
|
||||
description: A unique integer value identifying this group object permission.
|
||||
required: true
|
||||
tags:
|
||||
- rbac
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PatchedExtraRoleObjectPermissionRequest'
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ExtraRoleObjectPermission'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
delete:
|
||||
operationId: rbac_permissions_roles_destroy
|
||||
description: Get a role's assigned object permissions
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: integer
|
||||
description: A unique integer value identifying this group object permission.
|
||||
required: true
|
||||
tags:
|
||||
- rbac
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'204':
|
||||
description: No response body
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/rbac/permissions/users/:
|
||||
get:
|
||||
operationId: rbac_permissions_users_list
|
||||
@ -21776,7 +21930,6 @@ paths:
|
||||
name: user_id
|
||||
schema:
|
||||
type: integer
|
||||
required: true
|
||||
tags:
|
||||
- rbac
|
||||
security:
|
||||
@ -21800,6 +21953,146 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/rbac/permissions/users/{id}/:
|
||||
get:
|
||||
operationId: rbac_permissions_users_retrieve
|
||||
description: Get a users's assigned object permissions
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: integer
|
||||
description: A unique integer value identifying this user object permission.
|
||||
required: true
|
||||
tags:
|
||||
- rbac
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ExtraUserObjectPermission'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
put:
|
||||
operationId: rbac_permissions_users_update
|
||||
description: Get a users's assigned object permissions
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: integer
|
||||
description: A unique integer value identifying this user object permission.
|
||||
required: true
|
||||
tags:
|
||||
- rbac
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ExtraUserObjectPermissionRequest'
|
||||
required: true
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ExtraUserObjectPermission'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
patch:
|
||||
operationId: rbac_permissions_users_partial_update
|
||||
description: Get a users's assigned object permissions
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: integer
|
||||
description: A unique integer value identifying this user object permission.
|
||||
required: true
|
||||
tags:
|
||||
- rbac
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PatchedExtraUserObjectPermissionRequest'
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ExtraUserObjectPermission'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
delete:
|
||||
operationId: rbac_permissions_users_destroy
|
||||
description: Get a users's assigned object permissions
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: integer
|
||||
description: A unique integer value identifying this user object permission.
|
||||
required: true
|
||||
tags:
|
||||
- rbac
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'204':
|
||||
description: No response body
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/rbac/roles/:
|
||||
get:
|
||||
operationId: rbac_roles_list
|
||||
@ -36544,8 +36837,6 @@ components:
|
||||
readOnly: true
|
||||
object_pk:
|
||||
type: string
|
||||
title: Object ID
|
||||
readOnly: true
|
||||
name:
|
||||
type: string
|
||||
readOnly: true
|
||||
@ -36575,6 +36866,15 @@ components:
|
||||
- name
|
||||
- object_description
|
||||
- object_pk
|
||||
ExtraRoleObjectPermissionRequest:
|
||||
type: object
|
||||
description: User permission with additional object-related data
|
||||
properties:
|
||||
object_pk:
|
||||
type: string
|
||||
minLength: 1
|
||||
required:
|
||||
- object_pk
|
||||
ExtraUserObjectPermission:
|
||||
type: object
|
||||
description: User permission with additional object-related data
|
||||
@ -36594,8 +36894,6 @@ components:
|
||||
readOnly: true
|
||||
object_pk:
|
||||
type: string
|
||||
title: Object ID
|
||||
readOnly: true
|
||||
name:
|
||||
type: string
|
||||
readOnly: true
|
||||
@ -36625,6 +36923,15 @@ components:
|
||||
- name
|
||||
- object_description
|
||||
- object_pk
|
||||
ExtraUserObjectPermissionRequest:
|
||||
type: object
|
||||
description: User permission with additional object-related data
|
||||
properties:
|
||||
object_pk:
|
||||
type: string
|
||||
minLength: 1
|
||||
required:
|
||||
- object_pk
|
||||
FilePathRequest:
|
||||
type: object
|
||||
description: Serializer to upload file
|
||||
@ -42500,6 +42807,20 @@ components:
|
||||
expression:
|
||||
type: string
|
||||
minLength: 1
|
||||
PatchedExtraRoleObjectPermissionRequest:
|
||||
type: object
|
||||
description: User permission with additional object-related data
|
||||
properties:
|
||||
object_pk:
|
||||
type: string
|
||||
minLength: 1
|
||||
PatchedExtraUserObjectPermissionRequest:
|
||||
type: object
|
||||
description: User permission with additional object-related data
|
||||
properties:
|
||||
object_pk:
|
||||
type: string
|
||||
minLength: 1
|
||||
PatchedFlowRequest:
|
||||
type: object
|
||||
description: Flow Serializer
|
||||
@ -44497,6 +44818,14 @@ components:
|
||||
minLength: 1
|
||||
required:
|
||||
- permissions
|
||||
PermissionAssignResult:
|
||||
type: object
|
||||
description: Result from assigning permissions to a user/role
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
required:
|
||||
- id
|
||||
PlexAuthenticationChallenge:
|
||||
type: object
|
||||
description: Challenge shown to the user in identification stage
|
||||
@ -46317,8 +46646,6 @@ components:
|
||||
readOnly: true
|
||||
object_pk:
|
||||
type: string
|
||||
title: Object ID
|
||||
readOnly: true
|
||||
name:
|
||||
type: string
|
||||
readOnly: true
|
||||
@ -49162,8 +49489,6 @@ components:
|
||||
readOnly: true
|
||||
object_pk:
|
||||
type: string
|
||||
title: Object ID
|
||||
readOnly: true
|
||||
name:
|
||||
type: string
|
||||
readOnly: true
|
||||
|
@ -53,7 +53,7 @@ export class RoleObjectPermissionForm extends ModelForm<RoleAssignData, number>
|
||||
}
|
||||
|
||||
send(data: RoleAssignData): Promise<unknown> {
|
||||
return new RbacApi(DEFAULT_CONFIG).rbacPermissionsAssignedByRolesAssignCreate({
|
||||
return new RbacApi(DEFAULT_CONFIG).rbacPermissionsAssignedByRolesAssign({
|
||||
uuid: data.role,
|
||||
permissionAssignRequest: {
|
||||
permissions: Object.keys(data.permissions).filter((key) => data.permissions[key]),
|
||||
|
@ -54,7 +54,7 @@ export class UserObjectPermissionForm extends ModelForm<UserAssignData, number>
|
||||
}
|
||||
|
||||
send(data: UserAssignData): Promise<unknown> {
|
||||
return new RbacApi(DEFAULT_CONFIG).rbacPermissionsAssignedByUsersAssignCreate({
|
||||
return new RbacApi(DEFAULT_CONFIG).rbacPermissionsAssignedByUsersAssign({
|
||||
id: data.user,
|
||||
permissionAssignRequest: {
|
||||
permissions: Object.keys(data.permissions).filter((key) => data.permissions[key]),
|
||||
|
@ -37,7 +37,7 @@ export class RolePermissionForm extends ModelForm<RolePermissionAssign, number>
|
||||
}
|
||||
|
||||
async send(data: RolePermissionAssign) {
|
||||
await new RbacApi(DEFAULT_CONFIG).rbacPermissionsAssignedByRolesAssignCreate({
|
||||
await new RbacApi(DEFAULT_CONFIG).rbacPermissionsAssignedByRolesAssign({
|
||||
uuid: this.roleUuid || "",
|
||||
permissionAssignRequest: {
|
||||
permissions: data.permissions,
|
||||
|
@ -37,7 +37,7 @@ export class UserPermissionForm extends ModelForm<UserPermissionAssign, number>
|
||||
}
|
||||
|
||||
async send(data: UserPermissionAssign) {
|
||||
await new RbacApi(DEFAULT_CONFIG).rbacPermissionsAssignedByUsersAssignCreate({
|
||||
await new RbacApi(DEFAULT_CONFIG).rbacPermissionsAssignedByUsersAssign({
|
||||
id: this.userId || 0,
|
||||
permissionAssignRequest: {
|
||||
permissions: data.permissions,
|
||||
|
@ -2,7 +2,9 @@
|
||||
|
||||
Some models behave differently and allow for access to different API fields when created via blueprint.
|
||||
|
||||
### `authentik_core.token`
|
||||
## `authentik_core.token`
|
||||
|
||||
### `key`
|
||||
|
||||
:::info
|
||||
Requires authentik 2023.4
|
||||
@ -26,7 +28,9 @@ For example:
|
||||
intent: api
|
||||
```
|
||||
|
||||
### `authentik_core.user`
|
||||
## `authentik_core.user`
|
||||
|
||||
### `password`
|
||||
|
||||
:::info
|
||||
Requires authentik 2023.6
|
||||
@ -49,7 +53,29 @@ For example:
|
||||
password: this-should-be-a-long-value
|
||||
```
|
||||
|
||||
### `authentik_core.application`
|
||||
### `permissions`
|
||||
|
||||
:::info
|
||||
Requires authentik 2024.8
|
||||
:::
|
||||
|
||||
The `permissions` field can be used to set global permissions for a user. A full list of possible permissions is included in the JSON schema for blueprints.
|
||||
|
||||
For example:
|
||||
|
||||
```yaml
|
||||
# [...]
|
||||
- model: authentik_core.user
|
||||
identifiers:
|
||||
username: test-user
|
||||
attrs:
|
||||
permissions:
|
||||
- authentik_blueprints.view_blueprintinstance
|
||||
```
|
||||
|
||||
## `authentik_core.application`
|
||||
|
||||
### `icon`
|
||||
|
||||
:::info
|
||||
Requires authentik 2023.5
|
||||
@ -69,7 +95,9 @@ For example:
|
||||
icon: https://goauthentik.io/img/icon.png
|
||||
```
|
||||
|
||||
### `authentik_sources_oauth.oauthsource`, `authentik_sources_saml.samlsource`, `authentik_sources_plex.plexsource`
|
||||
## `authentik_sources_oauth.oauthsource`, `authentik_sources_saml.samlsource`, `authentik_sources_plex.plexsource`
|
||||
|
||||
### `icon`
|
||||
|
||||
:::info
|
||||
Requires authentik 2023.5
|
||||
@ -89,7 +117,9 @@ For example:
|
||||
icon: https://goauthentik.io/img/icon.png
|
||||
```
|
||||
|
||||
### `authentik_flows.flow`
|
||||
## `authentik_flows.flow`
|
||||
|
||||
### `icon`
|
||||
|
||||
:::info
|
||||
Requires authentik 2023.5
|
||||
@ -110,3 +140,25 @@ For example:
|
||||
designation: authentication
|
||||
background: https://goauthentik.io/img/icon.png
|
||||
```
|
||||
|
||||
## `authentik_rbac.role`
|
||||
|
||||
### `permissions`
|
||||
|
||||
:::info
|
||||
Requires authentik 2024.8
|
||||
:::
|
||||
|
||||
The `permissions` field can be used to set global permissions for a role. A full list of possible permissions is included in the JSON schema for blueprints.
|
||||
|
||||
For example:
|
||||
|
||||
```yaml
|
||||
# [...]
|
||||
- model: authentik_rbac.role
|
||||
identifiers:
|
||||
name: test-role
|
||||
attrs:
|
||||
permissions:
|
||||
- authentik_blueprints.view_blueprintinstance
|
||||
```
|
||||
|
@ -60,6 +60,11 @@ entries:
|
||||
designation: stage_configuration
|
||||
name: default-oobe-setup
|
||||
title: Welcome to authentik!
|
||||
# Optionally set object-level permissions on the object
|
||||
# Requires authentik 2024.8
|
||||
permissions:
|
||||
- permission: inspect_flow
|
||||
user: !Find [authentik_core.user, [username, akadmin]]
|
||||
```
|
||||
|
||||
## Special Labels
|
||||
|
@ -7,15 +7,15 @@ For VS Code, for example, add these entries to your `settings.json`:
|
||||
```
|
||||
{
|
||||
"yaml.customTags": [
|
||||
"!KeyOf scalar",
|
||||
"!Condition sequence",
|
||||
"!Context scalar",
|
||||
"!Enumerate sequence",
|
||||
"!Env scalar",
|
||||
"!Find sequence",
|
||||
"!Context scalar",
|
||||
"!Format sequence",
|
||||
"!If sequence",
|
||||
"!Condition sequence",
|
||||
"!Enumerate sequence",
|
||||
"!Index scalar",
|
||||
"!KeyOf scalar",
|
||||
"!Value scalar"
|
||||
]
|
||||
}
|
||||
|
Reference in New Issue
Block a user