1
0
forked from baron/baron-sso

feat: improve Worksmobile tenant sync handling

This commit is contained in:
2026-06-02 18:05:36 +09:00
parent d6d39ca300
commit d32ca69eee
58 changed files with 4035 additions and 1400 deletions

View File

@@ -117,16 +117,18 @@ type tenantDomainConflict struct {
}
type tenantCSVRecord struct {
TenantID string
Name string
Type string
ParentTenantID *string
ParentTenantSlug string
Slug string
Memo string
Domains []string
Visibility string
OrgUnitType string
TenantID string
Name string
Type string
ParentTenantID *string
ParentTenantSlug string
Slug string
Memo string
Domains []string
Visibility string
OrgUnitType string
WorksmobileSync string
WorksmobileSyncSet bool
}
type orgContextTenant struct {
@@ -420,10 +422,10 @@ func (h *TenantHandler) ExportTenantsCSV(c *fiber.Ctx) error {
writer := csv.NewWriter(&buf)
includeIDs := includeCSVIds(c)
if includeIDs {
if err := writer.Write([]string{"tenant_id", "name", "type", "parent_tenant_id", "parent_tenant_slug", "slug", "memo", "email_domain", "visibility", "org_unit_type"}); err != nil {
if err := writer.Write([]string{"tenant_id", "name", "type", "parent_tenant_id", "parent_tenant_slug", "slug", "memo", "email_domain", "visibility", "org_unit_type", "worksmobile_sync"}); err != nil {
return errorJSON(c, fiber.StatusInternalServerError, err.Error())
}
} else if err := writer.Write([]string{"name", "type", "parent_tenant_slug", "slug", "memo", "email_domain", "visibility", "org_unit_type"}); err != nil {
} else if err := writer.Write([]string{"name", "type", "parent_tenant_slug", "slug", "memo", "email_domain", "visibility", "org_unit_type", "worksmobile_sync"}); err != nil {
return errorJSON(c, fiber.StatusInternalServerError, err.Error())
}
slugByID := make(map[string]string, len(allTenants))
@@ -444,7 +446,7 @@ func (h *TenantHandler) ExportTenantsCSV(c *fiber.Ctx) error {
domains = append(domains, domainName)
}
}
visibility, orgUnitType := tenantCSVOrgConfigValues(tenant.Config)
visibility, orgUnitType, worksmobileSync := tenantCSVOrgConfigValues(tenant.Config)
row := []string{
tenant.Name,
tenant.Type,
@@ -454,6 +456,7 @@ func (h *TenantHandler) ExportTenantsCSV(c *fiber.Ctx) error {
strings.Join(domains, ";"),
visibility,
orgUnitType,
worksmobileSync,
}
if includeIDs {
row = []string{
@@ -467,6 +470,7 @@ func (h *TenantHandler) ExportTenantsCSV(c *fiber.Ctx) error {
strings.Join(domains, ";"),
visibility,
orgUnitType,
worksmobileSync,
}
}
if err := writer.Write(row); err != nil {
@@ -683,17 +687,20 @@ func parseTenantCSVRecords(r io.Reader) ([]tenantCSVRecord, error) {
parentID = &parentValue
}
worksmobileSync, worksmobileSyncSet := tenantCSVWorksmobileSyncValue(row, header)
records = append(records, tenantCSVRecord{
TenantID: tenantCSVValue(row, header, "tenant_id"),
Name: name,
Type: tenantType,
ParentTenantID: parentID,
ParentTenantSlug: tenantCSVValue(row, header, "parent_tenant_slug"),
Slug: slug,
Memo: tenantCSVValue(row, header, "memo"),
Domains: splitTenantCSVDomains(tenantCSVValue(row, header, "email_domain")),
Visibility: tenantCSVValue(row, header, "visibility"),
OrgUnitType: tenantCSVValue(row, header, "org_unit_type"),
TenantID: tenantCSVValue(row, header, "tenant_id"),
Name: name,
Type: tenantType,
ParentTenantID: parentID,
ParentTenantSlug: tenantCSVValue(row, header, "parent_tenant_slug"),
Slug: slug,
Memo: tenantCSVValue(row, header, "memo"),
Domains: splitTenantCSVDomains(tenantCSVValue(row, header, "email_domain")),
Visibility: tenantCSVValue(row, header, "visibility"),
OrgUnitType: tenantCSVValue(row, header, "org_unit_type"),
WorksmobileSync: worksmobileSync,
WorksmobileSyncSet: worksmobileSyncSet,
})
}
@@ -703,35 +710,42 @@ func parseTenantCSVRecords(r io.Reader) ([]tenantCSVRecord, error) {
func tenantCSVHeaderIndex(header []string) map[string]int {
index := make(map[string]int, len(header))
aliases := map[string]string{
"id": "tenant_id",
"tenantid": "tenant_id",
"tenant_id": "tenant_id",
"name": "name",
"type": "type",
"parentid": "parent_tenant_id",
"parent_id": "parent_tenant_id",
"parenttenantid": "parent_tenant_id",
"parent_tenant_id": "parent_tenant_id",
"parenttenantslug": "parent_tenant_slug",
"parent_tenant_slug": "parent_tenant_slug",
"slug": "slug",
"memo": "memo",
"description": "memo",
"email-domain": "email_domain",
"emaildomain": "email_domain",
"email_domain": "email_domain",
"domain": "email_domain",
"domains": "email_domain",
"visibility": "visibility",
"public_setting": "visibility",
"publicsetting": "visibility",
"orgunittype": "org_unit_type",
"org_unit_type": "org_unit_type",
"org-unit-type": "org_unit_type",
"organizationtype": "org_unit_type",
"organization_type": "org_unit_type",
"orgtype": "org_unit_type",
"org_type": "org_unit_type",
"id": "tenant_id",
"tenantid": "tenant_id",
"tenant_id": "tenant_id",
"name": "name",
"type": "type",
"parentid": "parent_tenant_id",
"parent_id": "parent_tenant_id",
"parenttenantid": "parent_tenant_id",
"parent_tenant_id": "parent_tenant_id",
"parenttenantslug": "parent_tenant_slug",
"parent_tenant_slug": "parent_tenant_slug",
"slug": "slug",
"memo": "memo",
"description": "memo",
"email-domain": "email_domain",
"emaildomain": "email_domain",
"email_domain": "email_domain",
"domain": "email_domain",
"domains": "email_domain",
"visibility": "visibility",
"public_setting": "visibility",
"publicsetting": "visibility",
"orgunittype": "org_unit_type",
"org_unit_type": "org_unit_type",
"org-unit-type": "org_unit_type",
"organizationtype": "org_unit_type",
"organization_type": "org_unit_type",
"orgtype": "org_unit_type",
"org_type": "org_unit_type",
"worksmobile": "worksmobile_sync",
"worksmobilesync": "worksmobile_sync",
"worksmobile_sync": "worksmobile_sync",
"works_sync": "worksmobile_sync",
"works": "worksmobile_sync",
"worksmobileexcluded": "worksmobile_excluded",
"worksmobile_excluded": "worksmobile_excluded",
}
for i, column := range header {
key := strings.ToLower(strings.TrimSpace(column))
@@ -751,6 +765,28 @@ func tenantCSVValue(row []string, header map[string]int, key string) string {
return strings.TrimSpace(row[idx])
}
func tenantCSVWorksmobileSyncValue(row []string, header map[string]int) (string, bool) {
if _, ok := header["worksmobile_sync"]; ok {
value := tenantCSVValue(row, header, "worksmobile_sync")
if value == "" {
return "yes", true
}
return value, true
}
if _, ok := header["worksmobile_excluded"]; ok {
value := tenantCSVValue(row, header, "worksmobile_excluded")
excluded, err := normalizeTenantWorksmobileExcluded(value)
if err == nil && excluded {
return "no", true
}
if err == nil {
return "yes", true
}
return value, true
}
return "", false
}
func tenantCSVRowIsEmpty(row []string) bool {
for _, value := range row {
if strings.TrimSpace(value) != "" {
@@ -872,11 +908,38 @@ func normalizeTenantConfig(config map[string]any) (domain.JSONMap, error) {
normalized[key] = orgUnitType
continue
}
if key == "worksmobileExcluded" {
excluded, err := normalizeTenantWorksmobileExcluded(value)
if err != nil {
return nil, err
}
normalized[key] = excluded
continue
}
normalized[key] = value
}
return normalized, nil
}
func normalizeTenantWorksmobileExcluded(value any) (bool, error) {
switch typed := value.(type) {
case bool:
return typed, nil
case string:
normalized := strings.ToLower(strings.TrimSpace(typed))
switch normalized {
case "", "yes", "y", "true", "1", "on", "sync", "linked", "연동":
return false, nil
case "no", "n", "false", "0", "off", "none", "excluded", "exclude", "not_sync", "not-synced", "미연동", "연동안함", "제외":
return true, nil
default:
return false, fmt.Errorf("worksmobile_sync must be yes or no")
}
default:
return false, fmt.Errorf("worksmobile_sync must be yes or no")
}
}
func isAllowedOrgUnitType(value string) bool {
switch value {
case "실", "팀", "TF", "TF팀", "센터", "디비전", "셀", "본부", "지역본부", "부", "임원직속":
@@ -948,10 +1011,14 @@ func tenantVisibility(config domain.JSONMap) string {
}
}
func tenantCSVOrgConfigValues(config domain.JSONMap) (string, string) {
func tenantCSVOrgConfigValues(config domain.JSONMap) (string, string, string) {
visibility := tenantVisibility(config)
orgUnitType, _ := config["orgUnitType"].(string)
return visibility, strings.TrimSpace(orgUnitType)
worksmobileSync := "yes"
if excluded, err := normalizeTenantWorksmobileExcluded(config["worksmobileExcluded"]); err == nil && excluded {
worksmobileSync = "no"
}
return visibility, strings.TrimSpace(orgUnitType), worksmobileSync
}
func tenantCSVRecordConfig(record tenantCSVRecord) (domain.JSONMap, error) {
@@ -962,6 +1029,9 @@ func tenantCSVRecordConfig(record tenantCSVRecord) (domain.JSONMap, error) {
if strings.TrimSpace(record.OrgUnitType) != "" {
config["orgUnitType"] = record.OrgUnitType
}
if record.WorksmobileSyncSet {
config["worksmobileExcluded"] = record.WorksmobileSync
}
if len(config) == 0 {
return nil, nil
}
@@ -2319,7 +2389,7 @@ func mapOrgContextTenant(tenant domain.Tenant) orgContextTenant {
for _, domain := range tenant.Domains {
domains = append(domains, domain.Domain)
}
visibility, orgUnitType := tenantCSVOrgConfigValues(tenant.Config)
visibility, orgUnitType, _ := tenantCSVOrgConfigValues(tenant.Config)
return orgContextTenant{
ID: tenant.ID,
Type: tenant.Type,