Update Google SCIM bridge
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
@@ -104,17 +104,20 @@ func (s *Bridge) Run(ctx context.Context) (created, updated, deleted, deactivate
|
||||
}
|
||||
created++
|
||||
} else {
|
||||
needsUpdate := s.forceUpdate
|
||||
|
||||
if existingSCIM.Active != pu.Active {
|
||||
needsUpdate = true
|
||||
}
|
||||
if existingSCIM.DisplayName != pu.DisplayName {
|
||||
needsUpdate = true
|
||||
}
|
||||
if existingSCIM.Title != pu.Title {
|
||||
needsUpdate = true
|
||||
}
|
||||
needsUpdate := s.forceUpdate ||
|
||||
existingSCIM.Active != pu.Active ||
|
||||
existingSCIM.DisplayName != pu.DisplayName ||
|
||||
existingSCIM.Title != pu.Title ||
|
||||
existingSCIM.GivenName != pu.GivenName ||
|
||||
existingSCIM.FamilyName != pu.FamilyName ||
|
||||
existingSCIM.ExternalID != pu.ExternalID ||
|
||||
existingSCIM.Department != pu.Department ||
|
||||
existingSCIM.CostCenter != pu.CostCenter ||
|
||||
existingSCIM.EnterpriseOrganization != pu.EnterpriseOrganization ||
|
||||
existingSCIM.Division != pu.Division ||
|
||||
existingSCIM.EmployeeNumber != pu.EmployeeNumber ||
|
||||
existingSCIM.ManagerValue != pu.ManagerValue ||
|
||||
existingSCIM.PreferredLanguage != pu.PreferredLanguage
|
||||
|
||||
if needsUpdate {
|
||||
if !s.dryRun {
|
||||
|
||||
@@ -33,13 +33,21 @@ type (
|
||||
}
|
||||
|
||||
User struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
UserName string `json:"userName"`
|
||||
DisplayName string `json:"displayName"`
|
||||
GivenName string `json:"-"`
|
||||
FamilyName string `json:"-"`
|
||||
Active bool `json:"active"`
|
||||
Title string `json:"title"`
|
||||
ID string `json:"id,omitempty"`
|
||||
UserName string `json:"userName"`
|
||||
DisplayName string `json:"displayName"`
|
||||
GivenName string `json:"-"`
|
||||
FamilyName string `json:"-"`
|
||||
Active bool `json:"active"`
|
||||
Title string `json:"title"`
|
||||
ExternalID string `json:"-"`
|
||||
Department string `json:"-"`
|
||||
CostCenter string `json:"-"`
|
||||
EnterpriseOrganization string `json:"-"`
|
||||
Division string `json:"-"`
|
||||
EmployeeNumber string `json:"-"`
|
||||
ManagerValue string `json:"-"`
|
||||
PreferredLanguage string `json:"-"`
|
||||
}
|
||||
|
||||
Users []User
|
||||
@@ -114,25 +122,7 @@ func (c *Client) listUsersPage(ctx context.Context, startIndex, count int) (User
|
||||
}
|
||||
|
||||
func (c *Client) CreateUser(ctx context.Context, user *User) error {
|
||||
payload := map[string]any{
|
||||
"schemas": []string{"urn:ietf:params:scim:schemas:core:2.0:User"},
|
||||
"userName": user.UserName,
|
||||
"name": map[string]string{
|
||||
"givenName": user.GivenName,
|
||||
"familyName": user.FamilyName,
|
||||
"formatted": user.DisplayName,
|
||||
},
|
||||
"displayName": user.DisplayName,
|
||||
"active": user.Active,
|
||||
"emails": []map[string]any{
|
||||
{
|
||||
"value": user.UserName,
|
||||
"type": "work",
|
||||
"primary": true,
|
||||
},
|
||||
},
|
||||
"title": user.Title,
|
||||
}
|
||||
payload := buildUserPayload(user)
|
||||
|
||||
body, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
@@ -163,25 +153,7 @@ func (c *Client) CreateUser(ctx context.Context, user *User) error {
|
||||
}
|
||||
|
||||
func (c *Client) UpdateUser(ctx context.Context, userID string, user *User) error {
|
||||
payload := map[string]any{
|
||||
"schemas": []string{"urn:ietf:params:scim:schemas:core:2.0:User"},
|
||||
"userName": user.UserName,
|
||||
"name": map[string]string{
|
||||
"givenName": user.GivenName,
|
||||
"familyName": user.FamilyName,
|
||||
"formatted": user.DisplayName,
|
||||
},
|
||||
"displayName": user.DisplayName,
|
||||
"active": user.Active,
|
||||
"emails": []map[string]any{
|
||||
{
|
||||
"value": user.UserName,
|
||||
"type": "work",
|
||||
"primary": true,
|
||||
},
|
||||
},
|
||||
"title": user.Title,
|
||||
}
|
||||
payload := buildUserPayload(user)
|
||||
|
||||
body, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
@@ -211,6 +183,65 @@ func (c *Client) UpdateUser(ctx context.Context, userID string, user *User) erro
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildUserPayload(user *User) map[string]any {
|
||||
schemas := []string{"urn:ietf:params:scim:schemas:core:2.0:User"}
|
||||
|
||||
enterprise := map[string]any{}
|
||||
if user.EmployeeNumber != "" {
|
||||
enterprise["employeeNumber"] = user.EmployeeNumber
|
||||
}
|
||||
if user.Department != "" {
|
||||
enterprise["department"] = user.Department
|
||||
}
|
||||
if user.CostCenter != "" {
|
||||
enterprise["costCenter"] = user.CostCenter
|
||||
}
|
||||
if user.EnterpriseOrganization != "" {
|
||||
enterprise["organization"] = user.EnterpriseOrganization
|
||||
}
|
||||
if user.Division != "" {
|
||||
enterprise["division"] = user.Division
|
||||
}
|
||||
if user.ManagerValue != "" {
|
||||
enterprise["manager"] = map[string]string{"value": user.ManagerValue}
|
||||
}
|
||||
|
||||
payload := map[string]any{
|
||||
"schemas": schemas,
|
||||
"userName": user.UserName,
|
||||
"name": map[string]string{
|
||||
"givenName": user.GivenName,
|
||||
"familyName": user.FamilyName,
|
||||
"formatted": user.DisplayName,
|
||||
},
|
||||
"displayName": user.DisplayName,
|
||||
"active": user.Active,
|
||||
"emails": []map[string]any{
|
||||
{
|
||||
"value": user.UserName,
|
||||
"type": "work",
|
||||
"primary": true,
|
||||
},
|
||||
},
|
||||
"title": user.Title,
|
||||
}
|
||||
|
||||
if user.ExternalID != "" {
|
||||
payload["externalId"] = user.ExternalID
|
||||
}
|
||||
if user.PreferredLanguage != "" {
|
||||
payload["preferredLanguage"] = user.PreferredLanguage
|
||||
}
|
||||
|
||||
if len(enterprise) > 0 {
|
||||
schemas = append(schemas, "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User")
|
||||
payload["schemas"] = schemas
|
||||
payload["urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"] = enterprise
|
||||
}
|
||||
|
||||
return payload
|
||||
}
|
||||
|
||||
func (c *Client) DeactivateUser(ctx context.Context, userID string) error {
|
||||
payload := map[string]any{
|
||||
"schemas": []string{"urn:ietf:params:scim:api:messages:2.0:PatchOp"},
|
||||
|
||||
@@ -83,47 +83,20 @@ func (p *Provider) ListUsers(ctx context.Context) (scimclient.Users, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
var title string
|
||||
|
||||
if u.Organizations == nil {
|
||||
title = ""
|
||||
user := scimclient.User{
|
||||
UserName: u.PrimaryEmail,
|
||||
DisplayName: u.Name.FullName,
|
||||
GivenName: u.Name.GivenName,
|
||||
FamilyName: u.Name.FamilyName,
|
||||
Active: !u.Suspended && !u.Archived,
|
||||
}
|
||||
|
||||
data, err := json.Marshal(u.Organizations)
|
||||
if err != nil {
|
||||
title = ""
|
||||
}
|
||||
p.extractOrganizationFields(u.Organizations, &user)
|
||||
p.extractExternalID(u.ExternalIds, &user)
|
||||
p.extractRelations(u.Relations, &user)
|
||||
p.extractPreferredLanguage(u.Languages, &user)
|
||||
|
||||
var orgs []admin.UserOrganization
|
||||
if err := json.Unmarshal(data, &orgs); err != nil {
|
||||
title = ""
|
||||
}
|
||||
|
||||
// Prefer the primary organization's title
|
||||
for _, org := range orgs {
|
||||
if org.Primary && org.Title != "" {
|
||||
title = org.Title
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to the first organization with a title
|
||||
for _, org := range orgs {
|
||||
if org.Title != "" {
|
||||
title = org.Title
|
||||
}
|
||||
}
|
||||
|
||||
allUsers = append(
|
||||
allUsers,
|
||||
scimclient.User{
|
||||
UserName: u.PrimaryEmail,
|
||||
DisplayName: u.Name.FullName,
|
||||
GivenName: u.Name.GivenName,
|
||||
FamilyName: u.Name.FamilyName,
|
||||
Active: !u.Suspended && !u.Archived,
|
||||
Title: title,
|
||||
},
|
||||
)
|
||||
allUsers = append(allUsers, user)
|
||||
}
|
||||
|
||||
pageToken = resp.NextPageToken
|
||||
@@ -134,3 +107,122 @@ func (p *Provider) ListUsers(ctx context.Context) (scimclient.Users, error) {
|
||||
|
||||
return allUsers, nil
|
||||
}
|
||||
|
||||
func (p *Provider) extractOrganizationFields(raw interface{}, user *scimclient.User) {
|
||||
if raw == nil {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := json.Marshal(raw)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var orgs []admin.UserOrganization
|
||||
if err := json.Unmarshal(data, &orgs); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var primary *admin.UserOrganization
|
||||
var first *admin.UserOrganization
|
||||
for i := range orgs {
|
||||
if first == nil {
|
||||
first = &orgs[i]
|
||||
}
|
||||
if orgs[i].Primary {
|
||||
primary = &orgs[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
org := primary
|
||||
if org == nil {
|
||||
org = first
|
||||
}
|
||||
if org == nil {
|
||||
return
|
||||
}
|
||||
|
||||
user.Title = org.Title
|
||||
user.Department = org.Department
|
||||
user.CostCenter = org.CostCenter
|
||||
user.EnterpriseOrganization = org.Name
|
||||
user.Division = org.Description
|
||||
}
|
||||
|
||||
func (p *Provider) extractExternalID(raw interface{}, user *scimclient.User) {
|
||||
if raw == nil {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := json.Marshal(raw)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var ids []admin.UserExternalId
|
||||
if err := json.Unmarshal(data, &ids); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, id := range ids {
|
||||
if id.Type == "organization" && id.Value != "" {
|
||||
user.ExternalID = id.Value
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(ids) > 0 && ids[0].Value != "" {
|
||||
user.ExternalID = ids[0].Value
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Provider) extractRelations(raw interface{}, user *scimclient.User) {
|
||||
if raw == nil {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := json.Marshal(raw)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var relations []admin.UserRelation
|
||||
if err := json.Unmarshal(data, &relations); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, rel := range relations {
|
||||
if rel.Type == "manager" && rel.Value != "" {
|
||||
user.ManagerValue = rel.Value
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Provider) extractPreferredLanguage(raw interface{}, user *scimclient.User) {
|
||||
if raw == nil {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := json.Marshal(raw)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var languages []admin.UserLanguage
|
||||
if err := json.Unmarshal(data, &languages); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, lang := range languages {
|
||||
if lang.Preference == "preferred" && lang.LanguageCode != "" {
|
||||
user.PreferredLanguage = lang.LanguageCode
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(languages) > 0 && languages[0].LanguageCode != "" {
|
||||
user.PreferredLanguage = languages[0].LanguageCode
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user