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 }