forked from baron/baron-sso
패키징 개선
This commit is contained in:
@@ -34,6 +34,7 @@ type WorksmobileSyncer interface {
|
||||
type WorksmobileAdminService interface {
|
||||
GetTenantOverview(ctx context.Context, tenantID string) (WorksmobileTenantOverview, error)
|
||||
GetComparison(ctx context.Context, tenantID string, includeMatched bool) (WorksmobileComparison, error)
|
||||
ImportUsersFromWorks(ctx context.Context, tenantID string, worksmobileUserIDs []string) (WorksmobileImportUsersResult, error)
|
||||
EnqueueBackfillDryRun(ctx context.Context, tenantID string) (WorksmobileBackfillDryRun, error)
|
||||
EnqueueOrgUnitSync(ctx context.Context, tenantID, orgUnitID string) (*domain.WorksmobileOutbox, error)
|
||||
EnqueueOrgUnitDelete(ctx context.Context, tenantID, worksmobileOrgUnitID string) (*domain.WorksmobileOutbox, error)
|
||||
@@ -68,6 +69,27 @@ type WorksmobilePendingJobDeleteResult struct {
|
||||
DeletedCount int `json:"deletedCount"`
|
||||
}
|
||||
|
||||
type WorksmobileImportUsersResult struct {
|
||||
UpdatedCount int `json:"updatedCount"`
|
||||
CreatedCount int `json:"createdCount"`
|
||||
ExternalKeyUpdates int `json:"externalKeyUpdates"`
|
||||
Failures []WorksmobileImportUsersFailure `json:"failures,omitempty"`
|
||||
Items []WorksmobileImportUsersResultItem `json:"items,omitempty"`
|
||||
}
|
||||
|
||||
type WorksmobileImportUsersFailure struct {
|
||||
WorksmobileID string `json:"worksmobileId,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
type WorksmobileImportUsersResultItem struct {
|
||||
WorksmobileID string `json:"worksmobileId,omitempty"`
|
||||
BaronID string `json:"baronId,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
Action string `json:"action"`
|
||||
}
|
||||
|
||||
type WorksmobileInitialPasswordCredential struct {
|
||||
Email string `json:"email"`
|
||||
Name string `json:"name,omitempty"`
|
||||
@@ -178,6 +200,8 @@ type worksmobileSyncService struct {
|
||||
outboxRepo repository.WorksmobileOutboxRepository
|
||||
client WorksmobileDirectoryClient
|
||||
identityMirror WorksmobileIdentityMirror
|
||||
identityWriter IdentityWriteService
|
||||
kratos KratosAdminService
|
||||
}
|
||||
|
||||
type WorksmobileIdentityMirror interface {
|
||||
@@ -201,18 +225,30 @@ func (s *worksmobileSyncService) SetIdentityMirror(source WorksmobileIdentityMir
|
||||
s.identityMirror = source
|
||||
}
|
||||
|
||||
func (s *worksmobileSyncService) SetIdentityServices(writer IdentityWriteService, kratos KratosAdminService) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
s.identityWriter = writer
|
||||
s.kratos = kratos
|
||||
}
|
||||
|
||||
func (s *worksmobileSyncService) GetTenantOverview(ctx context.Context, tenantID string) (WorksmobileTenantOverview, error) {
|
||||
tenant, err := s.tenantService.GetTenant(ctx, tenantID)
|
||||
root, err := s.hanmacRoot(ctx, tenantID)
|
||||
if err != nil {
|
||||
return WorksmobileTenantOverview{}, err
|
||||
}
|
||||
jobs, _ := s.outboxRepo.ListRecent(ctx, 50)
|
||||
scopeTenants, err := s.hanmacSubtree(ctx, root.ID)
|
||||
if err != nil {
|
||||
return WorksmobileTenantOverview{}, err
|
||||
}
|
||||
jobs, _ := s.outboxRepo.ListRecentByTenantRoot(ctx, root.ID, worksmobileRecentResourceIDs(root.ID, scopeTenants), 50)
|
||||
jobs = redactWorksmobileOutboxPayloads(jobs)
|
||||
return WorksmobileTenantOverview{
|
||||
Tenant: *tenant,
|
||||
Tenant: *root,
|
||||
Config: WorksmobileConfigSummary{
|
||||
Enabled: WorksmobileEnabled(tenant.Config),
|
||||
DomainMappings: WorksmobileDomainMappings(tenant.Config),
|
||||
Enabled: WorksmobileEnabled(root.Config),
|
||||
DomainMappings: WorksmobileDomainMappings(root.Config),
|
||||
TokenConfigured: worksmobileDirectoryAuthConfigured(),
|
||||
AdminTenantID: strings.TrimSpace(os.Getenv("WORKS_ADMIN_TENANT_ID")),
|
||||
},
|
||||
@@ -231,6 +267,15 @@ func worksmobileDirectoryAuthConfigured() bool {
|
||||
strings.TrimSpace(os.Getenv("WORKS_ADMIN_OAUTH_CLIENT_PRIVATE_KEY_FILE")) != "")
|
||||
}
|
||||
|
||||
func worksmobileRecentResourceIDs(rootID string, tenants []domain.Tenant) []string {
|
||||
ids := make([]string, 0, len(tenants)+1)
|
||||
ids = append(ids, rootID)
|
||||
for _, tenant := range tenants {
|
||||
ids = append(ids, tenant.ID)
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
func WorksmobileExcluded(config domain.JSONMap) bool {
|
||||
rawValue, ok := config[worksmobileExcludedConfigKey]
|
||||
if !ok {
|
||||
@@ -403,6 +448,273 @@ func (s *worksmobileSyncService) GetComparison(ctx context.Context, tenantID str
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *worksmobileSyncService) ImportUsersFromWorks(ctx context.Context, tenantID string, worksmobileUserIDs []string) (WorksmobileImportUsersResult, error) {
|
||||
root, err := s.hanmacRoot(ctx, tenantID)
|
||||
if err != nil {
|
||||
return WorksmobileImportUsersResult{}, err
|
||||
}
|
||||
if s.client == nil {
|
||||
return WorksmobileImportUsersResult{}, errors.New("worksmobile client is not configured")
|
||||
}
|
||||
if len(worksmobileUserIDs) == 0 {
|
||||
return WorksmobileImportUsersResult{}, errors.New("worksmobile user ids are required")
|
||||
}
|
||||
scopeTenants, err := s.hanmacSubtree(ctx, root.ID)
|
||||
if err != nil {
|
||||
return WorksmobileImportUsersResult{}, err
|
||||
}
|
||||
tenantByID := worksmobileTenantByID(append([]domain.Tenant{*root}, scopeTenants...))
|
||||
remoteUsers, err := s.client.ListUsers(ctx)
|
||||
if err != nil {
|
||||
return WorksmobileImportUsersResult{}, err
|
||||
}
|
||||
remoteGroups, err := s.client.ListGroups(ctx)
|
||||
if err != nil {
|
||||
return WorksmobileImportUsersResult{}, err
|
||||
}
|
||||
remoteByID := make(map[string]WorksmobileRemoteUser, len(remoteUsers))
|
||||
for _, remote := range remoteUsers {
|
||||
if id := strings.TrimSpace(remote.ID); id != "" {
|
||||
remoteByID[id] = remote
|
||||
}
|
||||
}
|
||||
groupByID := make(map[string]WorksmobileRemoteGroup, len(remoteGroups))
|
||||
for _, group := range remoteGroups {
|
||||
if id := strings.TrimSpace(group.ID); id != "" {
|
||||
groupByID[id] = group
|
||||
}
|
||||
}
|
||||
|
||||
result := WorksmobileImportUsersResult{}
|
||||
seen := map[string]bool{}
|
||||
for _, rawID := range worksmobileUserIDs {
|
||||
worksmobileID := strings.TrimSpace(rawID)
|
||||
if worksmobileID == "" || seen[worksmobileID] {
|
||||
continue
|
||||
}
|
||||
seen[worksmobileID] = true
|
||||
remote, ok := remoteByID[worksmobileID]
|
||||
if !ok {
|
||||
result.Failures = append(result.Failures, WorksmobileImportUsersFailure{WorksmobileID: worksmobileID, Error: "worksmobile user not found"})
|
||||
continue
|
||||
}
|
||||
user, created, externalKeyUpdated, err := s.importSingleWorksmobileUser(ctx, root.ID, remote, tenantByID, groupByID)
|
||||
if err != nil {
|
||||
result.Failures = append(result.Failures, WorksmobileImportUsersFailure{WorksmobileID: worksmobileID, Email: remote.Email, Error: err.Error()})
|
||||
continue
|
||||
}
|
||||
action := "updated"
|
||||
if created {
|
||||
action = "created"
|
||||
result.CreatedCount++
|
||||
} else {
|
||||
result.UpdatedCount++
|
||||
}
|
||||
if externalKeyUpdated {
|
||||
result.ExternalKeyUpdates++
|
||||
}
|
||||
result.Items = append(result.Items, WorksmobileImportUsersResultItem{
|
||||
WorksmobileID: worksmobileID,
|
||||
BaronID: user.ID,
|
||||
Email: user.Email,
|
||||
Action: action,
|
||||
})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *worksmobileSyncService) importSingleWorksmobileUser(ctx context.Context, rootID string, remote WorksmobileRemoteUser, tenantByID map[string]domain.Tenant, groupByID map[string]WorksmobileRemoteGroup) (domain.User, bool, bool, error) {
|
||||
email := strings.ToLower(strings.TrimSpace(remote.Email))
|
||||
if email == "" {
|
||||
return domain.User{}, false, false, errors.New("worksmobile user email is required")
|
||||
}
|
||||
tenantID := worksmobileTenantIDForRemoteUser(remote, groupByID)
|
||||
tenant, ok := tenantByID[tenantID]
|
||||
if !ok || !isWorksmobileUserScopeTenant(tenant) {
|
||||
return domain.User{}, false, false, fmt.Errorf("worksmobile primary org is outside import scope: %s", tenantID)
|
||||
}
|
||||
|
||||
var existing *domain.User
|
||||
if externalKey := strings.TrimSpace(remote.ExternalID); externalKey != "" {
|
||||
if user, err := s.userRepo.FindByID(ctx, externalKey); err == nil {
|
||||
existing = user
|
||||
} else {
|
||||
return domain.User{}, false, false, fmt.Errorf("worksmobile external key does not match a Baron user: %s", externalKey)
|
||||
}
|
||||
} else if user, err := s.userRepo.FindByEmail(ctx, email); err == nil {
|
||||
existing = user
|
||||
}
|
||||
|
||||
if existing != nil {
|
||||
user := *existing
|
||||
applyWorksmobileRemoteToUser(&user, remote, tenant.ID)
|
||||
if err := s.updateImportedWorksmobileUserIdentity(ctx, user); err != nil {
|
||||
return domain.User{}, false, false, err
|
||||
}
|
||||
if err := s.userRepo.Update(ctx, &user); err != nil {
|
||||
return domain.User{}, false, false, err
|
||||
}
|
||||
updatedExternalKey := false
|
||||
if strings.TrimSpace(remote.ExternalID) == "" {
|
||||
if err := s.patchWorksmobileUserExternalKey(ctx, remote, user.ID); err != nil {
|
||||
return domain.User{}, false, false, err
|
||||
}
|
||||
updatedExternalKey = true
|
||||
}
|
||||
return user, false, updatedExternalKey, nil
|
||||
}
|
||||
|
||||
if strings.TrimSpace(remote.ExternalID) != "" {
|
||||
return domain.User{}, false, false, errors.New("creating Baron user from non-empty unmatched worksmobile external key is not supported")
|
||||
}
|
||||
if s.kratos == nil {
|
||||
return domain.User{}, false, false, errors.New("kratos admin service is required")
|
||||
}
|
||||
identityID, err := s.kratos.CreateUser(ctx, &domain.BrokerUser{
|
||||
Email: email,
|
||||
Name: strings.TrimSpace(remote.DisplayName),
|
||||
PhoneNumber: strings.TrimSpace(remote.CellPhone),
|
||||
Attributes: map[string]any{
|
||||
"tenant_id": tenant.ID,
|
||||
"role": domain.RoleUser,
|
||||
"status": domain.UserStatusActive,
|
||||
"grade": strings.TrimSpace(remote.LevelName),
|
||||
"jobTitle": strings.TrimSpace(remote.Task),
|
||||
},
|
||||
}, GenerateWorksmobileInitialPassword())
|
||||
if err != nil {
|
||||
return domain.User{}, false, false, err
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
user := domain.User{
|
||||
ID: identityID,
|
||||
Email: email,
|
||||
Name: strings.TrimSpace(remote.DisplayName),
|
||||
Phone: strings.TrimSpace(remote.CellPhone),
|
||||
Role: domain.RoleUser,
|
||||
Status: domain.UserStatusActive,
|
||||
TenantID: &tenant.ID,
|
||||
Grade: strings.TrimSpace(remote.LevelName),
|
||||
JobTitle: strings.TrimSpace(remote.Task),
|
||||
Metadata: worksmobileImportedUserMetadata(remote, tenant),
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
if err := s.userRepo.Update(ctx, &user); err != nil {
|
||||
return domain.User{}, false, false, err
|
||||
}
|
||||
if err := s.patchWorksmobileUserExternalKey(ctx, remote, user.ID); err != nil {
|
||||
return domain.User{}, false, false, err
|
||||
}
|
||||
return user, true, true, nil
|
||||
}
|
||||
|
||||
func (s *worksmobileSyncService) updateImportedWorksmobileUserIdentity(ctx context.Context, user domain.User) error {
|
||||
if s.identityWriter == nil {
|
||||
return nil
|
||||
}
|
||||
identity, err := s.identityWriter.GetIdentity(ctx, user.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
traits := map[string]any{}
|
||||
for key, value := range identity.Traits {
|
||||
traits[key] = value
|
||||
}
|
||||
traits["email"] = user.Email
|
||||
traits["name"] = user.Name
|
||||
if phone := strings.TrimSpace(user.Phone); phone != "" {
|
||||
traits["phone_number"] = phone
|
||||
}
|
||||
traits["tenant_id"] = strings.TrimSpace(stringPtrValue(user.TenantID))
|
||||
traits["role"] = user.Role
|
||||
traits["status"] = user.Status
|
||||
traits["grade"] = user.Grade
|
||||
traits["jobTitle"] = user.JobTitle
|
||||
_, err = s.identityWriter.UpdateIdentity(ctx, IdentityUpdateRequest{
|
||||
IdentityID: user.ID,
|
||||
Traits: traits,
|
||||
State: strings.TrimSpace(identity.State),
|
||||
Reason: "worksmobile_import_from_works",
|
||||
Source: "admin_worksmobile",
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *worksmobileSyncService) patchWorksmobileUserExternalKey(ctx context.Context, remote WorksmobileRemoteUser, userID string) error {
|
||||
return s.client.UpdateUserOnly(ctx, WorksmobileUserPayload{
|
||||
DomainID: remote.DomainID,
|
||||
Email: strings.TrimSpace(remote.Email),
|
||||
UserExternalKey: strings.TrimSpace(userID),
|
||||
CellPhone: strings.TrimSpace(remote.CellPhone),
|
||||
EmployeeNumber: strings.TrimSpace(remote.EmployeeNumber),
|
||||
Locale: "ko_KR",
|
||||
Task: strings.TrimSpace(remote.Task),
|
||||
})
|
||||
}
|
||||
|
||||
func applyWorksmobileRemoteToUser(user *domain.User, remote WorksmobileRemoteUser, tenantID string) {
|
||||
now := time.Now().UTC()
|
||||
user.Email = strings.ToLower(strings.TrimSpace(remote.Email))
|
||||
user.Name = strings.TrimSpace(remote.DisplayName)
|
||||
user.Phone = strings.TrimSpace(remote.CellPhone)
|
||||
user.Role = domain.NormalizeRole(user.Role)
|
||||
user.Status = domain.UserStatusActive
|
||||
user.TenantID = &tenantID
|
||||
user.Grade = strings.TrimSpace(remote.LevelName)
|
||||
user.JobTitle = strings.TrimSpace(remote.Task)
|
||||
user.Metadata = mergeWorksmobileImportedUserMetadata(user.Metadata, remote, tenantID)
|
||||
user.UpdatedAt = now
|
||||
}
|
||||
|
||||
func worksmobileImportedUserMetadata(remote WorksmobileRemoteUser, tenant domain.Tenant) domain.JSONMap {
|
||||
return mergeWorksmobileImportedUserMetadata(domain.JSONMap{}, remote, tenant.ID)
|
||||
}
|
||||
|
||||
func mergeWorksmobileImportedUserMetadata(metadata domain.JSONMap, remote WorksmobileRemoteUser, tenantID string) domain.JSONMap {
|
||||
if metadata == nil {
|
||||
metadata = domain.JSONMap{}
|
||||
}
|
||||
if value := strings.TrimSpace(remote.EmployeeNumber); value != "" {
|
||||
metadata["employeeNumber"] = value
|
||||
metadata["employee_id"] = value
|
||||
}
|
||||
if value := strings.TrimSpace(remote.LevelName); value != "" {
|
||||
metadata["grade"] = value
|
||||
}
|
||||
if value := strings.TrimSpace(remote.PrimaryOrgUnitName); value != "" {
|
||||
metadata["department"] = value
|
||||
}
|
||||
metadata["worksmobileImportedAt"] = time.Now().UTC().Format(time.RFC3339Nano)
|
||||
metadata["worksmobileId"] = strings.TrimSpace(remote.ID)
|
||||
metadata["worksmobileDomainId"] = remote.DomainID
|
||||
metadata["worksmobilePrimaryOrgUnitId"] = strings.TrimSpace(remote.PrimaryOrgUnitID)
|
||||
metadata["additionalAppointments"] = []domain.JSONMap{{
|
||||
"tenantId": tenantID,
|
||||
"isPrimary": true,
|
||||
"grade": strings.TrimSpace(remote.LevelName),
|
||||
}}
|
||||
return metadata
|
||||
}
|
||||
|
||||
func worksmobileTenantIDForRemoteUser(remote WorksmobileRemoteUser, groupByID map[string]WorksmobileRemoteGroup) string {
|
||||
primaryOrgUnitID := strings.TrimSpace(remote.PrimaryOrgUnitID)
|
||||
if tenantID, ok := strings.CutPrefix(primaryOrgUnitID, "externalKey:"); ok {
|
||||
return strings.TrimSpace(tenantID)
|
||||
}
|
||||
if group, ok := groupByID[primaryOrgUnitID]; ok {
|
||||
return strings.TrimSpace(group.ExternalID)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func stringPtrValue(value *string) string {
|
||||
if value == nil {
|
||||
return ""
|
||||
}
|
||||
return *value
|
||||
}
|
||||
|
||||
func (s *worksmobileSyncService) comparisonUsers(ctx context.Context, tenantIDs []string, tenantByID map[string]domain.Tenant) ([]domain.User, error) {
|
||||
if s.identityMirror != nil {
|
||||
status, err := s.identityMirror.GetIdentityCacheStatus(ctx)
|
||||
@@ -586,8 +898,9 @@ func (s *worksmobileSyncService) EnqueueBackfillDryRun(ctx context.Context, tena
|
||||
Action: domain.WorksmobileActionDryRun,
|
||||
DedupeKey: "backfill:dry-run:" + root.ID,
|
||||
Payload: domain.JSONMap{
|
||||
"tenantIds": orgUnitTenantIDs,
|
||||
"userCount": len(users),
|
||||
"tenantRootId": root.ID,
|
||||
"tenantIds": orgUnitTenantIDs,
|
||||
"userCount": len(users),
|
||||
},
|
||||
})
|
||||
return WorksmobileBackfillDryRun{OrgUnitCount: len(orgUnitTenantIDs), UserCount: len(users)}, nil
|
||||
@@ -604,10 +917,17 @@ func (s *worksmobileSyncService) EnqueueOrgUnitSync(ctx context.Context, tenantI
|
||||
}
|
||||
tenantRoot, ok, err := s.rootForTenant(ctx, *tenant)
|
||||
if err != nil {
|
||||
if recordErr := s.recordRejectedOrgUnitSync(ctx, root.ID, *tenant, err); recordErr != nil {
|
||||
return nil, errors.Join(err, recordErr)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if !ok || tenantRoot.ID != root.ID {
|
||||
return nil, errors.New("target orgunit is outside hanmac-family subtree")
|
||||
err := errors.New("target orgunit is outside hanmac-family subtree")
|
||||
if recordErr := s.recordRejectedOrgUnitSync(ctx, root.ID, *tenant, err); recordErr != nil {
|
||||
return nil, errors.Join(err, recordErr)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
scopeTenants, err := s.hanmacSubtree(ctx, root.ID)
|
||||
if err != nil {
|
||||
@@ -615,10 +935,18 @@ func (s *worksmobileSyncService) EnqueueOrgUnitSync(ctx context.Context, tenantI
|
||||
}
|
||||
tenantByID := worksmobileTenantByID(append([]domain.Tenant{*root}, scopeTenants...))
|
||||
if _, ok := tenantByID[tenant.ID]; !ok {
|
||||
return nil, errors.New("target tenant is excluded from Worksmobile sync")
|
||||
err := errors.New("target tenant is excluded from Worksmobile sync")
|
||||
if recordErr := s.recordRejectedOrgUnitSync(ctx, root.ID, *tenant, err); recordErr != nil {
|
||||
return nil, errors.Join(err, recordErr)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if !isWorksmobileOrgUnitTenant(*tenant, tenantByID) {
|
||||
return nil, errors.New("target tenant is not a worksmobile orgunit tenant")
|
||||
err := errors.New("target tenant is not a worksmobile orgunit tenant")
|
||||
if recordErr := s.recordRejectedOrgUnitSync(ctx, root.ID, *tenant, err); recordErr != nil {
|
||||
return nil, errors.Join(err, recordErr)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return s.enqueueOrgUnitUpsert(ctx, root, *tenant, scopeTenants)
|
||||
}
|
||||
@@ -632,6 +960,9 @@ func (s *worksmobileSyncService) enqueueOrgUnitUpsert(ctx context.Context, root
|
||||
0,
|
||||
)
|
||||
if err != nil {
|
||||
if recordErr := s.recordRejectedOrgUnitSync(ctx, root.ID, tenant, err); recordErr != nil {
|
||||
return nil, errors.Join(err, recordErr)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
payload = normalizeWorksmobileOrgUnitParent(payload, tenant, tenantByID, root.ID)
|
||||
@@ -641,6 +972,7 @@ func (s *worksmobileSyncService) enqueueOrgUnitUpsert(ctx context.Context, root
|
||||
Action: domain.WorksmobileActionUpsert,
|
||||
DedupeKey: "orgunit:upsert:" + tenant.ID,
|
||||
Payload: domain.JSONMap{
|
||||
"tenantRootId": root.ID,
|
||||
"request": payload,
|
||||
"matchLocalPart": tenant.Slug,
|
||||
},
|
||||
@@ -651,6 +983,36 @@ func (s *worksmobileSyncService) enqueueOrgUnitUpsert(ctx context.Context, root
|
||||
return item, nil
|
||||
}
|
||||
|
||||
func (s *worksmobileSyncService) recordRejectedOrgUnitSync(ctx context.Context, rootID string, tenant domain.Tenant, reason error) error {
|
||||
if s.outboxRepo == nil {
|
||||
return nil
|
||||
}
|
||||
payload := domain.JSONMap{
|
||||
"tenantRootId": rootID,
|
||||
"displayName": strings.TrimSpace(tenant.Name),
|
||||
"matchLocalPart": strings.TrimSpace(tenant.Slug),
|
||||
"tenantSlug": strings.TrimSpace(tenant.Slug),
|
||||
"requestSummary": domain.JSONMap{
|
||||
"orgUnitName": strings.TrimSpace(tenant.Name),
|
||||
"orgUnitExternalKey": tenant.ID,
|
||||
"tenantSlug": strings.TrimSpace(tenant.Slug),
|
||||
},
|
||||
}
|
||||
if tenant.ParentID != nil {
|
||||
payload["parentTenantId"] = strings.TrimSpace(*tenant.ParentID)
|
||||
}
|
||||
item := &domain.WorksmobileOutbox{
|
||||
ResourceType: domain.WorksmobileResourceOrgUnit,
|
||||
ResourceID: tenant.ID,
|
||||
Action: domain.WorksmobileActionUpsert,
|
||||
DedupeKey: "orgunit:rejected:" + tenant.ID + ":" + uuid.NewString(),
|
||||
Payload: payload,
|
||||
Status: domain.WorksmobileOutboxStatusFailed,
|
||||
LastError: reason.Error(),
|
||||
}
|
||||
return s.outboxRepo.Create(ctx, item)
|
||||
}
|
||||
|
||||
func (s *worksmobileSyncService) EnqueueOrgUnitDelete(ctx context.Context, tenantID, worksmobileOrgUnitID string) (*domain.WorksmobileOutbox, error) {
|
||||
root, err := s.hanmacRoot(ctx, tenantID)
|
||||
if err != nil {
|
||||
@@ -692,6 +1054,7 @@ func (s *worksmobileSyncService) EnqueueOrgUnitDelete(ctx context.Context, tenan
|
||||
Action: domain.WorksmobileActionDelete,
|
||||
DedupeKey: "orgunit:delete:works:" + worksmobileOrgUnitID,
|
||||
Payload: domain.JSONMap{
|
||||
"tenantRootId": root.ID,
|
||||
"worksmobileId": worksmobileOrgUnitID,
|
||||
"externalKey": target.ExternalID,
|
||||
"domainId": target.DomainID,
|
||||
@@ -756,7 +1119,7 @@ func (s *worksmobileSyncService) EnqueueUserSync(ctx context.Context, tenantID,
|
||||
return nil, err
|
||||
}
|
||||
action := WorksmobileUserStatusAction(user.Status)
|
||||
if action == domain.WorksmobileActionUpsert {
|
||||
if action == domain.WorksmobileActionUpsert && strings.TrimSpace(initialPassword) != "" {
|
||||
payload.PasswordConfig = worksmobileAdminInitialPasswordConfig(initialPassword)
|
||||
}
|
||||
item := &domain.WorksmobileOutbox{
|
||||
@@ -768,7 +1131,7 @@ func (s *worksmobileSyncService) EnqueueUserSync(ctx context.Context, tenantID,
|
||||
}
|
||||
item.Payload["displayName"] = strings.TrimSpace(user.Name)
|
||||
item.Payload["primaryLeafOrgName"] = worksmobileUserPrimaryOrgName(*user, tenantByID)
|
||||
if batchID := strings.TrimSpace(credentialBatchID); batchID != "" {
|
||||
if batchID := strings.TrimSpace(credentialBatchID); batchID != "" && strings.TrimSpace(payload.PasswordConfig.Password) != "" {
|
||||
item.Payload["credentialBatchId"] = batchID
|
||||
item.Payload["credentialOperation"] = "worksmobile_user_sync"
|
||||
item.Payload["credentialBatchCreatedAt"] = time.Now().UTC().Format(time.RFC3339Nano)
|
||||
@@ -783,7 +1146,7 @@ func (s *worksmobileSyncService) recordRejectedUserSync(ctx context.Context, roo
|
||||
payload := WorksmobileUserPayload{
|
||||
Email: strings.TrimSpace(user.Email),
|
||||
UserExternalKey: user.ID,
|
||||
UserName: WorksmobileUserName{LastName: strings.TrimSpace(user.Name)},
|
||||
UserName: worksmobileUserNameFromDisplayName(user.Name),
|
||||
CellPhone: domain.NormalizePhoneNumber(user.Phone),
|
||||
EmployeeNumber: metadataEmployeeNumber(user.Metadata),
|
||||
Locale: "ko_KR",
|
||||
|
||||
Reference in New Issue
Block a user