github.com/zcqzcg/fabric-ca@v2.0.0-alpha.0.20200416163940-d878ee6db75a+incompatible/lib/server/certificaterequest/certificaterequest.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package certificaterequest 8 9 import ( 10 "regexp" 11 "strconv" 12 "strings" 13 "time" 14 15 "github.com/cloudflare/cfssl/log" 16 "github.com/hyperledger/fabric-ca/api" 17 "github.com/pkg/errors" 18 ) 19 20 // CertificateRequest defines the properties of a certificate request 21 type CertificateRequest interface { 22 GetID() string 23 GetSerial() string 24 GetAKI() string 25 GetNotRevoked() bool 26 GetNotExpired() bool 27 GetRevokedTimeStart() *time.Time 28 GetRevokedTimeEnd() *time.Time 29 GetExpiredTimeStart() *time.Time 30 GetExpiredTimeEnd() *time.Time 31 } 32 33 // RequestContext describes the request 34 type RequestContext interface { 35 GetQueryParm(string) string 36 GetBoolQueryParm(string) (bool, error) 37 } 38 39 // Impl defines the properties of a certificate request 40 type Impl struct { 41 ID string 42 SerialNumber string 43 Aki string 44 Notexpired bool 45 Notrevoked bool 46 ExpiredTimeStart *time.Time 47 ExpiredTimeEnd *time.Time 48 RevokedTimeStart *time.Time 49 RevokedTimeEnd *time.Time 50 } 51 52 // TimeFilters defines the various times that can be used as filters 53 type TimeFilters struct { 54 revokedStart *time.Time 55 revokedEnd *time.Time 56 expiredStart *time.Time 57 expiredEnd *time.Time 58 } 59 60 // NewCertificateRequest returns a certificate request object 61 func NewCertificateRequest(ctx RequestContext) (*Impl, error) { 62 63 // Convert time string to time type 64 times, err := getTimes(ctx) 65 if err != nil { 66 return nil, err 67 } 68 69 // Parse the query parameters 70 req, err := getReq(ctx) 71 if err != nil { 72 return nil, err 73 } 74 75 // Check to make sure that the request does not have conflicting filters 76 err = validateReq(req, times) 77 if err != nil { 78 return nil, err 79 } 80 81 return &Impl{ 82 ID: req.ID, 83 SerialNumber: req.Serial, 84 Aki: req.AKI, 85 Notexpired: req.NotExpired, 86 Notrevoked: req.NotRevoked, 87 ExpiredTimeStart: times.expiredStart, 88 ExpiredTimeEnd: times.expiredEnd, 89 RevokedTimeStart: times.revokedStart, 90 RevokedTimeEnd: times.revokedEnd, 91 }, nil 92 } 93 94 // GetID returns the enrollment id filter value 95 func (c *Impl) GetID() string { 96 return c.ID 97 } 98 99 // GetSerial returns the serial number filter value 100 func (c *Impl) GetSerial() string { 101 return c.SerialNumber 102 } 103 104 // GetAKI returns the AKI filter value 105 func (c *Impl) GetAKI() string { 106 return c.Aki 107 } 108 109 // GetNotExpired returns the notexpired bool value 110 func (c *Impl) GetNotExpired() bool { 111 return c.Notexpired 112 } 113 114 // GetNotRevoked returns the notrevoked bool value 115 func (c *Impl) GetNotRevoked() bool { 116 return c.Notrevoked 117 } 118 119 // GetExpiredTimeStart returns the starting expiration time filter value 120 func (c *Impl) GetExpiredTimeStart() *time.Time { 121 return c.ExpiredTimeStart 122 } 123 124 // GetExpiredTimeEnd returns the ending expiration time filter value 125 func (c *Impl) GetExpiredTimeEnd() *time.Time { 126 return c.ExpiredTimeEnd 127 } 128 129 // GetRevokedTimeStart returns the starting revoked time filter value 130 func (c *Impl) GetRevokedTimeStart() *time.Time { 131 return c.RevokedTimeStart 132 } 133 134 // GetRevokedTimeEnd returns the ending revoked time filter value 135 func (c *Impl) GetRevokedTimeEnd() *time.Time { 136 return c.RevokedTimeEnd 137 } 138 139 // getTimes take the string input from query parameters and parses the 140 // input and generates time type response 141 func getTimes(ctx RequestContext) (*TimeFilters, error) { 142 times := &TimeFilters{} 143 var err error 144 145 times.revokedStart, err = getTime(ctx.GetQueryParm("revoked_start")) 146 if err != nil { 147 return nil, errors.WithMessage(err, "Invalid 'revoked_begin' value") 148 } 149 150 times.revokedEnd, err = getTime(ctx.GetQueryParm("revoked_end")) 151 if err != nil { 152 return nil, errors.WithMessage(err, "Invalid 'revoked_end' value") 153 } 154 155 times.expiredStart, err = getTime(ctx.GetQueryParm("expired_start")) 156 if err != nil { 157 return nil, errors.WithMessage(err, "Invalid 'expired_begin' value") 158 } 159 160 times.expiredEnd, err = getTime(ctx.GetQueryParm("expired_end")) 161 if err != nil { 162 return nil, errors.WithMessage(err, "Invalid 'expired_end' value") 163 } 164 165 return times, nil 166 } 167 168 // Converts string to time type 169 func getTime(timeStr string) (*time.Time, error) { 170 log.Debugf("Convert time string (%s) to time type", timeStr) 171 var err error 172 173 if timeStr == "" { 174 return nil, nil 175 } 176 177 if strings.HasPrefix(timeStr, "+") || strings.HasPrefix(timeStr, "-") { 178 timeStr = strings.ToLower(timeStr) 179 180 if strings.HasSuffix(timeStr, "y") { 181 return nil, errors.Errorf("Invalid time format, year (y) is not supported, please check: %s", timeStr) 182 } 183 184 currentTime := time.Now().UTC() 185 186 if strings.HasSuffix(timeStr, "d") { 187 timeStr, err = convertDayToHours(timeStr) 188 } 189 190 dur, err := time.ParseDuration(timeStr) 191 if err != nil { 192 return nil, errors.Wrap(err, "Failed to parse duration") 193 } 194 newTime := currentTime.Add(dur) 195 196 return &newTime, nil 197 } 198 199 if !strings.Contains(timeStr, "T") { 200 timeStr = timeStr + "T00:00:00Z" 201 } 202 203 parsedTime, err := time.Parse(time.RFC3339, timeStr) 204 if err != nil { 205 return nil, errors.WithMessage(err, "Failed to parse time based on RFC3339") 206 } 207 208 return &parsedTime, nil 209 } 210 211 func convertDayToHours(timeStr string) (string, error) { 212 log.Debugf("Converting days to hours: %s", timeStr) 213 214 re := regexp.MustCompile("\\d+") 215 durationValDays, err := strconv.Atoi(re.FindString(timeStr)) 216 if err != nil { 217 return "", errors.Errorf("Invalid time format, integer values required for duration, please check: %s", timeStr) 218 } 219 durationValHours := 24 * durationValDays 220 timeStr = string(timeStr[0]) + strconv.Itoa(durationValHours) + "h" 221 222 log.Debug("Duration value in hours: ", timeStr) 223 return timeStr, nil 224 } 225 226 // validateReq checks to make sure the request does not contain conflicting filters 227 func validateReq(req *api.GetCertificatesRequest, times *TimeFilters) error { 228 if req.NotExpired && (times.expiredStart != nil || times.expiredEnd != nil) { 229 return errors.New("Can't specify expiration time filter and the 'notexpired' filter") 230 } 231 232 if req.NotRevoked && (times.revokedStart != nil || times.revokedEnd != nil) { 233 return errors.New("Can't specify revocation time filter and the 'notrevoked' filter") 234 } 235 236 return nil 237 } 238 239 // getReq will examine get the query parameters and populate the GetCertificateRequest 240 // struct, which makes it easier to pass around 241 func getReq(ctx RequestContext) (*api.GetCertificatesRequest, error) { 242 var err error 243 req := new(api.GetCertificatesRequest) 244 245 req.ID = ctx.GetQueryParm("id") 246 req.Serial = ctx.GetQueryParm("serial") 247 req.AKI = ctx.GetQueryParm("aki") 248 req.NotRevoked, err = ctx.GetBoolQueryParm("notrevoked") 249 if err != nil { 250 return nil, err 251 } 252 req.NotExpired, err = ctx.GetBoolQueryParm("notexpired") 253 if err != nil { 254 return nil, err 255 } 256 257 return req, nil 258 }