1
0
forked from baron/baron-sso

네이버 웍스 연동기능 개선

This commit is contained in:
2026-05-18 15:36:30 +09:00
parent c71ece84b8
commit e29d056b9e
61 changed files with 4137 additions and 710 deletions

View File

@@ -126,6 +126,13 @@ func TestWorksmobileLiveGPDTDCOrgUnitProvisioning(t *testing.T) {
})
}
func TestWorksmobileLiveBaronGroupOrgUnitProvisioning(t *testing.T) {
if os.Getenv("WORKSMOBILE_LIVE_BARONGROUP_ORGUNIT_PROVISIONING") != "1" {
t.Skip("live Worksmobile Baron Group orgunit provisioning is disabled")
}
runWorksmobileLiveBaronGroupOrgUnitProvisioning(t)
}
func TestWorksmobileLiveSyncHanmacFamilyOrgUnits(t *testing.T) {
if os.Getenv("WORKSMOBILE_LIVE_SYNC_HANMAC_FAMILY_ORGUNITS") != "1" {
t.Skip("live Worksmobile Hanmac family orgunit sync is disabled")
@@ -548,6 +555,142 @@ func runWorksmobileLiveCompanyOrgUnitProvisioning(t *testing.T, companySlug stri
}
}
func runWorksmobileLiveBaronGroupOrgUnitProvisioning(t *testing.T) {
t.Helper()
ctx := context.Background()
db, err := gorm.Open(postgres.Open(worksmobileLiveDSN()), &gorm.Config{})
require.NoError(t, err)
tenantRepo := repository.NewTenantRepository(db)
userRepo := repository.NewUserRepository(db)
userGroupRepo := repository.NewUserGroupRepository(db)
tenantService := NewTenantService(tenantRepo, userRepo, userGroupRepo, nil)
client := NewWorksmobileHTTPClientWithAuth("", os.Getenv("SAMAN_SCIM_LONGLIVE_TOKEN"), WorksmobileOAuthConfig{
ClientID: os.Getenv("WORKS_ADMIN_OAUTH_CLIENT_ID"),
ClientSecret: os.Getenv("WORKS_ADMIN_OAUTH_CLIENT_SECRET"),
ServiceAccount: os.Getenv("WORKS_ADMIN_OAUTH_CLIENT_SERVICE_ACCOUNT"),
PrivateKey: os.Getenv("WORKS_ADMIN_OAUTH_CLIENT_PRIVATE_KEY"),
Scope: getenvDefault("WORKS_ADMIN_OAUTH_SCOPE", "directory"),
})
baronGroup, err := tenantService.GetTenantBySlug(ctx, "baron-group")
require.NoError(t, err)
tenants, err := listWorksmobileLiveTenantScope(db, baronGroup.ID)
require.NoError(t, err)
domainID, ok := worksmobileDomainIDFromEnv("BARONGROUP_DOMAIN_ID")
require.True(t, ok, "missing BARONGROUP_DOMAIN_ID")
mailDomain := getenvDefault("BARONGROUP_MAIL_DOMAIN", getenvDefault("WORKS_DEFAULT_DOMAIN_BARONGROUP", "brsw.kr"))
tenantByID := worksmobileTenantByID(append([]domain.Tenant{*baronGroup}, tenants...))
targets := worksmobileLiveBaronGroupOrgUnitTargets(t, tenants, tenantByID, *baronGroup, domainID, mailDomain)
targetByID := map[string]worksmobileLiveOrgUnitTarget{}
for _, target := range targets {
targetByID[target.Tenant.ID] = target
}
remoteGroups, err := client.ListGroups(ctx)
require.NoError(t, err)
remoteByExternalID, duplicateExternalKeys := worksmobileLiveRemoteByExternalID(remoteGroups)
require.Empty(t, duplicateExternalKeys, "duplicate Worksmobile external keys")
for _, target := range sortWorksmobileLiveTargetsTopologically(targets, tenantByID) {
remote, found := remoteByExternalID[target.Tenant.ID]
if found && remote.DomainID != target.Payload.DomainID {
t.Logf("REKEY conflicting external key slug=%s external=%s worksID=%s currentDomain=%d expectedDomain=%d", target.Tenant.Slug, target.Tenant.ID, remote.ID, remote.DomainID, target.Payload.DomainID)
if err := client.ClearOrgUnitExternalKey(ctx, remote.ID, remote.DomainID); err != nil {
legacyPatch := WorksmobileOrgUnitPatchPayload{
DomainID: remote.DomainID,
OrgUnitExternalKey: "legacy-" + remote.ID,
}
require.NoError(t, client.PatchOrgUnit(ctx, remote.ID, legacyPatch))
}
time.Sleep(1100 * time.Millisecond)
remoteGroups, err = client.ListGroups(ctx)
require.NoError(t, err)
remoteByExternalID, duplicateExternalKeys = worksmobileLiveRemoteByExternalID(remoteGroups)
require.Empty(t, duplicateExternalKeys, "duplicate Worksmobile external keys")
remote, found = remoteByExternalID[target.Tenant.ID]
if found && remote.DomainID != target.Payload.DomainID {
require.Failf(t, "external key is attached to a different Worksmobile domain after rekey", "slug=%s external=%s currentDomain=%d expectedDomain=%d", target.Tenant.Slug, target.Tenant.ID, remote.DomainID, target.Payload.DomainID)
}
}
if !found {
remote, found = findWorksmobileLiveRemoteByPath(remoteGroups, worksmobileLiveRemoteByID(remoteGroups), target.Payload.DomainID, worksmobileLiveTenantOrgPath(target.Tenant, tenantByID))
}
if found {
t.Logf("PATCH orgunit slug=%s id=%s worksID=%s email=%s parent=%s", target.Tenant.Slug, target.Tenant.ID, remote.ID, target.Payload.Email, target.Payload.ParentOrgUnitID)
require.NoError(t, patchWorksmobileLiveOrgUnit(ctx, client, remote.ID, target.Payload))
} else {
t.Logf("CREATE orgunit slug=%s id=%s email=%s parent=%s", target.Tenant.Slug, target.Tenant.ID, target.Payload.Email, target.Payload.ParentOrgUnitID)
require.NoError(t, client.UpsertOrgUnit(ctx, target.Payload, target.Tenant.Slug))
}
time.Sleep(1100 * time.Millisecond)
remoteGroups, err = client.ListGroups(ctx)
require.NoError(t, err)
remoteByExternalID, duplicateExternalKeys = worksmobileLiveRemoteByExternalID(remoteGroups)
require.Empty(t, duplicateExternalKeys, "duplicate Worksmobile external keys")
}
remoteGroups, err = client.ListGroups(ctx)
require.NoError(t, err)
remoteByExternalID, duplicateExternalKeys = worksmobileLiveRemoteByExternalID(remoteGroups)
require.Empty(t, duplicateExternalKeys, "duplicate Worksmobile external keys")
remoteByID := worksmobileLiveRemoteByID(remoteGroups)
for _, target := range targets {
remote, ok := remoteByExternalID[target.Tenant.ID]
require.True(t, ok, "missing Worksmobile orgunit after sync: %s", target.Tenant.Slug)
require.Equal(t, target.Payload.DomainID, remote.DomainID, "domain mismatch: %s", target.Tenant.Slug)
require.Equal(t, target.Tenant.Name, remote.DisplayName, "name mismatch: %s", target.Tenant.Slug)
require.Equal(t, worksmobileMailLocalPart(target.Payload.Email), remote.MailLocalPart, "email local-part mismatch: %s", target.Tenant.Slug)
require.Equal(t, strings.ToLower(strings.TrimSpace(target.Payload.Email)), strings.ToLower(strings.TrimSpace(remote.Email)), "email mismatch: %s", target.Tenant.Slug)
expectedParentID := ""
if parentExternalKey := strings.TrimPrefix(target.Payload.ParentOrgUnitID, "externalKey:"); parentExternalKey != "" && parentExternalKey != target.Payload.ParentOrgUnitID {
parentRemote, ok := remoteByExternalID[parentExternalKey]
require.True(t, ok, "missing Worksmobile parent for %s", target.Tenant.Slug)
expectedParentID = parentRemote.ID
parentTarget, ok := targetByID[parentExternalKey]
require.True(t, ok, "missing Baron parent target for %s", target.Tenant.Slug)
require.Equal(t, worksmobileMailLocalPart(parentTarget.Payload.Email), parentRemote.MailLocalPart, "parent email local-part mismatch: %s", target.Tenant.Slug)
}
require.Equal(t, expectedParentID, remote.ParentID, "parent mismatch: %s", target.Tenant.Slug)
require.Equal(t, worksmobileLiveTenantOrgPath(target.Tenant, tenantByID), worksmobileLiveRemotePath(remoteByID, remote), "path mismatch: %s", target.Tenant.Slug)
}
t.Logf("SUMMARY synced=%d domainID=%d", len(targets), domainID)
}
func worksmobileLiveBaronGroupOrgUnitTargets(t *testing.T, tenants []domain.Tenant, tenantByID map[string]domain.Tenant, root domain.Tenant, domainID int64, mailDomain string) []worksmobileLiveOrgUnitTarget {
t.Helper()
mailDomain = strings.ToLower(strings.TrimSpace(mailDomain))
require.NotEmpty(t, mailDomain, "baron group mail domain is required")
targets := make([]worksmobileLiveOrgUnitTarget, 0)
seenExternalKeys := map[string]string{}
seenEmails := map[string]string{}
for index, tenant := range tenants {
if !isWorksmobileOrgUnitTenant(tenant, tenantByID) || worksmobileLiveSkipOrgUnitTenant(tenant) {
continue
}
payload, err := BuildWorksmobileOrgUnitPayloadForDomainTenant(tenant, root, root.Config, index+1)
require.NoError(t, err, "payload build failed: %s", tenant.Slug)
payload = normalizeWorksmobileOrgUnitParent(payload, tenant, tenantByID, root.ID)
payload.DomainID = domainID
payload.Email = strings.ToLower(strings.TrimSpace(tenant.Slug)) + "@" + mailDomain
require.NotEmpty(t, payload.Email, "orgunit email is required: %s", tenant.Slug)
if owner, exists := seenExternalKeys[payload.OrgUnitExternalKey]; exists {
require.Failf(t, "duplicate Baron external key", "external=%s owner=%s duplicate=%s", payload.OrgUnitExternalKey, owner, tenant.Slug)
}
seenExternalKeys[payload.OrgUnitExternalKey] = tenant.Slug
normalizedEmail := strings.ToLower(strings.TrimSpace(payload.Email))
if owner, exists := seenEmails[normalizedEmail]; exists {
require.Failf(t, "duplicate Baron orgunit email", "email=%s owner=%s duplicate=%s", normalizedEmail, owner, tenant.Slug)
}
seenEmails[normalizedEmail] = tenant.Slug
targets = append(targets, worksmobileLiveOrgUnitTarget{Tenant: tenant, Payload: payload})
}
return targets
}
func createWorksmobileLiveOrgUnitIfMissing(t *testing.T, ctx context.Context, client *WorksmobileHTTPClient, tenant domain.Tenant) {
t.Helper()
payload, err := BuildWorksmobileOrgUnitPayload(tenant, nil, 1)