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

     1  /*
     2   * Copyright 2018 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  	"encoding/json"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/Venafi/vcert/v5/pkg/certificate"
    25  	"github.com/Venafi/vcert/v5/pkg/util"
    26  )
    27  
    28  func TestSearchRequest(t *testing.T) {
    29  
    30  	// encoding to JSON
    31  	req := &SearchRequest{
    32  		Expression: &Expression{
    33  			Operands: []Operand{
    34  				{
    35  					Field:    "fingerprint",
    36  					Operator: MATCH,
    37  					Value:    "A7BDECDA0B67D5CEF28D6C8C7D7CFA882E3DC9D6",
    38  				},
    39  			},
    40  		},
    41  		Paging: &Paging{10, 10},
    42  	}
    43  	var expectedJson = `{"expression":{"operands":[{"field":"fingerprint","operator":"MATCH","value":"A7BDECDA0B67D5CEF28D6C8C7D7CFA882E3DC9D6"}]},"paging":{"pageNumber":10,"pageSize":10}}`
    44  
    45  	data, err := json.Marshal(req)
    46  	if err != nil {
    47  		t.Fatal(err)
    48  	}
    49  	if string(data) != expectedJson {
    50  		t.Fatalf("expected different JSON:\nhave:     %s\nexpected: %s", data, expectedJson)
    51  	}
    52  	t.Logf("%s\n", data)
    53  
    54  	// decoding from JSON
    55  	var req2 = &SearchRequest{}
    56  	err = json.Unmarshal(data, req2)
    57  	if err != nil {
    58  		t.Fatal(err)
    59  	}
    60  	t.Logf("%+v\n", req2)
    61  	data2, err := json.Marshal(req2)
    62  	if err != nil {
    63  		t.Fatal(err)
    64  	}
    65  	if string(data) != string(data2) {
    66  		t.Fatalf("one expected to be the same as another:\none:   %s\nother: %s", data, data2)
    67  	}
    68  
    69  	// if Paging is not specified, it should not be included to JSON
    70  	req = &SearchRequest{
    71  		Expression: &Expression{
    72  			Operands: []Operand{
    73  				{
    74  					Field:    "fingerprint",
    75  					Operator: MATCH,
    76  					Value:    "A7BDECDA0B67D5CEF28D6C8C7D7CFA882E3DC9D6",
    77  				},
    78  			},
    79  		},
    80  	}
    81  	expectedJson = `{"expression":{"operands":[{"field":"fingerprint","operator":"MATCH","value":"A7BDECDA0B67D5CEF28D6C8C7D7CFA882E3DC9D6"}]}}`
    82  
    83  	data, err = json.Marshal(req)
    84  	if err != nil {
    85  		t.Fatal(err)
    86  	}
    87  	if string(data) != expectedJson {
    88  		t.Fatalf("expected different JSON:\nhave:     %s\nexpected: %s", data, expectedJson)
    89  	}
    90  }
    91  
    92  func TestParseCertificateSearchResponse(t *testing.T) {
    93  	var code int
    94  	var body []byte
    95  	var searchResult *CertificateSearchResponse
    96  	var err error
    97  
    98  	code = 200
    99  	body = []byte(`
   100  {
   101    "count": 1,
   102    "certificates": [
   103      {
   104        "id": "ab239880-5de9-11e8-bb9b-8d6e819a14f1",
   105        "companyId": "b5ed6d60-22c4-11e7-ac27-035f0608fd2c",
   106        "managedCertificateId": "ab239881-5de9-11e8-bb9b-8d6e819a14f1",
   107        "fingerprint": "73CF2CC98C7DEC4045EDB93151750F5B9609FF44",
   108        "issuerCertificateIds": [
   109          "828a2b70-22ce-11e7-ba19-0da4a5ff6335",
   110          "82734810-22ce-11e7-ba19-0da4a5ff6335"
   111        ],
   112        "certificateRequestId": "8ceb8ad0-5de9-11e8-9c7a-e596bbf80f56",
   113        "certificateSource": "USER_PROVIDED",
   114        "certificateStatuses": [
   115          "NONE"
   116        ],
   117        "certificateType": "END_ENTITY",
   118        "ownerUsername": "alexander.tarasenko@venafi.com",
   119        "creationDate": "2018-05-22T17:58:01.480+00:00",
   120        "modificationDate": "2018-05-22T17:58:01.480+00:00",
   121        "totalInstanceCount": 0,
   122        "validityStart": "2018-05-22T00:00:00.000+00:00",
   123        "validityEnd": "2018-08-20T12:00:00.000+00:00",
   124        "validityPeriodDays": 90,
   125        "validityPeriodRange": "GT_30_DAYS_LTE_2_YEARS",
   126        "selfSigned": false,
   127        "signatureAlgorithm": "SHA256_WITH_RSA_ENCRYPTION",
   128        "signatureHashAlgorithm": "SHA256",
   129        "encryptionType": "RSA",
   130        "keyStrength": 2048,
   131        "publicKeyHash": "0048AA1D7E2F0017F9CA2E687D8776A1A340553D",
   132        "subjectKeyIdentifierHash": "C6E7C18CADE684CB420CA4764A6469086536D08E",
   133        "authorityKeyIdentifierHash": "AC90A22B9320CE93369173BC3074121005D7F909",
   134        "serialNumber": "07F3FE39F4E1A4B6075633ECFB748D84",
   135        "subjectCN": [
   136          "renew-test.venafi.example.com"
   137        ],
   138        "subjectOU": [
   139          "SerialNumber"
   140        ],
   141        "subjectST": "California",
   142        "subjectL": "Palo Alto",
   143        "subjectC": "US",
   144        "subjectAlternativeNamesByType": {
   145          "otherName": [],
   146          "rfc822Name": [],
   147          "dNSName": [
   148            "renew-test.venafi.example.com"
   149          ],
   150          "x400Address": [],
   151          "directoryName": [],
   152          "ediPartyName": [],
   153          "uniformResourceIdentifier": [],
   154          "iPAddress": [],
   155          "registeredID": []
   156        },
   157        "subjectAlternativeNameDns": [
   158          "renew-test.venafi.example.com"
   159        ],
   160        "issuerCN": [
   161          "DigiCert Test SHA2 Intermediate CA-1"
   162        ],
   163        "issuerC": "US",
   164        "keyUsage": [
   165          "digitalSignature",
   166          "keyEncipherment"
   167        ],
   168        "ocspNoCheck": false,
   169        "compliance": {
   170          "score": 0.8728395061728398
   171        },
   172        "instances": [
   173          {
   174  	  	"id": "ab28c8a0-5de9-11e8-bb9b-8d6e819a14f1",
   175  	  	"certificateId": "ab239880-5de9-11e8-bb9b-8d6e819a14f1",
   176  	  	"managedCertificateId": "ab239881-5de9-11e8-bb9b-8d6e819a14f1",
   177  	  	"companyId": "b5ed6d60-22c4-11e7-ac27-035f0608fd2c",
   178  	  	"zoneId": "b5f69520-22c4-11e7-ac27-035f0608fd2c",
   179  	  	"fingerprint": "73CF2CC98C7DEC4045EDB93151750F5B9609FF44",
   180  	  	"certificateSource": "USER_PROVIDED",
   181  	  	"certificateStatuses": [
   182  	  		"NONE"
   183  	  	],
   184  	  	"ownerUsername": "alexander.tarasenko@venafi.com",
   185  	  	"creationDate": "2018-05-22T17:58:01.514+00:00",
   186  	  	"modificationDate": "2018-05-22T17:58:01.514+00:00",
   187  	  	"ipAddress": "254.254.254.254",
   188  	  	"ipAddressAsLong": 4278124286,
   189  	  	"hostname": " ",
   190  	  	"port": -1,
   191  	  	"sslProtocolsSecurityStatus": "UNKNOWN",
   192  	  	"cipherSuitesSecurityStatus": "UNKNOWN",
   193  	  	"compliance": {
   194  	  		"score": 0.0
   195  	  	}
   196          }
   197        ],
   198        "applicationIds": []
   199      }
   200    ]
   201  }
   202  `)
   203  
   204  	searchResult, err = ParseCertificateSearchResponse(code, body)
   205  	if err != nil {
   206  		t.Fatal(err)
   207  	}
   208  	if searchResult.Count != 1 {
   209  		t.Fatal("wrong count field")
   210  	}
   211  	if len(searchResult.Certificates) != 1 {
   212  		t.Fatal("wrong count")
   213  	}
   214  	if searchResult.Certificates[0].ManagedCertificateId != "ab239881-5de9-11e8-bb9b-8d6e819a14f1" {
   215  		t.Fatal("wrong ManagedCertificateId value")
   216  	}
   217  
   218  	code = 400
   219  	body = []byte("")
   220  	searchResult, err = ParseCertificateSearchResponse(code, body)
   221  	if err == nil {
   222  		t.Fatal("should trigger error")
   223  	}
   224  
   225  	code = 400
   226  	body = []byte(`
   227  		{
   228  		  "errors": [
   229  		    {
   230  		      "code": 1004,
   231  		      "message": "Invalid or missing request header [SESSION, tppl-api-key]",
   232  		      "args": [
   233  		        [
   234  		          "SESSION",
   235  		          "tppl-api-key"
   236  		        ]
   237  		      ]
   238  		    },
   239  		    {
   240  		      "code": 1005,
   241  		      "message": "Something else was wrong"
   242  		    }
   243  		  ]
   244  		}
   245  	`)
   246  	searchResult, err = ParseCertificateSearchResponse(code, body)
   247  	if err == nil {
   248  		t.Fatal("JSON body should trigger error")
   249  	}
   250  }
   251  
   252  func TestGetAppNameFromZone(t *testing.T) {
   253  	testCases := []struct {
   254  		name     string
   255  		input    string
   256  		expected string
   257  	}{
   258  		{
   259  			name:     "Empty",
   260  			input:    "",
   261  			expected: "",
   262  		},
   263  		{
   264  			name:     "App",
   265  			input:    "Just The App Name",
   266  			expected: "Just The App Name",
   267  		},
   268  		{
   269  			name:     "App+Cit",
   270  			input:    "The application\\With Cit",
   271  			expected: "The application",
   272  		},
   273  		{
   274  			name:     "App+Cit Complex",
   275  			input:    "The complex application\\name\\and the cit",
   276  			expected: "The complex application\\name",
   277  		},
   278  	}
   279  
   280  	for _, testCase := range testCases {
   281  		t.Run(testCase.name, func(t *testing.T) {
   282  			appName := getAppNameFromZone(testCase.input)
   283  			if testCase.expected != appName {
   284  				t.Errorf("unmatched application name\nExpected:\n%v\nGot:\n%v", testCase.expected, appName)
   285  			}
   286  		})
   287  	}
   288  }
   289  
   290  type FormatSearchCertificateArgumentsMock struct {
   291  	cn              string
   292  	sans            *certificate.Sans
   293  	certMinTimeLeft time.Duration
   294  }
   295  
   296  func TestFormatSearchCertificateArguments(t *testing.T) {
   297  	testCases := []struct {
   298  		name     string
   299  		input    FormatSearchCertificateArgumentsMock
   300  		expected *SearchRequest
   301  	}{
   302  		{
   303  			// test empty arguments, should return just the validityPeriodDays
   304  			// argument
   305  			name:  "Empty",
   306  			input: FormatSearchCertificateArgumentsMock{},
   307  			expected: &SearchRequest{
   308  				Expression: &Expression{
   309  					Operator: AND,
   310  					Operands: []Operand{
   311  						{
   312  							Field:    "validityPeriodDays",
   313  							Operator: GTE,
   314  							Value:    0,
   315  						},
   316  					},
   317  				},
   318  			},
   319  		},
   320  		{
   321  			// test with just CN, should return subjectCN and validityPeriodDays
   322  			// arguments
   323  			name: "CN",
   324  			input: FormatSearchCertificateArgumentsMock{
   325  				cn: "test.example.com",
   326  			},
   327  			expected: &SearchRequest{
   328  				Expression: &Expression{
   329  					Operator: AND,
   330  					Operands: []Operand{
   331  						{
   332  							Field:    "validityPeriodDays",
   333  							Operator: GTE,
   334  							Value:    0,
   335  						},
   336  						{
   337  							Field:    "subjectCN",
   338  							Operator: EQ,
   339  							Value:    "test.example.com",
   340  						},
   341  					},
   342  				},
   343  			},
   344  		},
   345  		{
   346  			// test with just 1 DNS, should return subjectAlternativeNameDns and
   347  			// validityPeriodDays arguments
   348  			name: "SANS_1",
   349  			input: FormatSearchCertificateArgumentsMock{
   350  				sans: &certificate.Sans{DNS: []string{"one.example.com"}},
   351  			},
   352  			expected: &SearchRequest{
   353  				Expression: &Expression{
   354  					Operator: AND,
   355  					Operands: []Operand{
   356  						{
   357  							Field:    "validityPeriodDays",
   358  							Operator: GTE,
   359  							Value:    0,
   360  						},
   361  						{
   362  							Field:    "subjectAlternativeNameDns",
   363  							Operator: IN,
   364  							Values:   []string{"one.example.com"},
   365  						},
   366  					},
   367  				},
   368  			},
   369  		},
   370  		{
   371  			// test with just 2 DNS, should return both subjectAlternativeNameDns and
   372  			// validityPeriodDays arguments
   373  			name: "SANS_2",
   374  			input: FormatSearchCertificateArgumentsMock{
   375  				sans: &certificate.Sans{DNS: []string{"one.example.com", "two.example.com"}},
   376  			},
   377  			expected: &SearchRequest{
   378  				Expression: &Expression{
   379  					Operator: AND,
   380  					Operands: []Operand{
   381  						{
   382  							Field:    "validityPeriodDays",
   383  							Operator: GTE,
   384  							Value:    0,
   385  						},
   386  						{
   387  							Field:    "subjectAlternativeNameDns",
   388  							Operator: IN,
   389  							Values:   []string{"one.example.com", "two.example.com"},
   390  						},
   391  					},
   392  				},
   393  			},
   394  		},
   395  		{
   396  			// test with CN and 1 DNS, should return the subjectCN, 1
   397  			// subjectAlternativeNameDns and validityPeriodDays arguments
   398  			name: "CN SANS_1",
   399  			input: FormatSearchCertificateArgumentsMock{
   400  				cn:   "one.example.com",
   401  				sans: &certificate.Sans{DNS: []string{"one.example.com"}},
   402  			},
   403  			expected: &SearchRequest{
   404  				Expression: &Expression{
   405  					Operator: AND,
   406  					Operands: []Operand{
   407  						{
   408  							Field:    "validityPeriodDays",
   409  							Operator: GTE,
   410  							Value:    0,
   411  						},
   412  						{
   413  							Field:    "subjectAlternativeNameDns",
   414  							Operator: IN,
   415  							Values:   []string{"one.example.com"},
   416  						},
   417  						{
   418  							Field:    "subjectCN",
   419  							Operator: EQ,
   420  							Value:    "one.example.com",
   421  						},
   422  					},
   423  				},
   424  			},
   425  		},
   426  		{
   427  			// test with CN and 2 DNS, should return the subjectCN, 2
   428  			// subjectAlternativeNameDns and validityPeriodDays arguments
   429  			name: "CN SANS_2",
   430  			input: FormatSearchCertificateArgumentsMock{
   431  				cn:   "one.example.com",
   432  				sans: &certificate.Sans{DNS: []string{"one.example.com", "two.example.com"}},
   433  			},
   434  			expected: &SearchRequest{
   435  				Expression: &Expression{
   436  					Operator: AND,
   437  					Operands: []Operand{
   438  						{
   439  							Field:    "validityPeriodDays",
   440  							Operator: GTE,
   441  							Value:    0,
   442  						},
   443  						{
   444  							Field:    "subjectAlternativeNameDns",
   445  							Operator: IN,
   446  							Values:   []string{"one.example.com", "two.example.com"},
   447  						},
   448  						{
   449  							Field:    "subjectCN",
   450  							Operator: EQ,
   451  							Value:    "one.example.com",
   452  						},
   453  					},
   454  				},
   455  			},
   456  		},
   457  	}
   458  
   459  	for _, testCase := range testCases {
   460  		t.Run(testCase.name, func(t *testing.T) {
   461  			req := formatSearchCertificateArguments(testCase.input.cn, testCase.input.sans, testCase.input.certMinTimeLeft)
   462  			// stringify the instances
   463  			expected := util.GetJsonAsString(testCase.expected)
   464  			request := util.GetJsonAsString(req)
   465  			// compare as string
   466  			matches := expected == request
   467  			if !matches {
   468  				t.Errorf("unmatched regexp\nExpected:\n%v\nGot:\n%v", expected, request)
   469  			}
   470  		})
   471  	}
   472  }