diff --git a/pkg/venafi/cloud/connector.go b/pkg/venafi/cloud/connector.go index 5d7fd0fd..134388ec 100644 --- a/pkg/venafi/cloud/connector.go +++ b/pkg/venafi/cloud/connector.go @@ -841,6 +841,13 @@ func (c *Connector) RequestCertificate(req *certificate.Request) (requestID stri return "", err } + // We set timeout for the http client + if req.Timeout != 0 { + if c.client == nil { + c.client = &http.Client{} + } + c.client.Timeout = req.Timeout + } statusCode, status, body, err := c.request("POST", url, cloudReq) if err != nil { @@ -895,7 +902,13 @@ func (c *Connector) SupportSynchronousRequestCertificate() bool { // RetrieveCertificate retrieves the certificate for the specified ID func (c *Connector) RetrieveCertificate(req *certificate.Request) (certificates *certificate.PEMCollection, err error) { - + // We set timeout for the http client + if req.Timeout != 0 { + if c.client == nil { + c.client = &http.Client{} + } + c.client.Timeout = req.Timeout + } if req.PickupID == "" && req.CertID == "" && req.Thumbprint != "" { // search cert by Thumbprint and fill pickupID var certificateRequestId string @@ -1184,6 +1197,7 @@ func (c *Connector) waitForCertificate(url string, request *certificate.Request) if statusCode == http.StatusOK { return } + if request.Timeout == 0 { err = endpoint.ErrCertificatePending{CertificateID: request.PickupID, Status: status} return diff --git a/pkg/venafi/cloud/connector_test.go b/pkg/venafi/cloud/connector_test.go index 8cd33fc6..a8d1e738 100644 --- a/pkg/venafi/cloud/connector_test.go +++ b/pkg/venafi/cloud/connector_test.go @@ -146,6 +146,33 @@ func TestRequestCertificate(t *testing.T) { } } +func TestRequestCertificateWithExtendTime(t *testing.T) { + t.Skip("Skipping as we cannot make VaaS to hold the amount of time we want to properly test this") + conn := getTestConnector(ctx.CloudZone) + conn.verbose = true + err := conn.Authenticate(&endpoint.Authentication{APIKey: ctx.CloudAPIkey}) + if err != nil { + t.Fatalf("%s", err) + } + zoneConfig, err := conn.ReadZoneConfiguration() + if err != nil { + t.Fatalf("%s", err) + } + req := certificate.Request{} + req.Subject.CommonName = test.RandCN() + req.Subject.Organization = []string{"Venafi, Inc."} + req.Subject.OrganizationalUnit = []string{"Automated Tests"} + req.Timeout, _ = time.ParseDuration("45s") + err = conn.GenerateRequest(zoneConfig, &req) + if err != nil { + t.Fatalf("%s", err) + } + _, err = conn.RequestCertificate(&req) + if err != nil { + t.Fatalf("%s", err) + } +} + func TestRequestCertificateED25519WithValidation(t *testing.T) { conn := getTestConnector(ctx.VAASzoneEC) conn.verbose = true @@ -469,6 +496,74 @@ func TestRetrieveCertificate(t *testing.T) { } } +func TestRetrieveCertificateWithExtendedTime(t *testing.T) { + t.Skip("Skipping as we cannot make VaaS to hold the amount of time we want to properly test this") + conn := getTestConnector(ctx.CloudZone) + err := conn.Authenticate(&endpoint.Authentication{APIKey: ctx.CloudAPIkey}) + if err != nil { + t.Fatalf("%s", err) + } + zoneConfig, err := conn.ReadZoneConfiguration() + if err != nil { + t.Fatalf("%s", err) + } + req := &certificate.Request{} + req.Subject.CommonName = test.RandCN() + req.Subject.Organization = []string{"Venafi, Inc."} + req.Subject.OrganizationalUnit = []string{"Automated Tests"} + req.Timeout, _ = time.ParseDuration("45s") + err = conn.GenerateRequest(zoneConfig, req) + if err != nil { + t.Fatalf("%s", err) + } + pickupID, err := conn.RequestCertificate(req) + if err != nil { + t.Fatalf("%s", err) + } + req.PickupID = pickupID + req.ChainOption = certificate.ChainOptionRootLast + + pcc, _ := certificate.NewPEMCollection(nil, nil, nil) + startTime := time.Now() + for { + + pcc, err = conn.RetrieveCertificate(req) + if err != nil { + _, ok := err.(endpoint.ErrCertificatePending) + if ok { + if time.Now().After(startTime.Add(time.Duration(600) * time.Second)) { + err = endpoint.ErrRetrieveCertificateTimeout{CertificateID: pickupID} + break + } + time.Sleep(time.Duration(10) * time.Second) + continue + } + break + } + break + } + if err != nil { + t.Fatalf("%s", err) + } + p, _ := pem.Decode([]byte(pcc.Certificate)) + cert, err := x509.ParseCertificate(p.Bytes) + if err != nil { + t.Fatalf("%s", err) + } + if req.Subject.CommonName != cert.Subject.CommonName { + t.Fatalf("Retrieved certificate did not contain expected CN. Expected: %s -- Actual: %s", req.Subject.CommonName, cert.Subject.CommonName) + } + + p, _ = pem.Decode([]byte(pcc.Chain[0])) + cert, err = x509.ParseCertificate(p.Bytes) + if err != nil { + t.Fatalf("%s", err) + } + if !cert.IsCA || fmt.Sprintf("%v", cert.Subject) == fmt.Sprintf("%v", cert.Issuer) { + t.Fatalf("Expected Intermediate Root Certificate first, instead got Subject: %v -- Issuer %v", cert.Subject, cert.Issuer) + } +} + func TestRetrieveCertificateRootFirst(t *testing.T) { conn := getTestConnector(ctx.CloudZone) err := conn.Authenticate(&endpoint.Authentication{APIKey: ctx.CloudAPIkey}) diff --git a/pkg/venafi/tpp/connector.go b/pkg/venafi/tpp/connector.go index 22ab58a3..cfe5177d 100644 --- a/pkg/venafi/tpp/connector.go +++ b/pkg/venafi/tpp/connector.go @@ -653,6 +653,14 @@ func (c *Connector) prepareRequest(req *certificate.Request, zone string) (tppRe // - true: Clear the Disabled attribute, reenable, and then renew the certificate (in this request). Reuse the same CertificateDN, that is also known as a Certificate object. tppReq.Reenable = true + // We set timeout at 2 levels: + // - for the platform request, in this case TPP + // - for the Go HTTP client + // In the following we set the timeout for TPP + if req.Timeout != 0 { + tppReq.WorkToDoTimeout = strconv.FormatFloat(req.Timeout.Seconds(), 'f', 0, 64) + } + return tppReq, err } @@ -715,6 +723,17 @@ func (c *Connector) RequestCertificate(req *certificate.Request) (requestID stri return "", err } + // We set timeout at 2 levels: + // - for the platform request, in this case TPP + // - for the Go HTTP client + // In the following we will add for the http client + if req.Timeout != 0 { + if c.client == nil { + c.client = &http.Client{} + } + c.client.Timeout = req.Timeout + } + statusCode, status, body, err := c.request("POST", urlResourceCertificateRequest, tppCertificateRequest) if err != nil { return "", err @@ -1328,6 +1347,18 @@ func (c *Connector) RetrieveCertificate(req *certificate.Request) (certificates includeChain := req.ChainOption != certificate.ChainOptionIgnore rootFirstOrder := includeChain && req.ChainOption == certificate.ChainOptionRootFirst + // We set timeout at 2 levels: + // - for the platform request, in this case TPP + // - for the Go HTTP client + // In the following we will add for the http client + + if req.Timeout != 0 { + if c.client == nil { + c.client = &http.Client{} + } + c.client.Timeout = req.Timeout + } + if req.PickupID == "" && req.Thumbprint != "" { // search cert by Thumbprint and fill pickupID searchResult, err := c.searchCertificatesByFingerprint(req.Thumbprint) @@ -1360,6 +1391,7 @@ func (c *Connector) RetrieveCertificate(req *certificate.Request) (certificates startTime := time.Now() for { var retrieveResponse *certificateRetrieveResponse + retrieveResponse, err = c.retrieveCertificateOnce(certReq) if err != nil { return nil, fmt.Errorf("unable to retrieve: %s", err) diff --git a/pkg/venafi/tpp/connector_test.go b/pkg/venafi/tpp/connector_test.go index 5252bdca..625c5c11 100644 --- a/pkg/venafi/tpp/connector_test.go +++ b/pkg/venafi/tpp/connector_test.go @@ -131,9 +131,9 @@ func TestNewConnectorURLErrors(t *testing.T) { "trailing_other": "https://example.com/foo/", "nested_vedsdk": "https://example.com/foo/vedsdk", } - for label, url := range tests { + for label, testUrl := range tests { t.Run(label, func(t *testing.T) { - c, err := NewConnector(url, "", false, nil) + c, err := NewConnector(testUrl, "", false, nil) if err == nil { t.Error("expected an error") } @@ -500,7 +500,7 @@ func TestRequestCertificateUserPassword(t *testing.T) { t.Fatalf("err is not nil, err: %s", err) } } - DoRequestCertificate(t, tpp) + DoRequestCertificate(t, tpp, 0) } func TestRequestCertificateToken(t *testing.T) { @@ -515,7 +515,24 @@ func TestRequestCertificateToken(t *testing.T) { t.Fatalf("err is not nil, err: %s", err) } } - DoRequestCertificate(t, tpp) + DoRequestCertificate(t, tpp, 0) +} + +func TestRequestCertificateTokenWithExtendedTimeout(t *testing.T) { + t.Skip("Skipping as we cannot make TPP to hold the amount of time we want to properly test this") + tpp, err := getTestConnector(ctx.TPPurl, ctx.TPPZone) + if err != nil { + t.Fatalf("err is not nil, err: %s url: %s", err, expectedURL) + } + + if tpp.apiKey == "" { + err = tpp.Authenticate(&endpoint.Authentication{AccessToken: ctx.TPPaccessToken}) + if err != nil { + t.Fatalf("err is not nil, err: %s", err) + } + } + timeout, _ := time.ParseDuration("45s") + DoRequestCertificate(t, tpp, timeout) } func TestRequestCertificateWithValidityHours(t *testing.T) { @@ -884,15 +901,6 @@ func TestRetrieveCertificate(t *testing.T) { givenTimeout: 3 * time.Second, expectErr: "unable to retrieve: Unexpected status code on TPP Certificate Retrieval. Status: 500 Certificate \\VED\\Policy\\TLS/SSL\\aexample.com has encountered an error while processing, Status: Post CSR failed with error: Cannot connect to the certificate authority (CA), Stage: 500.", }, - { - name: "should fail when timeout too small while waiting for the cert", - mockRetrieve: []mockResp{ - {`202 Certificate \VED\Policy\TLS/SSL\aexample.com being processed, Status: Post CSR, Stage: 500.`, - `{"Stage": 500, "Status": "Post CSR"}`}, - }, - givenTimeout: 1 * time.Millisecond, - expectErr: "Operation timed out. You may try retrieving the certificate later using Pickup ID: \\VED\\Policy\\Test\\bexample.com", - }, } serverWith := func(mockRetrieve []mockResp) (_ *httptest.Server, retrieveCount *int32) { @@ -1106,7 +1114,7 @@ func DoRequestCertificateWithValidityDuration(t *testing.T, tpp *Connector) { } -func DoRequestCertificate(t *testing.T, tpp *Connector) { +func DoRequestCertificate(t *testing.T, tpp *Connector, timeout time.Duration) { config, err := tpp.ReadZoneConfiguration() if err != nil { t.Fatalf("err is not nil, err: %s", err) @@ -1126,6 +1134,9 @@ func DoRequestCertificate(t *testing.T, tpp *Connector) { req.CustomFields = []certificate.CustomField{ {Name: "custom", Value: "2019-10-10"}, } + if timeout != 0 { + req.Timeout = timeout + } err = tpp.GenerateRequest(config, req) if err != nil { t.Fatalf("err is not nil, err: %s", err) diff --git a/pkg/venafi/tpp/tpp.go b/pkg/venafi/tpp/tpp.go index dc232e60..ddce8748 100644 --- a/pkg/venafi/tpp/tpp.go +++ b/pkg/venafi/tpp/tpp.go @@ -84,6 +84,7 @@ type certificateRequest struct { Devices []device `json:",omitempty"` CertificateType string `json:",omitempty"` Reenable bool `json:",omitempty"` + WorkToDoTimeout string `json:",omitempty"` } type certificateRetrieveRequest struct {