1
0
forked from baron/baron-sso

chore: consolidate local integration changes

This commit is contained in:
2026-06-09 21:03:05 +09:00
parent aa2848c3b6
commit 1341f07ef9
158 changed files with 10995 additions and 1490 deletions

View File

@@ -3,6 +3,7 @@ package service
import (
"baron-sso-backend/internal/domain"
"crypto/rand"
"encoding/json"
"errors"
"fmt"
"math/big"
@@ -30,14 +31,14 @@ type WorksmobileOrgUnitPayload struct {
type WorksmobileUserPayload struct {
DomainID int64 `json:"domainId"`
Email string `json:"email"`
UserExternalKey string `json:"userExternalKey"`
UserExternalKey string `json:"userExternalKey,omitempty"`
UserName WorksmobileUserName `json:"userName"`
CellPhone string `json:"cellPhone,omitempty"`
EmployeeNumber string `json:"employeeNumber,omitempty"`
PrivateEmail string `json:"privateEmail,omitempty"`
AliasEmails []string `json:"aliasEmails,omitempty"`
Locale string `json:"locale,omitempty"`
PasswordConfig WorksmobilePasswordConfig `json:"passwordConfig"`
PasswordConfig WorksmobilePasswordConfig `json:"passwordConfig,omitempty"`
Task string `json:"task,omitempty"`
Organizations []WorksmobileUserOrganization `json:"organizations,omitempty"`
}
@@ -47,8 +48,52 @@ type WorksmobileUserName struct {
}
type WorksmobilePasswordConfig struct {
PasswordCreationType string `json:"passwordCreationType"`
Password string `json:"password"`
PasswordCreationType string `json:"passwordCreationType"`
Password string `json:"password"`
ChangePasswordAtNextLogin *bool `json:"changePasswordAtNextLogin,omitempty"`
}
func (c WorksmobilePasswordConfig) IsZero() bool {
return strings.TrimSpace(c.PasswordCreationType) == "" &&
strings.TrimSpace(c.Password) == "" &&
c.ChangePasswordAtNextLogin == nil
}
func (p WorksmobileUserPayload) MarshalJSON() ([]byte, error) {
type payloadJSON struct {
DomainID int64 `json:"domainId"`
Email string `json:"email"`
UserExternalKey string `json:"userExternalKey,omitempty"`
UserName WorksmobileUserName `json:"userName"`
CellPhone string `json:"cellPhone,omitempty"`
EmployeeNumber string `json:"employeeNumber,omitempty"`
PrivateEmail string `json:"privateEmail,omitempty"`
AliasEmails []string `json:"aliasEmails,omitempty"`
Locale string `json:"locale,omitempty"`
PasswordConfig *WorksmobilePasswordConfig `json:"passwordConfig,omitempty"`
Task string `json:"task,omitempty"`
Organizations []WorksmobileUserOrganization `json:"organizations,omitempty"`
}
var passwordConfig *WorksmobilePasswordConfig
if !p.PasswordConfig.IsZero() {
passwordConfig = &p.PasswordConfig
}
return json.Marshal(payloadJSON{
DomainID: p.DomainID,
Email: p.Email,
UserExternalKey: p.UserExternalKey,
UserName: p.UserName,
CellPhone: p.CellPhone,
EmployeeNumber: p.EmployeeNumber,
PrivateEmail: p.PrivateEmail,
AliasEmails: p.AliasEmails,
Locale: p.Locale,
PasswordConfig: passwordConfig,
Task: p.Task,
Organizations: p.Organizations,
})
}
type WorksmobilePasswordResetPayload struct {
@@ -184,15 +229,11 @@ func BuildWorksmobileUserPayloadForDomainTenants(user domain.User, tenant domain
Email: strings.TrimSpace(user.Email),
UserExternalKey: user.ID,
UserName: WorksmobileUserName{LastName: strings.TrimSpace(user.Name)},
CellPhone: strings.TrimSpace(user.Phone),
CellPhone: domain.NormalizePhoneNumber(user.Phone),
EmployeeNumber: employeeNumber,
Locale: "ko_KR",
PasswordConfig: WorksmobilePasswordConfig{
PasswordCreationType: "ADMIN",
Password: GenerateWorksmobileInitialPassword(),
},
Task: task,
Organizations: organizations,
Task: task,
Organizations: organizations,
}
payload.AliasEmails = BuildWorksmobileAliasEmails(user, tenant)
return payload, nil
@@ -205,12 +246,20 @@ type worksmobileAppointment struct {
HasManager bool
JobTitle string
PositionID string
Source string
}
func buildWorksmobileUserOrganizations(user domain.User, tenant domain.Tenant, tenantByID map[string]domain.Tenant, rootConfig domain.JSONMap) ([]WorksmobileUserOrganization, string, error) {
appointments := worksmobileAppointmentsFromMetadata(user.Metadata)
if len(appointments) == 0 {
appointments = []worksmobileAppointment{{TenantID: tenant.ID, IsPrimary: true}}
} else if !worksmobileAppointmentsContainTenant(appointments, tenant.ID) && !worksmobileAppointmentsHavePrimary(appointments) {
appointments = append([]worksmobileAppointment{{
TenantID: tenant.ID,
IsPrimary: true,
JobTitle: strings.TrimSpace(user.JobTitle),
PositionID: metadataString(user.Metadata, "worksmobilePositionId", "positionId", "position_id"),
}}, appointments...)
}
accountDomainTenant := worksmobileAccountDomainTenantFromEmail(user.Email, tenant, tenantByID)
accountDomainEnvKey := worksmobileTenantDomainIDEnvKey(accountDomainTenant)
@@ -235,6 +284,17 @@ func buildWorksmobileUserOrganizations(user domain.User, tenant domain.Tenant, t
if !ok {
continue
}
if worksmobileShouldSkipEmailDomainRootAppointment(appointment, appointmentTenant, appointments, tenantByID) {
seen[appointment.TenantID] = true
continue
}
if isWorksmobileDomainRootTenant(appointmentTenant) {
if appointment.IsPrimary && strings.TrimSpace(appointment.JobTitle) != "" && task == "" {
task = strings.TrimSpace(appointment.JobTitle)
}
seen[appointment.TenantID] = true
continue
}
if err := ValidateWorksmobileExternalKey(appointmentTenant.ID); err != nil {
return nil, "", err
}
@@ -276,7 +336,7 @@ func buildWorksmobileUserOrganizations(user domain.User, tenant domain.Tenant, t
seen[appointment.TenantID] = true
}
if len(organizations) == 0 {
return nil, "", errors.New("no valid worksmobile organization")
return nil, task, nil
}
if !worksmobileOrganizationsHavePrimary(organizations) {
organizations[0].Primary = true
@@ -288,6 +348,28 @@ func buildWorksmobileUserOrganizations(user domain.User, tenant domain.Tenant, t
return organizations, task, nil
}
func worksmobileAppointmentsContainTenant(appointments []worksmobileAppointment, tenantID string) bool {
tenantID = strings.TrimSpace(tenantID)
if tenantID == "" {
return false
}
for _, appointment := range appointments {
if strings.TrimSpace(appointment.TenantID) == tenantID {
return true
}
}
return false
}
func worksmobileAppointmentsHavePrimary(appointments []worksmobileAppointment) bool {
for _, appointment := range appointments {
if appointment.IsPrimary {
return true
}
}
return false
}
func worksmobileAppointmentsContainDomain(appointments []worksmobileAppointment, tenantByID map[string]domain.Tenant, envKey string) bool {
for _, appointment := range appointments {
tenant, ok := tenantByID[appointment.TenantID]
@@ -302,6 +384,26 @@ func worksmobileAppointmentsContainDomain(appointments []worksmobileAppointment,
return false
}
func worksmobileShouldSkipEmailDomainRootAppointment(appointment worksmobileAppointment, tenant domain.Tenant, appointments []worksmobileAppointment, tenantByID map[string]domain.Tenant) bool {
if strings.TrimSpace(appointment.Source) != "email_domain" || !isWorksmobileDomainRootTenant(tenant) {
return false
}
envKey := worksmobileTenantDomainIDEnvKey(tenant)
for _, candidate := range appointments {
if strings.TrimSpace(candidate.TenantID) == "" || strings.TrimSpace(candidate.TenantID) == tenant.ID {
continue
}
candidateTenant, ok := tenantByID[candidate.TenantID]
if !ok || isWorksmobileDomainRootTenant(candidateTenant) {
continue
}
if worksmobileTenantDomainIDEnvKey(worksmobileDomainClassificationTenant(candidateTenant, tenantByID)) == envKey {
return true
}
}
return false
}
func worksmobileOrganizationsHavePrimary(organizations []WorksmobileUserOrganization) bool {
for _, organization := range organizations {
if organization.Primary {
@@ -327,6 +429,7 @@ func worksmobileAppointmentsFromMetadata(metadata domain.JSONMap) []worksmobileA
IsPrimary: metadataBool(domain.JSONMap(item), "isPrimary", "primary"),
JobTitle: metadataString(domain.JSONMap(item), "jobTitle", "job_title", "task"),
PositionID: metadataString(domain.JSONMap(item), "worksmobilePositionId", "positionId", "position_id"),
Source: metadataString(domain.JSONMap(item), "assignmentSource", "source"),
}
if isManager, ok := metadataOptionalBool(domain.JSONMap(item), "isManager", "lead", "isLead"); ok {
appointment.IsManager = isManager
@@ -416,7 +519,7 @@ func ValidateWorksmobileAliasEmails(primaryEmail string, aliasEmails []string, e
func GenerateWorksmobileInitialPassword() string {
digits := "0123456789"
letters := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
symbols := "!@#$%^&*()-_=+[]{}"
symbols := "!@#$%"
all := digits + letters + symbols
password := []byte{