mirror of
https://github.com/smallstep/certificates.git
synced 2024-11-07 15:20:27 +00:00
162 lines
4.1 KiB
Go
162 lines
4.1 KiB
Go
|
package api
|
||
|
|
||
|
import (
|
||
|
"crypto/x509"
|
||
|
"encoding/base64"
|
||
|
"encoding/json"
|
||
|
"net/http"
|
||
|
"time"
|
||
|
|
||
|
"github.com/go-chi/chi"
|
||
|
"github.com/pkg/errors"
|
||
|
"github.com/smallstep/certificates/acme"
|
||
|
"github.com/smallstep/certificates/api"
|
||
|
)
|
||
|
|
||
|
// NewOrderRequest represents the body for a NewOrder request.
|
||
|
type NewOrderRequest struct {
|
||
|
Identifiers []acme.Identifier `json:"identifiers"`
|
||
|
NotBefore time.Time `json:"notBefore,omitempty"`
|
||
|
NotAfter time.Time `json:"notAfter,omitempty"`
|
||
|
}
|
||
|
|
||
|
// Validate validates a new-order request body.
|
||
|
func (n *NewOrderRequest) Validate() error {
|
||
|
if len(n.Identifiers) == 0 {
|
||
|
return acme.MalformedErr(errors.Errorf("identifiers list cannot be empty"))
|
||
|
}
|
||
|
for _, id := range n.Identifiers {
|
||
|
if id.Type != "dns" {
|
||
|
return acme.MalformedErr(errors.Errorf("identifier type unsupported: %s", id.Type))
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// FinalizeRequest captures the body for a Finalize order request.
|
||
|
type FinalizeRequest struct {
|
||
|
CSR string `json:"csr"`
|
||
|
csr *x509.CertificateRequest
|
||
|
}
|
||
|
|
||
|
// Validate validates a finalize request body.
|
||
|
func (f *FinalizeRequest) Validate() error {
|
||
|
var err error
|
||
|
csrBytes, err := base64.RawURLEncoding.DecodeString(f.CSR)
|
||
|
if err != nil {
|
||
|
return acme.MalformedErr(errors.Wrap(err, "error base64url decoding csr"))
|
||
|
}
|
||
|
f.csr, err = x509.ParseCertificateRequest(csrBytes)
|
||
|
if err != nil {
|
||
|
return acme.MalformedErr(errors.Wrap(err, "unable to parse csr"))
|
||
|
}
|
||
|
if err = f.csr.CheckSignature(); err != nil {
|
||
|
return acme.MalformedErr(errors.Wrap(err, "csr failed signature check"))
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// NewOrder ACME api for creating a new order.
|
||
|
func (h *Handler) NewOrder(w http.ResponseWriter, r *http.Request) {
|
||
|
prov, err := provisionerFromContext(r)
|
||
|
if err != nil {
|
||
|
api.WriteError(w, err)
|
||
|
return
|
||
|
}
|
||
|
acc, err := accountFromContext(r)
|
||
|
if err != nil {
|
||
|
api.WriteError(w, err)
|
||
|
return
|
||
|
}
|
||
|
payload, err := payloadFromContext(r)
|
||
|
if err != nil {
|
||
|
api.WriteError(w, err)
|
||
|
return
|
||
|
}
|
||
|
var nor NewOrderRequest
|
||
|
if err := json.Unmarshal(payload.value, &nor); err != nil {
|
||
|
api.WriteError(w, acme.MalformedErr(errors.Wrap(err,
|
||
|
"failed to unmarshal new-order request payload")))
|
||
|
return
|
||
|
}
|
||
|
if err := nor.Validate(); err != nil {
|
||
|
api.WriteError(w, err)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
o, err := h.Auth.NewOrder(prov, acme.OrderOptions{
|
||
|
AccountID: acc.GetID(),
|
||
|
Identifiers: nor.Identifiers,
|
||
|
NotBefore: nor.NotBefore,
|
||
|
NotAfter: nor.NotAfter,
|
||
|
})
|
||
|
if err != nil {
|
||
|
api.WriteError(w, err)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
w.Header().Set("Location", h.Auth.GetLink(acme.OrderLink, acme.URLSafeProvisionerName(prov), true, o.GetID()))
|
||
|
api.JSONStatus(w, o, http.StatusCreated)
|
||
|
}
|
||
|
|
||
|
// GetOrder ACME api for retrieving an order.
|
||
|
func (h *Handler) GetOrder(w http.ResponseWriter, r *http.Request) {
|
||
|
prov, err := provisionerFromContext(r)
|
||
|
if err != nil {
|
||
|
api.WriteError(w, err)
|
||
|
return
|
||
|
}
|
||
|
acc, err := accountFromContext(r)
|
||
|
if err != nil {
|
||
|
api.WriteError(w, err)
|
||
|
return
|
||
|
}
|
||
|
oid := chi.URLParam(r, "ordID")
|
||
|
o, err := h.Auth.GetOrder(prov, acc.GetID(), oid)
|
||
|
if err != nil {
|
||
|
api.WriteError(w, err)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
w.Header().Set("Location", h.Auth.GetLink(acme.OrderLink, acme.URLSafeProvisionerName(prov), true, o.GetID()))
|
||
|
api.JSON(w, o)
|
||
|
}
|
||
|
|
||
|
// FinalizeOrder attemptst to finalize an order and create a certificate.
|
||
|
func (h *Handler) FinalizeOrder(w http.ResponseWriter, r *http.Request) {
|
||
|
prov, err := provisionerFromContext(r)
|
||
|
if err != nil {
|
||
|
api.WriteError(w, err)
|
||
|
return
|
||
|
}
|
||
|
acc, err := accountFromContext(r)
|
||
|
if err != nil {
|
||
|
api.WriteError(w, err)
|
||
|
return
|
||
|
}
|
||
|
payload, err := payloadFromContext(r)
|
||
|
if err != nil {
|
||
|
api.WriteError(w, err)
|
||
|
return
|
||
|
}
|
||
|
var fr FinalizeRequest
|
||
|
if err := json.Unmarshal(payload.value, &fr); err != nil {
|
||
|
api.WriteError(w, acme.MalformedErr(errors.Wrap(err, "failed to unmarshal finalize-order request payload")))
|
||
|
return
|
||
|
}
|
||
|
if err := fr.Validate(); err != nil {
|
||
|
api.WriteError(w, err)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
oid := chi.URLParam(r, "ordID")
|
||
|
o, err := h.Auth.FinalizeOrder(prov, acc.GetID(), oid, fr.csr)
|
||
|
if err != nil {
|
||
|
api.WriteError(w, err)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
w.Header().Set("Location", h.Auth.GetLink(acme.OrderLink, acme.URLSafeProvisionerName(prov), true, o.ID))
|
||
|
api.JSON(w, o)
|
||
|
}
|