Skip to content

Commit 00634fb

Browse files
authored
api/render, api/log: initial implementation of the packages (smallstep#860)
* api/render: initial implementation of the package * acme/api: refactored to support api/render * authority/admin: refactored to support api/render * ca: refactored to support api/render * api: refactored to support api/render * api/render: implemented Error * api: refactored to support api/render.Error * acme/api: refactored to support api/render.Error * authority/admin: refactored to support api/render.Error * ca: refactored to support api/render.Error * ca: fixed broken tests * api/render, api/log: moved error logging to this package * acme: refactored Error so that it implements render.RenderableError * authority/admin: refactored Error so that it implements render.RenderableError * api/render: implemented RenderableError * api/render: added test coverage for Error * api/render: implemented statusCodeFromError * api: refactored RootsPEM to work with render.Error * acme, authority/admin: fixed pointer receiver name for consistency * api/render, errs: moved StatusCoder & StackTracer to the render package
1 parent abf5fc3 commit 00634fb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+859
-762
lines changed

acme/api/account.go

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import (
55
"net/http"
66

77
"github.com/go-chi/chi"
8+
89
"github.com/smallstep/certificates/acme"
9-
"github.com/smallstep/certificates/api"
10+
"github.com/smallstep/certificates/api/render"
1011
"github.com/smallstep/certificates/logging"
1112
)
1213

@@ -70,23 +71,23 @@ func (h *Handler) NewAccount(w http.ResponseWriter, r *http.Request) {
7071
ctx := r.Context()
7172
payload, err := payloadFromContext(ctx)
7273
if err != nil {
73-
api.WriteError(w, err)
74+
render.Error(w, err)
7475
return
7576
}
7677
var nar NewAccountRequest
7778
if err := json.Unmarshal(payload.value, &nar); err != nil {
78-
api.WriteError(w, acme.WrapError(acme.ErrorMalformedType, err,
79+
render.Error(w, acme.WrapError(acme.ErrorMalformedType, err,
7980
"failed to unmarshal new-account request payload"))
8081
return
8182
}
8283
if err := nar.Validate(); err != nil {
83-
api.WriteError(w, err)
84+
render.Error(w, err)
8485
return
8586
}
8687

8788
prov, err := acmeProvisionerFromContext(ctx)
8889
if err != nil {
89-
api.WriteError(w, err)
90+
render.Error(w, err)
9091
return
9192
}
9293

@@ -96,26 +97,26 @@ func (h *Handler) NewAccount(w http.ResponseWriter, r *http.Request) {
9697
acmeErr, ok := err.(*acme.Error)
9798
if !ok || acmeErr.Status != http.StatusBadRequest {
9899
// Something went wrong ...
99-
api.WriteError(w, err)
100+
render.Error(w, err)
100101
return
101102
}
102103

103104
// Account does not exist //
104105
if nar.OnlyReturnExisting {
105-
api.WriteError(w, acme.NewError(acme.ErrorAccountDoesNotExistType,
106+
render.Error(w, acme.NewError(acme.ErrorAccountDoesNotExistType,
106107
"account does not exist"))
107108
return
108109
}
109110

110111
jwk, err := jwkFromContext(ctx)
111112
if err != nil {
112-
api.WriteError(w, err)
113+
render.Error(w, err)
113114
return
114115
}
115116

116117
eak, err := h.validateExternalAccountBinding(ctx, &nar)
117118
if err != nil {
118-
api.WriteError(w, err)
119+
render.Error(w, err)
119120
return
120121
}
121122

@@ -125,18 +126,18 @@ func (h *Handler) NewAccount(w http.ResponseWriter, r *http.Request) {
125126
Status: acme.StatusValid,
126127
}
127128
if err := h.db.CreateAccount(ctx, acc); err != nil {
128-
api.WriteError(w, acme.WrapErrorISE(err, "error creating account"))
129+
render.Error(w, acme.WrapErrorISE(err, "error creating account"))
129130
return
130131
}
131132

132133
if eak != nil { // means that we have a (valid) External Account Binding key that should be bound, updated and sent in the response
133134
err := eak.BindTo(acc)
134135
if err != nil {
135-
api.WriteError(w, err)
136+
render.Error(w, err)
136137
return
137138
}
138139
if err := h.db.UpdateExternalAccountKey(ctx, prov.ID, eak); err != nil {
139-
api.WriteError(w, acme.WrapErrorISE(err, "error updating external account binding key"))
140+
render.Error(w, acme.WrapErrorISE(err, "error updating external account binding key"))
140141
return
141142
}
142143
acc.ExternalAccountBinding = nar.ExternalAccountBinding
@@ -149,20 +150,20 @@ func (h *Handler) NewAccount(w http.ResponseWriter, r *http.Request) {
149150
h.linker.LinkAccount(ctx, acc)
150151

151152
w.Header().Set("Location", h.linker.GetLink(r.Context(), AccountLinkType, acc.ID))
152-
api.JSONStatus(w, acc, httpStatus)
153+
render.JSONStatus(w, acc, httpStatus)
153154
}
154155

155156
// GetOrUpdateAccount is the api for updating an ACME account.
156157
func (h *Handler) GetOrUpdateAccount(w http.ResponseWriter, r *http.Request) {
157158
ctx := r.Context()
158159
acc, err := accountFromContext(ctx)
159160
if err != nil {
160-
api.WriteError(w, err)
161+
render.Error(w, err)
161162
return
162163
}
163164
payload, err := payloadFromContext(ctx)
164165
if err != nil {
165-
api.WriteError(w, err)
166+
render.Error(w, err)
166167
return
167168
}
168169

@@ -171,12 +172,12 @@ func (h *Handler) GetOrUpdateAccount(w http.ResponseWriter, r *http.Request) {
171172
if !payload.isPostAsGet {
172173
var uar UpdateAccountRequest
173174
if err := json.Unmarshal(payload.value, &uar); err != nil {
174-
api.WriteError(w, acme.WrapError(acme.ErrorMalformedType, err,
175+
render.Error(w, acme.WrapError(acme.ErrorMalformedType, err,
175176
"failed to unmarshal new-account request payload"))
176177
return
177178
}
178179
if err := uar.Validate(); err != nil {
179-
api.WriteError(w, err)
180+
render.Error(w, err)
180181
return
181182
}
182183
if len(uar.Status) > 0 || len(uar.Contact) > 0 {
@@ -187,7 +188,7 @@ func (h *Handler) GetOrUpdateAccount(w http.ResponseWriter, r *http.Request) {
187188
}
188189

189190
if err := h.db.UpdateAccount(ctx, acc); err != nil {
190-
api.WriteError(w, acme.WrapErrorISE(err, "error updating account"))
191+
render.Error(w, acme.WrapErrorISE(err, "error updating account"))
191192
return
192193
}
193194
}
@@ -196,7 +197,7 @@ func (h *Handler) GetOrUpdateAccount(w http.ResponseWriter, r *http.Request) {
196197
h.linker.LinkAccount(ctx, acc)
197198

198199
w.Header().Set("Location", h.linker.GetLink(ctx, AccountLinkType, acc.ID))
199-
api.JSON(w, acc)
200+
render.JSON(w, acc)
200201
}
201202

202203
func logOrdersByAccount(w http.ResponseWriter, oids []string) {
@@ -213,22 +214,22 @@ func (h *Handler) GetOrdersByAccountID(w http.ResponseWriter, r *http.Request) {
213214
ctx := r.Context()
214215
acc, err := accountFromContext(ctx)
215216
if err != nil {
216-
api.WriteError(w, err)
217+
render.Error(w, err)
217218
return
218219
}
219220
accID := chi.URLParam(r, "accID")
220221
if acc.ID != accID {
221-
api.WriteError(w, acme.NewError(acme.ErrorUnauthorizedType, "account ID '%s' does not match url param '%s'", acc.ID, accID))
222+
render.Error(w, acme.NewError(acme.ErrorUnauthorizedType, "account ID '%s' does not match url param '%s'", acc.ID, accID))
222223
return
223224
}
224225
orders, err := h.db.GetOrdersByAccountID(ctx, acc.ID)
225226
if err != nil {
226-
api.WriteError(w, err)
227+
render.Error(w, err)
227228
return
228229
}
229230

230231
h.linker.LinkOrdersByAccountID(ctx, orders)
231232

232-
api.JSON(w, orders)
233+
render.JSON(w, orders)
233234
logOrdersByAccount(w, orders)
234235
}

acme/api/handler.go

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ import (
1212
"time"
1313

1414
"github.com/go-chi/chi"
15+
1516
"github.com/smallstep/certificates/acme"
1617
"github.com/smallstep/certificates/api"
18+
"github.com/smallstep/certificates/api/render"
1719
"github.com/smallstep/certificates/authority/provisioner"
1820
)
1921

@@ -181,11 +183,11 @@ func (h *Handler) GetDirectory(w http.ResponseWriter, r *http.Request) {
181183
ctx := r.Context()
182184
acmeProv, err := acmeProvisionerFromContext(ctx)
183185
if err != nil {
184-
api.WriteError(w, err)
186+
render.Error(w, err)
185187
return
186188
}
187189

188-
api.JSON(w, &Directory{
190+
render.JSON(w, &Directory{
189191
NewNonce: h.linker.GetLink(ctx, NewNonceLinkType),
190192
NewAccount: h.linker.GetLink(ctx, NewAccountLinkType),
191193
NewOrder: h.linker.GetLink(ctx, NewOrderLinkType),
@@ -200,51 +202,51 @@ func (h *Handler) GetDirectory(w http.ResponseWriter, r *http.Request) {
200202
// NotImplemented returns a 501 and is generally a placeholder for functionality which
201203
// MAY be added at some point in the future but is not in any way a guarantee of such.
202204
func (h *Handler) NotImplemented(w http.ResponseWriter, r *http.Request) {
203-
api.WriteError(w, acme.NewError(acme.ErrorNotImplementedType, "this API is not implemented"))
205+
render.Error(w, acme.NewError(acme.ErrorNotImplementedType, "this API is not implemented"))
204206
}
205207

206208
// GetAuthorization ACME api for retrieving an Authz.
207209
func (h *Handler) GetAuthorization(w http.ResponseWriter, r *http.Request) {
208210
ctx := r.Context()
209211
acc, err := accountFromContext(ctx)
210212
if err != nil {
211-
api.WriteError(w, err)
213+
render.Error(w, err)
212214
return
213215
}
214216
az, err := h.db.GetAuthorization(ctx, chi.URLParam(r, "authzID"))
215217
if err != nil {
216-
api.WriteError(w, acme.WrapErrorISE(err, "error retrieving authorization"))
218+
render.Error(w, acme.WrapErrorISE(err, "error retrieving authorization"))
217219
return
218220
}
219221
if acc.ID != az.AccountID {
220-
api.WriteError(w, acme.NewError(acme.ErrorUnauthorizedType,
222+
render.Error(w, acme.NewError(acme.ErrorUnauthorizedType,
221223
"account '%s' does not own authorization '%s'", acc.ID, az.ID))
222224
return
223225
}
224226
if err = az.UpdateStatus(ctx, h.db); err != nil {
225-
api.WriteError(w, acme.WrapErrorISE(err, "error updating authorization status"))
227+
render.Error(w, acme.WrapErrorISE(err, "error updating authorization status"))
226228
return
227229
}
228230

229231
h.linker.LinkAuthorization(ctx, az)
230232

231233
w.Header().Set("Location", h.linker.GetLink(ctx, AuthzLinkType, az.ID))
232-
api.JSON(w, az)
234+
render.JSON(w, az)
233235
}
234236

235237
// GetChallenge ACME api for retrieving a Challenge.
236238
func (h *Handler) GetChallenge(w http.ResponseWriter, r *http.Request) {
237239
ctx := r.Context()
238240
acc, err := accountFromContext(ctx)
239241
if err != nil {
240-
api.WriteError(w, err)
242+
render.Error(w, err)
241243
return
242244
}
243245
// Just verify that the payload was set, since we're not strictly adhering
244246
// to ACME V2 spec for reasons specified below.
245247
_, err = payloadFromContext(ctx)
246248
if err != nil {
247-
api.WriteError(w, err)
249+
render.Error(w, err)
248250
return
249251
}
250252

@@ -257,49 +259,49 @@ func (h *Handler) GetChallenge(w http.ResponseWriter, r *http.Request) {
257259
azID := chi.URLParam(r, "authzID")
258260
ch, err := h.db.GetChallenge(ctx, chi.URLParam(r, "chID"), azID)
259261
if err != nil {
260-
api.WriteError(w, acme.WrapErrorISE(err, "error retrieving challenge"))
262+
render.Error(w, acme.WrapErrorISE(err, "error retrieving challenge"))
261263
return
262264
}
263265
ch.AuthorizationID = azID
264266
if acc.ID != ch.AccountID {
265-
api.WriteError(w, acme.NewError(acme.ErrorUnauthorizedType,
267+
render.Error(w, acme.NewError(acme.ErrorUnauthorizedType,
266268
"account '%s' does not own challenge '%s'", acc.ID, ch.ID))
267269
return
268270
}
269271
jwk, err := jwkFromContext(ctx)
270272
if err != nil {
271-
api.WriteError(w, err)
273+
render.Error(w, err)
272274
return
273275
}
274276
if err = ch.Validate(ctx, h.db, jwk, h.validateChallengeOptions); err != nil {
275-
api.WriteError(w, acme.WrapErrorISE(err, "error validating challenge"))
277+
render.Error(w, acme.WrapErrorISE(err, "error validating challenge"))
276278
return
277279
}
278280

279281
h.linker.LinkChallenge(ctx, ch, azID)
280282

281283
w.Header().Add("Link", link(h.linker.GetLink(ctx, AuthzLinkType, azID), "up"))
282284
w.Header().Set("Location", h.linker.GetLink(ctx, ChallengeLinkType, azID, ch.ID))
283-
api.JSON(w, ch)
285+
render.JSON(w, ch)
284286
}
285287

286288
// GetCertificate ACME api for retrieving a Certificate.
287289
func (h *Handler) GetCertificate(w http.ResponseWriter, r *http.Request) {
288290
ctx := r.Context()
289291
acc, err := accountFromContext(ctx)
290292
if err != nil {
291-
api.WriteError(w, err)
293+
render.Error(w, err)
292294
return
293295
}
294296
certID := chi.URLParam(r, "certID")
295297

296298
cert, err := h.db.GetCertificate(ctx, certID)
297299
if err != nil {
298-
api.WriteError(w, acme.WrapErrorISE(err, "error retrieving certificate"))
300+
render.Error(w, acme.WrapErrorISE(err, "error retrieving certificate"))
299301
return
300302
}
301303
if cert.AccountID != acc.ID {
302-
api.WriteError(w, acme.NewError(acme.ErrorUnauthorizedType,
304+
render.Error(w, acme.NewError(acme.ErrorUnauthorizedType,
303305
"account '%s' does not own certificate '%s'", acc.ID, certID))
304306
return
305307
}

0 commit comments

Comments
 (0)