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.showCountsInTree": true, | ||||||
|     "todo-tree.tree.showBadges": true, |     "todo-tree.tree.showBadges": true, | ||||||
|     "yaml.customTags": [ |     "yaml.customTags": [ | ||||||
|         "!Find sequence", |  | ||||||
|         "!KeyOf scalar", |  | ||||||
|         "!Context scalar", |  | ||||||
|         "!Context sequence", |  | ||||||
|         "!Format sequence", |  | ||||||
|         "!Condition sequence", |         "!Condition sequence", | ||||||
|         "!Env sequence", |         "!Context scalar", | ||||||
|  |         "!Enumerate sequence", | ||||||
|         "!Env scalar", |         "!Env scalar", | ||||||
|         "!If sequence" |         "!Find sequence", | ||||||
|  |         "!Format sequence", | ||||||
|  |         "!If sequence", | ||||||
|  |         "!Index scalar", | ||||||
|  |         "!KeyOf scalar", | ||||||
|  |         "!Value scalar" | ||||||
|     ], |     ], | ||||||
|     "typescript.preferences.importModuleSpecifier": "non-relative", |     "typescript.preferences.importModuleSpecifier": "non-relative", | ||||||
|     "typescript.preferences.importModuleSpecifierEnding": "index", |     "typescript.preferences.importModuleSpecifierEnding": "index", | ||||||
|  | |||||||
| @ -113,16 +113,19 @@ class Command(BaseCommand): | |||||||
|             ) |             ) | ||||||
|             model_path = f"{model._meta.app_label}.{model._meta.model_name}" |             model_path = f"{model._meta.app_label}.{model._meta.model_name}" | ||||||
|             self.schema["properties"]["entries"]["items"]["oneOf"].append( |             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""" |         """Template entry for a single model""" | ||||||
|         model_schema = self.to_jsonschema(serializer) |         model_schema = self.to_jsonschema(serializer) | ||||||
|         model_schema["required"] = [] |         model_schema["required"] = [] | ||||||
|         def_name = f"model_{model_path}" |         def_name = f"model_{model_path}" | ||||||
|         def_path = f"#/$defs/{def_name}" |         def_path = f"#/$defs/{def_name}" | ||||||
|         self.schema["$defs"][def_name] = model_schema |         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 { |         return { | ||||||
|             "type": "object", |             "type": "object", | ||||||
|             "required": ["model", "identifiers"], |             "required": ["model", "identifiers"], | ||||||
| @ -135,6 +138,7 @@ class Command(BaseCommand): | |||||||
|                     "default": "present", |                     "default": "present", | ||||||
|                 }, |                 }, | ||||||
|                 "conditions": {"type": "array", "items": {"type": "boolean"}}, |                 "conditions": {"type": "array", "items": {"type": "boolean"}}, | ||||||
|  |                 "permissions": {"$ref": def_path_perm}, | ||||||
|                 "attrs": {"$ref": def_path}, |                 "attrs": {"$ref": def_path}, | ||||||
|                 "identifiers": {"$ref": def_path}, |                 "identifiers": {"$ref": def_path}, | ||||||
|             }, |             }, | ||||||
| @ -185,3 +189,20 @@ class Command(BaseCommand): | |||||||
|         if required: |         if required: | ||||||
|             result["required"] = required |             result["required"] = required | ||||||
|         return result |         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""" | """transfer common classes""" | ||||||
|  |  | ||||||
| from collections import OrderedDict | from collections import OrderedDict | ||||||
| from collections.abc import Iterable, Mapping | from collections.abc import Generator, Iterable, Mapping | ||||||
| from copy import copy | from copy import copy | ||||||
| from dataclasses import asdict, dataclass, field, is_dataclass | from dataclasses import asdict, dataclass, field, is_dataclass | ||||||
| from enum import Enum | from enum import Enum | ||||||
| @ -58,6 +58,15 @@ class BlueprintEntryDesiredState(Enum): | |||||||
|     MUST_CREATED = "must_created" |     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 | @dataclass | ||||||
| class BlueprintEntry: | class BlueprintEntry: | ||||||
|     """Single entry of a blueprint""" |     """Single entry of a blueprint""" | ||||||
| @ -69,6 +78,7 @@ class BlueprintEntry: | |||||||
|     conditions: list[Any] = field(default_factory=list) |     conditions: list[Any] = field(default_factory=list) | ||||||
|     identifiers: dict[str, Any] = field(default_factory=dict) |     identifiers: dict[str, Any] = field(default_factory=dict) | ||||||
|     attrs: dict[str, Any] | None = field(default_factory=dict) |     attrs: dict[str, Any] | None = field(default_factory=dict) | ||||||
|  |     permissions: list[BlueprintEntryPermission] = field(default_factory=list) | ||||||
|  |  | ||||||
|     id: str | None = None |     id: str | None = None | ||||||
|  |  | ||||||
| @ -150,6 +160,17 @@ class BlueprintEntry: | |||||||
|         """Get the blueprint model, with yaml tags resolved if present""" |         """Get the blueprint model, with yaml tags resolved if present""" | ||||||
|         return str(self.tag_resolver(self.model, blueprint)) |         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: |     def check_all_conditions_match(self, blueprint: "Blueprint") -> bool: | ||||||
|         """Check all conditions of this entry match (evaluate to True)""" |         """Check all conditions of this entry match (evaluate to True)""" | ||||||
|         return all(self.tag_resolver(self.conditions, blueprint)) |         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.transaction import atomic | ||||||
| from django.db.utils import IntegrityError | from django.db.utils import IntegrityError | ||||||
| from guardian.models import UserObjectPermission | from guardian.models import UserObjectPermission | ||||||
|  | from guardian.shortcuts import assign_perm | ||||||
| from rest_framework.exceptions import ValidationError | from rest_framework.exceptions import ValidationError | ||||||
| from rest_framework.serializers import BaseSerializer, Serializer | from rest_framework.serializers import BaseSerializer, Serializer | ||||||
| from structlog.stdlib import BoundLogger, get_logger | from structlog.stdlib import BoundLogger, get_logger | ||||||
| @ -35,6 +36,7 @@ from authentik.core.models import ( | |||||||
|     PropertyMapping, |     PropertyMapping, | ||||||
|     Provider, |     Provider, | ||||||
|     Source, |     Source, | ||||||
|  |     User, | ||||||
|     UserSourceConnection, |     UserSourceConnection, | ||||||
| ) | ) | ||||||
| from authentik.enterprise.license import LicenseKey | 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.flows.models import FlowToken, Stage | ||||||
| from authentik.lib.models import SerializerModel | from authentik.lib.models import SerializerModel | ||||||
| from authentik.lib.sentry import SentryIgnoredException | from authentik.lib.sentry import SentryIgnoredException | ||||||
|  | from authentik.lib.utils.reflection import get_apps | ||||||
| from authentik.outposts.models import OutpostServiceConnection | from authentik.outposts.models import OutpostServiceConnection | ||||||
| from authentik.policies.models import Policy, PolicyBindingModel | from authentik.policies.models import Policy, PolicyBindingModel | ||||||
| from authentik.policies.reputation.models import Reputation | from authentik.policies.reputation.models import Reputation | ||||||
| from authentik.providers.oauth2.models import AccessToken, AuthorizationCode, RefreshToken | from authentik.providers.oauth2.models import AccessToken, AuthorizationCode, RefreshToken | ||||||
| from authentik.providers.scim.models import SCIMProviderGroup, SCIMProviderUser | from authentik.providers.scim.models import SCIMProviderGroup, SCIMProviderUser | ||||||
|  | from authentik.rbac.models import Role | ||||||
| from authentik.sources.scim.models import SCIMSourceGroup, SCIMSourceUser | from authentik.sources.scim.models import SCIMSourceGroup, SCIMSourceUser | ||||||
| from authentik.stages.authenticator_webauthn.models import WebAuthnDeviceType | from authentik.stages.authenticator_webauthn.models import WebAuthnDeviceType | ||||||
| from authentik.tenants.models import Tenant | from authentik.tenants.models import Tenant | ||||||
| @ -136,6 +140,16 @@ def transaction_rollback(): | |||||||
|         pass |         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: | class Importer: | ||||||
|     """Import Blueprint from raw dict or YAML/JSON""" |     """Import Blueprint from raw dict or YAML/JSON""" | ||||||
|  |  | ||||||
| @ -154,7 +168,10 @@ class Importer: | |||||||
|  |  | ||||||
|     def default_context(self): |     def default_context(self): | ||||||
|         """Default context""" |         """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 |     @staticmethod | ||||||
|     def from_string(yaml_input: str, context: dict | None = None) -> "Importer": |     def from_string(yaml_input: str, context: dict | None = None) -> "Importer": | ||||||
| @ -320,6 +337,15 @@ class Importer: | |||||||
|             ) from exc |             ) from exc | ||||||
|         return serializer |         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: |     def apply(self) -> bool: | ||||||
|         """Apply (create/update) models yaml, in database transaction""" |         """Apply (create/update) models yaml, in database transaction""" | ||||||
|         try: |         try: | ||||||
| @ -384,6 +410,7 @@ class Importer: | |||||||
|                 if "pk" in entry.identifiers: |                 if "pk" in entry.identifiers: | ||||||
|                     self.__pk_map[entry.identifiers["pk"]] = instance.pk |                     self.__pk_map[entry.identifiers["pk"]] = instance.pk | ||||||
|                 entry._state = BlueprintEntryState(instance) |                 entry._state = BlueprintEntryState(instance) | ||||||
|  |                 self._apply_permissions(instance, entry) | ||||||
|             elif state == BlueprintEntryDesiredState.ABSENT: |             elif state == BlueprintEntryDesiredState.ABSENT: | ||||||
|                 instance: Model | None = serializer.instance |                 instance: Model | None = serializer.instance | ||||||
|                 if instance.pk: |                 if instance.pk: | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ from json import loads | |||||||
| from typing import Any | from typing import Any | ||||||
|  |  | ||||||
| from django.contrib.auth import update_session_auth_hash | 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.contrib.sessions.backends.cache import KEY_PREFIX | ||||||
| from django.core.cache import cache | from django.core.cache import cache | ||||||
| from django.db.models.functions import ExtractHour | 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 guardian.shortcuts import get_objects_for_user | ||||||
| from rest_framework.decorators import action | 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.request import Request | ||||||
| from rest_framework.response import Response | from rest_framework.response import Response | ||||||
| from rest_framework.serializers import ( | from rest_framework.serializers import ( | ||||||
|     BooleanField, |  | ||||||
|     DateTimeField, |  | ||||||
|     ListSerializer, |     ListSerializer, | ||||||
|     PrimaryKeyRelatedField, |     PrimaryKeyRelatedField, | ||||||
|     ValidationError, |  | ||||||
| ) | ) | ||||||
| from rest_framework.validators import UniqueValidator | from rest_framework.validators import UniqueValidator | ||||||
| from rest_framework.viewsets import ModelViewSet | 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.flows.views.executor import QS_KEY_TOKEN | ||||||
| from authentik.lib.avatars import get_avatar | from authentik.lib.avatars import get_avatar | ||||||
| from authentik.rbac.decorators import permission_required | 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.models import EmailStage | ||||||
| from authentik.stages.email.tasks import send_mails | from authentik.stages.email.tasks import send_mails | ||||||
| from authentik.stages.email.utils import TemplateEmailMessage | from authentik.stages.email.utils import TemplateEmailMessage | ||||||
| @ -141,12 +149,19 @@ class UserSerializer(ModelSerializer): | |||||||
|         super().__init__(*args, **kwargs) |         super().__init__(*args, **kwargs) | ||||||
|         if SERIALIZER_CONTEXT_BLUEPRINT in self.context: |         if SERIALIZER_CONTEXT_BLUEPRINT in self.context: | ||||||
|             self.fields["password"] = CharField(required=False, allow_null=True) |             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: |     def create(self, validated_data: dict) -> User: | ||||||
|         """If this serializer is used in the blueprint context, we allow for |         """If this serializer is used in the blueprint context, we allow for | ||||||
|         directly setting a password. However should be done via the `set_password` |         directly setting a password. However should be done via the `set_password` | ||||||
|         method instead of directly setting it like rest_framework.""" |         method instead of directly setting it like rest_framework.""" | ||||||
|         password = validated_data.pop("password", None) |         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) |         instance: User = super().create(validated_data) | ||||||
|         self._set_password(instance, password) |         self._set_password(instance, password) | ||||||
|         return instance |         return instance | ||||||
| @ -155,6 +170,10 @@ class UserSerializer(ModelSerializer): | |||||||
|         """Same as `create` above, set the password directly if we're in a blueprint |         """Same as `create` above, set the password directly if we're in a blueprint | ||||||
|         context""" |         context""" | ||||||
|         password = validated_data.pop("password", None) |         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) |         instance = super().update(instance, validated_data) | ||||||
|         self._set_password(instance, password) |         self._set_password(instance, password) | ||||||
|         return instance |         return instance | ||||||
|  | |||||||
| @ -23,6 +23,7 @@ class SAMLPropertyMappingFilter(PropertyMappingFilterSet): | |||||||
|  |  | ||||||
|     class Meta(PropertyMappingFilterSet.Meta): |     class Meta(PropertyMappingFilterSet.Meta): | ||||||
|         model = SAMLPropertyMapping |         model = SAMLPropertyMapping | ||||||
|  |         fields = PropertyMappingFilterSet.Meta.fields + ["saml_name", "friendly_name"] | ||||||
|  |  | ||||||
|  |  | ||||||
| class SAMLPropertyMappingViewSet(UsedByMixin, ModelViewSet): | 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): | class PermissionFilter(FilterSet): | ||||||
|     """Filter permissions""" |     """Filter permissions""" | ||||||
|  |  | ||||||
|  | |||||||
| @ -16,7 +16,7 @@ from rest_framework.viewsets import GenericViewSet | |||||||
|  |  | ||||||
| from authentik.core.api.utils import ModelSerializer, PassiveSerializer | from authentik.core.api.utils import ModelSerializer, PassiveSerializer | ||||||
| from authentik.policies.event_matcher.models import model_choices | 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.decorators import permission_required | ||||||
| from authentik.rbac.models import Role | from authentik.rbac.models import Role | ||||||
|  |  | ||||||
| @ -28,7 +28,7 @@ class RoleObjectPermissionSerializer(ModelSerializer): | |||||||
|     model = ReadOnlyField(source="content_type.model") |     model = ReadOnlyField(source="content_type.model") | ||||||
|     codename = ReadOnlyField(source="permission.codename") |     codename = ReadOnlyField(source="permission.codename") | ||||||
|     name = ReadOnlyField(source="permission.name") |     name = ReadOnlyField(source="permission.name") | ||||||
|     object_pk = ReadOnlyField() |     object_pk = CharField() | ||||||
|  |  | ||||||
|     class Meta: |     class Meta: | ||||||
|         model = GroupObjectPermission |         model = GroupObjectPermission | ||||||
| @ -88,8 +88,9 @@ class RoleAssignedPermissionViewSet(ListModelMixin, GenericViewSet): | |||||||
|     @extend_schema( |     @extend_schema( | ||||||
|         request=PermissionAssignSerializer(), |         request=PermissionAssignSerializer(), | ||||||
|         responses={ |         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=[]) |     @action(methods=["POST"], detail=True, pagination_class=None, filter_backends=[]) | ||||||
|     def assign(self, request: Request, *args, **kwargs) -> Response: |     def assign(self, request: Request, *args, **kwargs) -> Response: | ||||||
| @ -98,10 +99,12 @@ class RoleAssignedPermissionViewSet(ListModelMixin, GenericViewSet): | |||||||
|         role: Role = self.get_object() |         role: Role = self.get_object() | ||||||
|         data = PermissionAssignSerializer(data=request.data) |         data = PermissionAssignSerializer(data=request.data) | ||||||
|         data.is_valid(raise_exception=True) |         data.is_valid(raise_exception=True) | ||||||
|  |         ids = [] | ||||||
|         with atomic(): |         with atomic(): | ||||||
|             for perm in data.validated_data["permissions"]: |             for perm in data.validated_data["permissions"]: | ||||||
|                 assign_perm(perm, role.group, data.validated_data["model_instance"]) |                 assigned_perm = assign_perm(perm, role.group, data.validated_data["model_instance"]) | ||||||
|         return Response(status=204) |                 ids.append(PermissionAssignResultSerializer(instance={"id": assigned_perm.pk}).data) | ||||||
|  |         return Response(ids, status=200) | ||||||
|  |  | ||||||
|     @permission_required("authentik_rbac.unassign_role_permissions") |     @permission_required("authentik_rbac.unassign_role_permissions") | ||||||
|     @extend_schema( |     @extend_schema( | ||||||
|  | |||||||
| @ -9,7 +9,7 @@ from guardian.models import UserObjectPermission | |||||||
| from guardian.shortcuts import assign_perm, remove_perm | from guardian.shortcuts import assign_perm, remove_perm | ||||||
| from rest_framework.decorators import action | from rest_framework.decorators import action | ||||||
| from rest_framework.exceptions import ValidationError | 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.mixins import ListModelMixin | ||||||
| from rest_framework.request import Request | from rest_framework.request import Request | ||||||
| from rest_framework.response import Response | 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.api.utils import ModelSerializer | ||||||
| from authentik.core.models import User, UserTypes | from authentik.core.models import User, UserTypes | ||||||
| from authentik.policies.event_matcher.models import model_choices | 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.decorators import permission_required | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -30,7 +30,7 @@ class UserObjectPermissionSerializer(ModelSerializer): | |||||||
|     model = ReadOnlyField(source="content_type.model") |     model = ReadOnlyField(source="content_type.model") | ||||||
|     codename = ReadOnlyField(source="permission.codename") |     codename = ReadOnlyField(source="permission.codename") | ||||||
|     name = ReadOnlyField(source="permission.name") |     name = ReadOnlyField(source="permission.name") | ||||||
|     object_pk = ReadOnlyField() |     object_pk = CharField() | ||||||
|  |  | ||||||
|     class Meta: |     class Meta: | ||||||
|         model = UserObjectPermission |         model = UserObjectPermission | ||||||
| @ -90,8 +90,9 @@ class UserAssignedPermissionViewSet(ListModelMixin, GenericViewSet): | |||||||
|     @extend_schema( |     @extend_schema( | ||||||
|         request=PermissionAssignSerializer(), |         request=PermissionAssignSerializer(), | ||||||
|         responses={ |         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=[]) |     @action(methods=["POST"], detail=True, pagination_class=None, filter_backends=[]) | ||||||
|     def assign(self, request: Request, *args, **kwargs) -> Response: |     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.") |             raise ValidationError("Permissions cannot be assigned to an internal service account.") | ||||||
|         data = PermissionAssignSerializer(data=request.data) |         data = PermissionAssignSerializer(data=request.data) | ||||||
|         data.is_valid(raise_exception=True) |         data.is_valid(raise_exception=True) | ||||||
|  |         ids = [] | ||||||
|         with atomic(): |         with atomic(): | ||||||
|             for perm in data.validated_data["permissions"]: |             for perm in data.validated_data["permissions"]: | ||||||
|                 assign_perm(perm, user, data.validated_data["model_instance"]) |                 assigned_perm = assign_perm(perm, user, data.validated_data["model_instance"]) | ||||||
|         return Response(status=204) |                 ids.append(PermissionAssignResultSerializer(instance={"id": assigned_perm.pk}).data) | ||||||
|  |         return Response(ids, status=200) | ||||||
|  |  | ||||||
|     @permission_required("authentik_core.unassign_user_permissions") |     @permission_required("authentik_core.unassign_user_permissions") | ||||||
|     @extend_schema( |     @extend_schema( | ||||||
|  | |||||||
| @ -6,7 +6,12 @@ from django_filters.filterset import FilterSet | |||||||
| from guardian.models import GroupObjectPermission | from guardian.models import GroupObjectPermission | ||||||
| from guardian.shortcuts import get_objects_for_group | from guardian.shortcuts import get_objects_for_group | ||||||
| from rest_framework.fields import SerializerMethodField | 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 rest_framework.viewsets import GenericViewSet | ||||||
|  |  | ||||||
| from authentik.api.pagination import SmallerPagination | from authentik.api.pagination import SmallerPagination | ||||||
| @ -64,10 +69,12 @@ class ExtraRoleObjectPermissionSerializer(RoleObjectPermissionSerializer): | |||||||
| class RolePermissionFilter(FilterSet): | class RolePermissionFilter(FilterSet): | ||||||
|     """Role permission filter""" |     """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""" |     """Get a role's assigned object permissions""" | ||||||
|  |  | ||||||
|     serializer_class = ExtraRoleObjectPermissionSerializer |     serializer_class = ExtraRoleObjectPermissionSerializer | ||||||
|  | |||||||
| @ -6,7 +6,12 @@ from django_filters.filterset import FilterSet | |||||||
| from guardian.models import UserObjectPermission | from guardian.models import UserObjectPermission | ||||||
| from guardian.shortcuts import get_objects_for_user | from guardian.shortcuts import get_objects_for_user | ||||||
| from rest_framework.fields import SerializerMethodField | 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 rest_framework.viewsets import GenericViewSet | ||||||
|  |  | ||||||
| from authentik.api.pagination import SmallerPagination | from authentik.api.pagination import SmallerPagination | ||||||
| @ -64,10 +69,12 @@ class ExtraUserObjectPermissionSerializer(UserObjectPermissionSerializer): | |||||||
| class UserPermissionFilter(FilterSet): | class UserPermissionFilter(FilterSet): | ||||||
|     """User-assigned permission filter""" |     """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""" |     """Get a users's assigned object permissions""" | ||||||
|  |  | ||||||
|     serializer_class = ExtraUserObjectPermissionSerializer |     serializer_class = ExtraUserObjectPermissionSerializer | ||||||
|  | |||||||
| @ -1,15 +1,44 @@ | |||||||
| """RBAC Roles""" | """RBAC Roles""" | ||||||
|  |  | ||||||
|  | from django.contrib.auth.models import Permission | ||||||
|  | from rest_framework.fields import ( | ||||||
|  |     ChoiceField, | ||||||
|  |     ListField, | ||||||
|  | ) | ||||||
| from rest_framework.viewsets import ModelViewSet | 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.used_by import UsedByMixin | ||||||
| from authentik.core.api.utils import ModelSerializer | 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): | class RoleSerializer(ModelSerializer): | ||||||
|     """Role serializer""" |     """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: |     class Meta: | ||||||
|         model = Role |         model = Role | ||||||
|         fields = ["pk", "name"] |         fields = ["pk", "name"] | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ | |||||||
|  |  | ||||||
| from uuid import uuid4 | from uuid import uuid4 | ||||||
|  |  | ||||||
|  | from django.contrib.auth.models import Permission | ||||||
| from django.db import models | from django.db import models | ||||||
| from django.db.transaction import atomic | from django.db.transaction import atomic | ||||||
| from django.utils.translation import gettext_lazy as _ | from django.utils.translation import gettext_lazy as _ | ||||||
| @ -11,6 +12,26 @@ from rest_framework.serializers import BaseSerializer | |||||||
| from authentik.lib.models import SerializerModel | 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): | class Role(SerializerModel): | ||||||
|     """RBAC role, which can have different permissions (both global and per-object) attached |     """RBAC role, which can have different permissions (both global and per-object) attached | ||||||
|     to it.""" |     to it.""" | ||||||
|  | |||||||
| @ -73,7 +73,7 @@ class TestRBACRoleAPI(APITestCase): | |||||||
|                 "permissions": ["authentik_stages_invitation.view_invitation"], |                 "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")) |         self.assertTrue(self.user.has_perm("authentik_stages_invitation.view_invitation")) | ||||||
|  |  | ||||||
|     def test_assign_object(self): |     def test_assign_object(self): | ||||||
| @ -96,7 +96,7 @@ class TestRBACRoleAPI(APITestCase): | |||||||
|                 "object_pk": str(inv.pk), |                 "object_pk": str(inv.pk), | ||||||
|             }, |             }, | ||||||
|         ) |         ) | ||||||
|         self.assertEqual(res.status_code, 204) |         self.assertEqual(res.status_code, 200) | ||||||
|         self.assertTrue( |         self.assertTrue( | ||||||
|             self.user.has_perm( |             self.user.has_perm( | ||||||
|                 "authentik_stages_invitation.view_invitation", |                 "authentik_stages_invitation.view_invitation", | ||||||
|  | |||||||
| @ -79,7 +79,7 @@ class TestRBACUserAPI(APITestCase): | |||||||
|                 "permissions": ["authentik_stages_invitation.view_invitation"], |                 "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")) |         self.assertTrue(self.user.has_perm("authentik_stages_invitation.view_invitation")) | ||||||
|  |  | ||||||
|     def test_assign_global_internal_sa(self): |     def test_assign_global_internal_sa(self): | ||||||
| @ -121,7 +121,7 @@ class TestRBACUserAPI(APITestCase): | |||||||
|                 "object_pk": str(inv.pk), |                 "object_pk": str(inv.pk), | ||||||
|             }, |             }, | ||||||
|         ) |         ) | ||||||
|         self.assertEqual(res.status_code, 204) |         self.assertEqual(res.status_code, 200) | ||||||
|         self.assertTrue( |         self.assertTrue( | ||||||
|             self.user.has_perm( |             self.user.has_perm( | ||||||
|                 "authentik_stages_invitation.view_invitation", |                 "authentik_stages_invitation.view_invitation", | ||||||
|  | |||||||
| @ -32,7 +32,7 @@ class TestRBACPermissionRoles(APITestCase): | |||||||
|         ) |         ) | ||||||
|         self.role.assign_permission("authentik_stages_invitation.view_invitation", obj=inv) |         self.role.assign_permission("authentik_stages_invitation.view_invitation", obj=inv) | ||||||
|         res = self.client.get(reverse("authentik_api:permissions-roles-list")) |         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): |     def test_list_role(self): | ||||||
|         """Test list of all permissions""" |         """Test list of all permissions""" | ||||||
|  | |||||||
| @ -33,7 +33,7 @@ class TestRBACPermissionUsers(APITestCase): | |||||||
|         ) |         ) | ||||||
|         assign_perm("authentik_stages_invitation.view_invitation", self.user, inv) |         assign_perm("authentik_stages_invitation.view_invitation", self.user, inv) | ||||||
|         res = self.client.get(reverse("authentik_api:permissions-users-list")) |         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): |     def test_list_role(self): | ||||||
|         """Test list of all permissions""" |         """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 |       operationId: propertymappings_saml_list | ||||||
|       description: SAMLPropertyMapping Viewset |       description: SAMLPropertyMapping Viewset | ||||||
|       parameters: |       parameters: | ||||||
|  |       - in: query | ||||||
|  |         name: friendly_name | ||||||
|  |         schema: | ||||||
|  |           type: string | ||||||
|       - in: query |       - in: query | ||||||
|         name: managed |         name: managed | ||||||
|         schema: |         schema: | ||||||
| @ -14859,6 +14863,10 @@ paths: | |||||||
|         description: Number of results to return per page. |         description: Number of results to return per page. | ||||||
|         schema: |         schema: | ||||||
|           type: integer |           type: integer | ||||||
|  |       - in: query | ||||||
|  |         name: saml_name | ||||||
|  |         schema: | ||||||
|  |           type: string | ||||||
|       - name: search |       - name: search | ||||||
|         required: false |         required: false | ||||||
|         in: query |         in: query | ||||||
| @ -15953,10 +15961,6 @@ paths: | |||||||
|       operationId: propertymappings_source_scim_list |       operationId: propertymappings_source_scim_list | ||||||
|       description: SCIMSourcePropertyMapping Viewset |       description: SCIMSourcePropertyMapping Viewset | ||||||
|       parameters: |       parameters: | ||||||
|       - in: query |  | ||||||
|         name: expression |  | ||||||
|         schema: |  | ||||||
|           type: string |  | ||||||
|       - in: query |       - in: query | ||||||
|         name: managed |         name: managed | ||||||
|         schema: |         schema: | ||||||
| @ -15965,6 +15969,10 @@ paths: | |||||||
|             type: string |             type: string | ||||||
|         explode: true |         explode: true | ||||||
|         style: form |         style: form | ||||||
|  |       - in: query | ||||||
|  |         name: managed__isnull | ||||||
|  |         schema: | ||||||
|  |           type: boolean | ||||||
|       - in: query |       - in: query | ||||||
|         name: name |         name: name | ||||||
|         schema: |         schema: | ||||||
| @ -15987,11 +15995,6 @@ paths: | |||||||
|         description: Number of results to return per page. |         description: Number of results to return per page. | ||||||
|         schema: |         schema: | ||||||
|           type: integer |           type: integer | ||||||
|       - in: query |  | ||||||
|         name: pm_uuid |  | ||||||
|         schema: |  | ||||||
|           type: string |  | ||||||
|           format: uuid |  | ||||||
|       - name: search |       - name: search | ||||||
|         required: false |         required: false | ||||||
|         in: query |         in: query | ||||||
| @ -21393,7 +21396,7 @@ paths: | |||||||
|           description: '' |           description: '' | ||||||
|   /rbac/permissions/assigned_by_roles/{uuid}/assign/: |   /rbac/permissions/assigned_by_roles/{uuid}/assign/: | ||||||
|     post: |     post: | ||||||
|       operationId: rbac_permissions_assigned_by_roles_assign_create |       operationId: rbac_permissions_assigned_by_roles_assign | ||||||
|       description: |- |       description: |- | ||||||
|         Assign permission(s) to role. When `object_pk` is set, the permissions |         Assign permission(s) to role. When `object_pk` is set, the permissions | ||||||
|         are only assigned to the specific object, otherwise they are assigned globally. |         are only assigned to the specific object, otherwise they are assigned globally. | ||||||
| @ -21416,8 +21419,14 @@ paths: | |||||||
|       security: |       security: | ||||||
|       - authentik: [] |       - authentik: [] | ||||||
|       responses: |       responses: | ||||||
|         '204': |         '200': | ||||||
|           description: Successfully assigned |           content: | ||||||
|  |             application/json: | ||||||
|  |               schema: | ||||||
|  |                 type: array | ||||||
|  |                 items: | ||||||
|  |                   $ref: '#/components/schemas/PermissionAssignResult' | ||||||
|  |           description: '' | ||||||
|         '400': |         '400': | ||||||
|           content: |           content: | ||||||
|             application/json: |             application/json: | ||||||
| @ -21614,7 +21623,7 @@ paths: | |||||||
|           description: '' |           description: '' | ||||||
|   /rbac/permissions/assigned_by_users/{id}/assign/: |   /rbac/permissions/assigned_by_users/{id}/assign/: | ||||||
|     post: |     post: | ||||||
|       operationId: rbac_permissions_assigned_by_users_assign_create |       operationId: rbac_permissions_assigned_by_users_assign | ||||||
|       description: Assign permission(s) to user |       description: Assign permission(s) to user | ||||||
|       parameters: |       parameters: | ||||||
|       - in: path |       - in: path | ||||||
| @ -21634,8 +21643,14 @@ paths: | |||||||
|       security: |       security: | ||||||
|       - authentik: [] |       - authentik: [] | ||||||
|       responses: |       responses: | ||||||
|         '204': |         '200': | ||||||
|           description: Successfully assigned |           content: | ||||||
|  |             application/json: | ||||||
|  |               schema: | ||||||
|  |                 type: array | ||||||
|  |                 items: | ||||||
|  |                   $ref: '#/components/schemas/PermissionAssignResult' | ||||||
|  |           description: '' | ||||||
|         '400': |         '400': | ||||||
|           content: |           content: | ||||||
|             application/json: |             application/json: | ||||||
| @ -21719,7 +21734,6 @@ paths: | |||||||
|         schema: |         schema: | ||||||
|           type: string |           type: string | ||||||
|           format: uuid |           format: uuid | ||||||
|         required: true |  | ||||||
|       tags: |       tags: | ||||||
|       - rbac |       - rbac | ||||||
|       security: |       security: | ||||||
| @ -21743,6 +21757,146 @@ paths: | |||||||
|               schema: |               schema: | ||||||
|                 $ref: '#/components/schemas/GenericError' |                 $ref: '#/components/schemas/GenericError' | ||||||
|           description: '' |           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/: |   /rbac/permissions/users/: | ||||||
|     get: |     get: | ||||||
|       operationId: rbac_permissions_users_list |       operationId: rbac_permissions_users_list | ||||||
| @ -21776,7 +21930,6 @@ paths: | |||||||
|         name: user_id |         name: user_id | ||||||
|         schema: |         schema: | ||||||
|           type: integer |           type: integer | ||||||
|         required: true |  | ||||||
|       tags: |       tags: | ||||||
|       - rbac |       - rbac | ||||||
|       security: |       security: | ||||||
| @ -21800,6 +21953,146 @@ paths: | |||||||
|               schema: |               schema: | ||||||
|                 $ref: '#/components/schemas/GenericError' |                 $ref: '#/components/schemas/GenericError' | ||||||
|           description: '' |           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/: |   /rbac/roles/: | ||||||
|     get: |     get: | ||||||
|       operationId: rbac_roles_list |       operationId: rbac_roles_list | ||||||
| @ -36544,8 +36837,6 @@ components: | |||||||
|           readOnly: true |           readOnly: true | ||||||
|         object_pk: |         object_pk: | ||||||
|           type: string |           type: string | ||||||
|           title: Object ID |  | ||||||
|           readOnly: true |  | ||||||
|         name: |         name: | ||||||
|           type: string |           type: string | ||||||
|           readOnly: true |           readOnly: true | ||||||
| @ -36575,6 +36866,15 @@ components: | |||||||
|       - name |       - name | ||||||
|       - object_description |       - object_description | ||||||
|       - object_pk |       - object_pk | ||||||
|  |     ExtraRoleObjectPermissionRequest: | ||||||
|  |       type: object | ||||||
|  |       description: User permission with additional object-related data | ||||||
|  |       properties: | ||||||
|  |         object_pk: | ||||||
|  |           type: string | ||||||
|  |           minLength: 1 | ||||||
|  |       required: | ||||||
|  |       - object_pk | ||||||
|     ExtraUserObjectPermission: |     ExtraUserObjectPermission: | ||||||
|       type: object |       type: object | ||||||
|       description: User permission with additional object-related data |       description: User permission with additional object-related data | ||||||
| @ -36594,8 +36894,6 @@ components: | |||||||
|           readOnly: true |           readOnly: true | ||||||
|         object_pk: |         object_pk: | ||||||
|           type: string |           type: string | ||||||
|           title: Object ID |  | ||||||
|           readOnly: true |  | ||||||
|         name: |         name: | ||||||
|           type: string |           type: string | ||||||
|           readOnly: true |           readOnly: true | ||||||
| @ -36625,6 +36923,15 @@ components: | |||||||
|       - name |       - name | ||||||
|       - object_description |       - object_description | ||||||
|       - object_pk |       - object_pk | ||||||
|  |     ExtraUserObjectPermissionRequest: | ||||||
|  |       type: object | ||||||
|  |       description: User permission with additional object-related data | ||||||
|  |       properties: | ||||||
|  |         object_pk: | ||||||
|  |           type: string | ||||||
|  |           minLength: 1 | ||||||
|  |       required: | ||||||
|  |       - object_pk | ||||||
|     FilePathRequest: |     FilePathRequest: | ||||||
|       type: object |       type: object | ||||||
|       description: Serializer to upload file |       description: Serializer to upload file | ||||||
| @ -42500,6 +42807,20 @@ components: | |||||||
|         expression: |         expression: | ||||||
|           type: string |           type: string | ||||||
|           minLength: 1 |           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: |     PatchedFlowRequest: | ||||||
|       type: object |       type: object | ||||||
|       description: Flow Serializer |       description: Flow Serializer | ||||||
| @ -44497,6 +44818,14 @@ components: | |||||||
|           minLength: 1 |           minLength: 1 | ||||||
|       required: |       required: | ||||||
|       - permissions |       - permissions | ||||||
|  |     PermissionAssignResult: | ||||||
|  |       type: object | ||||||
|  |       description: Result from assigning permissions to a user/role | ||||||
|  |       properties: | ||||||
|  |         id: | ||||||
|  |           type: string | ||||||
|  |       required: | ||||||
|  |       - id | ||||||
|     PlexAuthenticationChallenge: |     PlexAuthenticationChallenge: | ||||||
|       type: object |       type: object | ||||||
|       description: Challenge shown to the user in identification stage |       description: Challenge shown to the user in identification stage | ||||||
| @ -46317,8 +46646,6 @@ components: | |||||||
|           readOnly: true |           readOnly: true | ||||||
|         object_pk: |         object_pk: | ||||||
|           type: string |           type: string | ||||||
|           title: Object ID |  | ||||||
|           readOnly: true |  | ||||||
|         name: |         name: | ||||||
|           type: string |           type: string | ||||||
|           readOnly: true |           readOnly: true | ||||||
| @ -49162,8 +49489,6 @@ components: | |||||||
|           readOnly: true |           readOnly: true | ||||||
|         object_pk: |         object_pk: | ||||||
|           type: string |           type: string | ||||||
|           title: Object ID |  | ||||||
|           readOnly: true |  | ||||||
|         name: |         name: | ||||||
|           type: string |           type: string | ||||||
|           readOnly: true |           readOnly: true | ||||||
|  | |||||||
| @ -53,7 +53,7 @@ export class RoleObjectPermissionForm extends ModelForm<RoleAssignData, number> | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     send(data: RoleAssignData): Promise<unknown> { |     send(data: RoleAssignData): Promise<unknown> { | ||||||
|         return new RbacApi(DEFAULT_CONFIG).rbacPermissionsAssignedByRolesAssignCreate({ |         return new RbacApi(DEFAULT_CONFIG).rbacPermissionsAssignedByRolesAssign({ | ||||||
|             uuid: data.role, |             uuid: data.role, | ||||||
|             permissionAssignRequest: { |             permissionAssignRequest: { | ||||||
|                 permissions: Object.keys(data.permissions).filter((key) => data.permissions[key]), |                 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> { |     send(data: UserAssignData): Promise<unknown> { | ||||||
|         return new RbacApi(DEFAULT_CONFIG).rbacPermissionsAssignedByUsersAssignCreate({ |         return new RbacApi(DEFAULT_CONFIG).rbacPermissionsAssignedByUsersAssign({ | ||||||
|             id: data.user, |             id: data.user, | ||||||
|             permissionAssignRequest: { |             permissionAssignRequest: { | ||||||
|                 permissions: Object.keys(data.permissions).filter((key) => data.permissions[key]), |                 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) { |     async send(data: RolePermissionAssign) { | ||||||
|         await new RbacApi(DEFAULT_CONFIG).rbacPermissionsAssignedByRolesAssignCreate({ |         await new RbacApi(DEFAULT_CONFIG).rbacPermissionsAssignedByRolesAssign({ | ||||||
|             uuid: this.roleUuid || "", |             uuid: this.roleUuid || "", | ||||||
|             permissionAssignRequest: { |             permissionAssignRequest: { | ||||||
|                 permissions: data.permissions, |                 permissions: data.permissions, | ||||||
|  | |||||||
| @ -37,7 +37,7 @@ export class UserPermissionForm extends ModelForm<UserPermissionAssign, number> | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     async send(data: UserPermissionAssign) { |     async send(data: UserPermissionAssign) { | ||||||
|         await new RbacApi(DEFAULT_CONFIG).rbacPermissionsAssignedByUsersAssignCreate({ |         await new RbacApi(DEFAULT_CONFIG).rbacPermissionsAssignedByUsersAssign({ | ||||||
|             id: this.userId || 0, |             id: this.userId || 0, | ||||||
|             permissionAssignRequest: { |             permissionAssignRequest: { | ||||||
|                 permissions: data.permissions, |                 permissions: data.permissions, | ||||||
|  | |||||||
| @ -2,7 +2,9 @@ | |||||||
|  |  | ||||||
| Some models behave differently and allow for access to different API fields when created via blueprint. | Some models behave differently and allow for access to different API fields when created via blueprint. | ||||||
|  |  | ||||||
| ### `authentik_core.token` | ## `authentik_core.token` | ||||||
|  |  | ||||||
|  | ### `key` | ||||||
|  |  | ||||||
| :::info | :::info | ||||||
| Requires authentik 2023.4 | Requires authentik 2023.4 | ||||||
| @ -26,7 +28,9 @@ For example: | |||||||
|       intent: api |       intent: api | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| ### `authentik_core.user` | ## `authentik_core.user` | ||||||
|  |  | ||||||
|  | ### `password` | ||||||
|  |  | ||||||
| :::info | :::info | ||||||
| Requires authentik 2023.6 | Requires authentik 2023.6 | ||||||
| @ -49,7 +53,29 @@ For example: | |||||||
|       password: this-should-be-a-long-value |       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 | :::info | ||||||
| Requires authentik 2023.5 | Requires authentik 2023.5 | ||||||
| @ -69,7 +95,9 @@ For example: | |||||||
|       icon: https://goauthentik.io/img/icon.png |       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 | :::info | ||||||
| Requires authentik 2023.5 | Requires authentik 2023.5 | ||||||
| @ -89,7 +117,9 @@ For example: | |||||||
|       icon: https://goauthentik.io/img/icon.png |       icon: https://goauthentik.io/img/icon.png | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| ### `authentik_flows.flow` | ## `authentik_flows.flow` | ||||||
|  |  | ||||||
|  | ### `icon` | ||||||
|  |  | ||||||
| :::info | :::info | ||||||
| Requires authentik 2023.5 | Requires authentik 2023.5 | ||||||
| @ -110,3 +140,25 @@ For example: | |||||||
|       designation: authentication |       designation: authentication | ||||||
|       background: https://goauthentik.io/img/icon.png |       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 |           designation: stage_configuration | ||||||
|           name: default-oobe-setup |           name: default-oobe-setup | ||||||
|           title: Welcome to authentik! |           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 | ## Special Labels | ||||||
|  | |||||||
| @ -7,15 +7,15 @@ For VS Code, for example, add these entries to your `settings.json`: | |||||||
| ``` | ``` | ||||||
| { | { | ||||||
|     "yaml.customTags": [ |     "yaml.customTags": [ | ||||||
|         "!KeyOf scalar", |         "!Condition sequence", | ||||||
|  |         "!Context scalar", | ||||||
|  |         "!Enumerate sequence", | ||||||
|         "!Env scalar", |         "!Env scalar", | ||||||
|         "!Find sequence", |         "!Find sequence", | ||||||
|         "!Context scalar", |  | ||||||
|         "!Format sequence", |         "!Format sequence", | ||||||
|         "!If sequence", |         "!If sequence", | ||||||
|         "!Condition sequence", |  | ||||||
|         "!Enumerate sequence", |  | ||||||
|         "!Index scalar", |         "!Index scalar", | ||||||
|  |         "!KeyOf scalar", | ||||||
|         "!Value scalar" |         "!Value scalar" | ||||||
|     ] |     ] | ||||||
| } | } | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 Jens L.
					Jens L.