github.com/vmware/go-vcloud-director/v2@v2.24.0/govcd/roles.go (about) 1 /* 2 * Copyright 2021 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. 3 */ 4 5 package govcd 6 7 import ( 8 "fmt" 9 "net/url" 10 11 "github.com/vmware/go-vcloud-director/v2/types/v56" 12 ) 13 14 // Role uses OpenAPI endpoint to operate user roles 15 type Role struct { 16 Role *types.Role 17 client *Client 18 TenantContext *TenantContext 19 } 20 21 // GetRoleById retrieves role by given ID 22 func (adminOrg *AdminOrg) GetRoleById(id string) (*Role, error) { 23 endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRoles 24 minimumApiVersion, err := adminOrg.client.checkOpenApiEndpointCompatibility(endpoint) 25 if err != nil { 26 return nil, err 27 } 28 29 if id == "" { 30 return nil, fmt.Errorf("empty role id") 31 } 32 33 urlRef, err := adminOrg.client.OpenApiBuildEndpoint(endpoint, id) 34 if err != nil { 35 return nil, err 36 } 37 38 tenantContext, err := adminOrg.getTenantContext() 39 if err != nil { 40 return nil, err 41 } 42 role := &Role{ 43 Role: &types.Role{}, 44 client: adminOrg.client, 45 TenantContext: tenantContext, 46 } 47 48 err = adminOrg.client.OpenApiGetItem(minimumApiVersion, urlRef, nil, role.Role, getTenantContextHeader(tenantContext)) 49 if err != nil { 50 return nil, err 51 } 52 53 return role, nil 54 } 55 56 // GetRoleByName retrieves role by given name 57 func (adminOrg *AdminOrg) GetRoleByName(name string) (*Role, error) { 58 queryParams := url.Values{} 59 queryParams.Add("filter", "name=="+name) 60 roles, err := adminOrg.GetAllRoles(queryParams) 61 if err != nil { 62 return nil, err 63 } 64 if len(roles) == 0 { 65 return nil, ErrorEntityNotFound 66 } 67 if len(roles) > 1 { 68 return nil, fmt.Errorf("more than one role found with name '%s'", name) 69 } 70 return roles[0], nil 71 } 72 73 // getAllRoles retrieves all roles using OpenAPI endpoint. Query parameters can be supplied to perform additional 74 // filtering 75 func getAllRoles(client *Client, queryParameters url.Values, additionalHeader map[string]string) ([]*Role, error) { 76 endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRoles 77 minimumApiVersion, err := client.checkOpenApiEndpointCompatibility(endpoint) 78 if err != nil { 79 return nil, err 80 } 81 82 urlRef, err := client.OpenApiBuildEndpoint(endpoint) 83 if err != nil { 84 return nil, err 85 } 86 87 typeResponses := []*types.Role{{}} 88 err = client.OpenApiGetAllItems(minimumApiVersion, urlRef, queryParameters, &typeResponses, additionalHeader) 89 if err != nil { 90 return nil, err 91 } 92 93 // Wrap all typeResponses into Role types with client 94 returnRoles := make([]*Role, len(typeResponses)) 95 for sliceIndex := range typeResponses { 96 returnRoles[sliceIndex] = &Role{ 97 Role: typeResponses[sliceIndex], 98 client: client, 99 TenantContext: getTenantContextFromHeader(additionalHeader), 100 } 101 } 102 103 return returnRoles, nil 104 } 105 106 // GetAllRoles retrieves all roles as tenant user. Query parameters can be supplied to perform additional 107 // filtering 108 func (adminOrg *AdminOrg) GetAllRoles(queryParameters url.Values) ([]*Role, error) { 109 tenantContext, err := adminOrg.getTenantContext() 110 if err != nil { 111 return nil, err 112 } 113 return getAllRoles(adminOrg.client, queryParameters, getTenantContextHeader(tenantContext)) 114 } 115 116 // GetAllRoles retrieves all roles as System administrator. Query parameters can be supplied to perform additional 117 // filtering 118 func (client *Client) GetAllRoles(queryParameters url.Values) ([]*Role, error) { 119 return getAllRoles(client, queryParameters, nil) 120 } 121 122 // CreateRole creates a new role as a tenant administrator 123 func (adminOrg *AdminOrg) CreateRole(newRole *types.Role) (*Role, error) { 124 endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRoles 125 minimumApiVersion, err := adminOrg.client.checkOpenApiEndpointCompatibility(endpoint) 126 if err != nil { 127 return nil, err 128 } 129 130 if newRole.BundleKey == "" { 131 newRole.BundleKey = types.VcloudUndefinedKey 132 } 133 134 urlRef, err := adminOrg.client.OpenApiBuildEndpoint(endpoint) 135 if err != nil { 136 return nil, err 137 } 138 139 tenantContext, err := adminOrg.getTenantContext() 140 if err != nil { 141 return nil, err 142 } 143 returnRole := &Role{ 144 Role: &types.Role{}, 145 client: adminOrg.client, 146 TenantContext: tenantContext, 147 } 148 149 err = adminOrg.client.OpenApiPostItem(minimumApiVersion, urlRef, nil, newRole, returnRole.Role, getTenantContextHeader(tenantContext)) 150 if err != nil { 151 return nil, fmt.Errorf("error creating role: %s", err) 152 } 153 154 return returnRole, nil 155 } 156 157 // Update updates existing OpenAPI role 158 func (role *Role) Update() (*Role, error) { 159 endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRoles 160 minimumApiVersion, err := role.client.checkOpenApiEndpointCompatibility(endpoint) 161 if err != nil { 162 return nil, err 163 } 164 165 if role.Role.ID == "" { 166 return nil, fmt.Errorf("cannot update role without id") 167 } 168 169 urlRef, err := role.client.OpenApiBuildEndpoint(endpoint, role.Role.ID) 170 if err != nil { 171 return nil, err 172 } 173 174 returnRole := &Role{ 175 Role: &types.Role{}, 176 client: role.client, 177 TenantContext: role.TenantContext, 178 } 179 180 err = role.client.OpenApiPutItem(minimumApiVersion, urlRef, nil, role.Role, returnRole.Role, getTenantContextHeader(role.TenantContext)) 181 if err != nil { 182 return nil, fmt.Errorf("error updating role: %s", err) 183 } 184 185 return returnRole, nil 186 } 187 188 // Delete deletes OpenAPI role 189 func (role *Role) Delete() error { 190 endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRoles 191 minimumApiVersion, err := role.client.checkOpenApiEndpointCompatibility(endpoint) 192 if err != nil { 193 return err 194 } 195 196 if role.Role.ID == "" { 197 return fmt.Errorf("cannot delete role without id") 198 } 199 200 urlRef, err := role.client.OpenApiBuildEndpoint(endpoint, role.Role.ID) 201 if err != nil { 202 return err 203 } 204 205 err = role.client.OpenApiDeleteItem(minimumApiVersion, urlRef, nil, getTenantContextHeader(role.TenantContext)) 206 207 if err != nil { 208 return fmt.Errorf("error deleting role: %s", err) 209 } 210 211 return nil 212 } 213 214 // AddRights adds a collection of rights to a role 215 func (role *Role) AddRights(newRights []types.OpenApiReference) error { 216 endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRoles 217 return addRightsToRole(role.client, "Role", role.Role.Name, role.Role.ID, endpoint, newRights, getTenantContextHeader(role.TenantContext)) 218 } 219 220 // UpdateRights replaces existing rights with the given collection of rights 221 func (role *Role) UpdateRights(newRights []types.OpenApiReference) error { 222 endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRoles 223 return updateRightsInRole(role.client, "Role", role.Role.Name, role.Role.ID, endpoint, newRights, getTenantContextHeader(role.TenantContext)) 224 } 225 226 // RemoveRights removes specific rights from a role 227 func (role *Role) RemoveRights(removeRights []types.OpenApiReference) error { 228 endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRoles 229 return removeRightsFromRole(role.client, "Role", role.Role.Name, role.Role.ID, endpoint, removeRights, getTenantContextHeader(role.TenantContext)) 230 } 231 232 // RemoveAllRights removes all rights from a role 233 func (role *Role) RemoveAllRights() error { 234 endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRoles 235 return removeAllRightsFromRole(role.client, "Role", role.Role.Name, role.Role.ID, endpoint, getTenantContextHeader(role.TenantContext)) 236 } 237 238 // addRightsToRole is a generic function that can add rights to a rights collection (Role, Global Role, or Rights bundle) 239 // roleType is an informative string (one of "Role", "GlobalRole", or "RightsBundle") 240 // name and id are the name and ID of the collection 241 // endpoint is the API endpoint used as a basis for the POST operation 242 // newRights is a collection of rights (ID+name) to be added 243 // Note: the API call ignores duplicate rights. If the rights to be added already exist, the call succeeds 244 // but no changes are recorded 245 func addRightsToRole(client *Client, roleType, name, id, endpoint string, newRights []types.OpenApiReference, additionalHeader map[string]string) error { 246 minimumApiVersion, err := client.checkOpenApiEndpointCompatibility(endpoint) 247 if err != nil { 248 return err 249 } 250 251 if id == "" { 252 return fmt.Errorf("cannot update %s without id", roleType) 253 } 254 if name == "" { 255 return fmt.Errorf("empty name given for %s %s", roleType, id) 256 } 257 258 urlRef, err := client.OpenApiBuildEndpoint(endpoint, id, "/rights") 259 if err != nil { 260 return err 261 } 262 263 var input types.OpenApiItems 264 265 for _, right := range newRights { 266 input.Values = append(input.Values, types.OpenApiReference{ 267 Name: right.Name, 268 ID: right.ID, 269 }) 270 } 271 var pages types.OpenApiPages 272 273 err = client.OpenApiPostItem(minimumApiVersion, urlRef, nil, &input, &pages, additionalHeader) 274 275 if err != nil { 276 return fmt.Errorf("error adding rights to %s %s: %s", roleType, name, err) 277 } 278 279 return nil 280 } 281 282 // updateRightsInRole is a generic function that can change rights in a Role or Global Role 283 // roleType is an informative string (either "Role" or "GlobalRole") 284 // name and id are the name and ID of the role 285 // endpoint is the API endpoint used as a basis for the PUT operation 286 // newRights is a collection of rights (ID+name) to be added 287 func updateRightsInRole(client *Client, roleType, name, id, endpoint string, newRights []types.OpenApiReference, additionalHeader map[string]string) error { 288 minimumApiVersion, err := client.checkOpenApiEndpointCompatibility(endpoint) 289 if err != nil { 290 return err 291 } 292 293 if id == "" { 294 return fmt.Errorf("cannot update %s without id", roleType) 295 } 296 if name == "" { 297 return fmt.Errorf("empty name given for %s %s", roleType, id) 298 } 299 300 urlRef, err := client.OpenApiBuildEndpoint(endpoint, id, "/rights") 301 if err != nil { 302 return err 303 } 304 305 var input = types.OpenApiItems{ 306 Values: []types.OpenApiReference{}, 307 } 308 309 for _, right := range newRights { 310 input.Values = append(input.Values, types.OpenApiReference{ 311 Name: right.Name, 312 ID: right.ID, 313 }) 314 } 315 var pages types.OpenApiPages 316 317 err = client.OpenApiPutItem(minimumApiVersion, urlRef, nil, &input, &pages, additionalHeader) 318 319 if err != nil { 320 return fmt.Errorf("error updating rights in %s %s: %s", roleType, name, err) 321 } 322 323 return nil 324 } 325 326 // removeRightsFromRole is a generic function that can remove rights from a Role or Global Role 327 // roleType is an informative string (either "Role" or "GlobalRole") 328 // name and id are the name and ID of the role 329 // endpoint is the API endpoint used as a basis for the PUT operation 330 // removeRights is a collection of rights (ID+name) to be removed 331 func removeRightsFromRole(client *Client, roleType, name, id, endpoint string, removeRights []types.OpenApiReference, additionalHeader map[string]string) error { 332 minimumApiVersion, err := client.checkOpenApiEndpointCompatibility(endpoint) 333 if err != nil { 334 return err 335 } 336 337 if id == "" { 338 return fmt.Errorf("cannot update %s without id", roleType) 339 } 340 if name == "" { 341 return fmt.Errorf("empty name given for %s %s", roleType, id) 342 } 343 344 urlRef, err := client.OpenApiBuildEndpoint(endpoint, id, "/rights") 345 if err != nil { 346 return err 347 } 348 349 var input = types.OpenApiItems{ 350 Values: []types.OpenApiReference{}, 351 } 352 var pages types.OpenApiPages 353 354 currentRights, err := getRights(client, id, endpoint, nil, additionalHeader) 355 if err != nil { 356 return err 357 } 358 359 var foundToRemove = make(map[string]bool) 360 361 // Set the items to be removed as not found by default 362 for _, rr := range removeRights { 363 foundToRemove[rr.Name] = false 364 } 365 366 // Search the current rights for items to delete 367 for _, cr := range currentRights { 368 for _, rr := range removeRights { 369 if cr.ID == rr.ID { 370 foundToRemove[cr.Name] = true 371 } 372 } 373 } 374 375 for _, cr := range currentRights { 376 _, found := foundToRemove[cr.Name] 377 if !found { 378 input.Values = append(input.Values, types.OpenApiReference{Name: cr.Name, ID: cr.ID}) 379 } 380 } 381 382 // Check that all the items to be removed were found in the current rights list 383 notFoundNames := "" 384 for name, found := range foundToRemove { 385 if !found { 386 if notFoundNames != "" { 387 notFoundNames += ", " 388 } 389 notFoundNames += `"` + name + `"` 390 } 391 } 392 393 if notFoundNames != "" { 394 return fmt.Errorf("rights in %s %s not found for deletion: [%s]", roleType, name, notFoundNames) 395 } 396 397 err = client.OpenApiPutItem(minimumApiVersion, urlRef, nil, &input, &pages, additionalHeader) 398 399 if err != nil { 400 return fmt.Errorf("error updating rights in %s %s: %s", roleType, name, err) 401 } 402 403 return nil 404 } 405 406 // removeAllRightsFromRole removes all rights from the given role 407 func removeAllRightsFromRole(client *Client, roleType, name, id, endpoint string, additionalHeader map[string]string) error { 408 return updateRightsInRole(client, roleType, name, id, endpoint, []types.OpenApiReference{}, additionalHeader) 409 } 410 411 // FindMissingImpliedRights returns a list of the rights that are implied in the rights provided as input 412 func FindMissingImpliedRights(client *Client, rights []types.OpenApiReference) ([]types.OpenApiReference, error) { 413 var ( 414 impliedRights []types.OpenApiReference 415 uniqueInputRights = make(map[string]types.OpenApiReference) 416 uniqueImpliedRights = make(map[string]types.OpenApiReference) 417 ) 418 419 // Make a searchable collection of unique rights from the input 420 // This operation removes duplicates from the list 421 for _, right := range rights { 422 uniqueInputRights[right.Name] = right 423 } 424 425 // Find the implied rights 426 for _, right := range rights { 427 fullRight, err := client.GetRightByName(right.Name) 428 if err != nil { 429 return nil, err 430 } 431 for _, ir := range fullRight.ImpliedRights { 432 _, seenAsInput := uniqueInputRights[ir.Name] 433 _, seenAsImplied := uniqueImpliedRights[ir.Name] 434 // If the right has already been added either as explicit ro as implied right, we skip it 435 if seenAsInput || seenAsImplied { 436 continue 437 } 438 // Add to the unique collection of implied rights 439 uniqueImpliedRights[ir.Name] = types.OpenApiReference{ 440 Name: ir.Name, 441 ID: ir.ID, 442 } 443 } 444 } 445 446 // Create the output list from the implied rights collection 447 if len(uniqueImpliedRights) > 0 { 448 for _, right := range uniqueImpliedRights { 449 impliedRights = append(impliedRights, right) 450 } 451 } 452 453 return impliedRights, nil 454 }