diff --git a/Makefile b/Makefile
index 15049c040..b9628f180 100644
--- a/Makefile
+++ b/Makefile
@@ -62,7 +62,7 @@ gen-outpost:
--additional-properties=packageName=api,enumClassPrefix=true,useOneOfDiscriminatorLookup=true,disallowAdditionalPropertiesIfNotPresent=false
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:
python -m lifecycle.migrate
diff --git a/authentik/stages/user_write/api.py b/authentik/stages/user_write/api.py
index bda945b0f..fd7b7dee5 100644
--- a/authentik/stages/user_write/api.py
+++ b/authentik/stages/user_write/api.py
@@ -12,7 +12,7 @@ class UserWriteStageSerializer(StageSerializer):
class Meta:
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):
diff --git a/authentik/stages/user_write/migrations/0004_userwritestage_create_users_group.py b/authentik/stages/user_write/migrations/0004_userwritestage_create_users_group.py
new file mode 100644
index 000000000..3ce5ba63b
--- /dev/null
+++ b/authentik/stages/user_write/migrations/0004_userwritestage_create_users_group.py
@@ -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",
+ ),
+ ),
+ ]
diff --git a/authentik/stages/user_write/models.py b/authentik/stages/user_write/models.py
index be5b8d1d0..428c2f505 100644
--- a/authentik/stages/user_write/models.py
+++ b/authentik/stages/user_write/models.py
@@ -6,6 +6,7 @@ from django.utils.translation import gettext_lazy as _
from django.views import View
from rest_framework.serializers import BaseSerializer
+from authentik.core.models import Group
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."),
)
+ 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
def serializer(self) -> BaseSerializer:
from authentik.stages.user_write.api import UserWriteStageSerializer
diff --git a/authentik/stages/user_write/stage.py b/authentik/stages/user_write/stage.py
index 39945ba95..30abfe25a 100644
--- a/authentik/stages/user_write/stage.py
+++ b/authentik/stages/user_write/stage.py
@@ -92,6 +92,8 @@ class UserWriteStageView(StageView):
try:
with transaction.atomic():
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:
LOGGER.warning("Failed to save user", exc=exc)
return self.executor.stage_invalid()
diff --git a/authentik/stages/user_write/tests.py b/authentik/stages/user_write/tests.py
index 75ba7243f..0f078f73a 100644
--- a/authentik/stages/user_write/tests.py
+++ b/authentik/stages/user_write/tests.py
@@ -7,7 +7,7 @@ from django.urls import reverse
from django.utils.encoding import force_str
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.flows.challenge import ChallengeTypes
from authentik.flows.markers import StageMarker
@@ -29,7 +29,10 @@ class TestUserWriteStage(APITestCase):
slug="test-write",
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.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"])
self.assertTrue(user_qs.exists())
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]})
def test_user_update(self):
diff --git a/schema.yml b/schema.yml
index 553743e34..6dd0a2efa 100644
--- a/schema.yml
+++ b/schema.yml
@@ -17949,6 +17949,11 @@ paths:
name: create_users_as_inactive
schema:
type: boolean
+ - in: query
+ name: create_users_group
+ schema:
+ type: string
+ format: uuid
- in: query
name: name
schema:
@@ -26628,6 +26633,11 @@ components:
create_users_as_inactive:
type: boolean
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:
type: object
description: Serializer for WebAuthn authenticator devices
@@ -29283,6 +29293,11 @@ components:
create_users_as_inactive:
type: boolean
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:
- component
- name
@@ -29302,6 +29317,11 @@ components:
create_users_as_inactive:
type: boolean
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:
- name
ValidationError:
diff --git a/web/src/locales/en.po b/web/src/locales/en.po
index 62eea79bc..f4f59d101 100644
--- a/web/src/locales/en.po
+++ b/web/src/locales/en.po
@@ -1888,6 +1888,7 @@ msgstr "Go to previous page"
#: src/pages/policies/PolicyBindingForm.ts
#: src/pages/policies/PolicyBindingForm.ts
#: src/pages/providers/ldap/LDAPProviderForm.ts
+#: src/pages/stages/user_write/UserWriteStageForm.ts
msgid "Group"
msgstr "Group"
@@ -2371,6 +2372,7 @@ msgstr "Loading"
#: src/pages/stages/password/PasswordStageForm.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
@@ -2644,6 +2646,10 @@ msgstr "Negates the outcome of the binding. Messages are unaffected."
msgid "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/pages/applications/ApplicationCheckAccessForm.ts
#: src/pages/crypto/CertificateKeyPairListPage.ts
diff --git a/web/src/locales/pseudo-LOCALE.po b/web/src/locales/pseudo-LOCALE.po
index 8de31ae49..05dd00a6b 100644
--- a/web/src/locales/pseudo-LOCALE.po
+++ b/web/src/locales/pseudo-LOCALE.po
@@ -1880,6 +1880,7 @@ msgstr ""
#: src/pages/policies/PolicyBindingForm.ts
#: src/pages/policies/PolicyBindingForm.ts
#: src/pages/providers/ldap/LDAPProviderForm.ts
+#: src/pages/stages/user_write/UserWriteStageForm.ts
msgid "Group"
msgstr ""
@@ -2363,6 +2364,7 @@ msgstr ""
#: src/pages/stages/password/PasswordStageForm.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
@@ -2636,6 +2638,10 @@ msgstr ""
msgid "New version available!"
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/pages/applications/ApplicationCheckAccessForm.ts
#: src/pages/crypto/CertificateKeyPairListPage.ts
diff --git a/web/src/pages/stages/user_write/UserWriteStageForm.ts b/web/src/pages/stages/user_write/UserWriteStageForm.ts
index 402b05bb5..55191a852 100644
--- a/web/src/pages/stages/user_write/UserWriteStageForm.ts
+++ b/web/src/pages/stages/user_write/UserWriteStageForm.ts
@@ -1,4 +1,4 @@
-import { UserWriteStage, StagesApi } from "@goauthentik/api";
+import { UserWriteStage, StagesApi, CoreApi } from "@goauthentik/api";
import { t } from "@lingui/macro";
import { customElement } from "lit-element";
import { html, TemplateResult } from "lit-html";
@@ -8,6 +8,7 @@ import "../../../elements/forms/HorizontalFormElement";
import "../../../elements/forms/FormGroup";
import { ModelForm } from "../../../elements/forms/ModelForm";
import { first } from "../../../utils";
+import { until } from "lit-html/directives/until";
@customElement("ak-stage-user-write-form")
export class UserWriteStageForm extends ModelForm
+ ${t`Newly created users are added to this group, if a group is selected.`} +
+