diff --git a/codefresh/cfclient/user.go b/codefresh/cfclient/user.go index 00846843..0a3c5936 100644 --- a/codefresh/cfclient/user.go +++ b/codefresh/cfclient/user.go @@ -23,6 +23,10 @@ type ShortProfile struct { UserName string `json:"userName,omitempty"` } +type PublicProfile struct { + HasPassword bool `json:"hasPassword,omitempty"` +} + type Personal struct { FirstName string `json:"firstName,omitempty"` LastName string `json:"lastName,omitempty"` @@ -44,6 +48,7 @@ type User struct { HasPassword bool `json:"hasPassword,omitempty"` Notifications []NotificationEvent `json:"notifications,omitempty"` ShortProfile ShortProfile `json:"shortProfile,omitempty"` + PublicProfile PublicProfile `json:"publicProfile,omitempty"` Logins []Login `json:"logins,omitempty"` InviteURL string `json:"inviteUrl,omitempty"` } @@ -368,3 +373,42 @@ func (client *Client) UpdateUserDetails(accountId, userId, userName, userEmail s return &respUser, nil } + +func (client *Client) UpdateLocalUserPassword(userName, password string) (error) { + + fullPath := "/admin/user/localProvider" + + requestBody := fmt.Sprintf(`{"userName": "%s","password": "%s"}`, userName, password) + + opts := RequestOptions{ + Path: fullPath, + Method: "POST", + Body: []byte(requestBody), + } + + _, err := client.RequestAPI(&opts) + + if err != nil { + return err + } + + return nil +} + +func (client *Client) DeleteLocalUserPassword(userName string) (error) { + + fullPath := fmt.Sprintf("/admin/user/localProvider?userName=%s", userName) + + opts := RequestOptions{ + Path: fullPath, + Method: "DELETE", + } + + _, err := client.RequestAPI(&opts) + + if err != nil { + return err + } + + return nil +} diff --git a/codefresh/resource_user.go b/codefresh/resource_user.go index 3dc90e83..df8a1138 100644 --- a/codefresh/resource_user.go +++ b/codefresh/resource_user.go @@ -1,6 +1,7 @@ package codefresh import ( + "errors" "log" "github.com/codefresh-io/terraform-provider-codefresh/codefresh/cfclient" @@ -24,6 +25,17 @@ func resourceUser() *schema.Resource { Type: schema.TypeString, Required: true, }, + "password": { + Description: "Password - for users without SSO.", + Type: schema.TypeString, + Optional: true, + Sensitive: true, + }, + "has_password" : { + Description: "Whether the user has a local password.", + Type: schema.TypeBool, + Computed: true, + }, "email": { Description: "The email of the user.", Type: schema.TypeString, @@ -148,7 +160,11 @@ func resourceUsersCreate(d *schema.ResourceData, meta interface{}) error { client.ActivateUser(d.Id()) } - return nil + if d.Get("password") != "" { + client.UpdateLocalUserPassword(d.Get("user_name").(string), d.Get("password").(string)) + } + + return resourceUsersRead(d, meta) } func resourceUsersRead(d *schema.ResourceData, meta interface{}) error { @@ -198,7 +214,15 @@ func resourceUsersUpdate(d *schema.ResourceData, meta interface{}) error { for _, account := range *accounts { _ = client.AddUserToTeamByAdmin(userId, account.ID, "users") } - return nil + + // Update local password + err = updateUserLocalPassword(d, client) + + if err != nil { + return err + } + + return resourceUsersRead(d, meta) } func resourceUsersDelete(d *schema.ResourceData, meta interface{}) error { @@ -231,6 +255,7 @@ func mapUserToResource(user cfclient.User, d *schema.ResourceData) error { []map[string]interface{}{ {"user_name": user.ShortProfile.UserName}, }) + d.Set("has_password", user.PublicProfile.HasPassword) d.Set("roles", user.Roles) d.Set("login", flattenUserLogins(&user.Logins)) @@ -325,3 +350,33 @@ func mapResourceToNewUser(d *schema.ResourceData) *cfclient.NewUser { return user } + +func updateUserLocalPassword(d *schema.ResourceData, client *cfclient.Client) error { + + if (d.HasChange("password")) { + hasPassword := d.Get("has_password").(bool) + + if _, ok := d.GetOk("user_name"); !ok { + return errors.New("cannot update password as username attribute is not set") + } + + userName := d.Get("user_name").(string) + + if password := d.Get("password"); password != "" { + err := client.UpdateLocalUserPassword(userName, password.(string)) + + if err != nil { + return err + } + // If password is not set but has_password returns true, it means that it was removed + } else if hasPassword { + err := client.DeleteLocalUserPassword(userName) + + if err != nil { + return err + } + } + } + + return nil +} diff --git a/docs/resources/user.md b/docs/resources/user.md index 888c9d51..bbb2e1b2 100644 --- a/docs/resources/user.md +++ b/docs/resources/user.md @@ -75,11 +75,13 @@ resource "codefresh_user" "new" { - `activate` (Boolean) Whether to activate the user or to leave it as `pending`. - `login` (Block Set) Login settings for the user. (see [below for nested schema](#nestedblock--login)) +- `password` (String, Sensitive) Password - for users without SSO. - `personal` (Block List, Max: 1) Personal information about the user. (see [below for nested schema](#nestedblock--personal)) - `roles` (Set of String) The roles of the user. ### Read-Only +- `has_password` (Boolean) Whether the user has a local password. - `id` (String) The ID of this resource. - `short_profile` (List of Object) The computed short profile of the user. (see [below for nested schema](#nestedatt--short_profile)) - `status` (String) The status of the user (e.g. `new`, `pending`).