github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/api/usermanager/client.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package usermanager 5 6 import ( 7 "fmt" 8 "strings" 9 10 "github.com/juju/errors" 11 "github.com/juju/loggo" 12 "gopkg.in/juju/names.v2" 13 14 "github.com/juju/juju/api/base" 15 "github.com/juju/juju/apiserver/params" 16 ) 17 18 var logger = loggo.GetLogger("juju.api.usermanager") 19 20 // Client provides methods that the Juju client command uses to interact 21 // with users stored in the Juju Server. 22 type Client struct { 23 base.ClientFacade 24 facade base.FacadeCaller 25 } 26 27 // NewClient creates a new `Client` based on an existing authenticated API 28 // connection. 29 func NewClient(st base.APICallCloser) *Client { 30 frontend, backend := base.NewClientFacade(st, "UserManager") 31 return &Client{ClientFacade: frontend, facade: backend} 32 } 33 34 // AddUser creates a new local user in the controller, sharing with that user any specified models. 35 func (c *Client) AddUser( 36 username, displayName, password string, 37 ) (_ names.UserTag, secretKey []byte, _ error) { 38 if !names.IsValidUser(username) { 39 return names.UserTag{}, nil, fmt.Errorf("invalid user name %q", username) 40 } 41 42 userArgs := params.AddUsers{ 43 Users: []params.AddUser{{ 44 Username: username, 45 DisplayName: displayName, 46 Password: password, 47 }}, 48 } 49 var results params.AddUserResults 50 err := c.facade.FacadeCall("AddUser", userArgs, &results) 51 if err != nil { 52 return names.UserTag{}, nil, errors.Trace(err) 53 } 54 if count := len(results.Results); count != 1 { 55 logger.Errorf("expected 1 result, got %#v", results) 56 return names.UserTag{}, nil, errors.Errorf("expected 1 result, got %d", count) 57 } 58 result := results.Results[0] 59 if result.Error != nil { 60 return names.UserTag{}, nil, errors.Trace(result.Error) 61 } 62 tag, err := names.ParseUserTag(result.Tag) 63 if err != nil { 64 return names.UserTag{}, nil, errors.Trace(err) 65 } 66 return tag, result.SecretKey, nil 67 } 68 69 func (c *Client) userCall(username string, methodCall string) error { 70 if !names.IsValidUser(username) { 71 return errors.Errorf("%q is not a valid username", username) 72 } 73 tag := names.NewUserTag(username) 74 75 var results params.ErrorResults 76 args := params.Entities{ 77 []params.Entity{{tag.String()}}, 78 } 79 err := c.facade.FacadeCall(methodCall, args, &results) 80 if err != nil { 81 return errors.Trace(err) 82 } 83 return results.OneError() 84 } 85 86 // DisableUser disables a user. If the user is already disabled, the action 87 // is considered a success. 88 func (c *Client) DisableUser(username string) error { 89 return c.userCall(username, "DisableUser") 90 } 91 92 // EnableUser enables a users. If the user is already enabled, the action is 93 // considered a success. 94 func (c *Client) EnableUser(username string) error { 95 return c.userCall(username, "EnableUser") 96 } 97 98 // RemoveUser deletes a user. That is it permanently removes the user, while 99 // retaining the record of the user to maintain provenance. 100 func (c *Client) RemoveUser(username string) error { 101 return c.userCall(username, "RemoveUser") 102 } 103 104 // IncludeDisabled is a type alias to avoid bare true/false values 105 // in calls to the client method. 106 type IncludeDisabled bool 107 108 var ( 109 // ActiveUsers indicates to only return active users. 110 ActiveUsers IncludeDisabled = false 111 // AllUsers indicates that both enabled and disabled users should be 112 // returned. 113 AllUsers IncludeDisabled = true 114 ) 115 116 // UserInfo returns information about the specified users. If no users are 117 // specified, the call should return all users. If includeDisabled is set to 118 // ActiveUsers, only enabled users are returned. 119 func (c *Client) UserInfo(usernames []string, all IncludeDisabled) ([]params.UserInfo, error) { 120 var results params.UserInfoResults 121 var entities []params.Entity 122 for _, username := range usernames { 123 if !names.IsValidUser(username) { 124 return nil, errors.Errorf("%q is not a valid username", username) 125 } 126 tag := names.NewUserTag(username) 127 entities = append(entities, params.Entity{Tag: tag.String()}) 128 } 129 args := params.UserInfoRequest{ 130 Entities: entities, 131 IncludeDisabled: bool(all), 132 } 133 err := c.facade.FacadeCall("UserInfo", args, &results) 134 if err != nil { 135 return nil, errors.Trace(err) 136 } 137 // Only need to look for errors if users were explicitly specified, because 138 // if we didn't ask for any, we should get all, and we shouldn't get any 139 // errors for listing all. We care here because we index into the users 140 // slice. 141 if len(results.Results) == len(usernames) { 142 var errorStrings []string 143 for i, result := range results.Results { 144 if result.Error != nil { 145 annotated := errors.Annotate(result.Error, usernames[i]) 146 errorStrings = append(errorStrings, annotated.Error()) 147 } 148 } 149 // TODO(wallyworld) - we should return these errors to the caller so that any 150 // users which are successfully found can be handled. 151 if len(errorStrings) > 0 { 152 return nil, errors.New(strings.Join(errorStrings, ", ")) 153 } 154 } 155 info := []params.UserInfo{} 156 for i, result := range results.Results { 157 if result.Result == nil { 158 return nil, errors.Errorf("unexpected nil result at position %d", i) 159 } 160 info = append(info, *result.Result) 161 } 162 return info, nil 163 } 164 165 // SetPassword changes the password for the specified user. 166 func (c *Client) SetPassword(username, password string) error { 167 if !names.IsValidUser(username) { 168 return errors.Errorf("%q is not a valid username", username) 169 } 170 tag := names.NewUserTag(username) 171 args := params.EntityPasswords{ 172 Changes: []params.EntityPassword{{ 173 Tag: tag.String(), 174 Password: password}}, 175 } 176 var results params.ErrorResults 177 err := c.facade.FacadeCall("SetPassword", args, &results) 178 if err != nil { 179 return err 180 } 181 return results.OneError() 182 } 183 184 // ResetPassword resets password for the specified user. 185 func (c *Client) ResetPassword(username string) ([]byte, error) { 186 if !names.IsValidUser(username) { 187 return nil, fmt.Errorf("invalid user name %q", username) 188 } 189 190 in := params.Entities{ 191 Entities: []params.Entity{{ 192 Tag: names.NewUserTag(username).String(), 193 }}, 194 } 195 var out params.AddUserResults 196 err := c.facade.FacadeCall("ResetPassword", in, &out) 197 if err != nil { 198 return nil, errors.Trace(err) 199 } 200 if count := len(out.Results); count != 1 { 201 logger.Errorf("expected 1 result, got %#v", out) 202 return nil, errors.Errorf("expected 1 result, got %d", count) 203 } 204 result := out.Results[0] 205 if result.Error != nil { 206 return nil, errors.Trace(result.Error) 207 } 208 return result.SecretKey, nil 209 }