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  }