github.com/Venafi/vcert/v5@v5.10.2/pkg/venafi/cloud/search.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 "errors" 22 "fmt" 23 "log" 24 "math" 25 "net/http" 26 "strings" 27 "time" 28 29 "github.com/Venafi/vcert/v5/pkg/certificate" 30 ) 31 32 type SearchRequest struct { 33 Expression *Expression `json:"expression"` 34 Ordering *interface{} `json:"ordering,omitempty"` 35 Paging *Paging `json:"paging,omitempty"` 36 // ordering is not used here so far 37 // "ordering": {"orders": [{"direction": "ASC", "field": "subjectCN"},{"direction": "DESC", "field": "keyStrength"}]}, 38 } 39 40 type Expression struct { 41 Operator Operator `json:"operator,omitempty"` 42 Operands []Operand `json:"operands,omitempty"` 43 } 44 45 type Operand struct { 46 Field Field `json:"field"` 47 Operator Operator `json:"operator"` 48 Value interface{} `json:"value,omitempty"` 49 Values interface{} `json:"values,omitempty"` 50 } 51 52 type Field string 53 type Operator string 54 55 type Paging struct { 56 PageNumber int `json:"pageNumber"` 57 PageSize int `json:"pageSize"` 58 } 59 60 const ( 61 EQ Operator = "EQ" 62 FIND Operator = "FIND" 63 GT Operator = "GT" 64 GTE Operator = "GTE" 65 IN Operator = "IN" 66 LT Operator = "LT" 67 LTE Operator = "LTE" 68 MATCH Operator = "MATCH" 69 AND Operator = "AND" 70 ) 71 72 type CertificateSearchResponse struct { 73 Count int `json:"count"` 74 Certificates []Certificate `json:"certificates"` 75 } 76 77 type Certificate struct { 78 Id string `json:"id"` 79 ManagedCertificateId string `json:"managedCertificateId"` 80 CertificateRequestId string `json:"certificateRequestId"` 81 SubjectCN []string `json:"subjectCN"` 82 SubjectAlternativeNamesByType map[string][]string `json:"subjectAlternativeNamesByType"` 83 SerialNumber string `json:"serialNumber"` 84 Fingerprint string `json:"fingerprint"` 85 ValidityStart string `json:"validityStart"` 86 ValidityEnd string `json:"validityEnd"` 87 ApplicationIds []string `json:"applicationIds"` 88 /* ... and many more fields ... */ 89 } 90 91 func (c Certificate) ToCertificateInfo() certificate.CertificateInfo { 92 var cn string 93 if len(c.SubjectCN) > 0 { 94 cn = c.SubjectCN[0] 95 } 96 97 start, err := time.Parse(time.RFC3339, c.ValidityStart) 98 if err != nil { //we just print the error, and let the user know. 99 log.Println(err) 100 } 101 102 end, err := time.Parse(time.RFC3339, c.ValidityEnd) 103 if err != nil { //we just print the error, and let the user know. 104 log.Println(err) 105 } 106 107 return certificate.CertificateInfo{ 108 ID: c.Id, 109 CN: cn, 110 SANS: certificate.Sans{ 111 DNS: c.SubjectAlternativeNamesByType["dNSName"], 112 Email: c.SubjectAlternativeNamesByType["rfc822Name"], 113 IP: c.SubjectAlternativeNamesByType["iPAddress"], 114 URI: c.SubjectAlternativeNamesByType["uniformResourceIdentifier"], 115 // currently not supported on VaaS 116 // UPN: cert.SubjectAlternativeNamesByType["x400Address"], 117 }, 118 Serial: c.SerialNumber, 119 Thumbprint: c.Fingerprint, 120 ValidFrom: start, 121 ValidTo: end, 122 } 123 } 124 125 func ParseCertificateSearchResponse(httpStatusCode int, body []byte) (searchResult *CertificateSearchResponse, err error) { 126 switch httpStatusCode { 127 case http.StatusOK: 128 var searchResult = &CertificateSearchResponse{} 129 err = json.Unmarshal(body, searchResult) 130 if err != nil { 131 return nil, fmt.Errorf("failed to parse search results: %s, body: %s", err, body) 132 } 133 return searchResult, nil 134 default: 135 if body != nil { 136 respErrors, err := parseResponseErrors(body) 137 if err == nil { 138 respError := fmt.Sprintf("Unexpected status code on Venafi Cloud certificate search. Status: %d\n", httpStatusCode) 139 for _, e := range respErrors { 140 respError += fmt.Sprintf("Error Code: %d Error: %s\n", e.Code, e.Message) 141 } 142 return nil, errors.New(respError) 143 } 144 } 145 return nil, fmt.Errorf("unexpected status code on Venafi Cloud certificate search. Status: %d", httpStatusCode) 146 } 147 } 148 149 // returns everything up to the last slash (if any) 150 // 151 // example: 152 // Just The App Name 153 // -> Just The App Name 154 // 155 // The application\\With Cit 156 // -> The application 157 // 158 // The complex application\\name\\and the cit 159 // -> The complex application\\name 160 func getAppNameFromZone(zone string) string { 161 lastSlash := strings.LastIndex(zone, "\\") 162 163 // there is no backslash in zone, meaning it's just the application name, 164 // return it 165 if lastSlash == -1 { 166 return zone 167 } 168 169 return zone[:lastSlash] 170 } 171 172 // TODO: test this function 173 func formatSearchCertificateArguments(cn string, sans *certificate.Sans, certMinTimeLeft time.Duration) *SearchRequest { 174 // convert a time.Duration to days 175 certMinTimeDays := math.Floor(certMinTimeLeft.Hours() / 24) 176 177 // generate base request 178 req := &SearchRequest{ 179 Expression: &Expression{ 180 Operator: AND, 181 Operands: []Operand{ 182 { 183 Field: "validityPeriodDays", 184 Operator: GTE, 185 Value: &certMinTimeDays, 186 }, 187 }, 188 }, 189 } 190 191 if sans != nil && sans.DNS != nil { 192 addOperand(req, Operand{ 193 Field: "subjectAlternativeNameDns", 194 Operator: IN, 195 Values: sans.DNS, 196 }) 197 } 198 199 // only if a CN is provided, we add the field to the search request 200 if cn != "" { 201 addOperand(req, Operand{ 202 Field: "subjectCN", 203 Operator: EQ, 204 Value: &cn, 205 }) 206 } 207 208 return req 209 } 210 211 func addOperand(req *SearchRequest, o Operand) *SearchRequest { 212 req.Expression.Operands = append(req.Expression.Operands, o) 213 return req 214 }