File: errors.go

package info (click to toggle)
golang-github-smallstep-certificates 0.20.0-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 23,144 kB
  • sloc: sh: 278; makefile: 170
file content (357 lines) | stat: -rw-r--r-- 11,713 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
package acme

import (
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/pkg/errors"
	"github.com/smallstep/certificates/api/render"
)

// ProblemType is the type of the ACME problem.
type ProblemType int

const (
	// ErrorAccountDoesNotExistType request specified an account that does not exist
	ErrorAccountDoesNotExistType ProblemType = iota
	// ErrorAlreadyRevokedType request specified a certificate to be revoked that has already been revoked
	ErrorAlreadyRevokedType
	// ErrorBadCSRType CSR is unacceptable (e.g., due to a short key)
	ErrorBadCSRType
	// ErrorBadNonceType client sent an unacceptable anti-replay nonce
	ErrorBadNonceType
	// ErrorBadPublicKeyType JWS was signed by a public key the server does not support
	ErrorBadPublicKeyType
	// ErrorBadRevocationReasonType revocation reason provided is not allowed by the server
	ErrorBadRevocationReasonType
	// ErrorBadSignatureAlgorithmType JWS was signed with an algorithm the server does not support
	ErrorBadSignatureAlgorithmType
	// ErrorCaaType Authority Authorization (CAA) records forbid the CA from issuing a certificate
	ErrorCaaType
	// ErrorCompoundType error conditions are indicated in the “subproblems” array.
	ErrorCompoundType
	// ErrorConnectionType server could not connect to validation target
	ErrorConnectionType
	// ErrorDNSType was a problem with a DNS query during identifier validation
	ErrorDNSType
	// ErrorExternalAccountRequiredType request must include a value for the “externalAccountBinding” field
	ErrorExternalAccountRequiredType
	// ErrorIncorrectResponseType received didn’t match the challenge’s requirements
	ErrorIncorrectResponseType
	// ErrorInvalidContactType URL for an account was invalid
	ErrorInvalidContactType
	// ErrorMalformedType request message was malformed
	ErrorMalformedType
	// ErrorOrderNotReadyType request attempted to finalize an order that is not ready to be finalized
	ErrorOrderNotReadyType
	// ErrorRateLimitedType request exceeds a rate limit
	ErrorRateLimitedType
	// ErrorRejectedIdentifierType server will not issue certificates for the identifier
	ErrorRejectedIdentifierType
	// ErrorServerInternalType server experienced an internal error
	ErrorServerInternalType
	// ErrorTLSType server received a TLS error during validation
	ErrorTLSType
	// ErrorUnauthorizedType client lacks sufficient authorization
	ErrorUnauthorizedType
	// ErrorUnsupportedContactType URL for an account used an unsupported protocol scheme
	ErrorUnsupportedContactType
	// ErrorUnsupportedIdentifierType identifier is of an unsupported type
	ErrorUnsupportedIdentifierType
	// ErrorUserActionRequiredType the “instance” URL and take actions specified there
	ErrorUserActionRequiredType
	// ErrorNotImplementedType operation is not implemented
	ErrorNotImplementedType
)

// String returns the string representation of the acme problem type,
// fulfilling the Stringer interface.
func (ap ProblemType) String() string {
	switch ap {
	case ErrorAccountDoesNotExistType:
		return "accountDoesNotExist"
	case ErrorAlreadyRevokedType:
		return "alreadyRevoked"
	case ErrorBadCSRType:
		return "badCSR"
	case ErrorBadNonceType:
		return "badNonce"
	case ErrorBadPublicKeyType:
		return "badPublicKey"
	case ErrorBadRevocationReasonType:
		return "badRevocationReason"
	case ErrorBadSignatureAlgorithmType:
		return "badSignatureAlgorithm"
	case ErrorCaaType:
		return "caa"
	case ErrorCompoundType:
		return "compound"
	case ErrorConnectionType:
		return "connection"
	case ErrorDNSType:
		return "dns"
	case ErrorExternalAccountRequiredType:
		return "externalAccountRequired"
	case ErrorInvalidContactType:
		return "incorrectResponse"
	case ErrorMalformedType:
		return "malformed"
	case ErrorOrderNotReadyType:
		return "orderNotReady"
	case ErrorRateLimitedType:
		return "rateLimited"
	case ErrorRejectedIdentifierType:
		return "rejectedIdentifier"
	case ErrorServerInternalType:
		return "serverInternal"
	case ErrorTLSType:
		return "tls"
	case ErrorUnauthorizedType:
		return "unauthorized"
	case ErrorUnsupportedContactType:
		return "unsupportedContact"
	case ErrorUnsupportedIdentifierType:
		return "unsupportedIdentifier"
	case ErrorUserActionRequiredType:
		return "userActionRequired"
	case ErrorNotImplementedType:
		return "notImplemented"
	default:
		return fmt.Sprintf("unsupported type ACME error type '%d'", int(ap))
	}
}

