github.com/letsencrypt/boulder@v0.20251208.0/probs/probs.go (about)

     1  package probs
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  
     7  	"github.com/go-jose/go-jose/v4"
     8  
     9  	"github.com/letsencrypt/boulder/identifier"
    10  )
    11  
    12  const (
    13  	// Error types that can be used in ACME payloads. These are sorted in the
    14  	// same order as they are defined in RFC8555 Section 6.7. We do not implement
    15  	// the `compound`, `externalAccountRequired`, or `userActionRequired` errors,
    16  	// because we have no path that would return them.
    17  	AccountDoesNotExistProblem = ProblemType("accountDoesNotExist")
    18  	// AlreadyReplacedProblem is a problem type that is defined in Section 7.4
    19  	// of draft-ietf-acme-ari-08, for more information see:
    20  	// https://datatracker.ietf.org/doc/html/draft-ietf-acme-ari-08#section-7.4
    21  	AlreadyReplacedProblem       = ProblemType("alreadyReplaced")
    22  	AlreadyRevokedProblem        = ProblemType("alreadyRevoked")
    23  	BadCSRProblem                = ProblemType("badCSR")
    24  	BadNonceProblem              = ProblemType("badNonce")
    25  	BadPublicKeyProblem          = ProblemType("badPublicKey")
    26  	BadRevocationReasonProblem   = ProblemType("badRevocationReason")
    27  	BadSignatureAlgorithmProblem = ProblemType("badSignatureAlgorithm")
    28  	CAAProblem                   = ProblemType("caa")
    29  	// ConflictProblem is a problem type that is not defined in RFC8555.
    30  	ConflictProblem              = ProblemType("conflict")
    31  	ConnectionProblem            = ProblemType("connection")
    32  	DNSProblem                   = ProblemType("dns")
    33  	InvalidContactProblem        = ProblemType("invalidContact")
    34  	MalformedProblem             = ProblemType("malformed")
    35  	OrderNotReadyProblem         = ProblemType("orderNotReady")
    36  	PausedProblem                = ProblemType("rateLimited")
    37  	RateLimitedProblem           = ProblemType("rateLimited")
    38  	RejectedIdentifierProblem    = ProblemType("rejectedIdentifier")
    39  	ServerInternalProblem        = ProblemType("serverInternal")
    40  	TLSProblem                   = ProblemType("tls")
    41  	UnauthorizedProblem          = ProblemType("unauthorized")
    42  	UnsupportedContactProblem    = ProblemType("unsupportedContact")
    43  	UnsupportedIdentifierProblem = ProblemType("unsupportedIdentifier")
    44  
    45  	// Defined in https://datatracker.ietf.org/doc/draft-aaron-acme-profiles/
    46  	InvalidProfileProblem = ProblemType("invalidProfile")
    47  
    48  	ErrorNS = "urn:ietf:params:acme:error:"
    49  )
    50  
    51  // ProblemType defines the error types in the ACME protocol
    52  type ProblemType string
    53  
    54  // ProblemDetails objects represent problem documents
    55  // https://tools.ietf.org/html/draft-ietf-appsawg-http-problem-00
    56  type ProblemDetails struct {
    57  	Type   ProblemType `json:"type,omitempty"`
    58  	Detail string      `json:"detail,omitempty"`
    59  	// HTTPStatus is the HTTP status code the ProblemDetails should probably be sent
    60  	// as.
    61  	HTTPStatus int `json:"status,omitempty"`
    62  	// SubProblems are optional additional per-identifier problems. See
    63  	// RFC 8555 Section 6.7.1: https://tools.ietf.org/html/rfc8555#section-6.7.1
    64  	SubProblems []SubProblemDetails `json:"subproblems,omitempty"`
    65  	// Algorithms is an extension field defined only for problem documents of type
    66  	// badSignatureAlgorithm. See RFC 8555, Section 6.2:
    67  	// https://datatracker.ietf.org/doc/html/rfc8555#section-6.2
    68  	Algorithms []jose.SignatureAlgorithm `json:"algorithms,omitempty"`
    69  }
    70  
    71  // SubProblemDetails represents sub-problems specific to an identifier that are
    72  // related to a top-level ProblemDetails.
    73  // See RFC 8555 Section 6.7.1: https://tools.ietf.org/html/rfc8555#section-6.7.1
    74  type SubProblemDetails struct {
    75  	ProblemDetails
    76  	Identifier identifier.ACMEIdentifier `json:"identifier"`
    77  }
    78  
    79  func (pd *ProblemDetails) String() string {
    80  	return fmt.Sprintf("%s :: %s", pd.Type, pd.Detail)
    81  }
    82  
    83  // WithSubProblems returns a new ProblemsDetails instance created by adding the
    84  // provided subProbs to the existing ProblemsDetail.
    85  func (pd *ProblemDetails) WithSubProblems(subProbs []SubProblemDetails) *ProblemDetails {
    86  	return &ProblemDetails{
    87  		Type:        pd.Type,
    88  		Detail:      pd.Detail,
    89  		HTTPStatus:  pd.HTTPStatus,
    90  		SubProblems: append(pd.SubProblems, subProbs...),
    91  	}
    92  }
    93  
    94  // Helper functions which construct the basic RFC8555 Problem Documents, with
    95  // the Type already set and the Details supplied by the caller.
    96  
    97  // AccountDoesNotExist returns a ProblemDetails representing an
    98  // AccountDoesNotExistProblem error
    99  func AccountDoesNotExist(detail string) *ProblemDetails {
   100  	return &ProblemDetails{
   101  		Type:       AccountDoesNotExistProblem,
   102  		Detail:     detail,
   103  		HTTPStatus: http.StatusBadRequest,
   104  	}
   105  }
   106  
   107  // AlreadyReplaced returns a ProblemDetails with a AlreadyReplacedProblem and a
   108  // 409 Conflict status code.
   109  func AlreadyReplaced(detail string) *ProblemDetails {
   110  	return &ProblemDetails{
   111  		Type:       AlreadyReplacedProblem,
   112  		Detail:     detail,
   113  		HTTPStatus: http.StatusConflict,
   114  	}
   115  }
   116  
   117  // AlreadyRevoked returns a ProblemDetails with a AlreadyRevokedProblem and a 400 Bad
   118  // Request status code.
   119  func AlreadyRevoked(detail string) *ProblemDetails {
   120  	return &ProblemDetails{
   121  		Type:       AlreadyRevokedProblem,
   122  		Detail:     detail,
   123  		HTTPStatus: http.StatusBadRequest,
   124  	}
   125  }
   126  
   127  // BadCSR returns a ProblemDetails representing a BadCSRProblem.
   128  func BadCSR(detail string) *ProblemDetails {
   129  	return &ProblemDetails{
   130  		Type:       BadCSRProblem,
   131  		Detail:     detail,
   132  		HTTPStatus: http.StatusBadRequest,
   133  	}
   134  }
   135  
   136  // BadNonce returns a ProblemDetails with a BadNonceProblem and a 400 Bad
   137  // Request status code.
   138  func BadNonce(detail string) *ProblemDetails {
   139  	return &ProblemDetails{
   140  		Type:       BadNonceProblem,
   141  		Detail:     detail,
   142  		HTTPStatus: http.StatusBadRequest,
   143  	}
   144  }
   145  
   146  // BadPublicKey returns a ProblemDetails with a BadPublicKeyProblem and a 400 Bad
   147  // Request status code.
   148  func BadPublicKey(detail string) *ProblemDetails {
   149  	return &ProblemDetails{
   150  		Type:       BadPublicKeyProblem,
   151  		Detail:     detail,
   152  		HTTPStatus: http.StatusBadRequest,
   153  	}
   154  }
   155  
   156  // BadRevocationReason returns a ProblemDetails representing
   157  // a BadRevocationReasonProblem
   158  func BadRevocationReason(detail string) *ProblemDetails {
   159  	return &ProblemDetails{
   160  		Type:       BadRevocationReasonProblem,
   161  		Detail:     detail,
   162  		HTTPStatus: http.StatusBadRequest,
   163  	}
   164  }
   165  
   166  // BadSignatureAlgorithm returns a ProblemDetails with a BadSignatureAlgorithmProblem
   167  // and a 400 Bad Request status code.
   168  func BadSignatureAlgorithm(detail string) *ProblemDetails {
   169  	return &ProblemDetails{
   170  		Type:       BadSignatureAlgorithmProblem,
   171  		Detail:     detail,
   172  		HTTPStatus: http.StatusBadRequest,
   173  	}
   174  }
   175  
   176  // CAA returns a ProblemDetails representing a CAAProblem
   177  func CAA(detail string) *ProblemDetails {
   178  	return &ProblemDetails{
   179  		Type:       CAAProblem,
   180  		Detail:     detail,
   181  		HTTPStatus: http.StatusForbidden,
   182  	}
   183  }
   184  
   185  // Connection returns a ProblemDetails representing a ConnectionProblem
   186  // error
   187  func Connection(detail string) *ProblemDetails {
   188  	return &ProblemDetails{
   189  		Type:       ConnectionProblem,
   190  		Detail:     detail,
   191  		HTTPStatus: http.StatusBadRequest,
   192  	}
   193  }
   194  
   195  // DNS returns a ProblemDetails representing a DNSProblem
   196  func DNS(detail string) *ProblemDetails {
   197  	return &ProblemDetails{
   198  		Type:       DNSProblem,
   199  		Detail:     detail,
   200  		HTTPStatus: http.StatusBadRequest,
   201  	}
   202  }
   203  
   204  // InvalidContact returns a ProblemDetails representing an InvalidContactProblem.
   205  func InvalidContact(detail string) *ProblemDetails {
   206  	return &ProblemDetails{
   207  		Type:       InvalidContactProblem,
   208  		Detail:     detail,
   209  		HTTPStatus: http.StatusBadRequest,
   210  	}
   211  }
   212  
   213  // Malformed returns a ProblemDetails with a MalformedProblem and a 400 Bad
   214  // Request status code.
   215  func Malformed(detail string, a ...any) *ProblemDetails {
   216  	if len(a) > 0 {
   217  		detail = fmt.Sprintf(detail, a...)
   218  	}
   219  	return &ProblemDetails{
   220  		Type:       MalformedProblem,
   221  		Detail:     detail,
   222  		HTTPStatus: http.StatusBadRequest,
   223  	}
   224  }
   225  
   226  // OrderNotReady returns a ProblemDetails representing a OrderNotReadyProblem
   227  func OrderNotReady(detail string) *ProblemDetails {
   228  	return &ProblemDetails{
   229  		Type:       OrderNotReadyProblem,
   230  		Detail:     detail,
   231  		HTTPStatus: http.StatusForbidden,
   232  	}
   233  }
   234  
   235  // RateLimited returns a ProblemDetails representing a RateLimitedProblem error
   236  func RateLimited(detail string) *ProblemDetails {
   237  	return &ProblemDetails{
   238  		Type:       RateLimitedProblem,
   239  		Detail:     detail,
   240  		HTTPStatus: http.StatusTooManyRequests,
   241  	}
   242  }
   243  
   244  // Paused returns a ProblemDetails representing a RateLimitedProblem error
   245  func Paused(detail string) *ProblemDetails {
   246  	return &ProblemDetails{
   247  		Type:       PausedProblem,
   248  		Detail:     detail,
   249  		HTTPStatus: http.StatusTooManyRequests,
   250  	}
   251  }
   252  
   253  // RejectedIdentifier returns a ProblemDetails with a RejectedIdentifierProblem and a 400 Bad
   254  // Request status code.
   255  func RejectedIdentifier(detail string) *ProblemDetails {
   256  	return &ProblemDetails{
   257  		Type:       RejectedIdentifierProblem,
   258  		Detail:     detail,
   259  		HTTPStatus: http.StatusBadRequest,
   260  	}
   261  }
   262  
   263  // ServerInternal returns a ProblemDetails with a ServerInternalProblem and a
   264  // 500 Internal Server Failure status code.
   265  func ServerInternal(detail string) *ProblemDetails {
   266  	return &ProblemDetails{
   267  		Type:       ServerInternalProblem,
   268  		Detail:     detail,
   269  		HTTPStatus: http.StatusInternalServerError,
   270  	}
   271  }
   272  
   273  // TLS returns a ProblemDetails representing a TLSProblem error
   274  func TLS(detail string) *ProblemDetails {
   275  	return &ProblemDetails{
   276  		Type:       TLSProblem,
   277  		Detail:     detail,
   278  		HTTPStatus: http.StatusBadRequest,
   279  	}
   280  }
   281  
   282  // Unauthorized returns a ProblemDetails with an UnauthorizedProblem and a 403
   283  // Forbidden status code.
   284  func Unauthorized(detail string) *ProblemDetails {
   285  	return &ProblemDetails{
   286  		Type:       UnauthorizedProblem,
   287  		Detail:     detail,
   288  		HTTPStatus: http.StatusForbidden,
   289  	}
   290  }
   291  
   292  // UnsupportedContact returns a ProblemDetails representing an
   293  // UnsupportedContactProblem
   294  func UnsupportedContact(detail string) *ProblemDetails {
   295  	return &ProblemDetails{
   296  		Type:       UnsupportedContactProblem,
   297  		Detail:     detail,
   298  		HTTPStatus: http.StatusBadRequest,
   299  	}
   300  }
   301  
   302  // UnsupportedIdentifier returns a ProblemDetails representing an
   303  // UnsupportedIdentifierProblem
   304  func UnsupportedIdentifier(detail string, a ...any) *ProblemDetails {
   305  	return &ProblemDetails{
   306  		Type:       UnsupportedIdentifierProblem,
   307  		Detail:     fmt.Sprintf(detail, a...),
   308  		HTTPStatus: http.StatusBadRequest,
   309  	}
   310  }
   311  
   312  // Additional helper functions that return variations on MalformedProblem with
   313  // different HTTP status codes set.
   314  
   315  // Conflict returns a ProblemDetails with a ConflictProblem and a 409 Conflict
   316  // status code.
   317  func Conflict(detail string) *ProblemDetails {
   318  	return &ProblemDetails{
   319  		Type:       ConflictProblem,
   320  		Detail:     detail,
   321  		HTTPStatus: http.StatusConflict,
   322  	}
   323  }
   324  
   325  // MethodNotAllowed returns a ProblemDetails representing a disallowed HTTP
   326  // method error.
   327  func MethodNotAllowed() *ProblemDetails {
   328  	return &ProblemDetails{
   329  		Type:       MalformedProblem,
   330  		Detail:     "Method not allowed",
   331  		HTTPStatus: http.StatusMethodNotAllowed,
   332  	}
   333  }
   334  
   335  // NotFound returns a ProblemDetails with a MalformedProblem and a 404 Not Found
   336  // status code.
   337  func NotFound(detail string) *ProblemDetails {
   338  	return &ProblemDetails{
   339  		Type:       MalformedProblem,
   340  		Detail:     detail,
   341  		HTTPStatus: http.StatusNotFound,
   342  	}
   343  }
   344  
   345  // InvalidProfile returns a ProblemDetails with type InvalidProfile, specified
   346  // in https://datatracker.ietf.org/doc/draft-aaron-acme-profiles/.
   347  func InvalidProfile(detail string) *ProblemDetails {
   348  	return &ProblemDetails{
   349  		Type:       InvalidProfileProblem,
   350  		Detail:     detail,
   351  		HTTPStatus: http.StatusBadRequest,
   352  	}
   353  }