stages/user_write: add option to add newly created users to a group

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2021-09-14 21:45:34 +02:00
parent 198e5ce642
commit ef341dd405
10 changed files with 106 additions and 5 deletions

View File

@ -62,7 +62,7 @@ gen-outpost:
--additional-properties=packageName=api,enumClassPrefix=true,useOneOfDiscriminatorLookup=true,disallowAdditionalPropertiesIfNotPresent=false --additional-properties=packageName=api,enumClassPrefix=true,useOneOfDiscriminatorLookup=true,disallowAdditionalPropertiesIfNotPresent=false
rm -f api/go.mod api/go.sum rm -f api/go.mod api/go.sum
gen: gen-build gen-clean gen-web gen-outpost gen: gen-build gen-clean gen-web
migrate: migrate:
python -m lifecycle.migrate python -m lifecycle.migrate

View File

@ -12,7 +12,7 @@ class UserWriteStageSerializer(StageSerializer):
class Meta: class Meta:
model = UserWriteStage model = UserWriteStage
fields = StageSerializer.Meta.fields + ["create_users_as_inactive"] fields = StageSerializer.Meta.fields + ["create_users_as_inactive", "create_users_group"]
class UserWriteStageViewSet(UsedByMixin, ModelViewSet): class UserWriteStageViewSet(UsedByMixin, ModelViewSet):

View File

@ -0,0 +1,26 @@
# Generated by Django 3.2.7 on 2021-09-14 19:27
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_core", "0028_alter_token_intent"),
("authentik_stages_user_write", "0003_userwritestage_create_users_as_inactive"),
]
operations = [
migrations.AddField(
model_name="userwritestage",
name="create_users_group",
field=models.ForeignKey(
default=None,
help_text="Optionally add newly created users to this group.",
null=True,
on_delete=django.db.models.deletion.SET_DEFAULT,
to="authentik_core.group",
),
),
]

View File

@ -6,6 +6,7 @@ from django.utils.translation import gettext_lazy as _
from django.views import View from django.views import View
from rest_framework.serializers import BaseSerializer from rest_framework.serializers import BaseSerializer
from authentik.core.models import Group
from authentik.flows.models import Stage from authentik.flows.models import Stage
@ -18,6 +19,14 @@ class UserWriteStage(Stage):
help_text=_("When set, newly created users are inactive and cannot login."), help_text=_("When set, newly created users are inactive and cannot login."),
) )
create_users_group = models.ForeignKey(
Group,
null=True,
default=None,
on_delete=models.SET_DEFAULT,
help_text=_("Optionally add newly created users to this group."),
)
@property @property
def serializer(self) -> BaseSerializer: def serializer(self) -> BaseSerializer:
from authentik.stages.user_write.api import UserWriteStageSerializer from authentik.stages.user_write.api import UserWriteStageSerializer

View File

@ -92,6 +92,8 @@ class UserWriteStageView(StageView):
try: try:
with transaction.atomic(): with transaction.atomic():
user.save() user.save()
if self.executor.current_stage.create_users_group:
user.ak_groups.add(self.executor.current_stage.create_users_group)
except IntegrityError as exc: except IntegrityError as exc:
LOGGER.warning("Failed to save user", exc=exc) LOGGER.warning("Failed to save user", exc=exc)
return self.executor.stage_invalid() return self.executor.stage_invalid()

View File

