diff --git a/README.md b/README.md index ff6c3658..e816757c 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,8 @@ The user can either `sign up`, `sign in` or `sign up or in` loginID := "desmond@descope.com" user := &descope.User{ Name: "Desmond Copeland", + GivenName: "Desmond", + FamilyName: "Copeland", Phone: "212-555-1234", Email: loginID, } @@ -286,6 +288,8 @@ Existing users can add TOTP using the `update` function. loginID := "desmond@descope.com" user := &descope.User{ Name: "Desmond Copeland", + GivenName: "Desmond", + FamilyName: "Copeland", Phone: "212-555-1234", Email: loginID, } @@ -328,6 +332,8 @@ loginID := "desmond@descope.com" password := "qYlvi65KaX" user := &descope.User{ Name: "Desmond Copeland", + GivenName: "Desmond", + FamilyName: "Copeland", Email: loginID, } authInfo, err := descopeClient.Auth.Password().SignUp(loginID, user, password, nil) @@ -596,6 +602,8 @@ You can create, update, delete or load users, as well as search according to fil userReq := &descope.UserRequest{} userReq.Email = "desmond@descope.com" userReq.Name = "Desmond Copeland" +userReq.GivenName = "Desmond" +userReq.FamilyName = "Copeland" userReq.Tenants = []*descope.AssociatedTenant{ {TenantID: "tenant-ID1", Roles: []string{"role-name1"}}, {TenantID: "tenant-ID2"}, diff --git a/descope/internal/mgmt/user.go b/descope/internal/mgmt/user.go index dd5799c4..9752e90c 100644 --- a/descope/internal/mgmt/user.go +++ b/descope/internal/mgmt/user.go @@ -16,14 +16,14 @@ func (u *user) Create(loginID string, user *descope.UserRequest) (*descope.UserR if user == nil { user = &descope.UserRequest{} } - return u.create(loginID, user.Email, user.Phone, user.Name, user.Picture, user.Roles, user.Tenants, false, false, user.CustomAttributes, user.VerifiedEmail, user.VerifiedPhone, user.AdditionalLoginIDs, nil) + return u.create(loginID, user.Email, user.Phone, user.Name, user.GivenName, user.MiddleName, user.FamilyName, user.Picture, user.Roles, user.Tenants, false, false, user.CustomAttributes, user.VerifiedEmail, user.VerifiedPhone, user.AdditionalLoginIDs, nil) } func (u *user) CreateTestUser(loginID string, user *descope.UserRequest) (*descope.UserResponse, error) { if user == nil { user = &descope.UserRequest{} } - return u.create(loginID, user.Email, user.Phone, user.Name, user.Picture, user.Roles, user.Tenants, false, true, user.CustomAttributes, user.VerifiedEmail, user.VerifiedPhone, user.AdditionalLoginIDs, nil) + return u.create(loginID, user.Email, user.Phone, user.Name, user.GivenName, user.MiddleName, user.FamilyName, user.Picture, user.Roles, user.Tenants, false, true, user.CustomAttributes, user.VerifiedEmail, user.VerifiedPhone, user.AdditionalLoginIDs, nil) } func (u *user) CreateBatch(users []*descope.BatchUser) (*descope.UsersBatchResponse, error) { @@ -37,7 +37,7 @@ func (u *user) Invite(loginID string, user *descope.UserRequest, options *descop if user == nil { user = &descope.UserRequest{} } - return u.create(loginID, user.Email, user.Phone, user.Name, user.Picture, user.Roles, user.Tenants, true, false, user.CustomAttributes, user.VerifiedEmail, user.VerifiedPhone, user.AdditionalLoginIDs, options) + return u.create(loginID, user.Email, user.Phone, user.Name, user.GivenName, user.MiddleName, user.FamilyName, user.Picture, user.Roles, user.Tenants, true, false, user.CustomAttributes, user.VerifiedEmail, user.VerifiedPhone, user.AdditionalLoginIDs, options) } func (u *user) InviteBatch(users []*descope.BatchUser, options *descope.InviteOptions) (*descope.UsersBatchResponse, error) { @@ -47,11 +47,11 @@ func (u *user) InviteBatch(users []*descope.BatchUser, options *descope.InviteOp return u.createBatch(users, options) } -func (u *user) create(loginID, email, phone, displayName, picture string, roles []string, tenants []*descope.AssociatedTenant, invite, test bool, customAttributes map[string]any, verifiedEmail *bool, verifiedPhone *bool, additionalLoginIDs []string, options *descope.InviteOptions) (*descope.UserResponse, error) { +func (u *user) create(loginID, email, phone, displayName, givenName, middleName, familyName, picture string, roles []string, tenants []*descope.AssociatedTenant, invite, test bool, customAttributes map[string]any, verifiedEmail *bool, verifiedPhone *bool, additionalLoginIDs []string, options *descope.InviteOptions) (*descope.UserResponse, error) { if loginID == "" { return nil, utils.NewInvalidArgumentError("loginID") } - req := makeCreateUserRequest(loginID, email, phone, displayName, picture, roles, tenants, invite, test, customAttributes, verifiedEmail, verifiedPhone, additionalLoginIDs, options) + req := makeCreateUserRequest(loginID, email, phone, displayName, givenName, middleName, familyName, picture, roles, tenants, invite, test, customAttributes, verifiedEmail, verifiedPhone, additionalLoginIDs, options) res, err := u.client.DoPostRequest(api.Routes.ManagementUserCreate(), req, nil, u.conf.ManagementKey) if err != nil { return nil, err @@ -75,7 +75,7 @@ func (u *user) Update(loginID string, user *descope.UserRequest) (*descope.UserR if user == nil { user = &descope.UserRequest{} } - req := makeUpdateUserRequest(loginID, user.Email, user.Phone, user.Name, user.Picture, user.Roles, user.Tenants, user.CustomAttributes, user.VerifiedEmail, user.VerifiedPhone, user.AdditionalLoginIDs) + req := makeUpdateUserRequest(loginID, user.Email, user.Phone, user.Name, user.GivenName, user.MiddleName, user.FamilyName, user.Picture, user.Roles, user.Tenants, user.CustomAttributes, user.VerifiedEmail, user.VerifiedPhone, user.AdditionalLoginIDs) res, err := u.client.DoPostRequest(api.Routes.ManagementUserUpdate(), req, nil, u.conf.ManagementKey) if err != nil { return nil, err @@ -235,6 +235,18 @@ func (u *user) UpdateDisplayName(loginID, displayName string) (*descope.UserResp return unmarshalUserResponse(res) } +func (u *user) UpdateUserNames(loginID, givenName, middleName, familyName string) (*descope.UserResponse, error) { + if loginID == "" { + return nil, utils.NewInvalidArgumentError("loginID") + } + req := map[string]any{"loginId": loginID, "givenName": givenName, "middleName": middleName, "familyName": familyName} + res, err := u.client.DoPostRequest(api.Routes.ManagementUserUpdateDisplayName(), req, nil, u.conf.ManagementKey) + if err != nil { + return nil, err + } + return unmarshalUserResponse(res) +} + func (u *user) UpdatePicture(loginID, picture string) (*descope.UserResponse, error) { if loginID == "" { return nil, utils.NewInvalidArgumentError("loginID") @@ -455,8 +467,8 @@ func (u *user) GenerateEmbeddedLink(loginID string, customClaims map[string]any) return tRes.Token, nil } -func makeCreateUserRequest(loginID, email, phone, displayName, picture string, roles []string, tenants []*descope.AssociatedTenant, invite, test bool, customAttributes map[string]any, verifiedEmail *bool, verifiedPhone *bool, additionalLoginIDs []string, options *descope.InviteOptions) map[string]any { - req := makeUpdateUserRequest(loginID, email, phone, displayName, picture, roles, tenants, customAttributes, verifiedEmail, verifiedPhone, additionalLoginIDs) +func makeCreateUserRequest(loginID, email, phone, displayName, givenName, middleName, familyName, picture string, roles []string, tenants []*descope.AssociatedTenant, invite, test bool, customAttributes map[string]any, verifiedEmail *bool, verifiedPhone *bool, additionalLoginIDs []string, options *descope.InviteOptions) map[string]any { + req := makeUpdateUserRequest(loginID, email, phone, displayName, givenName, middleName, familyName, picture, roles, tenants, customAttributes, verifiedEmail, verifiedPhone, additionalLoginIDs) req["invite"] = invite req["additionalLoginIds"] = additionalLoginIDs if test { @@ -479,7 +491,7 @@ func makeCreateUserRequest(loginID, email, phone, displayName, picture string, r func makeCreateUsersBatchRequest(users []*descope.BatchUser, options *descope.InviteOptions) map[string]any { var usersReq []map[string]any for _, u := range users { - user := makeUpdateUserRequest(u.LoginID, u.Email, u.Phone, u.Name, u.Picture, u.Roles, u.Tenants, u.CustomAttributes, u.VerifiedEmail, u.VerifiedPhone, u.AdditionalLoginIDs) + user := makeUpdateUserRequest(u.LoginID, u.Email, u.Phone, u.Name, u.GivenName, u.MiddleName, u.FamilyName, u.Picture, u.Roles, u.Tenants, u.CustomAttributes, u.VerifiedEmail, u.VerifiedPhone, u.AdditionalLoginIDs) if u.Password != nil { if cleartext := u.Password.Cleartext; cleartext != "" { user["password"] = u.Password.Cleartext @@ -519,12 +531,15 @@ func makeCreateUsersBatchRequest(users []*descope.BatchUser, options *descope.In return req } -func makeUpdateUserRequest(loginID, email, phone, displayName, picture string, roles []string, tenants []*descope.AssociatedTenant, customAttributes map[string]any, verifiedEmail *bool, verifiedPhone *bool, additionalLoginIDs []string) map[string]any { +func makeUpdateUserRequest(loginID, email, phone, displayName, givenName, middleName, familyName, picture string, roles []string, tenants []*descope.AssociatedTenant, customAttributes map[string]any, verifiedEmail *bool, verifiedPhone *bool, additionalLoginIDs []string) map[string]any { res := map[string]any{ "loginId": loginID, "email": email, "phone": phone, "displayName": displayName, + "givenName": givenName, + "middleName": middleName, + "familyName": familyName, "roleNames": roles, "userTenants": makeAssociatedTenantList(tenants), "customAttributes": customAttributes, diff --git a/descope/sdk/mgmt.go b/descope/sdk/mgmt.go index 98fbdbaf..31d9f511 100644 --- a/descope/sdk/mgmt.go +++ b/descope/sdk/mgmt.go @@ -181,6 +181,11 @@ type User interface { // The displayName parameter can be empty in which case the name will be removed. UpdateDisplayName(loginID, displayName string) (*descope.UserResponse, error) + // Update an existing user's first/last/middle name. + // + // An empty parameter, means that this value will be removed. + UpdateUserNames(loginID, givenName, middleName, familyName string) (*descope.UserResponse, error) + // Update an existing user's picture (i.e., url to the avatar). // // The picture parameter can be empty in which case the picture will be removed. diff --git a/descope/tests/mocks/mgmt/managementmock.go b/descope/tests/mocks/mgmt/managementmock.go index 8de8a6b7..1ddd0a78 100644 --- a/descope/tests/mocks/mgmt/managementmock.go +++ b/descope/tests/mocks/mgmt/managementmock.go @@ -207,6 +207,10 @@ type MockUser struct { UpdateDisplayNameResponse *descope.UserResponse UpdateDisplayNameError error + UpdateUserNamesAssert func(loginID, givenName, middleName, familyName string) + UpdateUserNamesResponse *descope.UserResponse + UpdateUserNamesError error + UpdatePictureAssert func(loginID, picture string) UpdatePictureResponse *descope.UserResponse UpdatePictureError error @@ -410,6 +414,13 @@ func (m *MockUser) UpdateDisplayName(loginID, displayName string) (*descope.User return m.UpdateDisplayNameResponse, m.UpdateDisplayNameError } +func (m *MockUser) UpdateUserNames(loginID, givenName, middleName, familyName string) (*descope.UserResponse, error) { + if m.UpdateUserNamesAssert != nil { + m.UpdateUserNamesAssert(loginID, givenName, middleName, familyName) + } + return m.UpdateUserNamesResponse, m.UpdateUserNamesError +} + func (m *MockUser) UpdatePicture(loginID, picture string) (*descope.UserResponse, error) { if m.UpdatePictureAssert != nil { m.UpdatePictureAssert(loginID, picture) diff --git a/descope/types.go b/descope/types.go index 894aa2e2..6d32e7f1 100644 --- a/descope/types.go +++ b/descope/types.go @@ -267,9 +267,12 @@ type InviteOptions struct { } type User struct { - Name string `json:"name,omitempty"` - Phone string `json:"phone,omitempty"` - Email string `json:"email,omitempty"` + Name string `json:"name,omitempty"` + GivenName string `json:"givenName,omitempty"` + MiddleName string `json:"middleName,omitempty"` + FamilyName string `json:"familyName,omitempty"` + Phone string `json:"phone,omitempty"` + Email string `json:"email,omitempty"` } type UserRequest struct { @@ -387,6 +390,10 @@ type RoleMapping struct { // Represents a mapping between Descope and IDP user attributes type AttributeMapping struct { Name string `json:"name,omitempty"` + GivenName string `json:"givenName,omitempty"` + MiddleName string `json:"middleName,omitempty"` + FamilyName string `json:"familyName,omitempty"` + Picture string `json:"picture,omitempty"` Email string `json:"email,omitempty"` PhoneNumber string `json:"phoneNumber,omitempty"` Group string `json:"group,omitempty"`