...

Source file src/github.com/sigstore/rekor/pkg/api/error.go

Documentation: github.com/sigstore/rekor/pkg/api

     1  //
     2  // Copyright 2021 The Sigstore Authors.
     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  package api
    17  
    18  import (
    19  	"fmt"
    20  	"net/http"
    21  	"regexp"
    22  
    23  	"github.com/go-openapi/runtime/middleware"
    24  	"github.com/go-openapi/strfmt"
    25  	"github.com/mitchellh/mapstructure"
    26  
    27  	"github.com/sigstore/rekor/pkg/generated/models"
    28  	"github.com/sigstore/rekor/pkg/generated/restapi/operations/entries"
    29  	"github.com/sigstore/rekor/pkg/generated/restapi/operations/index"
    30  	"github.com/sigstore/rekor/pkg/generated/restapi/operations/pubkey"
    31  	"github.com/sigstore/rekor/pkg/generated/restapi/operations/tlog"
    32  	"github.com/sigstore/rekor/pkg/log"
    33  )
    34  
    35  const (
    36  	trillianCommunicationError     = "unexpected error communicating with transparency log"
    37  	trillianUnexpectedResult       = "unexpected result from transparency log"
    38  	validationError                = "error processing entry: %v"
    39  	failedToGenerateCanonicalEntry = "error generating canonicalized entry"
    40  	entryAlreadyExists             = "an equivalent entry already exists in the transparency log with UUID %v"
    41  	firstSizeLessThanLastSize      = "firstSize(%d) must be less than lastSize(%d)"
    42  	malformedUUID                  = "UUID must be a 64-character hexadecimal string"
    43  	malformedPublicKey             = "public key provided could not be parsed"
    44  	failedToGenerateCanonicalKey   = "error generating canonicalized public key"
    45  	indexStorageUnexpectedResult   = "unexpected result from searching index"
    46  	lastSizeGreaterThanKnown       = "the tree size requested(%d) was greater than what is currently observable(%d)"
    47  	signingError                   = "error signing"
    48  	sthGenerateError               = "error generating signed tree head"
    49  	unsupportedPKIFormat           = "the PKI format requested is not supported by this server"
    50  	unexpectedInactiveShardError   = "unexpected error communicating with inactive shard"
    51  	maxSearchQueryLimit            = "more than max allowed %d entries in request"
    52  )
    53  
    54  func errorMsg(message string, code int) *models.Error {
    55  	return &models.Error{
    56  		Code:    int64(code),
    57  		Message: message,
    58  	}
    59  }
    60  
    61  func handleRekorAPIError(params interface{}, code int, err error, message string, fields ...interface{}) middleware.Responder {
    62  	if message == "" {
    63  		message = http.StatusText(code)
    64  	}
    65  
    66  	re := regexp.MustCompile("^(.*)Params$")
    67  	typeStr := fmt.Sprintf("%T", params)
    68  	handler := re.FindStringSubmatch(typeStr)[1]
    69  
    70  	logMsg := func(r *http.Request) {
    71  		ctx := r.Context()
    72  		fields := append([]interface{}{"handler", handler, "statusCode", code, "clientMessage", message}, fields...)
    73  		if code >= 500 {
    74  			log.ContextLogger(ctx).Errorw(err.Error(), fields...)
    75  		} else {
    76  			log.ContextLogger(ctx).Warnw(err.Error(), fields...)
    77  		}
    78  		paramsFields := map[string]interface{}{}
    79  		if err := mapstructure.Decode(params, &paramsFields); err == nil {
    80  			log.ContextLogger(ctx).Debug(paramsFields)
    81  		}
    82  	}
    83  
    84  	switch params := params.(type) {
    85  	case entries.GetLogEntryByIndexParams:
    86  		logMsg(params.HTTPRequest)
    87  		switch code {
    88  		case http.StatusNotFound:
    89  			return entries.NewGetLogEntryByIndexNotFound()
    90  		default:
    91  			return entries.NewGetLogEntryByIndexDefault(code).WithPayload(errorMsg(message, code))
    92  		}
    93  	case entries.GetLogEntryByUUIDParams:
    94  		logMsg(params.HTTPRequest)
    95  		switch code {
    96  		case http.StatusNotFound:
    97  			return entries.NewGetLogEntryByUUIDNotFound()
    98  		default:
    99  			return entries.NewGetLogEntryByUUIDDefault(code).WithPayload(errorMsg(message, code))
   100  		}
   101  	case entries.CreateLogEntryParams:
   102  		switch code {
   103  		// We treat "duplicate entry" as an error, but it's not really an error, so we don't need to log it as one.
   104  		case http.StatusBadRequest:
   105  			logMsg(params.HTTPRequest)
   106  			return entries.NewCreateLogEntryBadRequest().WithPayload(errorMsg(message, code))
   107  		case http.StatusConflict:
   108  			resp := entries.NewCreateLogEntryConflict().WithPayload(errorMsg(message, code))
   109  			locationFound := false
   110  			for _, field := range fields {
   111  				if locationFound {
   112  					existingURL := field.(strfmt.URI)
   113  					resp.SetLocation(existingURL)
   114  					break
   115  				} else if field.(string) == "entryURL" {
   116  					locationFound = true
   117  					continue
   118  				}
   119  			}
   120  			return resp
   121  		default:
   122  			logMsg(params.HTTPRequest)
   123  			return entries.NewCreateLogEntryDefault(code).WithPayload(errorMsg(message, code))
   124  		}
   125  	case entries.SearchLogQueryParams:
   126  		logMsg(params.HTTPRequest)
   127  		switch code {
   128  		case http.StatusBadRequest:
   129  			return entries.NewSearchLogQueryBadRequest().WithPayload(errorMsg(message, code))
   130  		case http.StatusUnprocessableEntity:
   131  			return entries.NewSearchLogQueryUnprocessableEntity().WithPayload(errorMsg(message, code))
   132  		default:
   133  			return entries.NewSearchLogQueryDefault(code).WithPayload(errorMsg(message, code))
   134  		}
   135  	case tlog.GetLogInfoParams:
   136  		logMsg(params.HTTPRequest)
   137  		return tlog.NewGetLogInfoDefault(code).WithPayload(errorMsg(message, code))
   138  	case tlog.GetLogProofParams:
   139  		logMsg(params.HTTPRequest)
   140  		switch code {
   141  		case http.StatusBadRequest:
   142  			return tlog.NewGetLogProofBadRequest().WithPayload(errorMsg(message, code))
   143  		default:
   144  			return tlog.NewGetLogProofDefault(code).WithPayload(errorMsg(message, code))
   145  		}
   146  	case pubkey.GetPublicKeyParams:
   147  		logMsg(params.HTTPRequest)
   148  		return pubkey.NewGetPublicKeyDefault(code).WithPayload(errorMsg(message, code))
   149  	case index.SearchIndexParams:
   150  		logMsg(params.HTTPRequest)
   151  		switch code {
   152  		case http.StatusBadRequest:
   153  			return index.NewSearchIndexBadRequest().WithPayload(errorMsg(message, code))
   154  		default:
   155  			return index.NewSearchIndexDefault(code).WithPayload(errorMsg(message, code))
   156  		}
   157  	default:
   158  		log.Logger.Errorf("unable to find method for type %T; error: %v", params, err)
   159  		return middleware.Error(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
   160  	}
   161  }
   162  

View as plain text