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