github.com/vmware/go-vcloud-director/v2@v2.24.0/govcd/access_control.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  	"bytes"
     9  	"encoding/xml"
    10  	"fmt"
    11  	"net/http"
    12  	"net/url"
    13  	"strings"
    14  
    15  	"github.com/vmware/go-vcloud-director/v2/types/v56"
    16  )
    17  
    18  // orgInfoCache is a cache to save org information, avoid repeated calls to compute the same result.
    19  // The keys to this map are the requesting objects IDs.
    20  var orgInfoCache = make(map[string]*TenantContext)
    21  
    22  // GetAccessControl retrieves the access control information for the requested entity
    23  func (client Client) GetAccessControl(href, entityType, entityName string, headerValues map[string]string) (*types.ControlAccessParams, error) {
    24  
    25  	href += "/controlAccess"
    26  	var controlAccess types.ControlAccessParams
    27  
    28  	acUrl, err := url.ParseRequestURI(href)
    29  	if err != nil {
    30  		return nil, fmt.Errorf("[client.GetAccessControl] error parsing HREF %s: %s", href, err)
    31  	}
    32  	var additionalHeader = make(http.Header)
    33  
    34  	if len(headerValues) > 0 {
    35  		for k, v := range headerValues {
    36  			additionalHeader.Add(k, v)
    37  		}
    38  	}
    39  	req := client.newRequest(
    40  		nil,               // params
    41  		nil,               // notEncodedParams
    42  		http.MethodGet,    // method
    43  		*acUrl,            // reqUrl
    44  		nil,               // body
    45  		client.APIVersion, // apiVersion
    46  		additionalHeader,  // additionalHeader
    47  	)
    48  
    49  	resp, err := checkResp(client.Http.Do(req))
    50  	if err != nil {
    51  		return nil, fmt.Errorf("[client.GetAccessControl] error checking response to request %s: %s", href, err)
    52  	}
    53  	if resp == nil {
    54  		return nil, fmt.Errorf("[client.GetAccessControl] nil response received")
    55  	}
    56  	if err = decodeBody(types.BodyTypeXML, resp, &controlAccess); err != nil {
    57  		return nil, fmt.Errorf("[client.GetAccessControl] error decoding response: %s", err)
    58  	}
    59  
    60  	return &controlAccess, nil
    61  }
    62  
    63  // SetAccessControl changes the access control information for this entity
    64  // There are two ways of setting the access:
    65  // with accessControl.IsSharedToEveryone = true we give access to everyone
    66  // with accessControl.IsSharedToEveryone = false, accessControl.AccessSettings defines which subjects can access the vApp
    67  // For each setting we must provide:
    68  // * The subject (HREF and Type are mandatory)
    69  // * The access level (one of ReadOnly, Change, FullControl)
    70  func (client *Client) SetAccessControl(accessControl *types.ControlAccessParams, href, entityType, entityName string, headerValues map[string]string) error {
    71  	return client.setAccessControlWithHttpMethod(http.MethodPost, accessControl, href, entityType, entityName, headerValues)
    72  }
    73  
    74  // setAccessControlWithMethod is the same as Client.SetAccessControl but allowing passing a different HTTP method.
    75  // This method has been created since VDC accessControl endpoint works with PUT and SetAccessControl method worked
    76  // exclusively with POST. This private method gives the flexibility to use both POST and PUT passing it as httpMethod parameter.
    77  func (client *Client) setAccessControlWithHttpMethod(httpMethod string, accessControl *types.ControlAccessParams, href, entityType, entityName string, headerValues map[string]string) error {
    78  	href += "/action/controlAccess"
    79  	// Make sure that subjects in the setting list are used only once
    80  	if accessControl.AccessSettings != nil && len(accessControl.AccessSettings.AccessSetting) > 0 {
    81  		if accessControl.IsSharedToEveryone {
    82  			return fmt.Errorf("[client.SetAccessControl] can't set IsSharedToEveryone and AccessSettings at the same time for %s %s (%s)", entityType, entityName, href)
    83  		}
    84  		var used = make(map[string]bool)
    85  		for _, setting := range accessControl.AccessSettings.AccessSetting {
    86  			_, seen := used[setting.Subject.HREF]
    87  			if seen {
    88  				return fmt.Errorf("[client.SetAccessControl] subject %s (%s) used more than once", setting.Subject.Name, setting.Subject.HREF)
    89  			}
    90  			used[setting.Subject.HREF] = true
    91  			if setting.Subject.Type == "" {
    92  				return fmt.Errorf("[client.SetAccessControl] subject %s (%s) has no type defined", setting.Subject.Name, setting.Subject.HREF)
    93  			}
    94  		}
    95  	}
    96  
    97  	accessControl.Xmlns = types.XMLNamespaceVCloud
    98  	queryUrl, err := url.ParseRequestURI(href)
    99  	if err != nil {
   100  		return fmt.Errorf("[client.SetAccessControl] error parsing HREF %s: %s", href, err)
   101  	}
   102  
   103  	var header = make(http.Header)
   104  	if len(headerValues) > 0 {
   105  		for k, v := range headerValues {
   106  			header.Add(k, v)
   107  		}
   108  	}
   109  
   110  	marshaledXml, err := xml.MarshalIndent(accessControl, "  ", "    ")
   111  	if err != nil {
   112  		return fmt.Errorf("[client.SetAccessControl] error marshalling xml data: %s", err)
   113  	}
   114  	body := bytes.NewBufferString(xml.Header + string(marshaledXml))
   115  
   116  	req := client.newRequest(
   117  		nil,               // params
   118  		nil,               // notEncodedParams
   119  		httpMethod,        // method
   120  		*queryUrl,         // reqUrl
   121  		body,              // body
   122  		client.APIVersion, // apiVersion
   123  		header,            // additionalHeader
   124  	)
   125  
   126  	resp, err := checkResp(client.Http.Do(req))
   127  
   128  	if err != nil {
   129  		return fmt.Errorf("[client.SetAccessControl] error checking response to HREF %s: %s", href, err)
   130  	}
   131  	if resp == nil {
   132  		return fmt.Errorf("[client.SetAccessControl] nil response received")
   133  	}
   134  	_, err = checkResp(resp, err)
   135  	return err
   136  }
   137  
   138  // GetAccessControl retrieves the access control information for this vApp
   139  func (vapp VApp) GetAccessControl(useTenantContext bool) (*types.ControlAccessParams, error) {
   140  
   141  	if vapp.VApp.HREF == "" {
   142  		return nil, fmt.Errorf("vApp HREF is empty")
   143  	}
   144  	// if useTenantContext is false, we use an empty header (= default behavior)
   145  	// if it is true, we use a header populated with tenant context values
   146  	accessControlHeader, err := vapp.getAccessControlHeader(useTenantContext)
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  	return vapp.client.GetAccessControl(vapp.VApp.HREF, "vApp", vapp.VApp.Name, accessControlHeader)
   151  }
   152  
   153  // SetAccessControl changes the access control information for this vApp
   154  func (vapp VApp) SetAccessControl(accessControl *types.ControlAccessParams, useTenantContext bool) error {
   155  
   156  	if vapp.VApp.HREF == "" {
   157  		return fmt.Errorf("vApp HREF is empty")
   158  	}
   159  
   160  	// if useTenantContext is false, we use an empty header (= default behavior)
   161  	// if it is true, we use a header populated with tenant context values
   162  	accessControlHeader, err := vapp.getAccessControlHeader(useTenantContext)
   163  	if err != nil {
   164  		return err
   165  	}
   166  	return vapp.client.SetAccessControl(accessControl, vapp.VApp.HREF, "vApp", vapp.VApp.Name, accessControlHeader)
   167  
   168  }
   169  
   170  // RemoveAccessControl is a shortcut to SetAccessControl with all access disabled
   171  func (vapp VApp) RemoveAccessControl(useTenantContext bool) error {
   172  	return vapp.SetAccessControl(&types.ControlAccessParams{IsSharedToEveryone: false}, useTenantContext)
   173  }
   174  
   175  // IsShared shows whether a vApp is shared or not, regardless of the number of subjects sharing it
   176  func (vapp VApp) IsShared(useTenantContext bool) (bool, error) {
   177  	settings, err := vapp.GetAccessControl(useTenantContext)
   178  	if err != nil {
   179  		return false, err
   180  	}
   181  	if settings.IsSharedToEveryone {
   182  		return true, nil
   183  	}
   184  	return settings.AccessSettings != nil, nil
   185  }
   186  
   187  // GetAccessControl retrieves the access control information for this catalog
   188  func (adminCatalog AdminCatalog) GetAccessControl(useTenantContext bool) (*types.ControlAccessParams, error) {
   189  
   190  	if adminCatalog.AdminCatalog.HREF == "" {
   191  		return nil, fmt.Errorf("catalog HREF is empty")
   192  	}
   193  	href := strings.Replace(adminCatalog.AdminCatalog.HREF, "/admin/", "/", 1)
   194  
   195  	// if useTenantContext is false, we use an empty header (= default behavior)
   196  	// if it is true, we use a header populated with tenant context values
   197  	accessControlHeader, err := adminCatalog.getAccessControlHeader(useTenantContext)
   198  	if err != nil {
   199  		return nil, err
   200  	}
   201  	return adminCatalog.client.GetAccessControl(href, "catalog", adminCatalog.AdminCatalog.Name, accessControlHeader)
   202  }
   203  
   204  // SetAccessControl changes the access control information for this catalog
   205  func (adminCatalog AdminCatalog) SetAccessControl(accessControl *types.ControlAccessParams, useTenantContext bool) error {
   206  
   207  	if adminCatalog.AdminCatalog.HREF == "" {
   208  		return fmt.Errorf("catalog HREF is empty")
   209  	}
   210  	href := strings.Replace(adminCatalog.AdminCatalog.HREF, "/admin/", "/", 1)
   211  
   212  	// if useTenantContext is false, we use an empty header (= default behavior)
   213  	// if it is true, we use a header populated with tenant context values
   214  	accessControlHeader, err := adminCatalog.getAccessControlHeader(useTenantContext)
   215  	if err != nil {
   216  		return err
   217  	}
   218  	return adminCatalog.client.SetAccessControl(accessControl, href, "catalog", adminCatalog.AdminCatalog.Name, accessControlHeader)
   219  }
   220  
   221  // RemoveAccessControl is a shortcut to SetAccessControl with all access disabled
   222  func (adminCatalog AdminCatalog) RemoveAccessControl(useTenantContext bool) error {
   223  	return adminCatalog.SetAccessControl(&types.ControlAccessParams{IsSharedToEveryone: false}, useTenantContext)
   224  }
   225  
   226  // IsShared shows whether a catalog is shared or not, regardless of the number of subjects sharing it
   227  func (adminCatalog AdminCatalog) IsShared(useTenantContext bool) (bool, error) {
   228  	settings, err := adminCatalog.GetAccessControl(useTenantContext)
   229  	if err != nil {
   230  		return false, err
   231  	}
   232  	if settings.IsSharedToEveryone {
   233  		return true, nil
   234  	}
   235  	return settings.AccessSettings != nil, nil
   236  }
   237  
   238  // GetVappAccessControl is a convenience method to retrieve access control for a vApp
   239  // from a VDC.
   240  // The input variable vappIdentifier can be either the vApp name or its ID
   241  func (vdc *Vdc) GetVappAccessControl(vappIdentifier string, useTenantContext bool) (*types.ControlAccessParams, error) {
   242  	vapp, err := vdc.GetVAppByNameOrId(vappIdentifier, true)
   243  	if err != nil {
   244  		return nil, fmt.Errorf("error retrieving vApp %s: %s", vappIdentifier, err)
   245  	}
   246  	return vapp.GetAccessControl(useTenantContext)
   247  }
   248  
   249  // GetCatalogAccessControl is a convenience method to retrieve access control for a catalog
   250  // from an organization.
   251  // The input variable catalogIdentifier can be either the catalog name or its ID
   252  func (org *AdminOrg) GetCatalogAccessControl(catalogIdentifier string, useTenantContext bool) (*types.ControlAccessParams, error) {
   253  	catalog, err := org.GetAdminCatalogByNameOrId(catalogIdentifier, true)
   254  	if err != nil {
   255  		return nil, fmt.Errorf("error retrieving catalog %s: %s", catalogIdentifier, err)
   256  	}
   257  	return catalog.GetAccessControl(useTenantContext)
   258  }
   259  
   260  // GetCatalogAccessControl is a convenience method to retrieve access control for a catalog
   261  // from an organization.
   262  // The input variable catalogIdentifier can be either the catalog name or its ID
   263  func (org *Org) GetCatalogAccessControl(catalogIdentifier string, useTenantContext bool) (*types.ControlAccessParams, error) {
   264  	catalog, err := org.GetCatalogByNameOrId(catalogIdentifier, true)
   265  	if err != nil {
   266  		return nil, fmt.Errorf("error retrieving catalog %s: %s", catalogIdentifier, err)
   267  	}
   268  	return catalog.GetAccessControl(useTenantContext)
   269  }
   270  
   271  // GetAccessControl retrieves the access control information for this catalog
   272  func (catalog Catalog) GetAccessControl(useTenantContext bool) (*types.ControlAccessParams, error) {
   273  
   274  	if catalog.Catalog.HREF == "" {
   275  		return nil, fmt.Errorf("catalog HREF is empty")
   276  	}
   277  	href := strings.Replace(catalog.Catalog.HREF, "/admin/", "/", 1)
   278  	accessControlHeader, err := catalog.getAccessControlHeader(useTenantContext)
   279  	if err != nil {
   280  		return nil, err
   281  	}
   282  	return catalog.client.GetAccessControl(href, "catalog", catalog.Catalog.Name, accessControlHeader)
   283  }
   284  
   285  // SetAccessControl changes the access control information for this catalog
   286  func (catalog Catalog) SetAccessControl(accessControl *types.ControlAccessParams, useTenantContext bool) error {
   287  
   288  	if catalog.Catalog.HREF == "" {
   289  		return fmt.Errorf("catalog HREF is empty")
   290  	}
   291  
   292  	href := strings.Replace(catalog.Catalog.HREF, "/admin/", "/", 1)
   293  
   294  	// if useTenantContext is false, we use an empty header (= default behavior)
   295  	// if it is true, we use a header populated with tenant context values
   296  	accessControlHeader, err := catalog.getAccessControlHeader(useTenantContext)
   297  	if err != nil {
   298  		return err
   299  	}
   300  	return catalog.client.SetAccessControl(accessControl, href, "catalog", catalog.Catalog.Name, accessControlHeader)
   301  }
   302  
   303  // RemoveAccessControl is a shortcut to SetAccessControl with all access disabled
   304  func (catalog Catalog) RemoveAccessControl(useTenantContext bool) error {
   305  	return catalog.SetAccessControl(&types.ControlAccessParams{IsSharedToEveryone: false}, useTenantContext)
   306  }
   307  
   308  // IsShared shows whether a catalog is shared or not, regardless of the number of subjects sharing it
   309  func (catalog Catalog) IsShared(useTenantContext bool) (bool, error) {
   310  	settings, err := catalog.GetAccessControl(useTenantContext)
   311  	if err != nil {
   312  		return false, err
   313  	}
   314  	if settings.IsSharedToEveryone {
   315  		return true, nil
   316  	}
   317  	return settings.AccessSettings != nil, nil
   318  }
   319  
   320  // getAccessControlHeader builds the data needed to set the header when tenant context is required.
   321  // If useTenantContext is false, it returns an empty map.
   322  // Otherwise, it finds the Org ID and name (going up in the hierarchy through the VDC)
   323  // and creates the header data
   324  func (vapp *VApp) getAccessControlHeader(useTenantContext bool) (map[string]string, error) {
   325  	if !useTenantContext {
   326  		return map[string]string{}, nil
   327  	}
   328  	orgInfo, err := vapp.getOrgInfo()
   329  	if err != nil {
   330  		return nil, err
   331  	}
   332  	return map[string]string{types.HeaderTenantContext: orgInfo.OrgId, types.HeaderAuthContext: orgInfo.OrgName}, nil
   333  }
   334  
   335  // getAccessControlHeader builds the data needed to set the header when tenant context is required.
   336  // If useTenantContext is false, it returns an empty map.
   337  // Otherwise, it finds the Org ID and name and creates the header data
   338  func (catalog *Catalog) getAccessControlHeader(useTenantContext bool) (map[string]string, error) {
   339  	if !useTenantContext {
   340  		return map[string]string{}, nil
   341  	}
   342  	orgInfo, err := catalog.getOrgInfo()
   343  	if err != nil {
   344  		return nil, err
   345  	}
   346  	return map[string]string{types.HeaderTenantContext: orgInfo.OrgId, types.HeaderAuthContext: orgInfo.OrgName}, nil
   347  }
   348  
   349  // getAccessControlHeader builds the data needed to set the header when tenant context is required.
   350  // If useTenantContext is false, it returns an empty map.
   351  // Otherwise, it finds the Org ID and name and creates the header data
   352  func (adminCatalog *AdminCatalog) getAccessControlHeader(useTenantContext bool) (map[string]string, error) {
   353  	if !useTenantContext {
   354  		return map[string]string{}, nil
   355  	}
   356  	orgInfo, err := adminCatalog.getOrgInfo()
   357  
   358  	if err != nil {
   359  		return nil, err
   360  	}
   361  	return map[string]string{types.HeaderTenantContext: orgInfo.OrgId, types.HeaderAuthContext: orgInfo.OrgName}, nil
   362  }
   363  
   364  // GetControlAccess read and returns the control access parameters from a VDC
   365  func (vdc *Vdc) GetControlAccess(useTenantContext bool) (*types.ControlAccessParams, error) {
   366  	err := checkSanityVdcControlAccess(vdc)
   367  	if err != nil {
   368  		return nil, err
   369  	}
   370  
   371  	var tenantContextHeaders map[string]string
   372  
   373  	if useTenantContext {
   374  		tenantContext, err := vdc.getTenantContext()
   375  		if err != nil {
   376  			return nil, fmt.Errorf("error getting the tenant context - %s", err)
   377  		}
   378  
   379  		tenantContextHeaders = getTenantContextHeader(tenantContext)
   380  	}
   381  
   382  	controlAccessParams, err := vdc.client.GetAccessControl(vdc.Vdc.HREF, "vdc", vdc.Vdc.Name, tenantContextHeaders)
   383  	if err != nil {
   384  		return nil, fmt.Errorf("there was an error when retrieving VDC control access params - %s", err)
   385  	}
   386  
   387  	return controlAccessParams, nil
   388  }
   389  
   390  // SetControlAccess sets VDC control access parameters for everybody or individual users/groups.
   391  // This method either sets control for everybody, passing isSharedToEveryOne true, and everyoneAccessLevel (currently only ReadOnly is supported for VDC) and nil for accessSettings,
   392  // or can set access control for specific users/groups, passing isSharedToEveryOne false, everyoneAccessLevel "" and accessSettings filled as desired.
   393  // The method will fail if tries to configure access control for everybody and passes individual users/groups to configure.
   394  // It returns the control access parameters that are read from the API (using Vdc.GetControlAccess).
   395  func (vdc *Vdc) SetControlAccess(isSharedToEveryOne bool, everyoneAccessLevel string, accessSettings []*types.AccessSetting, useTenantContext bool) (*types.ControlAccessParams, error) {
   396  	err := checkSanityVdcControlAccess(vdc)
   397  	if err != nil {
   398  		return nil, err
   399  	}
   400  
   401  	if (isSharedToEveryOne && accessSettings != nil) && len(accessSettings) > 0 {
   402  		return nil, fmt.Errorf("either configure access for everybody or individual users, not both at the same time")
   403  	}
   404  
   405  	var tenantContextHeaders map[string]string
   406  	var accessControl = &types.ControlAccessParams{
   407  		Xmlns: types.XMLNamespaceVCloud,
   408  	}
   409  
   410  	if isSharedToEveryOne { // Do configuration for everyone
   411  		if everyoneAccessLevel == "" {
   412  			return nil, fmt.Errorf("everyoneAccessLevel needs to be set if isSharedToEveryOne is true")
   413  		}
   414  
   415  		accessControl.IsSharedToEveryone = true
   416  		accessControl.EveryoneAccessLevel = &everyoneAccessLevel
   417  
   418  	} else { // Do configuration for individual users/groups
   419  		if len(accessSettings) > 0 {
   420  			accessControl.AccessSettings = &types.AccessSettingList{
   421  				AccessSetting: accessSettings,
   422  			}
   423  		}
   424  	}
   425  
   426  	if useTenantContext {
   427  		tenantContext, err := vdc.getTenantContext()
   428  		if err != nil {
   429  			return nil, fmt.Errorf("error getting the tenant context - %s", err)
   430  		}
   431  
   432  		tenantContextHeaders = getTenantContextHeader(tenantContext)
   433  	}
   434  
   435  	err = vdc.client.setAccessControlWithHttpMethod(http.MethodPut, accessControl, vdc.Vdc.HREF, "vdc", vdc.Vdc.Name, tenantContextHeaders)
   436  	if err != nil {
   437  		return nil, fmt.Errorf("there was an error when setting VDC control access params - %s", err)
   438  	}
   439  
   440  	return vdc.GetControlAccess(useTenantContext)
   441  }
   442  
   443  // DeleteControlAccess makes stop sharing VDC with anyone
   444  func (vdc *Vdc) DeleteControlAccess(useTenantContext bool) (*types.ControlAccessParams, error) {
   445  	return vdc.SetControlAccess(false, "", nil, useTenantContext)
   446  }
   447  
   448  // checkSanityVdcControlAccess is a function that check some Vdc attributes and returns error if any is missing. It is useful for
   449  // checking sanity of Vdc struct before running controlAccess methods.
   450  func checkSanityVdcControlAccess(vdc *Vdc) error {
   451  	if vdc.client == nil {
   452  		return fmt.Errorf("client has not been set up on Vdc struct. Please initialize it before using this method")
   453  	}
   454  	if vdc.Vdc == nil || vdc.Vdc.Name == "" {
   455  		return fmt.Errorf("types.Vdc struct has not been set up on Vdc struct or Vdc.Vdc.Name is missing. Please initialize it before using this method ")
   456  	}
   457  	return nil
   458  }
   459  
   460  func publishCatalog(client *Client, catalogUrl string, tenantContext *TenantContext, publishCatalog types.PublishCatalogParams) error {
   461  	catalogUrl = catalogUrl + "/action/publish"
   462  
   463  	publishCatalog.Xmlns = types.XMLNamespaceVCloud
   464  
   465  	if tenantContext != nil {
   466  		client.SetCustomHeader(getTenantContextHeader(tenantContext))
   467  	}
   468  
   469  	err := client.ExecuteRequestWithoutResponse(catalogUrl, http.MethodPost,
   470  		types.PublishCatalog, "error setting catalog publishing state: %s", publishCatalog)
   471  
   472  	if tenantContext != nil {
   473  		client.RemoveProvidedCustomHeaders(getTenantContextHeader(tenantContext))
   474  	}
   475  
   476  	return err
   477  }
   478  
   479  // IsSharedReadOnly returns the state of the catalog read-only sharing to all organizations
   480  func (cat *Catalog) IsSharedReadOnly() (bool, error) {
   481  	accessControl, err := cat.GetAccessControl(true)
   482  	if err != nil {
   483  		return false, err
   484  	}
   485  	if accessControl.AccessSettings != nil || accessControl.IsSharedToEveryone {
   486  		return false, nil
   487  	}
   488  	err = cat.Refresh()
   489  	if err != nil {
   490  		return false, err
   491  	}
   492  	return cat.Catalog.IsPublished, nil
   493  }
   494  
   495  // IsSharedReadOnly returns the state of the catalog read-only sharing to all organizations
   496  func (cat *AdminCatalog) IsSharedReadOnly() (bool, error) {
   497  	accessControl, err := cat.GetAccessControl(true)
   498  	if err != nil {
   499  		return false, err
   500  	}
   501  	if accessControl.AccessSettings != nil || accessControl.IsSharedToEveryone {
   502  		return false, nil
   503  	}
   504  	err = cat.Refresh()
   505  	if err != nil {
   506  		return false, err
   507  	}
   508  	return cat.AdminCatalog.IsPublished, nil
   509  }
   510  
   511  // publish publishes a catalog read-only access control to all organizations.
   512  // This operation is usually the second step for a read-only sharing to all Orgs
   513  func (cat *Catalog) publish(isPublished bool) error {
   514  	if cat.Catalog == nil {
   515  		return fmt.Errorf("cannot publish catalog, Object is empty")
   516  	}
   517  
   518  	catalogUrl := cat.Catalog.HREF
   519  	if catalogUrl == "nil" || catalogUrl == "" {
   520  		return fmt.Errorf("cannot publish catalog, HREF is empty")
   521  	}
   522  
   523  	tenantContext, err := cat.getTenantContext()
   524  	if err != nil {
   525  		return fmt.Errorf("cannot publish catalog, tenant context error: %s", err)
   526  	}
   527  
   528  	publishParameters := types.PublishCatalogParams{
   529  		IsPublished: &isPublished,
   530  	}
   531  	err = publishCatalog(cat.client, catalogUrl, tenantContext, publishParameters)
   532  	if err != nil {
   533  		return err
   534  	}
   535  
   536  	return cat.Refresh()
   537  }
   538  
   539  // publish publishes a catalog read-only access control to all organizations.
   540  // This operation is usually the second step for a read-only sharing to all Orgs
   541  func (cat *AdminCatalog) publish(isPublished bool) error {
   542  	if cat.AdminCatalog == nil {
   543  		return fmt.Errorf("cannot publish catalog, Object is empty")
   544  	}
   545  
   546  	catalogUrl := cat.AdminCatalog.HREF
   547  	if catalogUrl == "nil" || catalogUrl == "" {
   548  		return fmt.Errorf("cannot publish catalog, HREF is empty")
   549  	}
   550  
   551  	tenantContext, err := cat.getTenantContext()
   552  	if err != nil {
   553  		return fmt.Errorf("cannot publish catalog, tenant context error: %s", err)
   554  	}
   555  
   556  	publishParameters := types.PublishCatalogParams{
   557  		IsPublished: &isPublished,
   558  	}
   559  	err = publishCatalog(cat.client, catalogUrl, tenantContext, publishParameters)
   560  	if err != nil {
   561  		return err
   562  	}
   563  
   564  	err = cat.Refresh()
   565  	if err != nil {
   566  		return err
   567  	}
   568  
   569  	return err
   570  }
   571  
   572  // SetReadOnlyAccessControl will create or rescind the read-only catalog sharing to all organizations
   573  func (cat *Catalog) SetReadOnlyAccessControl(isPublished bool) error {
   574  	if cat.Catalog == nil {
   575  		return fmt.Errorf("cannot set access control, Object is empty")
   576  	}
   577  	err := cat.SetAccessControl(&types.ControlAccessParams{
   578  		IsSharedToEveryone:  false,
   579  		EveryoneAccessLevel: addrOf(types.ControlAccessReadOnly),
   580  	}, true)
   581  	if err != nil {
   582  		return fmt.Errorf("error resetting access control record for catalog %s: %s", cat.Catalog.Name, err)
   583  	}
   584  	return cat.publish(isPublished)
   585  }
   586  
   587  // SetReadOnlyAccessControl will create or rescind the read-only AdminCatalog sharing to all organizations
   588  func (cat *AdminCatalog) SetReadOnlyAccessControl(isPublished bool) error {
   589  	if cat.AdminCatalog == nil {
   590  		return fmt.Errorf("cannot set access control, Object is empty")
   591  	}
   592  	err := cat.SetAccessControl(&types.ControlAccessParams{
   593  		IsSharedToEveryone:  false,
   594  		EveryoneAccessLevel: addrOf(types.ControlAccessReadOnly),
   595  	}, true)
   596  	if err != nil {
   597  		return err
   598  	}
   599  	return cat.publish(isPublished)
   600  }