type errorMetadata struct {
	details string
	status  int
	typ     string
	String  string
}

var (
	officialACMEPrefix          = "urn:ietf:params:acme:error:"
	errorServerInternalMetadata = errorMetadata{
		typ:     officialACMEPrefix + ErrorServerInternalType.String(),
		details: "The server experienced an internal error",
		status:  500,
	}
	errorMap = map[ProblemType]errorMetadata{
		ErrorAccountDoesNotExistType: {
			typ:     officialACMEPrefix + ErrorAccountDoesNotExistType.String(),
			details: "Account does not exist",
			status:  400,
		},
		ErrorAlreadyRevokedType: {
			typ:     officialACMEPrefix + ErrorAlreadyRevokedType.String(),
			details: "Certificate already revoked",
			status:  400,
		},
		ErrorBadCSRType: {
			typ:     officialACMEPrefix + ErrorBadCSRType.String(),
			details: "The CSR is unacceptable",
			status:  400,
		},
		ErrorBadNonceType: {
			typ:     officialACMEPrefix + ErrorBadNonceType.String(),
			details: "Unacceptable anti-replay nonce",
			status:  400,
		},
		ErrorBadPublicKeyType: {
			typ:     officialACMEPrefix + ErrorBadPublicKeyType.String(),
			details: "The jws was signed by a public key the server does not support",
			status:  400,
		},
		ErrorBadRevocationReasonType: {
			typ:     officialACMEPrefix + ErrorBadRevocationReasonType.String(),
			details: "The revocation reason provided is not allowed by the server",
			status:  400,
		},
		ErrorBadSignatureAlgorithmType: {
			typ:     officialACMEPrefix + ErrorBadSignatureAlgorithmType.String(),
			details: "The JWS was signed with an algorithm the server does not support",
			status:  400,
		},
		ErrorCaaType: {
			typ:     officialACMEPrefix + ErrorCaaType.String(),
			details: "Certification Authority Authorization (CAA) records forbid the CA from issuing a certificate",
			status:  400,
		},
		ErrorCompoundType: {
			typ:     officialACMEPrefix + ErrorCompoundType.String(),
			details: "Specific error conditions are indicated in the “subproblems” array",
			status:  400,
		},
		ErrorConnectionType: {
			typ:     officialACMEPrefix + ErrorConnectionType.String(),
			details: "The server could not connect to validation target",
			status:  400,
		},
		ErrorDNSType: {
			typ:     officialACMEPrefix + ErrorDNSType.String(),
			details: "There was a problem with a DNS query during identifier validation",
			status:  400,
		},
		ErrorExternalAccountRequiredType: {
			typ:     officialACMEPrefix + ErrorExternalAccountRequiredType.String(),
			details: "The request must include a value for the \"externalAccountBinding\" field",
			status:  400,
		},
		ErrorIncorrectResponseType: {
			typ:     officialACMEPrefix + ErrorIncorrectResponseType.String(),
			details: "Response received didn't match the challenge's requirements",
			status:  400,
		},
		ErrorInvalidContactType: {
			typ:     officialACMEPrefix + ErrorInvalidContactType.String(),
			details: "A contact URL for an account was invalid",
			status:  400,
		},
		ErrorMalformedType: {
			typ:     officialACMEPrefix + ErrorMalformedType.String(),
			details: "The request message was malformed",
			status:  400,
		},
		ErrorOrderNotReadyType: {
			typ:     officialACMEPrefix + ErrorOrderNotReadyType.String(),
			details: "The request attempted to finalize an order that is not ready to be finalized",
			status:  400,
		},
		ErrorRateLimitedType: {
			typ:     officialACMEPrefix + ErrorRateLimitedType.String(),
			details: "The request exceeds a rate limit",
			status:  400,
		},
		ErrorRejectedIdentifierType: {
			typ:     officialACMEPrefix + ErrorRejectedIdentifierType.String(),
			details: "The server will not issue certificates for the identifier",
			status:  400,
		},
		ErrorNotImplementedType: {
			typ:     officialACMEPrefix + ErrorRejectedIdentifierType.String(),
			details: "The requested operation is not implemented",
			status:  501,
		},
		ErrorTLSType: {
			typ:     officialACMEPrefix + ErrorTLSType.String(),
			details: "The server received a TLS error during validation",
			status:  400,
		},
		ErrorUnauthorizedType: {
			typ:     officialACMEPrefix + ErrorUnauthorizedType.String(),
			details: "The client lacks sufficient authorization",
			status:  401,
		},
		ErrorUnsupportedContactType: {
			typ:     officialACMEPrefix + ErrorUnsupportedContactType.String(),
			details: "A contact URL for an account used an unsupported protocol scheme",
			status:  400,
		},
		ErrorUnsupportedIdentifierType: {
			typ:     officialACMEPrefix + ErrorUnsupportedIdentifierType.String(),
			details: "An identifier is of an unsupported type",
			status:  400,
		},
		ErrorUserActionRequiredType: {
			typ:     officialACMEPrefix + ErrorUserActionRequiredType.String(),
			details: "Visit the “instance” URL and take actions specified there",
			status:  400,
		},
		ErrorServerInternalType: errorServerInternalMetadata,
	}
)