@ -7,7 +7,7 @@ from django.urls import reverse
from django.utils.encoding import force_str from django.utils.encoding import force_str
from rest_framework.test import APITestCase from rest_framework.test import APITestCase
from authentik.core.models import USER_ATTRIBUTE_SOURCES, Source, User, UserSourceConnection from authentik.core.models import USER_ATTRIBUTE_SOURCES, Group, Source, User, UserSourceConnection
from authentik.core.sources.stage import PLAN_CONTEXT_SOURCES_CONNECTION from authentik.core.sources.stage import PLAN_CONTEXT_SOURCES_CONNECTION
from authentik.flows.challenge import ChallengeTypes from authentik.flows.challenge import ChallengeTypes
from authentik.flows.markers import StageMarker from authentik.flows.markers import StageMarker
@ -29,7 +29,10 @@ class TestUserWriteStage(APITestCase):
slug="test-write", slug="test-write",
designation=FlowDesignation.AUTHENTICATION, designation=FlowDesignation.AUTHENTICATION,
) )
self.stage = UserWriteStage.objects.create(name="write") self.group = Group.objects.create(name="test-group")
self.stage = UserWriteStage.objects.create(
name="write", create_users_as_inactive=True, create_users_group=self.group
)
self.binding = FlowStageBinding.objects.create(target=self.flow, stage=self.stage, order=2) self.binding = FlowStageBinding.objects.create(target=self.flow, stage=self.stage, order=2)
self.source = Source.objects.create(name="fake_source") self.source = Source.objects.create(name="fake_source")
@ -67,6 +70,7 @@ class TestUserWriteStage(APITestCase):
user_qs = User.objects.filter(username=plan.context[PLAN_CONTEXT_PROMPT]["username"]) user_qs = User.objects.filter(username=plan.context[PLAN_CONTEXT_PROMPT]["username"])
self.assertTrue(user_qs.exists()) self.assertTrue(user_qs.exists())
self.assertTrue(user_qs.first().check_password(password)) self.assertTrue(user_qs.first().check_password(password))
self.assertEqual(list(user_qs.first().ak_groups.all()), [self.group])
self.assertEqual(user_qs.first().attributes, {USER_ATTRIBUTE_SOURCES: [self.source.name]}) self.assertEqual(user_qs.first().attributes, {USER_ATTRIBUTE_SOURCES: [self.source.name]})
def test_user_update(self): def test_user_update(self):

View File

@ -17949,6 +17949,11 @@ paths:
name: create_users_as_inactive name: create_users_as_inactive
schema: schema:
type: boolean type: boolean
- in: query
name: create_users_group
schema:
type: string
format: uuid
- in: query - in: query
name: name name: name
schema: schema:
@ -26628,6 +26633,11 @@ components:
create_users_as_inactive: create_users_as_inactive:
type: boolean type: boolean
description: When set, newly created users are inactive and cannot login. description: When set, newly created users are inactive and cannot login.
create_users_group:
type: string
format: uuid
nullable: true
description: Optionally add newly created users to this group.
PatchedWebAuthnDeviceRequest: PatchedWebAuthnDeviceRequest:
type: object type: object
description: Serializer for WebAuthn authenticator devices description: Serializer for WebAuthn authenticator devices
@ -29283,6 +29293,11 @@ components:
create_users_as_inactive: create_users_as_inactive:
type: boolean type: boolean
description: When set, newly created users are inactive and cannot login. description: When set, newly created users are inactive and cannot login.
create_users_group:
type: string
format: uuid
nullable: true
description: Optionally add newly created users to this group.
required: required:
- component - component
- name - name
@ -29302,6 +29317,11 @@ components:
create_users_as_inactive: create_users_as_inactive:
type: boolean type: boolean
description: When set, newly created users are inactive and cannot login. description: When set, newly created users are inactive and cannot login.
create_users_group:
type: string
format: uuid
nullable: true
description: Optionally add newly created users to this group.
required: required:
- name - name
ValidationError: ValidationError:

View File

@ -1888,6 +1888,7 @@ msgstr "Go to previous page"
#: src/pages/policies/PolicyBindingForm.ts #: src/pages/policies/PolicyBindingForm.ts
#: src/pages/policies/PolicyBindingForm.ts #: src/pages/policies/PolicyBindingForm.ts
#: src/pages/providers/ldap/LDAPProviderForm.ts #: src/pages/providers/ldap/LDAPProviderForm.ts
#: src/pages/stages/user_write/UserWriteStageForm.ts
msgid "Group" msgid "Group"
msgstr "Group" msgstr "Group"
@ -2371,6 +2372,7 @@ msgstr "Loading"
#: src/pages/stages/password/PasswordStageForm.ts #: src/pages/stages/password/PasswordStageForm.ts
#: src/pages/stages/prompt/PromptStageForm.ts #: src/pages/stages/prompt/PromptStageForm.ts
#: src/pages/stages/prompt/PromptStageForm.ts #: src/pages/stages/prompt/PromptStageForm.ts
#: src/pages/stages/user_write/UserWriteStageForm.ts
#: src/pages/tenants/TenantForm.ts #: src/pages/tenants/TenantForm.ts
#: src/pages/tenants/TenantForm.ts #: src/pages/tenants/TenantForm.ts
#: src/pages/tenants/TenantForm.ts #: src/pages/tenants/TenantForm.ts
@ -2644,6 +2646,10 @@ msgstr "Negates the outcome of the binding. Messages are unaffected."
msgid "New version available!" msgid "New version available!"
msgstr "New version available!" msgstr "New version available!"
#: src/pages/stages/user_write/UserWriteStageForm.ts
msgid "Newly created users are added to this group, if a group is selected."
msgstr "Newly created users are added to this group, if a group is selected."
#: src/elements/oauth/UserRefreshList.ts #: src/elements/oauth/UserRefreshList.ts
#: src/pages/applications/ApplicationCheckAccessForm.ts #: src/pages/applications/ApplicationCheckAccessForm.ts
#: src/pages/crypto/CertificateKeyPairListPage.ts #: src/pages/crypto/CertificateKeyPairListPage.ts

