github.com/vmware/go-vcloud-director/v2@v2.24.0/govcd/group.go (about)

     1  /*
     2   * Copyright 2020 VMware, Inc.  All rights reserved.  Licensed under the Apache v2 License.
     3   */
     4  
     5  package govcd
     6  
     7  import (
     8  	"fmt"
     9  	"net/http"
    10  	"net/url"
    11  
    12  	"github.com/vmware/go-vcloud-director/v2/types/v56"
    13  	"github.com/vmware/go-vcloud-director/v2/util"
    14  )
    15  
    16  // OrgGroup defines group structure
    17  type OrgGroup struct {
    18  	Group    *types.Group
    19  	client   *Client
    20  	AdminOrg *AdminOrg // needed to be able to update, as the list of roles is found in the Org
    21  }
    22  
    23  // NewGroup creates a new group structure which still needs to have Group attribute populated
    24  func NewGroup(cli *Client, org *AdminOrg) *OrgGroup {
    25  	return &OrgGroup{
    26  		Group:    new(types.Group),
    27  		client:   cli,
    28  		AdminOrg: org,
    29  	}
    30  }
    31  
    32  // CreateGroup creates a group in Org. Supported provider types are `OrgUserProviderIntegrated` and
    33  // `OrgUserProviderSAML`.
    34  //
    35  // Note. This request will return HTTP 403 if Org is not configured for SAML or LDAP usage.
    36  func (adminOrg *AdminOrg) CreateGroup(group *types.Group) (*OrgGroup, error) {
    37  	if err := validateCreateUpdateGroup(group); err != nil {
    38  		return nil, err
    39  	}
    40  
    41  	groupCreateHREF, err := url.ParseRequestURI(adminOrg.AdminOrg.HREF)
    42  	if err != nil {
    43  		return nil, fmt.Errorf("error parsing admin org url: %s", err)
    44  	}
    45  	groupCreateHREF.Path += "/groups"
    46  
    47  	grpgroup := NewGroup(adminOrg.client, adminOrg)
    48  	// Add default XML types
    49  	group.Xmlns = types.XMLNamespaceVCloud
    50  	group.Type = types.MimeAdminGroup
    51  
    52  	_, err = adminOrg.client.ExecuteRequest(groupCreateHREF.String(), http.MethodPost,
    53  		types.MimeAdminGroup, "error creating group: %s", group, grpgroup.Group)
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  
    58  	return grpgroup, nil
    59  }
    60  
    61  // GetGroupByHref retrieves group by HREF
    62  func (adminOrg *AdminOrg) GetGroupByHref(href string) (*OrgGroup, error) {
    63  	orgGroup := NewGroup(adminOrg.client, adminOrg)
    64  
    65  	_, err := adminOrg.client.ExecuteRequest(href, http.MethodGet,
    66  		types.MimeAdminUser, "error getting group: %s", nil, orgGroup.Group)
    67  
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  	return orgGroup, nil
    72  }
    73  
    74  // GetGroupByName retrieves group by Name
    75  func (adminOrg *AdminOrg) GetGroupByName(name string, refresh bool) (*OrgGroup, error) {
    76  	if refresh {
    77  		err := adminOrg.Refresh()
    78  		if err != nil {
    79  			return nil, err
    80  		}
    81  	}
    82  
    83  	for _, group := range adminOrg.AdminOrg.Groups.Group {
    84  		if group.Name == name {
    85  			return adminOrg.GetGroupByHref(group.HREF)
    86  		}
    87  	}
    88  	return nil, ErrorEntityNotFound
    89  }
    90  
    91  // GetGroupById retrieves group by Id
    92  func (adminOrg *AdminOrg) GetGroupById(id string, refresh bool) (*OrgGroup, error) {
    93  	if refresh {
    94  		err := adminOrg.Refresh()
    95  		if err != nil {
    96  			return nil, err
    97  		}
    98  	}
    99  
   100  	for _, group := range adminOrg.AdminOrg.Groups.Group {
   101  		if equalIds(id, group.ID, group.HREF) {
   102  			return adminOrg.GetGroupByHref(group.HREF)
   103  		}
   104  	}
   105  	return nil, ErrorEntityNotFound
   106  }
   107  
   108  // GetGroupByNameOrId retrieves group by Name or Id. Id is prioritized for search
   109  func (adminOrg *AdminOrg) GetGroupByNameOrId(identifier string, refresh bool) (*OrgGroup, error) {
   110  	getByName := func(name string, refresh bool) (interface{}, error) { return adminOrg.GetGroupByName(name, refresh) }
   111  	getById := func(name string, refresh bool) (interface{}, error) { return adminOrg.GetGroupById(name, refresh) }
   112  	entity, err := getEntityByNameOrId(getByName, getById, identifier, refresh)
   113  	if entity == nil {
   114  		return nil, err
   115  	}
   116  	return entity.(*OrgGroup), err
   117  }
   118  
   119  // Update allows to update group. vCD API allows to update only role
   120  func (group *OrgGroup) Update() error {
   121  	util.Logger.Printf("[TRACE] Updating group: %s", group.Group.Name)
   122  
   123  	if err := validateCreateUpdateGroup(group.Group); err != nil {
   124  		return err
   125  	}
   126  
   127  	groupHREF, err := url.ParseRequestURI(group.Group.Href)
   128  	if err != nil {
   129  		return fmt.Errorf("error getting HREF for group %s : %s", group.Group.Href, err)
   130  	}
   131  	util.Logger.Printf("[TRACE] Url for updating group : %s and name: %s", groupHREF.String(), group.Group.Name)
   132  
   133  	_, err = group.client.ExecuteRequest(groupHREF.String(), http.MethodPut,
   134  		types.MimeAdminGroup, "error updating group : %s", copyWithoutUserList(group.Group), nil)
   135  	return err
   136  }
   137  
   138  // Delete removes a group
   139  func (group *OrgGroup) Delete() error {
   140  	if err := validateDeleteGroup(group.Group); err != nil {
   141  		return err
   142  	}
   143  
   144  	groupHREF, err := url.ParseRequestURI(group.Group.Href)
   145  	if err != nil {
   146  		return fmt.Errorf("error getting HREF for group %s : %s", group.Group.Name, err)
   147  	}
   148  	util.Logger.Printf("[TRACE] Url for deleting group : %s and name: %s", groupHREF, group.Group.Name)
   149  
   150  	return group.client.ExecuteRequestWithoutResponse(groupHREF.String(), http.MethodDelete,
   151  		types.MimeAdminGroup, "error deleting group : %s", nil)
   152  }
   153  
   154  // validateCreateGroup checks if mandatory fields are set for group creation and update
   155  func validateCreateUpdateGroup(group *types.Group) error {
   156  	if group == nil {
   157  		return fmt.Errorf("group cannot be nil")
   158  	}
   159  
   160  	if group.Name == "" {
   161  		return fmt.Errorf("group must have a name")
   162  	}
   163  
   164  	if group.ProviderType == "" {
   165  		return fmt.Errorf("group must have provider type set")
   166  	}
   167  
   168  	if group.Role.HREF == "" {
   169  		return fmt.Errorf("group role must have HREF set")
   170  	}
   171  	return nil
   172  }
   173  
   174  // validateDeleteGroup checks if mandatory fields are set for delete
   175  func validateDeleteGroup(group *types.Group) error {
   176  	if group == nil {
   177  		return fmt.Errorf("group cannot be nil")
   178  	}
   179  
   180  	if group.Href == "" {
   181  		return fmt.Errorf("HREF must be set to delete group")
   182  	}
   183  
   184  	return nil
   185  }
   186  
   187  // copyWithoutUserList returns a copy of the given group, with the UserList attribute set to nil.
   188  // This can and should be used to interact with VCD after a group read from the LDAP,
   189  // as having this list populated will return an error 400 as VCD doesn't expect this list to be updatable.
   190  func copyWithoutUserList(group *types.Group) *types.Group {
   191  	return &types.Group{
   192  		XMLName:      group.XMLName,
   193  		Xmlns:        group.Xmlns,
   194  		ID:           group.ID,
   195  		Href:         group.Href,
   196  		Type:         group.Type,
   197  		Description:  group.Description,
   198  		Name:         group.Name,
   199  		ProviderType: group.ProviderType,
   200  		Role:         group.Role,
   201  		UsersList:    nil,
   202  	}
   203  }