stages/invitation: add single_use flag to delete invitation after use
closes #821 Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
6ae660aea4
commit
4523550422
|
@ -39,6 +39,7 @@ class InvitationSerializer(ModelSerializer):
|
||||||
"expires",
|
"expires",
|
||||||
"fixed_data",
|
"fixed_data",
|
||||||
"created_by",
|
"created_by",
|
||||||
|
"single_use",
|
||||||
]
|
]
|
||||||
depth = 2
|
depth = 2
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Generated by Django 3.2 on 2021-05-03 07:46
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("authentik_stages_invitation", "0003_auto_20201227_1210"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="invitation",
|
||||||
|
name="single_use",
|
||||||
|
field=models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text="When enabled, the invitation will be deleted after usage.",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -53,6 +53,11 @@ class Invitation(models.Model):
|
||||||
|
|
||||||
invite_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
|
invite_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
|
||||||
|
|
||||||
|
single_use = models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text=_("When enabled, the invitation will be deleted after usage."),
|
||||||
|
)
|
||||||
|
|
||||||
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
|
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
expires = models.DateTimeField(default=None, blank=True, null=True)
|
expires = models.DateTimeField(default=None, blank=True, null=True)
|
||||||
fixed_data = models.JSONField(
|
fixed_data = models.JSONField(
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
"""invitation stage logic"""
|
"""invitation stage logic"""
|
||||||
|
from copy import deepcopy
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
|
@ -38,7 +39,9 @@ class InvitationStageView(StageView):
|
||||||
return self.executor.stage_invalid()
|
return self.executor.stage_invalid()
|
||||||
|
|
||||||
invite: Invitation = get_object_or_404(Invitation, pk=token)
|
invite: Invitation = get_object_or_404(Invitation, pk=token)
|
||||||
self.executor.plan.context[PLAN_CONTEXT_PROMPT] = invite.fixed_data
|
self.executor.plan.context[PLAN_CONTEXT_PROMPT] = deepcopy(invite.fixed_data)
|
||||||
self.executor.plan.context[INVITATION_IN_EFFECT] = True
|
self.executor.plan.context[INVITATION_IN_EFFECT] = True
|
||||||
invitation_used.send(sender=self, request=request, invitation=invite)
|
invitation_used.send(sender=self, request=request, invitation=invite)
|
||||||
|
if invite.single_use:
|
||||||
|
invite.delete()
|
||||||
return self.executor.stage_ok()
|
return self.executor.stage_ok()
|
||||||
|
|
|
@ -130,7 +130,9 @@ class TestUserLoginStage(TestCase):
|
||||||
"""Test with invitation, check data in session"""
|
"""Test with invitation, check data in session"""
|
||||||
data = {"foo": "bar"}
|
data = {"foo": "bar"}
|
||||||
invite = Invitation.objects.create(
|
invite = Invitation.objects.create(
|
||||||
created_by=get_anonymous_user(), fixed_data=data
|
created_by=get_anonymous_user(),
|
||||||
|
fixed_data=data,
|
||||||
|
single_use=True
|
||||||
)
|
)
|
||||||
|
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
|
@ -156,6 +158,7 @@ class TestUserLoginStage(TestCase):
|
||||||
force_str(response.content),
|
force_str(response.content),
|
||||||
{"to": reverse("authentik_core:root-redirect"), "type": "redirect"},
|
{"to": reverse("authentik_core:root-redirect"), "type": "redirect"},
|
||||||
)
|
)
|
||||||
|
self.assertFalse(Invitation.objects.filter(pk=invite.pk))
|
||||||
|
|
||||||
|
|
||||||
class TestInvitationsAPI(APITestCase):
|
class TestInvitationsAPI(APITestCase):
|
||||||
|
|
|
@ -18248,6 +18248,10 @@ definitions:
|
||||||
x-nullable: true
|
x-nullable: true
|
||||||
readOnly: true
|
readOnly: true
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
single_use:
|
||||||
|
title: Single use
|
||||||
|
description: When enabled, the invitation will be deleted after usage.
|
||||||
|
type: boolean
|
||||||
InvitationStage:
|
InvitationStage:
|
||||||
required:
|
required:
|
||||||
- name
|
- name
|
||||||
|
|
|
@ -2841,6 +2841,10 @@ msgstr "Signing keypair"
|
||||||
msgid "Single Prompts that can be used for Prompt Stages."
|
msgid "Single Prompts that can be used for Prompt Stages."
|
||||||
msgstr "Single Prompts that can be used for Prompt Stages."
|
msgstr "Single Prompts that can be used for Prompt Stages."
|
||||||
|
|
||||||
|
#: src/pages/stages/invitation/InvitationForm.ts:62
|
||||||
|
msgid "Single use"
|
||||||
|
msgstr "Single use"
|
||||||
|
|
||||||
#: src/pages/providers/proxy/ProxyProviderForm.ts:173
|
#: src/pages/providers/proxy/ProxyProviderForm.ts:173
|
||||||
msgid "Skip path regex"
|
msgid "Skip path regex"
|
||||||
msgstr "Skip path regex"
|
msgstr "Skip path regex"
|
||||||
|
@ -3877,6 +3881,10 @@ msgstr "When a valid username/email has been entered, and this option is enabled
|
||||||
msgid "When enabled, global Email connection settings will be used and connection settings below will be ignored."
|
msgid "When enabled, global Email connection settings will be used and connection settings below will be ignored."
|
||||||
msgstr "When enabled, global Email connection settings will be used and connection settings below will be ignored."
|
msgstr "When enabled, global Email connection settings will be used and connection settings below will be ignored."
|
||||||
|
|
||||||
|
#: src/pages/stages/invitation/InvitationForm.ts:66
|
||||||
|
msgid "When enabled, the invitation will be deleted after usage."
|
||||||
|
msgstr "When enabled, the invitation will be deleted after usage."
|
||||||
|
|
||||||
#: src/pages/stages/identification/IdentificationStageForm.ts:94
|
#: src/pages/stages/identification/IdentificationStageForm.ts:94
|
||||||
msgid "When enabled, user fields are matched regardless of their casing."
|
msgid "When enabled, user fields are matched regardless of their casing."
|
||||||
msgstr "When enabled, user fields are matched regardless of their casing."
|
msgstr "When enabled, user fields are matched regardless of their casing."
|
||||||
|
|
|
@ -2833,6 +2833,10 @@ msgstr ""
|
||||||
msgid "Single Prompts that can be used for Prompt Stages."
|
msgid "Single Prompts that can be used for Prompt Stages."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/stages/invitation/InvitationForm.ts:62
|
||||||
|
msgid "Single use"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/providers/proxy/ProxyProviderForm.ts:173
|
#: src/pages/providers/proxy/ProxyProviderForm.ts:173
|
||||||
msgid "Skip path regex"
|
msgid "Skip path regex"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -3865,6 +3869,10 @@ msgstr ""
|
||||||
msgid "When enabled, global Email connection settings will be used and connection settings below will be ignored."
|
msgid "When enabled, global Email connection settings will be used and connection settings below will be ignored."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/stages/invitation/InvitationForm.ts:66
|
||||||
|
msgid "When enabled, the invitation will be deleted after usage."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/stages/identification/IdentificationStageForm.ts:94
|
#: src/pages/stages/identification/IdentificationStageForm.ts:94
|
||||||
msgid "When enabled, user fields are matched regardless of their casing."
|
msgid "When enabled, user fields are matched regardless of their casing."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
@ -51,6 +51,17 @@ export class InvitationForm extends Form<Invitation> {
|
||||||
</ak-codemirror>
|
</ak-codemirror>
|
||||||
<p class="pf-c-form__helper-text">${t`Optional data which is loaded into the flow's 'prompt_data' context variable. YAML or JSON.`}</p>
|
<p class="pf-c-form__helper-text">${t`Optional data which is loaded into the flow's 'prompt_data' context variable. YAML or JSON.`}</p>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
|
<ak-form-element-horizontal name="singleUse">
|
||||||
|
<div class="pf-c-check">
|
||||||
|
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.invitation?.singleUse, true)}>
|
||||||
|
<label class="pf-c-check__label">
|
||||||
|
${t`Single use`}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<p class="pf-c-form__helper-text">
|
||||||
|
${t`When enabled, the invitation will be deleted after usage.`}
|
||||||
|
</p>
|
||||||
|
</ak-form-element-horizontal>
|
||||||
</form>`;
|
</form>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Reference in a new issue