forgejo/services/auth/source/source_quota_group_sync.go
thezzisu e31d67e0aa feat: allow sync quota groups with oauth2 auth source (#8554)
Implements synchronizing an external user's quota group with provided OAuth2 claim.

This functionality will allow system administrators to manage user's quota groups automatically.

Documentation is at forgejo/docs#1337

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8554
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: thezzisu <thezzisu@gmail.com>
Co-committed-by: thezzisu <thezzisu@gmail.com>
2025-12-01 14:12:00 +01:00

77 lines
2.5 KiB
Go

// Copyright 2025 The Forgejo Contributors. All rights reserved.
// SPDX-License-Identifier: MIT
package source
import (
"context"
"forgejo.org/models/quota"
user_model "forgejo.org/models/user"
"forgejo.org/modules/container"
"forgejo.org/modules/log"
)
func SyncGroupsToQuotaGroups(ctx context.Context, user *user_model.User, sourceUserGroups container.Set[string], sourceGroupQuotaGroupMapping map[string]container.Set[string], performRemoval bool) error {
qgroupCache := make(map[string]*quota.Group)
qgroupsToAdd, qgroupsToRemove := resolveMappedQuotaGroups(sourceUserGroups, sourceGroupQuotaGroupMapping)
if performRemoval {
if err := syncGroupsToQuotaGroupsCached(ctx, user, qgroupsToRemove, syncRemove, qgroupCache); err != nil {
return err
}
}
return syncGroupsToQuotaGroupsCached(ctx, user, qgroupsToAdd, syncAdd, qgroupCache)
}
func resolveMappedQuotaGroups(sourceUserGroups container.Set[string], sourceGroupQuotaGroupMapping map[string]container.Set[string]) (container.Set[string], container.Set[string]) {
qgroupsToAdd := make(container.Set[string])
qgroupsToRemove := make(container.Set[string])
for group, qgroups := range sourceGroupQuotaGroupMapping {
isUserInGroup := sourceUserGroups.Contains(group)
if isUserInGroup {
for qgroup := range qgroups {
qgroupsToAdd[qgroup] = struct{}{}
}
} else {
for qgroup := range qgroups {
qgroupsToRemove[qgroup] = struct{}{}
}
}
}
return qgroupsToAdd, qgroupsToRemove
}
func syncGroupsToQuotaGroupsCached(ctx context.Context, user *user_model.User, qgroups container.Set[string], action syncType, qgroupCache map[string]*quota.Group) error {
for qgroupName := range qgroups {
var err error
qgroup, ok := qgroupCache[qgroupName]
if !ok {
qgroup, err = quota.GetGroupByName(ctx, qgroupName)
if err != nil {
return err
}
if qgroup == nil {
log.Warn("quota group sync: Could not find quota group %s: %v", qgroupName, err)
continue
}
qgroupCache[qgroup.Name] = qgroup
}
isMember, err := qgroup.IsUserInGroup(ctx, user.ID)
if err != nil {
return err
}
if action == syncAdd && !isMember {
if err := qgroup.AddUserByID(ctx, user.ID); err != nil {
log.Error("quota group sync: Could not add user to quota group: %v", err)
return err
}
} else if action == syncRemove && isMember {
if err := qgroup.RemoveUserByID(ctx, user.ID); err != nil {
log.Error("quota group sync: Could not remove user from quota group: %v", err)
return err
}
}
}
return nil
}