forked from baron/baron-sso
SSOT 전환
This commit is contained in:
121
backend/internal/service/user_group_service.go
Normal file
121
backend/internal/service/user_group_service.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"baron-sso-backend/internal/domain"
|
||||
"baron-sso-backend/internal/repository"
|
||||
"context"
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
type UserGroupService interface {
|
||||
Create(ctx context.Context, group *domain.UserGroup) error
|
||||
Update(ctx context.Context, group *domain.UserGroup) error
|
||||
Delete(ctx context.Context, id string) error
|
||||
Get(ctx context.Context, id string) (*domain.UserGroup, error)
|
||||
List(ctx context.Context, tenantID string) ([]domain.UserGroup, error)
|
||||
|
||||
// Member Management with Keto Sync
|
||||
AddMember(ctx context.Context, groupID, userID string) error
|
||||
RemoveMember(ctx context.Context, groupID, userID string) error
|
||||
}
|
||||
|
||||
type userGroupService struct {
|
||||
repo repository.UserGroupRepository
|
||||
userRepo repository.UserRepository
|
||||
ketoService KetoService
|
||||
}
|
||||
|
||||
func NewUserGroupService(repo repository.UserGroupRepository, userRepo repository.UserRepository, keto KetoService) UserGroupService {
|
||||
return &userGroupService{
|
||||
repo: repo,
|
||||
userRepo: userRepo,
|
||||
ketoService: keto,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *userGroupService) Create(ctx context.Context, group *domain.UserGroup) error {
|
||||
if err := s.repo.Create(ctx, group); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Keto: UserGroup:<id>#parent_tenant@Tenant:<tid>
|
||||
err := s.ketoService.CreateRelation(ctx, "UserGroup", group.ID, "parent_tenant", "Tenant:"+group.TenantID)
|
||||
if err != nil {
|
||||
slog.Error("Failed to create keto relation for user group", "error", err, "group_id", group.ID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *userGroupService) Update(ctx context.Context, group *domain.UserGroup) error {
|
||||
return s.repo.Update(ctx, group)
|
||||
}
|
||||
|
||||
func (s *userGroupService) Delete(ctx context.Context, id string) error {
|
||||
// Optional: Delete relations in Keto before DB delete
|
||||
return s.repo.Delete(ctx, id)
|
||||
}
|
||||
|
||||
func (s *userGroupService) Get(ctx context.Context, id string) (*domain.UserGroup, error) {
|
||||
group, err := s.repo.FindByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Fetch members from Keto
|
||||
tuples, err := s.ketoService.ListRelations(ctx, "UserGroup", group.ID, "members", "")
|
||||
if err != nil {
|
||||
slog.Error("Failed to fetch group members from keto", "error", err, "group_id", group.ID)
|
||||
// Return group without members rather than failing?
|
||||
// But if we fail here, we might hide partial failure. Let's log and proceed or return error?
|
||||
// For now, let's proceed with empty members to avoid blocking UI if keto is down?
|
||||
// No, SSOT is Keto. If Keto is down, we can't show members.
|
||||
// Returning error might be safer.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var userIDs []string
|
||||
for _, t := range tuples {
|
||||
// SubjectID is like "User:uuid"
|
||||
if len(t.SubjectID) > 5 && t.SubjectID[:5] == "User:" {
|
||||
userIDs = append(userIDs, t.SubjectID[5:])
|
||||
}
|
||||
}
|
||||
|
||||
if len(userIDs) > 0 {
|
||||
members, err := s.userRepo.FindByIDs(ctx, userIDs)
|
||||
if err != nil {
|
||||
slog.Error("Failed to fetch member details from db", "error", err)
|
||||
return nil, err
|
||||
}
|
||||
group.Members = members
|
||||
}
|
||||
|
||||
return group, nil
|
||||
}
|
||||
|
||||
func (s *userGroupService) List(ctx context.Context, tenantID string) ([]domain.UserGroup, error) {
|
||||
return s.repo.ListByTenantID(ctx, tenantID)
|
||||
}
|
||||
|
||||
func (s *userGroupService) AddMember(ctx context.Context, groupID, userID string) error {
|
||||
// Keto: UserGroup:<groupID>#members@User:<userID>
|
||||
err := s.ketoService.CreateRelation(ctx, "UserGroup", groupID, "members", "User:"+userID)
|
||||
if err != nil {
|
||||
slog.Error("Failed to sync group membership to keto", "error", err, "group", groupID, "user", userID)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *userGroupService) RemoveMember(ctx context.Context, groupID, userID string) error {
|
||||
// Keto: Delete relation
|
||||
err := s.ketoService.DeleteRelation(ctx, "UserGroup", groupID, "members", "User:"+userID)
|
||||
if err != nil {
|
||||
slog.Error("Failed to remove group membership from keto", "error", err, "group", groupID, "user", userID)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user