Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 101 additions & 1 deletion pkg/connector/connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ type userFilterConfig struct {

type Config struct {
Domain string
ApiToken string //nolint:gosec // Not a credential
ApiToken string
OktaClientId string
OktaPrivateKey string
OktaPrivateKeyId string
Expand Down Expand Up @@ -325,6 +325,106 @@ func (c *Okta) Metadata(ctx context.Context) (*v2.ConnectorMetadata, error) {
Placeholder: "True/False",
Order: 5,
},
"create_in_staged_status": {
DisplayName: "Create in Staged Status",
Required: false,
Description: "When 'true', the user is created in STAGED status instead of being activated. Staged users can be activated later via Okta workflows.",
Field: &v2.ConnectorAccountCreationSchema_Field_StringField{
StringField: &v2.ConnectorAccountCreationSchema_StringField{},
},
Placeholder: "True/False",
Order: 6,
},
"department": {
DisplayName: "Department",
Required: false,
Description: "The department of the user.",
Field: &v2.ConnectorAccountCreationSchema_Field_StringField{
StringField: &v2.ConnectorAccountCreationSchema_StringField{},
},
Placeholder: "Department",
Order: 7,
},
"title": {
DisplayName: "Title",
Required: false,
Description: "The job title of the user.",
Field: &v2.ConnectorAccountCreationSchema_Field_StringField{
StringField: &v2.ConnectorAccountCreationSchema_StringField{},
},
Placeholder: "Title",
Order: 8,
},
"display_name": {
DisplayName: "Display Name",
Required: false,
Description: "The display name of the user. If not set, Okta will derive it from first and last name.",
Field: &v2.ConnectorAccountCreationSchema_Field_StringField{
StringField: &v2.ConnectorAccountCreationSchema_StringField{},
},
Placeholder: "Display Name",
Order: 9,
},
"user_type": {
DisplayName: "User Type",
Required: false,
Description: "The user type (e.g., 'Employee', 'Contractor').",
Field: &v2.ConnectorAccountCreationSchema_Field_StringField{
StringField: &v2.ConnectorAccountCreationSchema_StringField{},
},
Placeholder: "User Type",
Order: 10,
},
"organization": {
DisplayName: "Organization",
Required: false,
Description: "The organization of the user.",
Field: &v2.ConnectorAccountCreationSchema_Field_StringField{
StringField: &v2.ConnectorAccountCreationSchema_StringField{},
},
Placeholder: "Organization",
Order: 11,
},
"manager_id": {
DisplayName: "Manager ID",
Required: false,
Description: "The Okta user ID of the user's manager.",
Field: &v2.ConnectorAccountCreationSchema_Field_StringField{
StringField: &v2.ConnectorAccountCreationSchema_StringField{},
},
Placeholder: "Manager ID",
Order: 12,
},
"cost_center": {
DisplayName: "Cost Center",
Required: false,
Description: "The cost center of the user.",
Field: &v2.ConnectorAccountCreationSchema_Field_StringField{
StringField: &v2.ConnectorAccountCreationSchema_StringField{},
},
Placeholder: "Cost Center",
Order: 13,
},
"division": {
DisplayName: "Division",
Required: false,
Description: "The division of the user.",
Field: &v2.ConnectorAccountCreationSchema_Field_StringField{
StringField: &v2.ConnectorAccountCreationSchema_StringField{},
},
Placeholder: "Division",
Order: 14,
},
"employee_number": {
DisplayName: "Employee Number",
Required: false,
Description: "The employee number of the user.",
Field: &v2.ConnectorAccountCreationSchema_Field_StringField{
StringField: &v2.ConnectorAccountCreationSchema_StringField{},
},
Placeholder: "Employee Number",
Order: 15,
},
},
},
}, nil
Expand Down
73 changes: 59 additions & 14 deletions pkg/connector/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,19 @@ func getCredentialOption(credentialOptions *v2.LocalCredentialOptions) (*okta.Us
}, nil
}

// optionalProfileFields maps account creation schema field names to Okta profile attribute names.
var optionalProfileFields = map[string]string{
"department": "department",
"title": "title",
"display_name": "displayName",
"user_type": "userType",
"organization": "organization",
"manager_id": "managerId",
"cost_center": "costCenter",
"division": "division",
"employee_number": "employeeNumber",
}

func getUserProfile(accountInfo *v2.AccountInfo) (*okta.UserProfile, error) {
pMap := accountInfo.Profile.AsMap()
firstName, ok := pMap["first_name"]
Expand All @@ -563,33 +576,65 @@ func getUserProfile(accountInfo *v2.AccountInfo) (*okta.UserProfile, error) {
login = email
}

return &okta.UserProfile{
profile := &okta.UserProfile{
"firstName": firstName,
"lastName": lastName,
"email": email,
"login": login,
}, nil
}
}

func getAccountCreationQueryParams(accountInfo *v2.AccountInfo, credentialOptions *v2.LocalCredentialOptions) (*query.Params, error) {
if credentialOptions.GetNoPassword() != nil {
return nil, nil
for schemaField, oktaField := range optionalProfileFields {
if val, ok := pMap[schemaField]; ok {
if strVal, isStr := val.(string); isStr && strVal != "" {
(*profile)[oktaField] = strVal

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: okta.UserProfile is a map type (map[string]interface{}), so the pointer dereference is unnecessary when indexing. You can simplify this to profile[oktaField] = strVal.

Suggested change
(*profile)[oktaField] = strVal
profile[oktaField] = strVal

}
}
}

pMap := accountInfo.Profile.AsMap()
requirePass := pMap["password_change_on_login_required"]
requirePasswordChanged := false
switch v := requirePass.(type) {
return profile, nil
}

func parseBoolField(pMap map[string]interface{}, fieldName string) (bool, error) {
val := pMap[fieldName]
switch v := val.(type) {
case bool:
requirePasswordChanged = v
return v, nil
case string:
parsed, err := strconv.ParseBool(v)
if err != nil {
return nil, err
return false, err
}
requirePasswordChanged = parsed
return parsed, nil
case nil:
// Do nothing
return false, nil
default:
return false, fmt.Errorf("okta-connectorv2: unsupported type for %s: %T", fieldName, val)
}
}
Comment on lines +597 to +613

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: The parseBoolField refactor adds a default error case that didn't exist in the original inline switch (which silently treated unexpected types as false). This is a good improvement — explicitly erroring on unexpected types is safer than silent fallthrough. Just noting this is a minor behavioral change from the previous code.


func getAccountCreationQueryParams(accountInfo *v2.AccountInfo, credentialOptions *v2.LocalCredentialOptions) (*query.Params, error) {
pMap := accountInfo.Profile.AsMap()

createStaged, err := parseBoolField(pMap, "create_in_staged_status")
if err != nil {
return nil, err
}

// If creating in staged status, set Activate to false and return early.
// Staged users are not activated on creation regardless of credential options.
if createStaged {
return &query.Params{
Activate: ToPtr(false),
}, nil
}

if credentialOptions.GetNoPassword() != nil {
return nil, nil
}

requirePasswordChanged, err := parseBoolField(pMap, "password_change_on_login_required")
if err != nil {
return nil, err
}

params := &query.Params{}
Expand Down
Loading