// Error represents an ACME
type Error struct {
	Type        string        `json:"type"`
	Detail      string        `json:"detail"`
	Subproblems []interface{} `json:"subproblems,omitempty"`
	Identifier  interface{}   `json:"identifier,omitempty"`
	Err         error         `json:"-"`
	Status      int           `json:"-"`
}

// NewError creates a new Error type.
func NewError(pt ProblemType, msg string, args ...interface{}) *Error {
	return newError(pt, errors.Errorf(msg, args...))
}

func newError(pt ProblemType, err error) *Error {
	meta, ok := errorMap[pt]
	if !ok {
		meta = errorServerInternalMetadata
		return &Error{
			Type:   meta.typ,
			Detail: meta.details,
			Status: meta.status,
			Err:    err,
		}
	}

	return &Error{
		Type:   meta.typ,
		Detail: meta.details,
		Status: meta.status,
		Err:    err,
	}
}

// NewErrorISE creates a new ErrorServerInternalType Error.
func NewErrorISE(msg string, args ...interface{}) *Error {
	return NewError(ErrorServerInternalType, msg, args...)
}

// WrapError attempts to wrap the internal error.
func WrapError(typ ProblemType, err error, msg string, args ...interface{}) *Error {
	switch e := err.(type) {
	case nil:
		return nil
	case *Error:
		if e.Err == nil {
			e.Err = errors.Errorf(msg+"; "+e.Detail, args...)
		} else {
			e.Err = errors.Wrapf(e.Err, msg, args...)
		}
		return e
	default:
		return newError(typ, errors.Wrapf(err, msg, args...))
	}
}

// WrapErrorISE shortcut to wrap an internal server error type.
func WrapErrorISE(err error, msg string, args ...interface{}) *Error {
	return WrapError(ErrorServerInternalType, err, msg, args...)
}

// StatusCode returns the status code and implements the StatusCoder interface.
func (e *Error) StatusCode() int {
	return e.Status
}

// Error allows AError to implement the error interface.
func (e *Error) Error() string {
	return e.Detail
}

// Cause returns the internal error and implements the Causer interface.
func (e *Error) Cause() error {
	if e.Err == nil {
		return errors.New(e.Detail)
	}
	return e.Err
}

// ToLog implements the EnableLogger interface.
func (e *Error) ToLog() (interface{}, error) {
	b, err := json.Marshal(e)
	if err != nil {
		return nil, WrapErrorISE(err, "error marshaling acme.Error for logging")
	}
	return string(b), nil
}

// Render implements render.RenderableError for Error.
func (e *Error) Render(w http.ResponseWriter) {
	w.Header().Set("Content-Type", "application/problem+json")
	render.JSONStatus(w, e, e.StatusCode())
}