Compare commits

...

3 Commits

Author SHA1 Message Date
Jens Langhammer
f457c05f21 fix go build
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2026-02-25 16:35:36 +01:00
Jens Langhammer
34cda6688f fix meta
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2026-02-25 16:28:05 +01:00
Jens Langhammer
5cdcd5f336 core: add concrete model for group membership
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2026-02-25 16:19:36 +01:00
6 changed files with 110 additions and 19 deletions

View File

@@ -0,0 +1,80 @@
# Generated by Django 5.2.11 on 2026-02-25 15:09
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
from django.apps.registry import Apps
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
from authentik.lib.migrations import progress_bar
def migrate_group_membership(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
db_alias = schema_editor.connection.alias
Group = apps.get_model("authentik_core", "group")
GroupMembership = apps.get_model("authentik_core", "GroupMembership")
print("Re-creating group memberships, this might take a couple of minutes...")
for group in progress_bar(Group.objects.using(db_alias).all()):
GroupMembership.objects.bulk_create(
[GroupMembership(group=group, user=user) for user in group.users.all()]
)
class Migration(migrations.Migration):
dependencies = [
("authentik_core", "0057_remove_user_groups_remove_user_user_permissions_and_more"),
]
operations = [
migrations.CreateModel(
name="GroupMembership",
fields=[
(
"id",
models.AutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
("attributes", models.JSONField(blank=True, default=dict)),
("expires", models.DateTimeField(default=None, null=True)),
("expiring", models.BooleanField(default=True)),
(
"group",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to="authentik_core.group"
),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL
),
),
],
options={
"verbose_name": "Group Membership",
"verbose_name_plural": "Group Memberships",
"abstract": False,
"indexes": [
models.Index(fields=["expires"], name="authentik_c_expires_a2bb46_idx"),
models.Index(fields=["expiring"], name="authentik_c_expirin_bdd43a_idx"),
models.Index(
fields=["expiring", "expires"], name="authentik_c_expirin_ff3ad1_idx"
),
],
},
),
migrations.RunPython(migrate_group_membership, migrations.RunPython.noop),
migrations.RemoveField(model_name="user", name="groups"),
migrations.AddField(
model_name="user",
name="groups",
field=models.ManyToManyField(
related_name="users",
through="authentik_core.GroupMembership",
to="authentik_core.group",
),
),
]

View File

