1
0
forked from baron/baron-sso

fix(backend): prevent duplicate key constraint on empty login id when syncing users

This commit is contained in:
2026-03-31 13:11:32 +09:00
parent 4b34ab8161
commit 5029b8049b
6 changed files with 154 additions and 18 deletions

View File

@@ -213,10 +213,52 @@ func (s *userGroupService) List(ctx context.Context, tenantID string) ([]domain.
func (s *userGroupService) AddMember(ctx context.Context, groupID, userID string) error {
// Validate group exists
if _, err := s.repo.FindByID(ctx, groupID); err != nil {
group, err := s.repo.FindByID(ctx, groupID)
if err != nil {
return fmt.Errorf("user group not found: %w", err)
}
// [Fix] Sync Kratos Traits & Local DB when a user is added to an organization
if s.kratos != nil && s.tenantRepo != nil {
tenant, err := s.tenantRepo.FindByID(ctx, group.TenantID)
if err == nil && tenant != nil {
// Fetch Kratos Identity
identity, err := s.kratos.GetIdentity(ctx, userID)
if err == nil && identity != nil {
traits := identity.Traits
if traits == nil {
traits = make(map[string]interface{})
}
traits["companyCode"] = tenant.Slug
traits["tenant_id"] = tenant.ID
traits["department"] = group.Name
// Update Kratos
_, updateErr := s.kratos.UpdateIdentity(ctx, userID, traits, identity.State)
if updateErr != nil {
slog.Error("Failed to update identity traits during AddMember", "user", userID, "error", updateErr)
}
}
}
}
// Sync local user repo
if s.userRepo != nil && s.tenantRepo != nil {
tenant, _ := s.tenantRepo.FindByID(ctx, group.TenantID)
if tenant != nil {
localUser, err := s.userRepo.FindByID(ctx, userID)
if err == nil && localUser != nil {
localUser.CompanyCode = tenant.Slug
localUser.TenantID = &tenant.ID
localUser.Department = group.Name
if localUser.LoginID == "" {
localUser.LoginID = localUser.ID
}
_ = s.userRepo.Update(ctx, localUser)
}
}
}
// Keto via Outbox: Tenant:<groupID>#members@User:<userID>
if s.outboxRepo != nil {
_ = s.outboxRepo.Create(ctx, &domain.KetoOutbox{
@@ -226,6 +268,15 @@ func (s *userGroupService) AddMember(ctx context.Context, groupID, userID string
Subject: "User:" + userID,
Action: domain.KetoOutboxActionCreate,
})
// Also add direct Tenant membership to Keto for member counting
_ = s.outboxRepo.Create(ctx, &domain.KetoOutbox{
Namespace: "Tenant",
Object: group.TenantID,
Relation: "members",
Subject: "User:" + userID,
Action: domain.KetoOutboxActionCreate,
})
}
return nil