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 }