more progress

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
Jens Langhammer
2026-06-05 15:37:31 +02:00
parent 1a4c7fd518
commit bc71be356f
5 changed files with 50 additions and 9 deletions
+10 -3
View File
@@ -1,5 +1,5 @@
from drf_spectacular.utils import extend_schema
from rest_framework.fields import ListField
from rest_framework.decorators import action
from rest_framework.mixins import (
DestroyModelMixin,
ListModelMixin,
@@ -32,7 +32,7 @@ class GrantRequestViewSet(RetrieveModelMixin, DestroyModelMixin, ListModelMixin,
serializer_class = GrantRequestSerializer
class GrantRequestCreateSerializer(PassiveSerializer):
pbms = ListField(child=PrimaryKeyRelatedField(queryset=PolicyBindingModel.objects.all()))
pbms = PrimaryKeyRelatedField(queryset=PolicyBindingModel.objects.all(), many=True)
@extend_schema(request=GrantRequestCreateSerializer, responses={200: LinkSerializer})
@validate(GrantRequestCreateSerializer)
@@ -49,4 +49,11 @@ class GrantRequestViewSet(RetrieveModelMixin, DestroyModelMixin, ListModelMixin,
},
)
plan.append_stage(in_memory_stage(GrantRequestFinalStageView))
return Response({"link": plan.to_redirect(request, flow)})
return Response({"link": plan.to_redirect(request, flow).url})
@action(["POST"], detail=True)
def fulfill(self, request: Request, *args, **kwargs):
grant: GrantRequest = self.get_object()
# TODO: Check if this user can fulfill this grant
grant.fulfill(request.user)
return Response(status=204)
+14 -2
View File
@@ -1,4 +1,4 @@
# Generated by Django 5.2.14 on 2026-06-05 09:52
# Generated by Django 5.2.14 on 2026-06-05 13:35
import authentik.core.models
import django.db.models.deletion
@@ -41,7 +41,19 @@ class Migration(migrations.Migration):
(
"created_by",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL
on_delete=django.db.models.deletion.CASCADE,
related_name="grant_requests_created",
to=settings.AUTH_USER_MODEL,
),
),
(
"fulfilled_by",
models.ForeignKey(
default=None,
null=True,
on_delete=django.db.models.deletion.SET_DEFAULT,
related_name="grant_requests_fulfilled",
to=settings.AUTH_USER_MODEL,
),
),
],
+13 -2
View File
@@ -39,7 +39,16 @@ class GrantRequest(SerializerModel, ExpiringModel, CreatedUpdatedModel):
uuid = models.UUIDField(default=uuid4, primary_key=True)
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
created_by = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="grant_requests_created"
)
fulfilled_by = models.ForeignKey(
User,
on_delete=models.SET_DEFAULT,
related_name="grant_requests_fulfilled",
null=True,
default=None,
)
# Targets access was requested to
targets = models.ManyToManyField(PolicyBindingModel, through="GrantRequestTarget")
@@ -55,7 +64,9 @@ class GrantRequest(SerializerModel, ExpiringModel, CreatedUpdatedModel):
return GrantRequestSerializer
@transaction.atomic
def fulfill(self):
def fulfill(self, user: User):
self.fulfilled_by = user
self.save()
if self.status != RequestState.APPROVED:
return
for target in GrantRequestTarget.objects.filter(request=self).all():
+12 -2
View File
@@ -2,8 +2,11 @@ from datetime import timedelta
from django.db import transaction
from django.http import HttpRequest, HttpResponse
from django.urls import reverse
from django.utils.timezone import now
from authentik.events.middleware import audit_ignore
from authentik.events.models import Event, EventAction
from authentik.flows.stage import StageView
from authentik.pam.models import GrantRequest, GrantRequestTarget, RequestState
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
@@ -17,8 +20,8 @@ class GrantRequestFinalStageView(StageView):
user = self.get_pending_user()
pbms = self.executor.plan.context.get(PLAN_CONTEXT_GRANT_REQUESTED_PBMS)
expires = now() + timedelta(hours=1)
with transaction.atomic():
req = GrantRequest(
with transaction.atomic(), audit_ignore():
req = GrantRequest.objects.create(
created_by=user,
data=self.executor.plan.context.get(PLAN_CONTEXT_PROMPT, {}),
expiring=True,
@@ -31,6 +34,13 @@ class GrantRequestFinalStageView(StageView):
target=pbm,
binding=None,
)
Event.new(
EventAction.MODEL_CREATED,
model=req,
hyperlink=request.build_absolute_uri(reverse("authentik_core:if-admin"))
+ "#/pam/requests/respond",
hyperlink_label="Respond",
).from_http(request, user)
return self.executor.stage_ok()
def post(self, request: HttpRequest) -> HttpResponse:
+1
View File
@@ -6,3 +6,4 @@
- [ ] Figure out how credentials for Personas work (API Key?)
- [ ] Check if we need a new stage type for requesting access
- [ ] Figure out how to configure which apps are "discoverable"
- [ ] Figure out where to configure approvals