github.com/Venafi/vcert/v5@v5.10.2/pkg/venafi/cloud/connector.go (about)

     1  /*
     2   * Copyright 2018-2022 Venafi, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *  http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package cloud
    18  
    19  import (
    20  	"archive/zip"
    21  	"bytes"
    22  	"crypto/rand"
    23  	"crypto/x509"
    24  	"encoding/base64"
    25  	"encoding/json"
    26  	"encoding/pem"
    27  	"errors"
    28  	"fmt"
    29  	"io"
    30  	"log"
    31  	"net/http"
    32  	netUrl "net/url"
    33  	"strings"
    34  	"time"
    35  
    36  	"github.com/go-http-utils/headers"
    37  	"golang.org/x/crypto/nacl/box"
    38  
    39  	"github.com/Venafi/vcert/v5/pkg/certificate"
    40  	"github.com/Venafi/vcert/v5/pkg/endpoint"
    41  	"github.com/Venafi/vcert/v5/pkg/policy"
    42  	"github.com/Venafi/vcert/v5/pkg/util"
    43  	"github.com/Venafi/vcert/v5/pkg/verror"
    44  	"github.com/Venafi/vcert/v5/pkg/webclient/cloudproviders"
    45  	"github.com/Venafi/vcert/v5/pkg/webclient/notificationservice"
    46  )
    47  
    48  type urlResource string
    49  
    50  const (
    51  	apiURL                                        = "api.venafi.cloud/"
    52  	apiVersion                                    = "v1/"
    53  	basePath                                      = "outagedetection/" + apiVersion
    54  	urlResourceUserAccounts           urlResource = apiVersion + "useraccounts"
    55  	urlResourceCertificateRequests    urlResource = basePath + "certificaterequests"
    56  	urlResourceCertificatesRetirement             = urlResourceCertificates + "/retirement"
    57  	urlResourceCertificateStatus                  = urlResourceCertificateRequests + "/%s"
    58  	urlResourceCertificates           urlResource = basePath + "certificates"
    59  	urlResourceCertificateByID                    = urlResourceCertificates + "/%s"
    60  	urlResourceCertificateRetrievePem             = urlResourceCertificates + "/%s/contents"
    61  	urlResourceCertificateSearch      urlResource = basePath + "certificatesearch"
    62  	urlResourceTemplate               urlResource = basePath + "applications/%s/certificateissuingtemplates/%s"
    63  	urlAppDetailsByName               urlResource = basePath + "applications/name/%s"
    64  	urlIssuingTemplate                urlResource = apiVersion + "certificateissuingtemplates"
    65  	urlAppRoot                        urlResource = basePath + "applications"
    66  	urlCAAccounts                     urlResource = apiVersion + "certificateauthorities/%s/accounts"
    67  	urlCAAccountDetails                           = urlCAAccounts + "/%s"
    68  	urlResourceCertificateKS                      = urlResourceCertificates + "/%s/keystore"
    69  	urlDekPublicKey                   urlResource = apiVersion + "edgeencryptionkeys/%s"
    70  	urlUsers                          urlResource = apiVersion + "users"
    71  	urlUserById                                   = urlUsers + "/%s"
    72  	urlUsersByName                                = urlUsers + "/username/%s"
    73  	urlTeams                          urlResource = apiVersion + "teams"
    74  	urlCertificateDetails                         = basePath + "certificates/%s"
    75  	urlGraphql                                    = "graphql"
    76  
    77  	defaultAppName = "Default"
    78  	oauthTokenType = "Bearer"
    79  )
    80  
    81  type condorChainOption string
    82  
    83  const (
    84  	condorChainOptionRootFirst condorChainOption = "ROOT_FIRST"
    85  	condorChainOptionRootLast  condorChainOption = "EE_FIRST"
    86  )
    87  
    88  // Connector contains the base data needed to communicate with the Venafi Cloud servers
    89  type Connector struct {
    90  	baseURL               string
    91  	apiKey                string
    92  	accessToken           string
    93  	verbose               bool
    94  	user                  *userDetails
    95  	trust                 *x509.CertPool
    96  	zone                  cloudZone
    97  	client                *http.Client
    98  	userAgent             string
    99  	cloudProvidersClient  *cloudproviders.CloudProvidersClient
   100  	notificationSvcClient *notificationservice.NotificationServiceClient
   101  }
   102  
   103  // NewConnector creates a new Venafi Cloud Connector object used to communicate with Venafi Cloud
   104  func NewConnector(url string, zone string, verbose bool, trust *x509.CertPool) (*Connector, error) {
   105  	cZone := cloudZone{zone: zone}
   106  	c := Connector{verbose: verbose, trust: trust, zone: cZone, userAgent: util.DefaultUserAgent}
   107  
   108  	var err error
   109  	c.baseURL, err = normalizeURL(url)
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  	return &c, nil
   114  }
   115  
   116  func (c *Connector) GetType() endpoint.ConnectorType {
   117  	return endpoint.ConnectorTypeCloud
   118  }
   119  
   120  func (c *Connector) SetZone(z string) {
   121  	cZone := cloudZone{zone: z}
   122  	c.zone = cZone
   123  }
   124  
   125  func (c *Connector) SetUserAgent(userAgent string) {
   126  	c.userAgent = userAgent
   127  }
   128  
   129  func (c *Connector) SetHTTPClient(client *http.Client) {
   130  	c.client = client
   131  }
   132  
   133  // Ping attempts to connect to the Venafi Cloud API and returns an error if it cannot
   134  func (c *Connector) Ping() (err error) {
   135  	return nil
   136  }
   137  
   138  // Authenticate authenticates the user with Venafi Cloud using the provided API Key
   139  func (c *Connector) Authenticate(auth *endpoint.Authentication) error {
   140  	if auth == nil {
   141  		return fmt.Errorf("failed to authenticate: missing credentials")
   142  	}
   143  
   144  	//1. Access token. Assign it to connector
   145  	if auth.AccessToken != "" {
   146  		c.accessToken = auth.AccessToken
   147  	} else if auth.TokenURL != "" && auth.ExternalJWT != "" {
   148  		//2. JWT and token URL. use it to request new access token
   149  		tokenResponse, err := c.GetAccessToken(auth)
   150  		if err != nil {
   151  			return err
   152  		}
   153  		c.accessToken = tokenResponse.AccessToken
   154  	} else if auth.APIKey != "" {
   155  		// 3. API key. Get user to test authentication
   156  		c.apiKey = auth.APIKey
   157  		url := c.getURL(urlResourceUserAccounts)
   158  		statusCode, status, body, err := c.request("GET", url, nil, true)
   159  		if err != nil {
   160  			return err
   161  		}
   162  		ud, err := parseUserDetailsResult(http.StatusOK, statusCode, status, body)
   163  		if err != nil {
   164  			return err
   165  		}
   166  		c.user = ud
   167  	}
   168  
   169  	// Initialize clients
   170  	c.cloudProvidersClient = cloudproviders.NewCloudProvidersClient(c.getURL(urlGraphql), c.getGraphqlHTTPClient())
   171  	c.notificationSvcClient = notificationservice.NewNotificationServiceClient(c.baseURL, c.accessToken, c.apiKey)
   172  
   173  	return nil
   174  }
   175  
   176  func (c *Connector) ReadPolicyConfiguration() (policy *endpoint.Policy, err error) {
   177  	if !c.isAuthenticated() {
   178  		return nil, fmt.Errorf("must be autheticated to request a certificate")
   179  
   180  	}
   181  	config, err := c.ReadZoneConfiguration()
   182  	if err != nil {
   183  		return nil, err
   184  	}
   185  	policy = &config.Policy
   186  	return
   187  }
   188  
   189  // ReadZoneConfiguration reads the Zone information needed for generating and requesting a certificate from Venafi Cloud
   190  func (c *Connector) ReadZoneConfiguration() (config *endpoint.ZoneConfiguration, err error) {
   191  	if !c.isAuthenticated() {
   192  		return nil, fmt.Errorf("must be autheticated to request a certificate")
   193  	}
   194  
   195  	var template *certificateTemplate
   196  	var statusCode int
   197  
   198  	// to fully support the "headless registration" use case...
   199  	// if application does not exist and is for the default CIT, create the application
   200  	citAlias := c.zone.getTemplateAlias()
   201  	if citAlias == "Default" {
   202  		appName := c.zone.getApplicationName()
   203  		_, statusCode, err = c.getAppDetailsByName(appName)
   204  		if err != nil && statusCode == 404 {
   205  			log.Printf("creating application %s for issuing template %s", appName, citAlias)
   206  
   207  			ps := policy.PolicySpecification{}
   208  			template, err = getCit(c, citAlias)
   209  			if err != nil {
   210  				return
   211  			}
   212  			_, err = c.createApplication(appName, &ps, template)
   213  			if err != nil {
   214  				return
   215  			}
   216  		}
   217  	}
   218  	if template == nil {
   219  		template, err = c.getTemplateByID()
   220  		if err != nil {
   221  			return
   222  		}
   223  	}
   224  	config = getZoneConfiguration(template)
   225  	return config, nil
   226  }
   227  
   228  // GetZonesByParent returns a list of valid zones for a VaaS application specified by parent
   229  func (c *Connector) GetZonesByParent(parent string) ([]string, error) {
   230  	if !c.isAuthenticated() {
   231  		return nil, fmt.Errorf("must be autheticated to request a certificate")
   232  	}
   233  
   234  	zones := make([]string, 0)
   235  	appDetails, _, err := c.getAppDetailsByName(parent)
   236  	if err != nil {
   237  		return nil, err
   238  	}
   239  
   240  	for citAlias := range appDetails.CitAliasToIdMap {
   241  		zone := fmt.Sprintf("%s\\%s", parent, citAlias)
   242  		zones = append(zones, zone)
   243  	}
   244  	return zones, nil
   245  }
   246  
   247  // ResetCertificate resets the state of a certificate.
   248  func (c *Connector) ResetCertificate(_ *certificate.Request, _ bool) (err error) {
   249  	return fmt.Errorf("not supported by endpoint")
   250  }
   251  
   252  // RequestCertificate submits the CSR to the Venafi Cloud API for processing
   253  func (c *Connector) RequestCertificate(req *certificate.Request) (requestID string, err error) {
   254  	if !c.isAuthenticated() {
   255  		return "", fmt.Errorf("must be autheticated to request a certificate")
   256  	}
   257  
   258  	url := c.getURL(urlResourceCertificateRequests)
   259  	cloudReq, err := c.getCloudRequest(req)
   260  	if err != nil {
   261  		return "", err
   262  	}
   263  
   264  	statusCode, status, body, err := c.request("POST", url, cloudReq)
   265  
   266  	if err != nil {
   267  		return "", err
   268  	}
   269  	cr, err := parseCertificateRequestResult(statusCode, status, body)
   270  	if err != nil {
   271  		return "", err
   272  	}
   273  	requestID = cr.CertificateRequests[0].ID
   274  	req.PickupID = requestID
   275  	return requestID, nil
   276  }
   277  
   278  // RetrieveCertificate retrieves the certificate for the specified ID
   279  func (c *Connector) RetrieveCertificate(req *certificate.Request) (*certificate.PEMCollection, error) {
   280  	if !c.isAuthenticated() {
   281  		return nil, fmt.Errorf("must be autheticated to request a certificate")
   282  	}
   283  
   284  	if req.PickupID == "" && req.CertID == "" && req.Thumbprint != "" {
   285  		// search cert by Thumbprint and fill pickupID
   286  		var certificateRequestId string
   287  		searchResult, err := c.searchCertificatesByFingerprint(req.Thumbprint)
   288  		if err != nil {
   289  			return nil, fmt.Errorf("failed to retrieve certificate: %s", err)
   290  		}
   291  		if len(searchResult.Certificates) == 0 {
   292  			return nil, fmt.Errorf("no certificate found using fingerprint %s", req.Thumbprint)
   293  		}
   294  
   295  		var reqIds []string
   296  		isOnlyOneCertificateRequestId := true
   297  		for _, c := range searchResult.Certificates {
   298  			reqIds = append(reqIds, c.CertificateRequestId)
   299  			if certificateRequestId != "" && certificateRequestId != c.CertificateRequestId {
   300  				isOnlyOneCertificateRequestId = false
   301  			}
   302  			if c.CertificateRequestId != "" {
   303  				certificateRequestId = c.CertificateRequestId
   304  			}
   305  			if c.Id != "" {
   306  				req.CertID = c.Id
   307  			}
   308  		}
   309  		if !isOnlyOneCertificateRequestId {
   310  			return nil, fmt.Errorf("more than one CertificateRequestId was found with the same Fingerprint: %s", reqIds)
   311  		}
   312  
   313  		req.PickupID = certificateRequestId
   314  	}
   315  
   316  	var certificateId string
   317  	if req.CertID == "" && req.PickupID != "" {
   318  		certId, err := c.getCertIDFromPickupID(req.PickupID, req.Timeout)
   319  		if err != nil {
   320  			return nil, err
   321  		}
   322  		certificateId = *certId
   323  	} else {
   324  		certificateId = req.CertID
   325  	}
   326  
   327  	// Download the private key and certificate in case the certificate is service generated
   328  	if req.CsrOrigin == certificate.ServiceGeneratedCSR || req.FetchPrivateKey {
   329  		var currentId string
   330  		if req.CertID != "" {
   331  			currentId = req.CertID
   332  		} else if certificateId != "" {
   333  			currentId = certificateId
   334  		}
   335  
   336  		dekInfo, err := getDekInfo(c, currentId)
   337  		if err != nil {
   338  			return nil, err
   339  		}
   340  
   341  		req.CertID = currentId
   342  		return retrieveServiceGeneratedCertData(c, req, dekInfo)
   343  	}
   344  
   345  	url := c.getURL(urlResourceCertificateRetrievePem)
   346  	url = fmt.Sprintf(url, certificateId)
   347  
   348  	switch {
   349  	case req.CertID != "":
   350  		statusCode, status, body, err := c.waitForCertificate(url, req) //c.request("GET", url, nil)
   351  		if err != nil {
   352  			return nil, err
   353  		}
   354  		if statusCode != http.StatusOK {
   355  			return nil, fmt.Errorf("failed to retrieve certificate. StatusCode: %d -- Status: %s -- Server Data: %s", statusCode, status, body)
   356  		}
   357  		return newPEMCollectionFromResponse(body, certificate.ChainOptionIgnore)
   358  	case req.PickupID != "":
   359  		url += "?chainOrder=%s&format=PEM"
   360  		switch req.ChainOption {
   361  		case certificate.ChainOptionRootFirst:
   362  			url = fmt.Sprintf(url, condorChainOptionRootFirst)
   363  		default:
   364  			url = fmt.Sprintf(url, condorChainOptionRootLast)
   365  		}
   366  		statusCode, status, body, err := c.waitForCertificate(url, req) //c.request("GET", url, nil)
   367  		if err != nil {
   368  			return nil, err
   369  		}
   370  		if statusCode == http.StatusOK {
   371  			certificates, err := newPEMCollectionFromResponse(body, req.ChainOption)
   372  			if err != nil {
   373  				return nil, err
   374  			}
   375  			err = req.CheckCertificate(certificates.Certificate)
   376  			// Add certificate id to the request
   377  			req.CertID = certificateId
   378  			return certificates, err
   379  		} else if statusCode == http.StatusConflict { // Http Status Code 409 means the certificate has not been signed by the ca yet.
   380  			return nil, endpoint.ErrCertificatePending{CertificateID: req.PickupID}
   381  		} else {
   382  			return nil, fmt.Errorf("failed to retrieve certificate. StatusCode: %d -- Status: %s", statusCode, status)
   383  		}
   384  	}
   385  	return nil, fmt.Errorf("couldn't retrieve certificate because both PickupID and CertId are empty")
   386  }
   387  
   388  // RenewCertificate attempts to renew the certificate
   389  func (c *Connector) RenewCertificate(renewReq *certificate.RenewalRequest) (requestID string, err error) {
   390  	if !c.isAuthenticated() {
   391  		return "", fmt.Errorf("must be autheticated to request a certificate")
   392  	}
   393  
   394  	/* 1st step is to get CertificateRequestId which is required to lookup managedCertificateId and zoneId */
   395  	var certificateRequestId string
   396  
   397  	if renewReq.Thumbprint != "" {
   398  		// by Thumbprint (aka Fingerprint)
   399  		searchResult, err := c.searchCertificatesByFingerprint(renewReq.Thumbprint)
   400  		if err != nil {
   401  			return "", fmt.Errorf("failed to create renewal request: %s", err)
   402  		}
   403  		if len(searchResult.Certificates) == 0 {
   404  			return "", fmt.Errorf("no certificate found using fingerprint %s", renewReq.Thumbprint)
   405  		}
   406  
   407  		var reqIds []string
   408  		isOnlyOneCertificateRequestId := true
   409  		for _, c := range searchResult.Certificates {
   410  			reqIds = append(reqIds, c.CertificateRequestId)
   411  			if certificateRequestId != "" && certificateRequestId != c.CertificateRequestId {
   412  				isOnlyOneCertificateRequestId = false
   413  			}
   414  			certificateRequestId = c.CertificateRequestId
   415  		}
   416  		if !isOnlyOneCertificateRequestId {
   417  			return "", fmt.Errorf("error: more than one CertificateRequestId was found with the same Fingerprint: %s", reqIds)
   418  		}
   419  	} else if renewReq.CertificateDN != "" {
   420  		// by CertificateDN (which is the same as CertificateRequestId for current implementation)
   421  		certificateRequestId = renewReq.CertificateDN
   422  	} else {
   423  		return "", fmt.Errorf("failed to create renewal request: CertificateDN or Thumbprint required")
   424  	}
   425  
   426  	/* 2nd step is to get ManagedCertificateId & ZoneId by looking up certificate request record */
   427  	previousRequest, err := c.getCertificateStatus(certificateRequestId)
   428  	if err != nil {
   429  		return "", fmt.Errorf("certificate renew failed: %s", err)
   430  	}
   431  	applicationId := previousRequest.ApplicationId
   432  	templateId := previousRequest.TemplateId
   433  	certificateId := previousRequest.CertificateIdsList[0]
   434  
   435  	emptyField := ""
   436  	if certificateId == "" {
   437  		emptyField = "certificateId"
   438  	} else if applicationId == "" {
   439  		emptyField = "applicationId"
   440  	} else if templateId == "" {
   441  		emptyField = "templateId"
   442  	}
   443  	if emptyField != "" {
   444  		return "", fmt.Errorf("failed to submit renewal request for certificate: %s is empty, certificate status is %s", emptyField, previousRequest.Status)
   445  	}
   446  
   447  	/* 3rd step is to get Certificate Object by id
   448  	and check if latestCertificateRequestId there equals to certificateRequestId from 1st step */
   449  	managedCertificate, err := c.getCertificate(certificateId)
   450  	if err != nil {
   451  		return "", fmt.Errorf("failed to renew certificate: %s", err)
   452  	}
   453  	if managedCertificate.CertificateRequestId != certificateRequestId {
   454  		withThumbprint := ""
   455  		if renewReq.Thumbprint != "" {
   456  			withThumbprint = fmt.Sprintf("with thumbprint %s ", renewReq.Thumbprint)
   457  		}
   458  		return "", fmt.Errorf(
   459  			"certificate under requestId %s %s is not the latest under CertificateId %s."+
   460  				"The latest request is %s. This error may happen when revoked certificate is requested to be renewed",
   461  			certificateRequestId, withThumbprint, certificateId, managedCertificate.CertificateRequestId)
   462  	}
   463  
   464  	/* 4th step is to send renewal request */
   465  	url := c.getURL(urlResourceCertificateRequests)
   466  
   467  	req := certificateRequest{
   468  		ExistingCertificateId: certificateId,
   469  		ApplicationId:         applicationId,
   470  		TemplateId:            templateId,
   471  	}
   472  
   473  	if renewReq.CertificateRequest.Location != nil {
   474  		workload := renewReq.CertificateRequest.Location.Workload
   475  		if workload == "" {
   476  			workload = defaultAppName
   477  		}
   478  		nodeName := renewReq.CertificateRequest.Location.Instance
   479  		appName := workload
   480  
   481  		req.CertificateUsageMetadata = []certificateUsageMetadata{
   482  			{
   483  				AppName:  appName,
   484  				NodeName: nodeName,
   485  			},
   486  		}
   487  	}
   488  
   489  	if renewReq.CertificateRequest != nil && len(renewReq.CertificateRequest.GetCSR()) != 0 {
   490  		req.CSR = string(renewReq.CertificateRequest.GetCSR())
   491  		req.ReuseCSR = false
   492  	} else {
   493  		req.ReuseCSR = true
   494  		return "", fmt.Errorf("reuseCSR option is not currently available for Renew Certificate operation. A new CSR must be provided in the request")
   495  	}
   496  	statusCode, status, body, err := c.request("POST", url, req)
   497  	if err != nil {
   498  		return
   499  	}
   500  
   501  	cr, err := parseCertificateRequestResult(statusCode, status, body)
   502  	if err != nil {
   503  		return "", fmt.Errorf("failed to renew certificate: %s", err)
   504  	}
   505  	return cr.CertificateRequests[0].ID, nil
   506  }
   507  
   508  // RetireCertificate attempts to retire the certificate
   509  func (c *Connector) RetireCertificate(retireReq *certificate.RetireRequest) error {
   510  	if !c.isAuthenticated() {
   511  		return fmt.Errorf("must be autheticated to request a certificate")
   512  	}
   513  
   514  	url := c.getURL(urlResourceCertificatesRetirement)
   515  	/* 1st step is to get CertificateRequestId which is required to retire certificate */
   516  	var certificateRequestId string
   517  	if retireReq.Thumbprint != "" {
   518  		// by Thumbprint (aka Fingerprint)
   519  		searchResult, err := c.searchCertificatesByFingerprint(retireReq.Thumbprint)
   520  		if err != nil {
   521  			return fmt.Errorf("failed to create retire request: %s", err)
   522  		}
   523  		if len(searchResult.Certificates) == 0 {
   524  			return fmt.Errorf("no certificate found using fingerprint %s", retireReq.Thumbprint)
   525  		}
   526  
   527  		var reqIds []string
   528  		isOnlyOneCertificateRequestId := true
   529  		for _, c := range searchResult.Certificates {
   530  			reqIds = append(reqIds, c.CertificateRequestId)
   531  			if certificateRequestId != "" && certificateRequestId != c.CertificateRequestId {
   532  				isOnlyOneCertificateRequestId = false
   533  			}
   534  			certificateRequestId = c.CertificateRequestId
   535  		}
   536  		if !isOnlyOneCertificateRequestId {
   537  			return fmt.Errorf("error: more than one CertificateRequestId was found with the same Fingerprint: %s", reqIds)
   538  		}
   539  	} else if retireReq.CertificateDN != "" {
   540  		// by CertificateDN (which is the same as CertificateRequestId for current implementation)
   541  		certificateRequestId = retireReq.CertificateDN
   542  	} else {
   543  		return fmt.Errorf("failed to create retire request: CertificateDN or Thumbprint required")
   544  	}
   545  
   546  	/* 2nd step is to get ManagedCertificateId & ZoneId by looking up certificate request record */
   547  	previousRequest, err := c.getCertificateStatus(certificateRequestId)
   548  	if err != nil {
   549  		if strings.Contains(err.Error(), "Unable to find certificateRequest") {
   550  			return fmt.Errorf("invalid thumbprint or certificate ID. No certificates were retired")
   551  		}
   552  		return fmt.Errorf("certificate retirement failed: error on getting Certificate ID: %s", err)
   553  	}
   554  	certificateId := previousRequest.CertificateIdsList[0]
   555  
   556  	/* Now we do retirement*/
   557  	retRequest := certificateRetireRequest{
   558  		CertificateIds: []string{certificateId},
   559  	}
   560  
   561  	statusCode, status, response, err := c.request("POST", url, retRequest)
   562  	if err != nil {
   563  		return err
   564  	}
   565  
   566  	err = checkCertificateRetireResults(statusCode, status, response)
   567  	if err != nil {
   568  		return err
   569  	}
   570  
   571  	return nil
   572  }
   573  
   574  // RevokeCertificate attempts to revoke the certificate
   575  func (c *Connector) RevokeCertificate(_ *certificate.RevocationRequest) (err error) {
   576  	return fmt.Errorf("not supported by endpoint")
   577  }
   578  
   579  func (c *Connector) ImportCertificate(req *certificate.ImportRequest) (*certificate.ImportResponse, error) {
   580  	if !c.isAuthenticated() {
   581  		return nil, fmt.Errorf("must be autheticated to request a certificate")
   582  	}
   583  
   584  	pBlock, _ := pem.Decode([]byte(req.CertificateData))
   585  	if pBlock == nil {
   586  		return nil, fmt.Errorf("%w can`t parse certificate", verror.UserDataError)
   587  	}
   588  	zone := req.PolicyDN
   589  	if zone == "" {
   590  		appDetails, _, err := c.getAppDetailsByName(c.zone.getApplicationName())
   591  		if err != nil {
   592  			return nil, err
   593  		}
   594  		zone = appDetails.ApplicationId
   595  	}
   596  	ipAddr := endpoint.LocalIP
   597  	origin := endpoint.SDKName
   598  	for _, f := range req.CustomFields {
   599  		if f.Type == certificate.CustomFieldOrigin {
   600  			origin = f.Value
   601  		}
   602  	}
   603  	base64.StdEncoding.EncodeToString(pBlock.Bytes)
   604  	fingerprint := certThumbprint(pBlock.Bytes)
   605  	request := importRequest{
   606  		Certificates: []importRequestCertInfo{
   607  			{
   608  				Certificate:    base64.StdEncoding.EncodeToString(pBlock.Bytes),
   609  				ApplicationIds: []string{zone},
   610  				ApiClientInformation: apiClientInformation{
   611  					Type:       origin,
   612  					Identifier: ipAddr,
   613  				},
   614  			},
   615  		},
   616  	}
   617  
   618  	url := c.getURL(urlResourceCertificates)
   619  	statusCode, status, body, err := c.request("POST", url, request)
   620  	if err != nil {
   621  		return nil, fmt.Errorf("%w: %v", verror.ServerTemporaryUnavailableError, err)
   622  	}
   623  	var r importResponse
   624  	switch statusCode {
   625  	case http.StatusOK, http.StatusCreated, http.StatusAccepted:
   626  	case http.StatusBadRequest, http.StatusForbidden, http.StatusConflict:
   627  		return nil, fmt.Errorf("%w: certificate can`t be imported. %d %s %s", verror.ServerBadDataResponce, statusCode, status, string(body))
   628  	case http.StatusInternalServerError, http.StatusBadGateway, http.StatusServiceUnavailable:
   629  		return nil, verror.ServerTemporaryUnavailableError
   630  	default:
   631  		return nil, verror.ServerError
   632  	}
   633  	err = json.Unmarshal(body, &r)
   634  	if err != nil {
   635  		return nil, fmt.Errorf("%w: can`t unmarshal json response %s", verror.ServerError, err)
   636  	} else if !(len(r.CertificateInformations) == 1) {
   637  		return nil, fmt.Errorf("%w: certificate was not imported on unknown reason", verror.ServerBadDataResponce)
   638  	}
   639  	time.Sleep(time.Second)
   640  	foundCert, err := c.searchCertificatesByFingerprint(fingerprint)
   641  	if err != nil {
   642  		return nil, err
   643  	}
   644  	if len(foundCert.Certificates) != 1 {
   645  		return nil, fmt.Errorf("%w certificate has been imported but could not be found on platform after that", verror.ServerError)
   646  	}
   647  	cert := foundCert.Certificates[0]
   648  	resp := &certificate.ImportResponse{CertificateDN: cert.SubjectCN[0], CertId: cert.Id}
   649  	return resp, nil
   650  }
   651  
   652  func (c *Connector) ListCertificates(filter endpoint.Filter) ([]certificate.CertificateInfo, error) {
   653  	if !c.isAuthenticated() {
   654  		return nil, fmt.Errorf("must be autheticated to request a certificate")
   655  	}
   656  
   657  	if c.zone.String() == "" {
   658  		return nil, fmt.Errorf("empty zone")
   659  	}
   660  	const batchSize = 50
   661  	limit := 100000000
   662  	if filter.Limit != nil {
   663  		limit = *filter.Limit
   664  	}
   665  	var buf [][]certificate.CertificateInfo
   666  	for page := 0; limit > 0; limit, page = limit-batchSize, page+1 {
   667  		var b []certificate.CertificateInfo
   668  		var err error
   669  		b, err = c.getCertsBatch(page, batchSize, filter.WithExpired)
   670  		if limit < batchSize && len(b) > limit {
   671  			b = b[:limit]
   672  		}
   673  		if err != nil {
   674  			return nil, err
   675  		}
   676  		buf = append(buf, b)
   677  		if len(b) < batchSize {
   678  			break
   679  		}
   680  	}
   681  	sumLen := 0
   682  	for _, b := range buf {
   683  		sumLen += len(b)
   684  	}
   685  	infos := make([]certificate.CertificateInfo, sumLen)
   686  	offset := 0
   687  	for _, b := range buf {
   688  		copy(infos[offset:], b[:])
   689  		offset += len(b)
   690  	}
   691  	return infos, nil
   692  }
   693  
   694  func (c *Connector) SearchCertificates(_ *certificate.SearchRequest) (*certificate.CertSearchResponse, error) {
   695  	panic("operation is not supported yet")
   696  }
   697  
   698  func (c *Connector) SearchCertificate(zone string, cn string, sans *certificate.Sans, certMinTimeLeft time.Duration) (certificateInfo *certificate.CertificateInfo, err error) {
   699  	if !c.isAuthenticated() {
   700  		return nil, fmt.Errorf("must be autheticated to request a certificate")
   701  	}
   702  
   703  	// retrieve application name from zone
   704  	appName := getAppNameFromZone(zone)
   705  	// get application id from name
   706  	app, _, err := c.getAppDetailsByName(appName)
   707  	if err != nil {
   708  		return nil, err
   709  	}
   710  
   711  	// format arguments for request
   712  	req := formatSearchCertificateArguments(cn, sans, certMinTimeLeft)
   713  
   714  	// perform request
   715  	searchResult, err := c.searchCertificates(req)
   716  	if err != nil {
   717  		return nil, err
   718  	}
   719  
   720  	// fail if no certificate is returned from api
   721  	if searchResult.Count == 0 {
   722  		return nil, verror.NoCertificateFoundError
   723  	}
   724  
   725  	// map (convert) response to an array of CertificateInfo
   726  	certificates := make([]*certificate.CertificateInfo, 0)
   727  	n := 0
   728  	for _, cert := range searchResult.Certificates {
   729  		if util.ArrayContainsString(cert.ApplicationIds, app.ApplicationId) {
   730  			match := cert.ToCertificateInfo()
   731  			certificates = append(certificates, &match)
   732  			n = n + 1
   733  		}
   734  	}
   735  
   736  	// fail if no certificates found with matching zone
   737  	if n == 0 {
   738  		return nil, verror.NoCertificateWithMatchingZoneFoundError
   739  	}
   740  
   741  	// at this point all certificates belong to our zone, the next step is
   742  	// finding the newest valid certificate matching the provided sans
   743  	return certificate.FindNewestCertificateWithSans(certificates, sans)
   744  }
   745  
   746  func (c *Connector) getCertIDFromPickupID(pickupId string, timeout time.Duration) (*string, error) {
   747  	if pickupId == "" {
   748  		return nil, fmt.Errorf("pickupID cannot be empty in order to get certificate ID")
   749  	}
   750  	startTime := time.Now()
   751  	//Wait for certificate to be issued by checking its PickupID
   752  	//If certID is filled then certificate should be already issued.
   753  
   754  	var certificateId string
   755  	for {
   756  		certStatus, err := c.getCertificateStatus(pickupId)
   757  		if err != nil {
   758  			return nil, fmt.Errorf("unable to retrieve: %s", err)
   759  		}
   760  		if certStatus.Status == "ISSUED" {
   761  			certificateId = certStatus.CertificateIdsList[0]
   762  			break // to fetch the cert itself
   763  		} else if certStatus.Status == "FAILED" {
   764  			return nil, fmt.Errorf("failed to retrieve certificate. Status: %v", certStatus)
   765  		}
   766  		if timeout == 0 {
   767  			return nil, endpoint.ErrCertificatePending{CertificateID: pickupId, Status: certStatus.Status}
   768  		} else {
   769  			log.Println("Issuance of certificate is pending...")
   770  		}
   771  		if time.Now().After(startTime.Add(timeout)) {
   772  			return nil, endpoint.ErrRetrieveCertificateTimeout{CertificateID: pickupId}
   773  		}
   774  		time.Sleep(2 * time.Second)
   775  	}
   776  	if certificateId == "" {
   777  		return nil, fmt.Errorf("something went wrong during polling cert status and we still got and empty CertificateID at the end")
   778  	}
   779  
   780  	return &certificateId, nil
   781  }
   782  
   783  func (c *Connector) IsCSRServiceGenerated(req *certificate.Request) (bool, error) {
   784  	if !c.isAuthenticated() {
   785  		return false, fmt.Errorf("must be autheticated to request a certificate")
   786  	}
   787  
   788  	if req.PickupID == "" && req.CertID == "" && req.Thumbprint != "" {
   789  		// search cert by Thumbprint and fill pickupID
   790  		var certificateRequestId string
   791  		searchResult, err := c.searchCertificatesByFingerprint(req.Thumbprint)
   792  		if err != nil {
   793  			return false, fmt.Errorf("failed to retrieve certificate: %s", err)
   794  		}
   795  		if len(searchResult.Certificates) == 0 {
   796  			return false, fmt.Errorf("no certificate found using fingerprint %s", req.Thumbprint)
   797  		}
   798  
   799  		var reqIds []string
   800  		for _, c := range searchResult.Certificates {
   801  			reqIds = append(reqIds, c.CertificateRequestId)
   802  			if certificateRequestId != "" && certificateRequestId != c.CertificateRequestId {
   803  				return false, fmt.Errorf("more than one CertificateRequestId was found with the same Fingerprint: %s", reqIds)
   804  			}
   805  			if c.CertificateRequestId != "" {
   806  				certificateRequestId = c.CertificateRequestId
   807  			}
   808  			if c.Id != "" {
   809  				req.CertID = c.Id
   810  			}
   811  		}
   812  		req.PickupID = certificateRequestId
   813  	}
   814  
   815  	var dekInfo *EdgeEncryptionKey
   816  	var currentId string
   817  	var err error
   818  	if req.CertID != "" {
   819  		dekInfo, err = getDekInfo(c, req.CertID)
   820  	} else {
   821  		var certificateId string
   822  		certificateId, err = getCertificateId(c, req)
   823  		if err == nil && certificateId != "" {
   824  			dekInfo, err = getDekInfo(c, certificateId)
   825  		}
   826  	}
   827  
   828  	if err == nil && dekInfo.Key != "" {
   829  		req.CertID = currentId
   830  		return true, err
   831  	}
   832  	return false, nil
   833  }
   834  
   835  func (c *Connector) RetrieveCertificateMetaData(_ string) (*certificate.CertificateMetaData, error) {
   836  	panic("operation is not supported yet")
   837  }
   838  
   839  // SynchronousRequestCertificate It's not supported yet in VaaS
   840  func (c *Connector) SynchronousRequestCertificate(_ *certificate.Request) (certificates *certificate.PEMCollection, err error) {
   841  	panic("operation is not supported yet")
   842  }
   843  
   844  // SupportSynchronousRequestCertificate returns if the connector support synchronous calls to request a certificate.
   845  func (c *Connector) SupportSynchronousRequestCertificate() bool {
   846  	return false
   847  }
   848  
   849  func (c *Connector) RetrieveSystemVersion() (response string, err error) {
   850  	panic("operation is not supported yet")
   851  }
   852  
   853  func getCertificateId(c *Connector, req *certificate.Request) (string, error) {
   854  	startTime := time.Now()
   855  	//Wait for certificate to be issued by checking its PickupID
   856  	//If certID is filled then certificate should be already issued.
   857  	for {
   858  		if req.PickupID == "" {
   859  			break
   860  		}
   861  		certStatus, err := c.getCertificateStatus(req.PickupID)
   862  		if err != nil {
   863  			return "", fmt.Errorf("unable to retrieve: %s", err)
   864  		}
   865  		if certStatus.Status == "ISSUED" {
   866  			return certStatus.CertificateIdsList[0], nil
   867  		} else if certStatus.Status == "FAILED" {
   868  			return "", fmt.Errorf("failed to retrieve certificate. Status: %v", certStatus)
   869  		}
   870  		if req.Timeout == 0 {
   871  			return "", endpoint.ErrCertificatePending{CertificateID: req.PickupID, Status: certStatus.Status}
   872  		} else {
   873  			log.Println("Issuance of certificate is pending...")
   874  		}
   875  		if time.Now().After(startTime.Add(req.Timeout)) {
   876  			return "", endpoint.ErrRetrieveCertificateTimeout{CertificateID: req.PickupID}
   877  		}
   878  		time.Sleep(2 * time.Second)
   879  	}
   880  
   881  	return "", endpoint.ErrRetrieveCertificateTimeout{CertificateID: req.PickupID}
   882  }
   883  
   884  // normalizeURL allows overriding the default URL used to communicate with Venafi Cloud
   885  func normalizeURL(url string) (normalizedURL string, err error) {
   886  	if url == "" {
   887  		url = apiURL
   888  	}
   889  	normalizedURL = util.NormalizeUrl(url)
   890  	return normalizedURL, nil
   891  }
   892  
   893  func (c *Connector) GetAccessToken(auth *endpoint.Authentication) (*TLSPCAccessTokenResponse, error) {
   894  	if auth == nil || auth.TokenURL == "" || auth.ExternalJWT == "" {
   895  		return nil, fmt.Errorf("failed to authenticate: missing credentials")
   896  	}
   897  
   898  	url, err := getServiceAccountTokenURL(auth.TokenURL)
   899  	if err != nil {
   900  		return nil, fmt.Errorf("failed to authenticate: %w", err)
   901  	}
   902  
   903  	body := netUrl.Values{}
   904  	body.Set("grant_type", "client_credentials")
   905  	body.Set("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer")
   906  	body.Set("client_assertion", auth.ExternalJWT)
   907  
   908  	r, err := http.NewRequest(http.MethodPost, url, strings.NewReader(body.Encode()))
   909  	if err != nil {
   910  		err = fmt.Errorf("%w: %v", verror.VcertError, err)
   911  		return nil, err
   912  	}
   913  	r.Header.Set(headers.UserAgent, c.userAgent)
   914  	r.Header.Add("Content-Type", "application/x-www-form-urlencoded")
   915  
   916  	httpClient := c.getHTTPClient()
   917  	resp, err := httpClient.Do(r)
   918  	if err != nil {
   919  		err = fmt.Errorf("%w: %v", verror.ServerUnavailableError, err)
   920  		return nil, err
   921  	}
   922  
   923  	statusCode := resp.StatusCode
   924  	status := resp.Status
   925  
   926  	defer resp.Body.Close()
   927  	respBody, err := io.ReadAll(resp.Body)
   928  	if err != nil {
   929  		err = fmt.Errorf("%w: %v", verror.ServerError, err)
   930  		return nil, err
   931  	}
   932  
   933  	accessTokenResponse, err := parseAccessTokenResponse(http.StatusOK, statusCode, status, respBody)
   934  	if err != nil {
   935  		return nil, err
   936  	}
   937  	if !strings.EqualFold(accessTokenResponse.TokenType, oauthTokenType) {
   938  		return nil, fmt.Errorf(
   939  			"%w: got an access token but token type is not %s. Expected: %s. Got: %s", verror.ServerError,
   940  			oauthTokenType, oauthTokenType, accessTokenResponse.TokenType)
   941  	}
   942  
   943  	return accessTokenResponse, nil
   944  }
   945  
   946  func (c *Connector) isAuthenticated() bool {
   947  	if c.accessToken != "" {
   948  		return true
   949  	}
   950  
   951  	if c.user != nil && c.user.Company != nil {
   952  		return true
   953  	}
   954  
   955  	return false
   956  }
   957  
   958  func (c *Connector) getCloudRequest(req *certificate.Request) (*certificateRequest, error) {
   959  	ipAddr := endpoint.LocalIP
   960  	origin := endpoint.SDKName
   961  	for _, f := range req.CustomFields {
   962  		if f.Type == certificate.CustomFieldOrigin {
   963  			origin = f.Value
   964  		}
   965  	}
   966  
   967  	appDetails, _, err := c.getAppDetailsByName(c.zone.getApplicationName())
   968  	if err != nil {
   969  		return nil, err
   970  	}
   971  	templateId := appDetails.CitAliasToIdMap[c.zone.getTemplateAlias()]
   972  
   973  	cloudReq := certificateRequest{
   974  		ApplicationId: appDetails.ApplicationId,
   975  		TemplateId:    templateId,
   976  		ApiClientInformation: certificateRequestClientInfo{
   977  			Type:       origin,
   978  			Identifier: ipAddr,
   979  		},
   980  	}
   981  
   982  	if req.CsrOrigin != certificate.ServiceGeneratedCSR {
   983  		cloudReq.CSR = string(req.GetCSR())
   984  	} else {
   985  
   986  		cloudReq.IsVaaSGenerated = true
   987  		csrAttr, err := getCsrAttributes(c, req)
   988  		if err != nil {
   989  			return nil, err
   990  		}
   991  		cloudReq.CsrAttributes = *(csrAttr)
   992  		cloudReq.ApplicationServerTypeId = util.ApplicationServerTypeID
   993  
   994  	}
   995  
   996  	if req.Location != nil {
   997  		workload := req.Location.Workload
   998  		if workload == "" {
   999  			workload = defaultAppName
  1000  		}
  1001  		nodeName := req.Location.Instance
  1002  		appName := workload
  1003  
  1004  		cloudReq.CertificateUsageMetadata = []certificateUsageMetadata{
  1005  			{
  1006  				AppName:  appName,
  1007  				NodeName: nodeName,
  1008  			},
  1009  		}
  1010  	}
  1011  
  1012  	validityDuration := req.ValidityDuration
  1013  
  1014  	// DEPRECATED: ValidityHours is deprecated in favor of ValidityDuration, but we
  1015  	// still support it for backwards compatibility.
  1016  	if validityDuration == nil && req.ValidityHours > 0 { //nolint:staticcheck
  1017  		duration := time.Duration(req.ValidityHours) * time.Hour //nolint:staticcheck
  1018  		validityDuration = &duration
  1019  	}
  1020  
  1021  	if validityDuration != nil {
  1022  		cloudReq.ValidityPeriod = "PT" + strings.ToUpper((*validityDuration).Truncate(time.Second).String())
  1023  	}
  1024  
  1025  	return &cloudReq, nil
  1026  }
  1027  
  1028  func (c *Connector) getCertificateStatus(requestID string) (certStatus *certificateStatus, err error) {
  1029  	url := c.getURL(urlResourceCertificateStatus)
  1030  	url = fmt.Sprintf(url, requestID)
  1031  	statusCode, _, body, err := c.request("GET", url, nil)
  1032  	if err != nil {
  1033  		return nil, err
  1034  	}
  1035  	if statusCode == http.StatusOK {
  1036  		certStatus = &certificateStatus{}
  1037  		err = json.Unmarshal(body, certStatus)
  1038  		if err != nil {
  1039  			return nil, fmt.Errorf("failed to parse certificate request status response: %s", err)
  1040  		}
  1041  		return
  1042  	}
  1043  	respErrors, err := parseResponseErrors(body)
  1044  	if err == nil {
  1045  		respError := fmt.Sprintf("Unexpected status code on Venafi Cloud certificate search. Status: %d\n", statusCode)
  1046  		for _, e := range respErrors {
  1047  			respError += fmt.Sprintf("Error Code: %d Error: %s\n", e.Code, e.Message)
  1048  		}
  1049  		return nil, errors.New(respError)
  1050  	}
  1051  
  1052  	return nil, fmt.Errorf("unexpected status code on Venafi Cloud certificate search. Status: %d", statusCode)
  1053  
  1054  }
  1055  
  1056  func retrieveServiceGeneratedCertData(c *Connector, req *certificate.Request, dekInfo *EdgeEncryptionKey) (*certificate.PEMCollection, error) {
  1057  
  1058  	pkDecoded, err := base64.StdEncoding.DecodeString(dekInfo.Key)
  1059  
  1060  	if err != nil {
  1061  		return nil, err
  1062  	}
  1063  
  1064  	publicKey, err := Load32KeyByte(pkDecoded)
  1065  	if err != nil {
  1066  		return nil, err
  1067  	}
  1068  
  1069  	encrypted, err := box.SealAnonymous(nil, []byte(req.KeyPassword), publicKey, rand.Reader)
  1070  
  1071  	if err != nil {
  1072  		return nil, err
  1073  	}
  1074  
  1075  	//Request keystore
  1076  	ksRequest := KeyStoreRequest{
  1077  		ExportFormat:                  "PEM",
  1078  		EncryptedPrivateKeyPassphrase: base64.StdEncoding.EncodeToString(encrypted),
  1079  		EncryptedKeystorePassphrase:   "",
  1080  		CertificateLabel:              "",
  1081  	}
  1082  
  1083  	url := c.getURL(urlResourceCertificateKS)
  1084  	url = fmt.Sprintf(url, req.CertID)
  1085  
  1086  	statusCode, status, body, err := c.request("POST", url, ksRequest)
  1087  
  1088  	if err != nil {
  1089  		return nil, err
  1090  	}
  1091  
  1092  	if statusCode != http.StatusOK && statusCode != http.StatusCreated {
  1093  		return nil, fmt.Errorf("failed to retrieve KeyStore on VaaS, status: %s", status)
  1094  	}
  1095  
  1096  	rootFirst := false
  1097  	if req.ChainOption == certificate.ChainOptionRootFirst {
  1098  		rootFirst = true
  1099  	}
  1100  
  1101  	return ConvertZipBytesToPem(body, rootFirst)
  1102  
  1103  }
  1104  
  1105  func getDekInfo(c *Connector, certId string) (*EdgeEncryptionKey, error) {
  1106  	//get certificate details for getting DekHash
  1107  
  1108  	managedCert, err := c.getCertificate(certId)
  1109  
  1110  	if err != nil {
  1111  		return nil, err
  1112  	}
  1113  
  1114  	//get Dek info for getting DEK's key
  1115  	url := c.getURL(urlDekPublicKey)
  1116  	url = fmt.Sprintf(url, managedCert.DekHash)
  1117  
  1118  	statusCode, status, body, err := c.request("GET", url, nil)
  1119  	if err != nil {
  1120  		return nil, err
  1121  	}
  1122  
  1123  	dekInfo, err := parseDEKInfo(statusCode, status, body)
  1124  	if err != nil {
  1125  		return nil, err
  1126  	}
  1127  
  1128  	return dekInfo, nil
  1129  
  1130  }
  1131  
  1132  func ConvertZipBytesToPem(dataByte []byte, rootFirst bool) (*certificate.PEMCollection, error) {
  1133  	collection := certificate.PEMCollection{}
  1134  	var cert string
  1135  	var privateKey string
  1136  	var chainArr []string
  1137  
  1138  	zipReader, err := zip.NewReader(bytes.NewReader(dataByte), int64(len(dataByte)))
  1139  	if err != nil {
  1140  		return nil, err
  1141  	}
  1142  
  1143  	for _, zipFile := range zipReader.File {
  1144  		if strings.HasSuffix(zipFile.Name, ".key") {
  1145  
  1146  			f, err := zipFile.Open()
  1147  			if err != nil {
  1148  				log.Println(err)
  1149  				continue
  1150  			}
  1151  			defer f.Close()
  1152  			fileBytes, err := io.ReadAll(f)
  1153  			if err != nil {
  1154  				return nil, err
  1155  			}
  1156  
  1157  			privateKey = strings.TrimSpace(string(fileBytes)) + "\n"
  1158  
  1159  		} else if strings.HasSuffix(zipFile.Name, "_root-first.pem") {
  1160  
  1161  			f, err := zipFile.Open()
  1162  
  1163  			if err != nil {
  1164  				return nil, err
  1165  			}
  1166  
  1167  			defer f.Close()
  1168  			fileBytes, err := io.ReadAll(f)
  1169  			if err != nil {
  1170  				return nil, err
  1171  			}
  1172  
  1173  			certs := strings.Split(strings.TrimSpace(string(fileBytes)), "\n\n")
  1174  
  1175  			for i := 0; i < len(certs); i++ {
  1176  				if i < len(certs)-1 {
  1177  					if len(chainArr) == 0 {
  1178  						chainArr = append(chainArr, certs[i]+"\n")
  1179  					} else {
  1180  						if rootFirst {
  1181  							chainArr = append(chainArr, certs[i]+"\n")
  1182  						} else {
  1183  							chainArr = append([]string{certs[i] + "\n"}, chainArr...)
  1184  						}
  1185  					}
  1186  				} else {
  1187  					cert = certs[i] + "\n"
  1188  				}
  1189  			}
  1190  		}
  1191  	}
  1192  
  1193  	collection.Certificate = cert
  1194  	collection.PrivateKey = privateKey
  1195  	collection.Chain = chainArr
  1196  
  1197  	return &collection, nil
  1198  }
  1199  
  1200  // Waits for the Certificate to be available. Fails when the timeout is exceeded
  1201  func (c *Connector) waitForCertificate(url string, request *certificate.Request) (statusCode int, status string, body []byte, err error) {
  1202  	startTime := time.Now()
  1203  	for {
  1204  		statusCode, status, body, err = c.request("GET", url, nil)
  1205  		if err != nil {
  1206  			return
  1207  		}
  1208  		if statusCode == http.StatusOK {
  1209  			return
  1210  		}
  1211  		if request.Timeout == 0 {
  1212  			err = endpoint.ErrCertificatePending{CertificateID: request.PickupID, Status: status}
  1213  			return
  1214  		}
  1215  		if time.Now().After(startTime.Add(request.Timeout)) {
  1216  			err = endpoint.ErrRetrieveCertificateTimeout{CertificateID: request.PickupID}
  1217  			return
  1218  		}
  1219  		time.Sleep(2 * time.Second)
  1220  	}
  1221  }
  1222  
  1223  // WriteLog Custom Logging not currently supported by VaaS
  1224  func (c *Connector) WriteLog(_ *endpoint.LogRequest) (err error) {
  1225  	return fmt.Errorf("outbound logging not supported by endpoint")
  1226  }
  1227  
  1228  func (c *Connector) searchCertificates(req *SearchRequest) (*CertificateSearchResponse, error) {
  1229  
  1230  	var err error
  1231  
  1232  	url := c.getURL(urlResourceCertificateSearch)
  1233  	statusCode, _, body, err := c.request("POST", url, req)
  1234  	if err != nil {
  1235  		return nil, err
  1236  	}
  1237  	searchResult, err := ParseCertificateSearchResponse(statusCode, body)
  1238  	if err != nil {
  1239  		return nil, err
  1240  	}
  1241  	return searchResult, nil
  1242  }
  1243  
  1244  func (c *Connector) searchCertificatesByFingerprint(fp string) (*CertificateSearchResponse, error) {
  1245  	fp = strings.Replace(fp, ":", "", -1)
  1246  	fp = strings.Replace(fp, ".", "", -1)
  1247  	fp = strings.ToUpper(fp)
  1248  	req := &SearchRequest{
  1249  		Expression: &Expression{
  1250  			Operands: []Operand{
  1251  				{
  1252  					Field:    "fingerprint",
  1253  					Operator: MATCH,
  1254  					Value:    fp,
  1255  				},
  1256  			},
  1257  		},
  1258  	}
  1259  	return c.searchCertificates(req)
  1260  }
  1261  
  1262  type managedCertificate struct {
  1263  	Id                   string `json:"id"`
  1264  	CompanyId            string `json:"companyId"`
  1265  	CertificateRequestId string `json:"certificateRequestId"`
  1266  	DekHash              string `json:"dekHash,omitempty"`
  1267  }
  1268  
  1269  func (c *Connector) getCertificate(certificateId string) (*managedCertificate, error) {
  1270  	url := c.getURL(urlResourceCertificateByID)
  1271  	url = fmt.Sprintf(url, certificateId)
  1272  
  1273  	// TODO: Remove following retry logic once VC-31590 is fixed
  1274  	// retry logic involves the loop to constantly, during 1 minute, to retry
  1275  	// to get certificate each 2 seconds when it is not found in certificate inventory
  1276  	timeout := time.Duration(60) * time.Second
  1277  
  1278  	startTime := time.Now()
  1279  	for {
  1280  		statusCode, _, body, err := c.request("GET", url, nil)
  1281  		if err != nil {
  1282  			return nil, err
  1283  		}
  1284  
  1285  		switch statusCode {
  1286  		case http.StatusOK:
  1287  			var res = &managedCertificate{}
  1288  			err = json.Unmarshal(body, res)
  1289  			if err != nil {
  1290  				return nil, fmt.Errorf("failed to parse search results: %s, body: %s", err, body)
  1291  			}
  1292  			return res, nil
  1293  		default:
  1294  			if body != nil {
  1295  				respErrors, err := parseResponseErrors(body)
  1296  				if err == nil {
  1297  					err = validateNotFoundTimeout(statusCode, startTime, timeout, certificateId, respErrors)
  1298  					if err != nil {
  1299  						return nil, err
  1300  					}
  1301  				}
  1302  				return nil, err
  1303  			}
  1304  			err = validateNotFoundTimeout(statusCode, startTime, timeout, certificateId, []responseError{})
  1305  			if err != nil {
  1306  				return nil, err
  1307  			}
  1308  		}
  1309  		time.Sleep(2 * time.Second)
  1310  	}
  1311  }
  1312  
  1313  // validateNotFoundTimeout function that returns nil for not found error if waiting time for timeout is not
  1314  // completed. This is while status code is NotFound
  1315  func validateNotFoundTimeout(statusCode int, startTime time.Time, timeout time.Duration, certificateId string, respErrors []responseError) error {
  1316  	respError := fmt.Sprintf("unexpected status code on Venafi Cloud certificate search. Status: %d\n", statusCode)
  1317  	if statusCode == http.StatusNotFound {
  1318  		if time.Now().After(startTime.Add(timeout)) {
  1319  			return endpoint.ErrRetrieveCertificateTimeout{CertificateID: certificateId}
  1320  		}
  1321  	} else {
  1322  		if len(respErrors) > 0 {
  1323  			for _, e := range respErrors {
  1324  				respError += fmt.Sprintf("Error Code: %d Error: %s\n", e.Code, e.Message)
  1325  			}
  1326  			return errors.New(respError)
  1327  		}
  1328  		return errors.New(respError)
  1329  	}
  1330  	return nil
  1331  }
  1332  
  1333  func (c *Connector) getCertsBatch(page, pageSize int, withExpired bool) ([]certificate.CertificateInfo, error) {
  1334  
  1335  	appDetails, _, err := c.getAppDetailsByName(c.zone.getApplicationName())
  1336  	if err != nil {
  1337  		return nil, err
  1338  	}
  1339  
  1340  	req := &SearchRequest{
  1341  		Expression: &Expression{
  1342  			Operands: []Operand{
  1343  				{
  1344  					Field:    "appstackIds",
  1345  					Operator: MATCH,
  1346  					Value:    appDetails.ApplicationId,
  1347  				},
  1348  			},
  1349  			Operator: AND,
  1350  		},
  1351  		Paging: &Paging{PageSize: pageSize, PageNumber: page},
  1352  	}
  1353  	if !withExpired {
  1354  		req.Expression.Operands = append(req.Expression.Operands, Operand{
  1355  			Field:    "validityEnd",
  1356  			Operator: GTE,
  1357  			Value:    time.Now().Format(time.RFC3339),
  1358  		})
  1359  	}
  1360  	r, err := c.searchCertificates(req)
  1361  	if err != nil {
  1362  		return nil, err
  1363  	}
  1364  	infos := make([]certificate.CertificateInfo, len(r.Certificates))
  1365  	for i, cert := range r.Certificates {
  1366  		infos[i] = cert.ToCertificateInfo()
  1367  	}
  1368  	return infos, nil
  1369  }
  1370  
  1371  func (c *Connector) getAppDetailsByName(appName string) (*ApplicationDetails, int, error) {
  1372  	url := c.getURL(urlAppDetailsByName)
  1373  	encodedAppName := netUrl.PathEscape(appName)
  1374  	url = fmt.Sprintf(url, encodedAppName)
  1375  	statusCode, status, body, err := c.request("GET", url, nil)
  1376  	if err != nil {
  1377  		return nil, statusCode, err
  1378  	}
  1379  	details, err := parseApplicationDetailsResult(statusCode, status, body)
  1380  	if err != nil {
  1381  		return nil, statusCode, err
  1382  	}
  1383  	return details, statusCode, nil
  1384  }
  1385  
  1386  func (c *Connector) getTemplateByID() (*certificateTemplate, error) {
  1387  	url := c.getURL(urlResourceTemplate)
  1388  	appNameEncoded := netUrl.PathEscape(c.zone.getApplicationName())
  1389  	citAliasEncoded := netUrl.PathEscape(c.zone.getTemplateAlias())
  1390  	url = fmt.Sprintf(url, appNameEncoded, citAliasEncoded)
  1391  	statusCode, status, body, err := c.request("GET", url, nil)
  1392  	if err != nil {
  1393  		return nil, err
  1394  	}
  1395  	t, err := parseCertificateTemplateResult(statusCode, status, body)
  1396  	return t, err
  1397  }
  1398  
  1399  func getCit(c *Connector, citName string) (*certificateTemplate, error) {
  1400  	url := c.getURL(urlIssuingTemplate)
  1401  	_, _, body, err := c.request("GET", url, nil)
  1402  
  1403  	if err != nil {
  1404  		return nil, err
  1405  	}
  1406  
  1407  	var cits CertificateTemplates
  1408  
  1409  	err = json.Unmarshal(body, &cits)
  1410  	if err != nil {
  1411  		return nil, err
  1412  	}
  1413  
  1414  	if len(cits.CertificateTemplates) > 0 {
  1415  		citArr := cits.CertificateTemplates
  1416  
  1417  		for _, cit := range citArr {
  1418  			if citName == cit.Name {
  1419  				return &cit, nil
  1420  			}
  1421  		}
  1422  
  1423  	}
  1424  
  1425  	//no error but cit was not found.
  1426  	return nil, nil
  1427  }
  1428  
  1429  func (c *Connector) CreateAPIUserAccount(userName string, password string) (int, *userDetails, error) {
  1430  
  1431  	indexOfAt := strings.Index(userName, "@")
  1432  
  1433  	if indexOfAt == -1 {
  1434  		indexOfAt = len(userName)
  1435  	}
  1436  
  1437  	userAccountReq := userAccount{
  1438  		UserAccountType: "API",
  1439  		Username:        userName,
  1440  		Password:        password,
  1441  		Firstname:       userName[0:indexOfAt], //Given the issue reported in https://jira.eng.venafi.com/browse/VC-16461 its
  1442  		// required the workaround to set something on firstName or lastName field. For now, we are setting the email's prefix
  1443  	}
  1444  
  1445  	return c.CreateUserAccount(&userAccountReq)
  1446  }
  1447  
  1448  func (c *Connector) CreateUserAccount(userAccount *userAccount) (int, *userDetails, error) {
  1449  
  1450  	url := c.getURL(urlResourceUserAccounts)
  1451  	statusCode, status, body, err := c.request("POST", url, userAccount, true)
  1452  	if err != nil {
  1453  		return statusCode, nil, err
  1454  	}
  1455  	ud, err := parseUserDetailsResultFromPOST(statusCode, status, body)
  1456  	if err != nil {
  1457  		return statusCode, nil, err
  1458  	}
  1459  	//c.user = ud
  1460  	return statusCode, ud, nil
  1461  }
  1462  
  1463  func (c *Connector) getUserDetails() (*userDetails, error) {
  1464  
  1465  	url := c.getURL(urlResourceUserAccounts)
  1466  	statusCode, status, body, err := c.request("GET", url, nil)
  1467  	if err != nil {
  1468  		return nil, err
  1469  	}
  1470  	ud, err := parseUserDetailsResult(http.StatusOK, statusCode, status, body)
  1471  	if err != nil {
  1472  		return nil, err
  1473  	}
  1474  	c.user = ud
  1475  	return ud, nil
  1476  }
  1477  
  1478  func (c *Connector) retrieveUser(id string) (*user, error) {
  1479  
  1480  	url := c.getURL(urlUserById)
  1481  	url = fmt.Sprintf(url, id)
  1482  
  1483  	statusCode, status, body, err := c.request("GET", url, nil)
  1484  	if err != nil {
  1485  		return nil, err
  1486  	}
  1487  	user, err := parseUserByIdResult(http.StatusOK, statusCode, status, body)
  1488  	if err != nil {
  1489  		return nil, err
  1490  	}
  1491  	return user, nil
  1492  }
  1493  
  1494  func (c *Connector) retrieveUsers(userName string) (*users, error) {
  1495  
  1496  	url := c.getURL(urlUsersByName)
  1497  	url = fmt.Sprintf(url, userName)
  1498  
  1499  	statusCode, status, body, err := c.request("GET", url, nil)
  1500  	if err != nil {
  1501  		return nil, err
  1502  	}
  1503  	users, err := parseUsersByNameResult(http.StatusOK, statusCode, status, body)
  1504  	if err != nil {
  1505  		return nil, err
  1506  	}
  1507  	return users, nil
  1508  }
  1509  
  1510  func (c *Connector) retrieveTeams() (*teams, error) {
  1511  
  1512  	url := c.getURL(urlTeams)
  1513  
  1514  	statusCode, status, body, err := c.request("GET", url, nil)
  1515  	if err != nil {
  1516  		return nil, err
  1517  	}
  1518  	teams, err := parseTeamsResult(http.StatusOK, statusCode, status, body)
  1519  	if err != nil {
  1520  		return nil, err
  1521  	}
  1522  	return teams, nil
  1523  }
  1524  
  1525  func (c *Connector) getCertificates(certificateId string) (*VenafiCertificate, error) {
  1526  	url := c.getURL(urlCertificateDetails)
  1527  	url = fmt.Sprintf(url, certificateId)
  1528  
  1529  	statusCode, status, body, err := c.request("GET", url, nil)
  1530  	if err != nil {
  1531  		return nil, err
  1532  	}
  1533  	cert, err := parseCertByIdResult(http.StatusOK, statusCode, status, body)
  1534  	if err != nil {
  1535  		return nil, err
  1536  	}
  1537  	return cert, nil
  1538  }
  1539  
  1540  func getAccounts(caName string, c *Connector) (*policy.Accounts, *policy.CertificateAuthorityInfo, error) {
  1541  	info, err := policy.GetCertAuthorityInfo(caName)
  1542  	if err != nil {
  1543  		return nil, nil, err
  1544  	}
  1545  
  1546  	caType := netUrl.PathEscape(info.CAType)
  1547  	url := c.getURL(urlCAAccounts)
  1548  	url = fmt.Sprintf(url, caType)
  1549  	_, _, body, err := c.request("GET", url, nil)
  1550  
  1551  	if err != nil {
  1552  		return nil, nil, err
  1553  	}
  1554  
  1555  	var accounts policy.Accounts
  1556  
  1557  	err = json.Unmarshal(body, &accounts)
  1558  
  1559  	if err != nil {
  1560  		return nil, nil, err
  1561  	}
  1562  
  1563  	return &accounts, &info, nil
  1564  }
  1565  
  1566  func getCertificateAuthorityDetails(caName string, c *Connector) (*policy.CADetails, error) {
  1567  
  1568  	accounts, info, err := getAccounts(caName, c)
  1569  	if err != nil {
  1570  		return nil, err
  1571  	}
  1572  
  1573  	var details policy.CADetails
  1574  
  1575  	for _, account := range accounts.Accounts {
  1576  		if account.Account.Key == info.CAAccountKey {
  1577  			for _, productOption := range account.ProductOption {
  1578  				if productOption.ProductName == info.VendorProductName {
  1579  					productOptionOrganizationId := productOption.ProductDetails.ProductTemplate.OrganizationId
  1580  					details.CertificateAuthorityOrganizationId = &productOptionOrganizationId
  1581  					productionOptionId := productOption.Id
  1582  					details.CertificateAuthorityProductOptionId = &productionOptionId
  1583  					return &details, nil
  1584  				}
  1585  			}
  1586  		}
  1587  	}
  1588  
  1589  	return nil, fmt.Errorf("specified CA doesn't exist")
  1590  }
  1591  
  1592  func getCertificateAuthorityInfoFromCloud(caName, caAccountId, caProductOptionId string, c *Connector) (*policy.CertificateAuthorityInfo, error) {
  1593  
  1594  	caName = netUrl.PathEscape(caName)
  1595  	url := c.getURL(urlCAAccountDetails)
  1596  	url = fmt.Sprintf(url, caName, caAccountId)
  1597  	_, _, body, err := c.request("GET", url, nil)
  1598  
  1599  	if err != nil {
  1600  		return nil, err
  1601  	}
  1602  
  1603  	var accountDetails policy.AccountDetails
  1604  
  1605  	err = json.Unmarshal(body, &accountDetails)
  1606  
  1607  	if err != nil {
  1608  		return nil, err
  1609  	}
  1610  
  1611  	var info policy.CertificateAuthorityInfo
  1612  
  1613  	if accountDetails.Account.CertificateAuthority == "" {
  1614  		return nil, fmt.Errorf("CertificateAuthority is empty")
  1615  	}
  1616  	info.CAType = accountDetails.Account.CertificateAuthority
  1617  
  1618  	if accountDetails.Account.Key == "" {
  1619  		return nil, fmt.Errorf("key is empty")
  1620  	}
  1621  
  1622  	info.CAAccountKey = accountDetails.Account.Key
  1623  
  1624  	for _, productOption := range accountDetails.ProductOption {
  1625  		if productOption.Id == caProductOptionId {
  1626  			info.VendorProductName = productOption.ProductName
  1627  		}
  1628  	}
  1629  
  1630  	if info.VendorProductName == "" {
  1631  		return nil, fmt.Errorf("ProductName is empty")
  1632  	}
  1633  
  1634  	return &info, nil
  1635  }