forked from baron/baron-sso
feat: improve Worksmobile tenant sync handling
This commit is contained in:
@@ -99,6 +99,70 @@ func sanitizeUserMetadata(metadata map[string]any) map[string]any {
|
||||
return sanitized
|
||||
}
|
||||
|
||||
func sanitizeUserRepresentativeTenants(ctx context.Context, tenantService service.TenantService, metadata map[string]any, appointments []map[string]any) (bool, error) {
|
||||
if tenantService == nil || metadata == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
cleared := false
|
||||
clearMetadataPrimary := func() {
|
||||
delete(metadata, "primaryTenantId")
|
||||
delete(metadata, "primaryTenantSlug")
|
||||
delete(metadata, "primaryTenantName")
|
||||
delete(metadata, "primaryTenantIsOwner")
|
||||
cleared = true
|
||||
}
|
||||
if isNonPublicRepresentativeTenant(ctx, tenantService, normalizeMetadataString(metadata["primaryTenantId"]), normalizeMetadataString(metadata["primaryTenantSlug"])) {
|
||||
clearMetadataPrimary()
|
||||
}
|
||||
|
||||
clearAppointment := func(appointment map[string]any) {
|
||||
if isPrimary, ok := metadataBoolFromMap(appointment, "isPrimary", "primary", "representative", "isRepresentative"); !ok || !isPrimary {
|
||||
return
|
||||
}
|
||||
tenantID := normalizeMetadataString(appointment["tenantId"])
|
||||
tenantSlug := normalizeMetadataString(appointment["tenantSlug"])
|
||||
if tenantSlug == "" {
|
||||
tenantSlug = normalizeMetadataString(appointment["slug"])
|
||||
}
|
||||
if !isNonPublicRepresentativeTenant(ctx, tenantService, tenantID, tenantSlug) {
|
||||
return
|
||||
}
|
||||
appointment["isPrimary"] = false
|
||||
appointment["primary"] = false
|
||||
appointment["representative"] = false
|
||||
appointment["isRepresentative"] = false
|
||||
clearMetadataPrimary()
|
||||
}
|
||||
|
||||
for _, appointment := range appointments {
|
||||
clearAppointment(appointment)
|
||||
}
|
||||
if rawAppointments, ok := metadata["additionalAppointments"].([]any); ok {
|
||||
for _, rawAppointment := range rawAppointments {
|
||||
if appointment, ok := rawAppointment.(map[string]any); ok {
|
||||
clearAppointment(appointment)
|
||||
}
|
||||
}
|
||||
}
|
||||
return cleared, nil
|
||||
}
|
||||
|
||||
func isNonPublicRepresentativeTenant(ctx context.Context, tenantService service.TenantService, tenantID string, tenantSlug string) bool {
|
||||
var tenant *domain.Tenant
|
||||
var err error
|
||||
if strings.TrimSpace(tenantID) != "" {
|
||||
tenant, err = tenantService.GetTenant(ctx, strings.TrimSpace(tenantID))
|
||||
} else if strings.TrimSpace(tenantSlug) != "" {
|
||||
tenant, err = tenantService.GetTenantBySlug(ctx, strings.TrimSpace(tenantSlug))
|
||||
}
|
||||
if err != nil || tenant == nil {
|
||||
return false
|
||||
}
|
||||
visibility := tenantVisibility(tenant.Config)
|
||||
return visibility == "internal" || visibility == "private"
|
||||
}
|
||||
|
||||
func primaryTenantIDFromRequest(primaryTenantID string, metadata map[string]any, appointments []map[string]any) string {
|
||||
if value := strings.TrimSpace(primaryTenantID); value != "" {
|
||||
return value
|
||||
@@ -651,6 +715,20 @@ func (h *UserHandler) CreateUser(c *fiber.Ctx) error {
|
||||
}
|
||||
req.CompanyCode = tenantSlug
|
||||
req.Metadata = sanitizeUserMetadata(mergeUserAppointmentMetadata(req.Metadata, req.AdditionalAppointments, req.PrimaryTenantID, req.PrimaryTenantName, req.PrimaryTenantIsOwner))
|
||||
representativeCleared := false
|
||||
if h.TenantService != nil {
|
||||
cleared, err := sanitizeUserRepresentativeTenants(c.Context(), h.TenantService, req.Metadata, req.AdditionalAppointments)
|
||||
if err != nil {
|
||||
return errorJSON(c, fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
representativeCleared = cleared
|
||||
if cleared {
|
||||
req.PrimaryTenantID = ""
|
||||
req.PrimaryTenantName = ""
|
||||
req.PrimaryTenantIsOwner = nil
|
||||
req.CompanyCode = ""
|
||||
}
|
||||
}
|
||||
|
||||
email := strings.TrimSpace(req.Email)
|
||||
if email == "" {
|
||||
@@ -725,7 +803,11 @@ func (h *UserHandler) CreateUser(c *fiber.Ctx) error {
|
||||
|
||||
// [Resolve TenantID and Custom Login IDs before Kratos creation]
|
||||
var tenantID string
|
||||
requestedPrimaryTenantID := primaryTenantIDFromRequest(req.PrimaryTenantID, req.Metadata, req.AdditionalAppointments)
|
||||
primaryAppointments := req.AdditionalAppointments
|
||||
if representativeCleared {
|
||||
primaryAppointments = nil
|
||||
}
|
||||
requestedPrimaryTenantID := primaryTenantIDFromRequest(req.PrimaryTenantID, req.Metadata, primaryAppointments)
|
||||
if req.CompanyCode == "" && h.TenantService != nil {
|
||||
if requestedPrimaryTenantID != "" {
|
||||
if tenant, err := h.TenantService.GetTenant(c.Context(), requestedPrimaryTenantID); err == nil && tenant != nil {
|
||||
@@ -1995,6 +2077,18 @@ func (h *UserHandler) UpdateUser(c *fiber.Ctx) error {
|
||||
}
|
||||
req.CompanyCode = tenantSlug
|
||||
req.Metadata = sanitizeUserMetadata(mergeUserAppointmentMetadata(req.Metadata, req.AdditionalAppointments, req.PrimaryTenantID, req.PrimaryTenantName, req.PrimaryTenantIsOwner))
|
||||
if h.TenantService != nil {
|
||||
cleared, err := sanitizeUserRepresentativeTenants(c.Context(), h.TenantService, req.Metadata, req.AdditionalAppointments)
|
||||
if err != nil {
|
||||
return errorJSON(c, fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
if cleared {
|
||||
req.PrimaryTenantID = ""
|
||||
req.PrimaryTenantName = ""
|
||||
req.PrimaryTenantIsOwner = nil
|
||||
req.CompanyCode = nil
|
||||
}
|
||||
}
|
||||
if req.Role != nil {
|
||||
if requester == nil || domain.NormalizeRole(requester.Role) != domain.RoleSuperAdmin {
|
||||
return errorJSON(c, fiber.StatusForbidden, "forbidden: only super admin can change user role")
|
||||
|
||||
Reference in New Issue
Block a user