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  }