View File

@ -1880,6 +1880,7 @@ msgstr ""
#: src/pages/policies/PolicyBindingForm.ts #: src/pages/policies/PolicyBindingForm.ts
#: src/pages/policies/PolicyBindingForm.ts #: src/pages/policies/PolicyBindingForm.ts
#: src/pages/providers/ldap/LDAPProviderForm.ts #: src/pages/providers/ldap/LDAPProviderForm.ts
#: src/pages/stages/user_write/UserWriteStageForm.ts
msgid "Group" msgid "Group"
msgstr "" msgstr ""
@ -2363,6 +2364,7 @@ msgstr ""
#: src/pages/stages/password/PasswordStageForm.ts #: src/pages/stages/password/PasswordStageForm.ts
#: src/pages/stages/prompt/PromptStageForm.ts #: src/pages/stages/prompt/PromptStageForm.ts
#: src/pages/stages/prompt/PromptStageForm.ts #: src/pages/stages/prompt/PromptStageForm.ts
#: src/pages/stages/user_write/UserWriteStageForm.ts
#: src/pages/tenants/TenantForm.ts #: src/pages/tenants/TenantForm.ts
#: src/pages/tenants/TenantForm.ts #: src/pages/tenants/TenantForm.ts
#: src/pages/tenants/TenantForm.ts #: src/pages/tenants/TenantForm.ts
@ -2636,6 +2638,10 @@ msgstr ""
msgid "New version available!" msgid "New version available!"
msgstr "" msgstr ""
#: src/pages/stages/user_write/UserWriteStageForm.ts
msgid "Newly created users are added to this group, if a group is selected."
msgstr ""
#: src/elements/oauth/UserRefreshList.ts #: src/elements/oauth/UserRefreshList.ts
#: src/pages/applications/ApplicationCheckAccessForm.ts #: src/pages/applications/ApplicationCheckAccessForm.ts
#: src/pages/crypto/CertificateKeyPairListPage.ts #: src/pages/crypto/CertificateKeyPairListPage.ts

View File

@ -1,4 +1,4 @@
import { UserWriteStage, StagesApi } from "@goauthentik/api"; import { UserWriteStage, StagesApi, CoreApi } from "@goauthentik/api";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { customElement } from "lit-element"; import { customElement } from "lit-element";
import { html, TemplateResult } from "lit-html"; import { html, TemplateResult } from "lit-html";
@ -8,6 +8,7 @@ import "../../../elements/forms/HorizontalFormElement";
import "../../../elements/forms/FormGroup"; import "../../../elements/forms/FormGroup";
import { ModelForm } from "../../../elements/forms/ModelForm"; import { ModelForm } from "../../../elements/forms/ModelForm";
import { first } from "../../../utils"; import { first } from "../../../utils";
import { until } from "lit-html/directives/until";
@customElement("ak-stage-user-write-form") @customElement("ak-stage-user-write-form")
export class UserWriteStageForm extends ModelForm<UserWriteStage, string> { export class UserWriteStageForm extends ModelForm<UserWriteStage, string> {
@ -70,6 +71,33 @@ export class UserWriteStageForm extends ModelForm<UserWriteStage, string> {
${t`Mark newly created users as inactive.`} ${t`Mark newly created users as inactive.`}
</p> </p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal label=${t`Group`} name="searchGroup">
<select class="pf-c-form-control">
<option
value=""
?selected=${this.instance?.createUsersGroup === undefined}
>
---------
</option>
${until(
new CoreApi(DEFAULT_CONFIG).coreGroupsList({}).then((groups) => {
return groups.results.map((group) => {
return html`<option
value=${ifDefined(group.pk)}
?selected=${this.instance?.createUsersGroup ===
group.pk}
>
${group.name}
</option>`;
});
}),
html`<option>${t`Loading...`}</option>`,
)}
</select>
<p class="pf-c-form__helper-text">
${t`Newly created users are added to this group, if a group is selected.`}
</p>
</ak-form-element-horizontal>
</div> </div>
</ak-form-group> </ak-form-group>
</form>`; </form>`;