github.com/cloudfoundry-attic/cli-with-i18n@v6.32.1-0.20171002233121-7401370d3b85+incompatible/cf/api/users.go (about) 1 package api 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io" 8 "net/http" 9 neturl "net/url" 10 "strings" 11 12 "code.cloudfoundry.org/cli/cf/api/resources" 13 "code.cloudfoundry.org/cli/cf/configuration/coreconfig" 14 "code.cloudfoundry.org/cli/cf/errors" 15 . "code.cloudfoundry.org/cli/cf/i18n" 16 "code.cloudfoundry.org/cli/cf/models" 17 "code.cloudfoundry.org/cli/cf/net" 18 ) 19 20 var orgRoleToPathMap = map[models.Role]string{ 21 models.RoleOrgUser: "users", 22 models.RoleOrgManager: "managers", 23 models.RoleBillingManager: "billing_managers", 24 models.RoleOrgAuditor: "auditors", 25 } 26 27 var spaceRoleToPathMap = map[models.Role]string{ 28 models.RoleSpaceManager: "managers", 29 models.RoleSpaceDeveloper: "developers", 30 models.RoleSpaceAuditor: "auditors", 31 } 32 33 type apiErrResponse struct { 34 Code int `json:"code,omitempty"` 35 ErrorCode string `json:"error_code,omitempty"` 36 Description string `json:"description,omitempty"` 37 } 38 39 //go:generate counterfeiter . UserRepository 40 41 type UserRepository interface { 42 FindByUsername(username string) (user models.UserFields, apiErr error) 43 FindAllByUsername(username string) (users []models.UserFields, apiErr error) 44 ListUsersInOrgForRole(orgGUID string, role models.Role) ([]models.UserFields, error) 45 ListUsersInOrgForRoleWithNoUAA(orgGUID string, role models.Role) ([]models.UserFields, error) 46 ListUsersInSpaceForRoleWithNoUAA(spaceGUID string, role models.Role) ([]models.UserFields, error) 47 Create(username, password string) (apiErr error) 48 Delete(userGUID string) (apiErr error) 49 SetOrgRoleByGUID(userGUID, orgGUID string, role models.Role) (apiErr error) 50 SetOrgRoleByUsername(username, orgGUID string, role models.Role) (apiErr error) 51 UnsetOrgRoleByGUID(userGUID, orgGUID string, role models.Role) (apiErr error) 52 UnsetOrgRoleByUsername(username, orgGUID string, role models.Role) (apiErr error) 53 SetSpaceRoleByGUID(userGUID, spaceGUID, orgGUID string, role models.Role) (apiErr error) 54 SetSpaceRoleByUsername(username, spaceGUID, orgGUID string, role models.Role) (apiErr error) 55 UnsetSpaceRoleByGUID(userGUID, spaceGUID string, role models.Role) (apiErr error) 56 UnsetSpaceRoleByUsername(userGUID, spaceGUID string, role models.Role) (apiErr error) 57 } 58 59 type CloudControllerUserRepository struct { 60 config coreconfig.Reader 61 uaaGateway net.Gateway 62 ccGateway net.Gateway 63 } 64 65 func NewCloudControllerUserRepository(config coreconfig.Reader, uaaGateway net.Gateway, ccGateway net.Gateway) (repo CloudControllerUserRepository) { 66 repo.config = config 67 repo.uaaGateway = uaaGateway 68 repo.ccGateway = ccGateway 69 return 70 } 71 72 func (repo CloudControllerUserRepository) FindByUsername(username string) (user models.UserFields, apiErr error) { 73 users, apiErr := repo.FindAllByUsername(username) 74 if apiErr != nil { 75 return user, apiErr 76 } 77 user = users[0] 78 79 return user, nil 80 } 81 82 func (repo CloudControllerUserRepository) FindAllByUsername(username string) (users []models.UserFields, apiErr error) { 83 uaaEndpoint, apiErr := repo.getAuthEndpoint() 84 if apiErr != nil { 85 return users, apiErr 86 } 87 88 usernameFilter := neturl.QueryEscape(fmt.Sprintf(`userName Eq "%s"`, username)) 89 path := fmt.Sprintf("%s/Users?attributes=id,userName&filter=%s", uaaEndpoint, usernameFilter) 90 users, apiErr = repo.updateOrFindUsersWithUAAPath([]models.UserFields{}, path) 91 92 if apiErr != nil { 93 errType, ok := apiErr.(errors.HTTPError) 94 if ok { 95 if errType.StatusCode() == 403 { 96 return users, errors.NewAccessDeniedError() 97 } 98 } 99 return users, apiErr 100 } else if len(users) == 0 { 101 return users, errors.NewModelNotFoundError("User", username) 102 } 103 104 return users, apiErr 105 } 106 107 func (repo CloudControllerUserRepository) ListUsersInOrgForRole(orgGUID string, roleName models.Role) (users []models.UserFields, apiErr error) { 108 return repo.listUsersWithPath(fmt.Sprintf("/v2/organizations/%s/%s", orgGUID, orgRoleToPathMap[roleName])) 109 } 110 111 func (repo CloudControllerUserRepository) ListUsersInOrgForRoleWithNoUAA(orgGUID string, roleName models.Role) (users []models.UserFields, apiErr error) { 112 return repo.listUsersWithPathWithNoUAA(fmt.Sprintf("/v2/organizations/%s/%s", orgGUID, orgRoleToPathMap[roleName])) 113 } 114 115 func (repo CloudControllerUserRepository) ListUsersInSpaceForRoleWithNoUAA(spaceGUID string, roleName models.Role) (users []models.UserFields, apiErr error) { 116 return repo.listUsersWithPathWithNoUAA(fmt.Sprintf("/v2/spaces/%s/%s", spaceGUID, spaceRoleToPathMap[roleName])) 117 } 118 119 func (repo CloudControllerUserRepository) listUsersWithPathWithNoUAA(path string) (users []models.UserFields, apiErr error) { 120 apiErr = repo.ccGateway.ListPaginatedResources( 121 repo.config.APIEndpoint(), 122 path, 123 resources.UserResource{}, 124 func(resource interface{}) bool { 125 user := resource.(resources.UserResource).ToFields() 126 users = append(users, user) 127 return true 128 }) 129 if apiErr != nil { 130 return 131 } 132 133 return 134 } 135 136 func (repo CloudControllerUserRepository) listUsersWithPath(path string) (users []models.UserFields, apiErr error) { 137 guidFilters := []string{} 138 139 apiErr = repo.ccGateway.ListPaginatedResources( 140 repo.config.APIEndpoint(), 141 path, 142 resources.UserResource{}, 143 func(resource interface{}) bool { 144 user := resource.(resources.UserResource).ToFields() 145 users = append(users, user) 146 guidFilters = append(guidFilters, fmt.Sprintf(`ID eq "%s"`, user.GUID)) 147 return true 148 }) 149 if apiErr != nil { 150 return 151 } 152 153 if len(guidFilters) == 0 { 154 return 155 } 156 157 uaaEndpoint, apiErr := repo.getAuthEndpoint() 158 if apiErr != nil { 159 return 160 } 161 162 filter := strings.Join(guidFilters, " or ") 163 usersURL := fmt.Sprintf("%s/Users?attributes=id,userName&filter=%s", uaaEndpoint, neturl.QueryEscape(filter)) 164 users, apiErr = repo.updateOrFindUsersWithUAAPath(users, usersURL) 165 return 166 } 167 168 func (repo CloudControllerUserRepository) updateOrFindUsersWithUAAPath(ccUsers []models.UserFields, path string) (updatedUsers []models.UserFields, apiErr error) { 169 uaaResponse := new(resources.UAAUserResources) 170 apiErr = repo.uaaGateway.GetResource(path, uaaResponse) 171 if apiErr != nil { 172 return 173 } 174 175 for _, uaaResource := range uaaResponse.Resources { 176 var ccUserFields models.UserFields 177 178 for _, u := range ccUsers { 179 if u.GUID == uaaResource.ID { 180 ccUserFields = u 181 break 182 } 183 } 184 185 updatedUsers = append(updatedUsers, models.UserFields{ 186 GUID: uaaResource.ID, 187 Username: uaaResource.Username, 188 IsAdmin: ccUserFields.IsAdmin, 189 }) 190 } 191 return 192 } 193 194 func (repo CloudControllerUserRepository) Create(username, password string) (err error) { 195 uaaEndpoint, err := repo.getAuthEndpoint() 196 if err != nil { 197 return 198 } 199 200 path := "/Users" 201 body, err := json.Marshal(resources.NewUAAUserResource(username, password)) 202 203 if err != nil { 204 return 205 } 206 207 createUserResponse := &resources.UAAUserFields{} 208 err = repo.uaaGateway.CreateResource(uaaEndpoint, path, bytes.NewReader(body), createUserResponse) 209 switch httpErr := err.(type) { 210 case nil: 211 case errors.HTTPError: 212 if httpErr.StatusCode() == http.StatusConflict { 213 err = errors.NewModelAlreadyExistsError("user", username) 214 return 215 } 216 return 217 default: 218 return 219 } 220 221 path = "/v2/users" 222 body, err = json.Marshal(resources.Metadata{ 223 GUID: createUserResponse.ID, 224 }) 225 226 if err != nil { 227 return 228 } 229 230 return repo.ccGateway.CreateResource(repo.config.APIEndpoint(), path, bytes.NewReader(body)) 231 } 232 233 func (repo CloudControllerUserRepository) Delete(userGUID string) (apiErr error) { 234 path := fmt.Sprintf("/v2/users/%s", userGUID) 235 236 apiErr = repo.ccGateway.DeleteResource(repo.config.APIEndpoint(), path) 237 238 if httpErr, ok := apiErr.(errors.HTTPError); ok && httpErr.ErrorCode() != errors.UserNotFound { 239 return 240 } 241 uaaEndpoint, apiErr := repo.getAuthEndpoint() 242 if apiErr != nil { 243 return 244 } 245 246 path = fmt.Sprintf("/Users/%s", userGUID) 247 return repo.uaaGateway.DeleteResource(uaaEndpoint, path) 248 } 249 250 func (repo CloudControllerUserRepository) SetOrgRoleByGUID(userGUID string, orgGUID string, role models.Role) (err error) { 251 path, err := userGUIDPath(repo.config.APIEndpoint(), userGUID, orgGUID, role) 252 if err != nil { 253 return 254 } 255 err = repo.callAPI("PUT", path, nil) 256 if err != nil { 257 return 258 } 259 return repo.assocUserWithOrgByUserGUID(userGUID, orgGUID) 260 } 261 262 func (repo CloudControllerUserRepository) UnsetOrgRoleByGUID(userGUID, orgGUID string, role models.Role) (err error) { 263 path, err := userGUIDPath(repo.config.APIEndpoint(), userGUID, orgGUID, role) 264 if err != nil { 265 return 266 } 267 return repo.callAPI("DELETE", path, nil) 268 } 269 270 func (repo CloudControllerUserRepository) UnsetOrgRoleByUsername(username, orgGUID string, role models.Role) error { 271 rolePath, err := rolePath(role) 272 if err != nil { 273 return err 274 } 275 276 path := fmt.Sprintf("%s/v2/organizations/%s/%s", repo.config.APIEndpoint(), orgGUID, rolePath) 277 278 return repo.callAPI("DELETE", path, usernamePayload(username)) 279 } 280 281 func (repo CloudControllerUserRepository) UnsetSpaceRoleByUsername(username, spaceGUID string, role models.Role) error { 282 rolePath := spaceRoleToPathMap[role] 283 path := fmt.Sprintf("%s/v2/spaces/%s/%s", repo.config.APIEndpoint(), spaceGUID, rolePath) 284 285 return repo.callAPI("DELETE", path, usernamePayload(username)) 286 } 287 288 func (repo CloudControllerUserRepository) SetOrgRoleByUsername(username string, orgGUID string, role models.Role) error { 289 rolePath, err := rolePath(role) 290 if err != nil { 291 return err 292 } 293 294 path := fmt.Sprintf("%s/v2/organizations/%s/%s", repo.config.APIEndpoint(), orgGUID, rolePath) 295 err = repo.callAPI("PUT", path, usernamePayload(username)) 296 if err != nil { 297 return err 298 } 299 return repo.assocUserWithOrgByUsername(username, orgGUID, nil) 300 } 301 302 func (repo CloudControllerUserRepository) callAPI(verb, path string, body io.ReadSeeker) (err error) { 303 request, err := repo.ccGateway.NewRequest(verb, path, repo.config.AccessToken(), body) 304 if err != nil { 305 return 306 } 307 _, err = repo.ccGateway.PerformRequest(request) 308 if err != nil { 309 return 310 } 311 return 312 } 313 314 func userGUIDPath(apiEndpoint, userGUID, orgGUID string, role models.Role) (string, error) { 315 rolePath, err := rolePath(role) 316 if err != nil { 317 return "", err 318 } 319 320 return fmt.Sprintf("%s/v2/organizations/%s/%s/%s", apiEndpoint, orgGUID, rolePath, userGUID), nil 321 } 322 323 func (repo CloudControllerUserRepository) SetSpaceRoleByGUID(userGUID, spaceGUID, orgGUID string, role models.Role) error { 324 rolePath, found := spaceRoleToPathMap[role] 325 if !found { 326 return fmt.Errorf(T("Invalid Role {{.Role}}", map[string]interface{}{"Role": role})) 327 } 328 329 err := repo.assocUserWithOrgByUserGUID(userGUID, orgGUID) 330 if err != nil { 331 return err 332 } 333 334 path := fmt.Sprintf("/v2/spaces/%s/%s/%s", spaceGUID, rolePath, userGUID) 335 336 return repo.ccGateway.UpdateResource(repo.config.APIEndpoint(), path, nil) 337 } 338 339 func (repo CloudControllerUserRepository) SetSpaceRoleByUsername(username, spaceGUID, orgGUID string, role models.Role) (apiErr error) { 340 rolePath, apiErr := repo.checkSpaceRole(spaceGUID, role) 341 if apiErr != nil { 342 return 343 } 344 345 setOrgRoleErr := apiErrResponse{} 346 apiErr = repo.assocUserWithOrgByUsername(username, orgGUID, &setOrgRoleErr) 347 if setOrgRoleErr.Code == 10003 { 348 //operator lacking the privilege to set org role 349 //user might already be in org, so ignoring error and attempt to set space role 350 } else if apiErr != nil { 351 return 352 } 353 354 setSpaceRoleErr := apiErrResponse{} 355 apiErr = repo.ccGateway.UpdateResourceSync(repo.config.APIEndpoint(), rolePath, usernamePayload(username), &setSpaceRoleErr) 356 if setSpaceRoleErr.Code == 1002 { 357 return errors.New(T("Server error, error code: 1002, message: cannot set space role because user is not part of the org")) 358 } 359 360 return apiErr 361 } 362 363 func (repo CloudControllerUserRepository) UnsetSpaceRoleByGUID(userGUID, spaceGUID string, role models.Role) error { 364 rolePath, found := spaceRoleToPathMap[role] 365 if !found { 366 return fmt.Errorf(T("Invalid Role {{.Role}}", map[string]interface{}{"Role": role})) 367 } 368 apiURL := fmt.Sprintf("/v2/spaces/%s/%s/%s", spaceGUID, rolePath, userGUID) 369 370 return repo.ccGateway.DeleteResource(repo.config.APIEndpoint(), apiURL) 371 } 372 373 func (repo CloudControllerUserRepository) checkSpaceRole(spaceGUID string, role models.Role) (string, error) { 374 var apiErr error 375 376 rolePath, found := spaceRoleToPathMap[role] 377 378 if !found { 379 apiErr = fmt.Errorf(T("Invalid Role {{.Role}}", 380 map[string]interface{}{"Role": role})) 381 } 382 383 apiPath := fmt.Sprintf("/v2/spaces/%s/%s", spaceGUID, rolePath) 384 return apiPath, apiErr 385 } 386 387 func (repo CloudControllerUserRepository) assocUserWithOrgByUsername(username, orgGUID string, resource interface{}) (apiErr error) { 388 path := fmt.Sprintf("/v2/organizations/%s/users", orgGUID) 389 return repo.ccGateway.UpdateResourceSync(repo.config.APIEndpoint(), path, usernamePayload(username), resource) 390 } 391 392 func (repo CloudControllerUserRepository) assocUserWithOrgByUserGUID(userGUID, orgGUID string) (apiErr error) { 393 path := fmt.Sprintf("/v2/organizations/%s/users/%s", orgGUID, userGUID) 394 return repo.ccGateway.UpdateResource(repo.config.APIEndpoint(), path, nil) 395 } 396 397 func (repo CloudControllerUserRepository) getAuthEndpoint() (string, error) { 398 uaaEndpoint := repo.config.UaaEndpoint() 399 if uaaEndpoint == "" { 400 return "", errors.New(T("UAA endpoint missing from config file")) 401 } 402 return uaaEndpoint, nil 403 } 404 405 func rolePath(role models.Role) (string, error) { 406 path, found := orgRoleToPathMap[role] 407 408 if !found { 409 return "", fmt.Errorf(T("Invalid Role {{.Role}}", 410 map[string]interface{}{"Role": role})) 411 } 412 return path, nil 413 } 414 415 func usernamePayload(username string) *strings.Reader { 416 return strings.NewReader(`{"username": "` + username + `"}`) 417 }