@@ -368,7 +368,7 @@ class User(SerializerModel, AttributesMixin, AbstractUser):
type = models.TextField(choices=UserTypes.choices, default=UserTypes.INTERNAL)
sources = models.ManyToManyField("Source", through="UserSourceConnection")
groups = models.ManyToManyField("Group", related_name="users")
groups = models.ManyToManyField("Group", related_name="users", through="GroupMembership")
roles = models.ManyToManyField("authentik_rbac.Role", related_name="users", blank=True)
password_change_date = models.DateTimeField(auto_now_add=True)
@@ -1151,6 +1151,19 @@ class ExpiringModel(models.Model):
return now() > self.expires
class GroupMembership(ExpiringModel, AttributesMixin):
user = models.ForeignKey(User, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
class Meta:
verbose_name = _("Group Membership")
verbose_name_plural = _("Group Memberships")
indexes = ExpiringModel.Meta.indexes
def __str__(self):
return f"Group membership between {self.user_id} and {self.group_id}"
class TokenIntents(models.TextChoices):
"""Intents a Token can be created for."""

View File

@@ -5277,13 +5277,6 @@
},
"title": "Parents"
},
"users": {
"type": "array",
"items": {
"type": "integer"
},
"title": "Users"
},
"attributes": {
"type": "object",
"additionalProperties": true,
@@ -5510,6 +5503,7 @@
"authentik_core.add_authenticatedsession",
"authentik_core.add_group",
"authentik_core.add_groupancestrynode",
"authentik_core.add_groupmembership",
"authentik_core.add_groupparentagenode",
"authentik_core.add_groupsourceconnection",
"authentik_core.add_propertymapping",
@@ -5524,6 +5518,7 @@
"authentik_core.change_authenticatedsession",
"authentik_core.change_group",
"authentik_core.change_groupancestrynode",
"authentik_core.change_groupmembership",
"authentik_core.change_groupparentagenode",
"authentik_core.change_groupsourceconnection",
"authentik_core.change_propertymapping",
@@ -5537,6 +5532,7 @@
"authentik_core.delete_authenticatedsession",
"authentik_core.delete_group",
"authentik_core.delete_groupancestrynode",
"authentik_core.delete_groupmembership",
"authentik_core.delete_groupparentagenode",
"authentik_core.delete_groupsourceconnection",
"authentik_core.delete_propertymapping",
@@ -5557,6 +5553,7 @@
"authentik_core.view_authenticatedsession",
"authentik_core.view_group",
"authentik_core.view_groupancestrynode",
"authentik_core.view_groupmembership",
"authentik_core.view_groupparentagenode",
"authentik_core.view_groupsourceconnection",
"authentik_core.view_propertymapping",
@@ -11102,6 +11099,7 @@
"authentik_core.add_authenticatedsession",
"authentik_core.add_group",
"authentik_core.add_groupancestrynode",
"authentik_core.add_groupmembership",
"authentik_core.add_groupparentagenode",
"authentik_core.add_groupsourceconnection",
"authentik_core.add_propertymapping",
@@ -11116,6 +11114,7 @@
"authentik_core.change_authenticatedsession",
"authentik_core.change_group",
"authentik_core.change_groupancestrynode",
"authentik_core.change_groupmembership",
"authentik_core.change_groupparentagenode",
"authentik_core.change_groupsourceconnection",
"authentik_core.change_propertymapping",
@@ -11129,6 +11128,7 @@
"authentik_core.delete_authenticatedsession",
"authentik_core.delete_group",
"authentik_core.delete_groupancestrynode",
"authentik_core.delete_groupmembership",
"authentik_core.delete_groupparentagenode",
"authentik_core.delete_groupsourceconnection",
"authentik_core.delete_propertymapping",
@@ -11149,6 +11149,7 @@
"authentik_core.view_authenticatedsession",
"authentik_core.view_group",
"authentik_core.view_groupancestrynode",
"authentik_core.view_groupmembership",
"authentik_core.view_groupparentagenode",
"authentik_core.view_groupsourceconnection",
"authentik_core.view_propertymapping",

View File

@@ -165,8 +165,12 @@ func (ms *MemorySearcher) Search(req *search.Request) (ldap.ServerSearchResult,
for _, u := range g.UsersObj {
if flag.UserPk == u.Pk {
// TODO: Is there a better way to clone this object?
fg := api.NewGroup(g.Pk, g.NumPk, g.Name, []api.RelatedGroup{}, []api.PartialUser{u}, []api.Role{}, nil, []string{}, []api.RelatedGroup{})
fg.SetUsers([]int32{flag.UserPk})
fg := api.NewGroupWithDefaults()
fg.Pk = g.Pk
fg.NumPk = g.NumPk
fg.Name = g.Name
fg.Users = []int32{flag.UserPk}
fg.UsersObj = []api.PartialUser{u}
fg.SetAttributes(g.Attributes)
fg.SetIsSuperuser(*g.IsSuperuser)
groups = append(groups, group.FromAPIGroup(*fg, ms.si))

View File

@@ -39957,6 +39957,7 @@ components:
type: array
items:
type: integer
readOnly: true
users_obj:
type: array
items:
@@ -40003,6 +40004,7 @@ components:
- parents_obj
- pk
- roles_obj
- users
- users_obj
GroupKerberosSourceConnection:
type: object
@@ -40237,10 +40239,6 @@ components:
items:
type: string
format: uuid
users:
type: array
items:
type: integer
attributes:
type: object
additionalProperties: {}
@@ -48270,10 +48268,6 @@ components:
items:
type: string
format: uuid
users:
type: array
items:
type: integer
attributes:
type: object
additionalProperties: {}

View File

@@ -92,7 +92,6 @@ export class GroupForm extends ModelForm<Group, string> {
patchedGroupRequest: data,
});
}
data.users = [];
return new CoreApi(DEFAULT_CONFIG).coreGroupsCreate({
groupRequest: data,
});