Adds authorizations to orders. (#7)
Refactors the `acme` package to clearly separate out `core` objects that are used by Pebble internally vs those that are read to/from protocol messages. Few misc fixes: * fixed returning the parsedCSR for the order endpoint * fixed the pebble-client shell to not bail on the "meta" directory * updated the in-memory DB's "add" functions to return a count to avoid needing funcs like `countRegistration` Added the authorization types, challenge types, identifier types, and a "pending" status type. New orders now have a pending authorization created for each of the identifiers present in the CSR's SAN fields. Each authorization has a http-01 challenge created for it. Authorizations, orders and challenges are persisted in memory and have GET methods. TODO: Create TLS-SNI-02 and DNS-01 challenges
This commit is contained in:
parent
34529377db
commit
be500adb10
|
|
@ -1,12 +1,6 @@
|
|||
package acme
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"gopkg.in/square/go-jose.v1"
|
||||
)
|
||||
|
||||
|
|
@ -19,48 +13,49 @@ const (
|
|||
ResourceNewOrder = Resource("new-order")
|
||||
)
|
||||
|
||||
const (
|
||||
StatusPending = "pending"
|
||||
|
||||
IdentifierDNS = "dns"
|
||||
|
||||
ChallengeHTTP01 = "http-01"
|
||||
)
|
||||
|
||||
type Identifier struct {
|
||||
Type string `json:"type"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// TODO(@cpu) - Rename Registration to Account, update refs
|
||||
type Registration struct {
|
||||
ID string `json:"id"`
|
||||
Status string `json:"status"`
|
||||
Key *jose.JsonWebKey `json:"key"`
|
||||
Contact []string `json:"contact"`
|
||||
ToSAgreed bool `json:"terms-of-service-agreed"`
|
||||
Orders string `json:"orders"`
|
||||
Status string
|
||||
}
|
||||
|
||||
// OrderRequest is used for new-order requests
|
||||
type OrderRequest struct {
|
||||
ID string `json:"id"`
|
||||
// An Order is created to request issuance for a CSR
|
||||
type Order struct {
|
||||
Status string `json:"status"`
|
||||
Expires string `json:"expires"`
|
||||
CSR string `json:"csr"`
|
||||
NotBefore string `json:"notBefore"`
|
||||
NotAfter string `json:"notAfter"`
|
||||
Authorizations []string `json:"authorizations"`
|
||||
Certificate string `json:"certificate"`
|
||||
Certificate string `json:"certificate,omitempty"`
|
||||
}
|
||||
|
||||
// Order is constructed out of an OrderRequest and is an internal type
|
||||
type Order struct {
|
||||
OrderRequest
|
||||
ParsedCSR *x509.CertificateRequest
|
||||
// An Authorization is created for each identifier in an order
|
||||
type Authorization struct {
|
||||
Status string `json:"status"`
|
||||
Identifier Identifier `json:"identifier"`
|
||||
Challenges []string `json:"challenges"`
|
||||
}
|
||||
|
||||
// TODO(@cpu): Create an "Authorizations" type
|
||||
|
||||
// RandomString and NewToken come from Boulder core/util.go
|
||||
// RandomString returns a randomly generated string of the requested length.
|
||||
func RandomString(byteLength int) string {
|
||||
b := make([]byte, byteLength)
|
||||
_, err := io.ReadFull(rand.Reader, b)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Error reading random bytes: %s", err))
|
||||
}
|
||||
return base64.RawURLEncoding.EncodeToString(b)
|
||||
}
|
||||
|
||||
// NewToken produces a random string for Challenges, etc.
|
||||
func NewToken() string {
|
||||
return RandomString(32)
|
||||
// A Challenge is used to validate an Authorization
|
||||
type Challenge struct {
|
||||
Type string `json:"type"`
|
||||
URL string `json:"url"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ func userAgent() string {
|
|||
|
||||
type client struct {
|
||||
server *url.URL
|
||||
directory map[string]string
|
||||
directory map[string]interface{}
|
||||
email string
|
||||
acctID string
|
||||
http *http.Client
|
||||
|
|
@ -98,7 +98,7 @@ func (c *client) updateDirectory() error {
|
|||
return err
|
||||
}
|
||||
|
||||
var directory map[string]string
|
||||
var directory map[string]interface{}
|
||||
err = json.Unmarshal(respBody, &directory)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -109,7 +109,7 @@ func (c *client) updateDirectory() error {
|
|||
}
|
||||
|
||||
func (c *client) updateNonce() error {
|
||||
nonceURL := c.directory["new-nonce"]
|
||||
nonceURL := c.directory["new-nonce"].(string)
|
||||
if nonceURL == "" {
|
||||
return fmt.Errorf("Missing \"new-nonce\" entry in server directory")
|
||||
}
|
||||
|
|
@ -129,7 +129,7 @@ func (c *client) updateNonce() error {
|
|||
}
|
||||
|
||||
func (c *client) register() error {
|
||||
regURL := c.directory["new-reg"]
|
||||
regURL := c.directory["new-reg"].(string)
|
||||
if regURL == "" {
|
||||
return fmt.Errorf("Missing \"new-reg\" entry in server directory")
|
||||
}
|
||||
|
|
@ -243,7 +243,7 @@ func (c *client) readEndpoint() (string, error) {
|
|||
fmt.Printf("$> Enter a directory endpoint to POST: ")
|
||||
continue
|
||||
}
|
||||
endpoint = c.directory[line]
|
||||
endpoint = c.directory[line].(string)
|
||||
break
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
|
||||
"github.com/letsencrypt/pebble/acme"
|
||||
)
|
||||
|
||||
type Order struct {
|
||||
acme.Order
|
||||
ID string
|
||||
ParsedCSR *x509.CertificateRequest
|
||||
}
|
||||
|
||||
type Registration struct {
|
||||
acme.Registration
|
||||
ID string
|
||||
}
|
||||
|
||||
type Authorization struct {
|
||||
acme.Authorization
|
||||
ID string
|
||||
URL string
|
||||
}
|
||||
|
||||
type Challenge struct {
|
||||
acme.Challenge
|
||||
ID string
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ import (
|
|||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/letsencrypt/pebble/acme"
|
||||
"github.com/letsencrypt/pebble/core"
|
||||
)
|
||||
|
||||
// Pebble keeps all of its various objects (registrations, orders, etc)
|
||||
|
|
@ -15,19 +15,25 @@ type memoryStore struct {
|
|||
|
||||
// Each Registration's ID is the hex encoding of a SHA256 sum over its public
|
||||
// key bytes.
|
||||
registrationsByID map[string]*acme.Registration
|
||||
registrationsByID map[string]*core.Registration
|
||||
|
||||
ordersByID map[string]*acme.Order
|
||||
ordersByID map[string]*core.Order
|
||||
|
||||
authorizationsByID map[string]*core.Authorization
|
||||
|
||||
challengesByID map[string]*core.Challenge
|
||||
}
|
||||
|
||||
func newMemoryStore() *memoryStore {
|
||||
return &memoryStore{
|
||||
registrationsByID: make(map[string]*acme.Registration),
|
||||
ordersByID: make(map[string]*acme.Order),
|
||||
registrationsByID: make(map[string]*core.Registration),
|
||||
ordersByID: make(map[string]*core.Order),
|
||||
authorizationsByID: make(map[string]*core.Authorization),
|
||||
challengesByID: make(map[string]*core.Challenge),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *memoryStore) getRegistrationByID(id string) *acme.Registration {
|
||||
func (m *memoryStore) getRegistrationByID(id string) *core.Registration {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
if reg, present := m.registrationsByID[id]; present {
|
||||
|
|
@ -36,47 +42,41 @@ func (m *memoryStore) getRegistrationByID(id string) *acme.Registration {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) countRegistrations() int {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
return len(m.registrationsByID)
|
||||
}
|
||||
|
||||
func (m *memoryStore) addRegistration(reg *acme.Registration) (*acme.Registration, error) {
|
||||
func (m *memoryStore) addRegistration(reg *core.Registration) (int, error) {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
regID := reg.ID
|
||||
if len(regID) == 0 {
|
||||
return nil, fmt.Errorf("registration must have a non-empty ID to add to memoryStore")
|
||||
return 0, fmt.Errorf("registration must have a non-empty ID to add to memoryStore")
|
||||
}
|
||||
|
||||
if _, present := m.registrationsByID[regID]; present {
|
||||
return nil, fmt.Errorf("registration %q already exists", regID)
|
||||
return 0, fmt.Errorf("registration %q already exists", regID)
|
||||
}
|
||||
|
||||
m.registrationsByID[regID] = reg
|
||||
return reg, nil
|
||||
return len(m.registrationsByID), nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) addOrder(order *acme.Order) (*acme.Order, error) {
|
||||
func (m *memoryStore) addOrder(order *core.Order) (int, error) {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
orderID := order.ID
|
||||
if len(orderID) == 0 {
|
||||
return nil, fmt.Errorf("order must have a non-empty ID to add to memoryStore")
|
||||
return 0, fmt.Errorf("order must have a non-empty ID to add to memoryStore")
|
||||
}
|
||||
|
||||
if _, present := m.ordersByID[orderID]; present {
|
||||
return nil, fmt.Errorf("order %q already exists", orderID)
|
||||
return 0, fmt.Errorf("order %q already exists", orderID)
|
||||
}
|
||||
|
||||
m.ordersByID[orderID] = order
|
||||
return order, nil
|
||||
return len(m.ordersByID), nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) getOrderByID(id string) *acme.Order {
|
||||
func (m *memoryStore) getOrderByID(id string) *core.Order {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
if order, present := m.ordersByID[id]; present {
|
||||
|
|
@ -84,3 +84,55 @@ func (m *memoryStore) getOrderByID(id string) *acme.Order {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) addAuthorization(authz *core.Authorization) (int, error) {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
authzID := authz.ID
|
||||
if len(authzID) == 0 {
|
||||
return 0, fmt.Errorf("authz must have a non-empty ID to add to memoryStore")
|
||||
}
|
||||
|
||||
if _, present := m.authorizationsByID[authzID]; present {
|
||||
return 0, fmt.Errorf("authz %q already exists", authzID)
|
||||
}
|
||||
|
||||
m.authorizationsByID[authzID] = authz
|
||||
return len(m.authorizationsByID), nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) getAuthorizationByID(id string) *core.Authorization {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
if authz, present := m.authorizationsByID[id]; present {
|
||||
return authz
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) addChallenge(chal *core.Challenge) (int, error) {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
chalID := chal.ID
|
||||
if len(chalID) == 0 {
|
||||
return 0, fmt.Errorf("challenge must have a non-empty ID to add to memoryStore")
|
||||
}
|
||||
|
||||
if _, present := m.challengesByID[chalID]; present {
|
||||
return 0, fmt.Errorf("challenge %q already exists", chalID)
|
||||
}
|
||||
|
||||
m.challengesByID[chalID] = chal
|
||||
return len(m.challengesByID), nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) getChallengeByID(id string) *core.Challenge {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
if chal, present := m.challengesByID[id]; present {
|
||||
return chal
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
package wfe
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// randomString and newToken come from Boulder core/util.go
|
||||
// randomString returns a randomly generated string of the requested length.
|
||||
func randomString(byteLength int) string {
|
||||
b := make([]byte, byteLength)
|
||||
_, err := io.ReadFull(rand.Reader, b)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Error reading random bytes: %s", err))
|
||||
}
|
||||
return base64.RawURLEncoding.EncodeToString(b)
|
||||
}
|
||||
|
||||
// newToken produces a random string for Challenges, etc.
|
||||
func newToken() string {
|
||||
return randomString(32)
|
||||
}
|
||||
224
wfe/wfe.go
224
wfe/wfe.go
|
|
@ -19,6 +19,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/letsencrypt/pebble/acme"
|
||||
"github.com/letsencrypt/pebble/core"
|
||||
"gopkg.in/square/go-jose.v1"
|
||||
)
|
||||
|
||||
|
|
@ -31,6 +32,8 @@ const (
|
|||
regPath = "/my-reg/"
|
||||
newOrderPath = "/order-plz"
|
||||
orderPath = "/my-order/"
|
||||
authzPath = "/authZ/"
|
||||
challengePath = "/chalZ/"
|
||||
)
|
||||
|
||||
type requestEvent struct {
|
||||
|
|
@ -149,6 +152,8 @@ func (wfe *WebFrontEndImpl) Handler() http.Handler {
|
|||
wfe.HandleFunc(m, newRegPath, wfe.NewRegistration, "POST")
|
||||
wfe.HandleFunc(m, newOrderPath, wfe.NewOrder, "POST")
|
||||
wfe.HandleFunc(m, orderPath, wfe.Order, "GET")
|
||||
wfe.HandleFunc(m, authzPath, wfe.Authz, "GET")
|
||||
wfe.HandleFunc(m, challengePath, wfe.Challenge, "GET")
|
||||
|
||||
// TODO(@cpu): Handle regPath for existing reg updates
|
||||
return m
|
||||
|
|
@ -160,7 +165,6 @@ func (wfe *WebFrontEndImpl) Directory(
|
|||
response http.ResponseWriter,
|
||||
request *http.Request) {
|
||||
|
||||
// TODO(@cpu): Add directory metadata (e.g. TOS url)
|
||||
directoryEndpoints := map[string]string{
|
||||
"new-nonce": noncePath,
|
||||
"new-reg": newRegPath,
|
||||
|
|
@ -358,6 +362,7 @@ func (wfe *WebFrontEndImpl) NewRegistration(
|
|||
return
|
||||
}
|
||||
|
||||
// newReg is the ACME registration information submitted by the client
|
||||
var newReg acme.Registration
|
||||
err := json.Unmarshal(body, &newReg)
|
||||
if err != nil {
|
||||
|
|
@ -366,15 +371,19 @@ func (wfe *WebFrontEndImpl) NewRegistration(
|
|||
return
|
||||
}
|
||||
|
||||
newReg.Key = key
|
||||
regID, err := keyToID(newReg.Key)
|
||||
// createdReg is the internal Pebble account object
|
||||
createdReg := core.Registration{
|
||||
Registration: newReg,
|
||||
}
|
||||
|
||||
regID, err := keyToID(key)
|
||||
if err != nil {
|
||||
wfe.sendError(acme.MalformedProblem(err.Error()), response)
|
||||
return
|
||||
}
|
||||
newReg.ID = regID
|
||||
createdReg.ID = regID
|
||||
|
||||
if existingReg := wfe.db.getRegistrationByID(newReg.ID); existingReg != nil {
|
||||
if existingReg := wfe.db.getRegistrationByID(regID); existingReg != nil {
|
||||
regURL := wfe.relativeEndpoint(request, fmt.Sprintf("%s%s", regPath, existingReg.ID))
|
||||
response.Header().Set("Location", regURL)
|
||||
wfe.sendError(acme.Conflict("Registration key is already in use"), response)
|
||||
|
|
@ -390,23 +399,116 @@ func (wfe *WebFrontEndImpl) NewRegistration(
|
|||
return
|
||||
}
|
||||
|
||||
wfe.db.addRegistration(&newReg)
|
||||
wfe.log.Printf("There are now %d registrations in memory\n", wfe.db.countRegistrations())
|
||||
count, err := wfe.db.addRegistration(&createdReg)
|
||||
if err != nil {
|
||||
wfe.sendError(acme.InternalErrorProblem("Error saving registration"), response)
|
||||
return
|
||||
}
|
||||
wfe.log.Printf("There are now %d registrations in memory\n", count)
|
||||
|
||||
regURL := wfe.relativeEndpoint(request, fmt.Sprintf("%s%s", regPath, newReg.ID))
|
||||
regURL := wfe.relativeEndpoint(request, fmt.Sprintf("%s%s", regPath, regID))
|
||||
|
||||
response.Header().Add("Location", regURL)
|
||||
err = wfe.writeJsonResponse(response, http.StatusCreated, newReg)
|
||||
if err != nil {
|
||||
wfe.sendError(acme.InternalErrorProblem("Error marshalling registration"), response)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (wfe *WebFrontEndImpl) validateOrder(order *acme.Order) error {
|
||||
// TODO(@cpu) - Apply some more advanced policy decisions on the CSR
|
||||
func (wfe *WebFrontEndImpl) verifyOrder(order *core.Order, reg *core.Registration) *acme.ProblemDetails {
|
||||
// Shouldn't happen - defensive check
|
||||
if order == nil {
|
||||
return acme.InternalErrorProblem("Order is nil")
|
||||
}
|
||||
if reg == nil {
|
||||
return acme.InternalErrorProblem("Registration is nil")
|
||||
}
|
||||
csr := order.ParsedCSR
|
||||
if csr == nil {
|
||||
return acme.InternalErrorProblem("Parsed CSR is nil")
|
||||
}
|
||||
if len(csr.DNSNames) == 0 {
|
||||
return acme.MalformedProblem("CSR has no names in it")
|
||||
}
|
||||
orderKeyID, err := keyToID(csr.PublicKey)
|
||||
if err != nil {
|
||||
return acme.MalformedProblem("CSR has an invalid PublicKey")
|
||||
}
|
||||
if orderKeyID == reg.ID {
|
||||
return acme.MalformedProblem("Certificate public key must be different than account key")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// makeAuthorizations populates an order with new authz's. The request parameter
|
||||
// is required to make the authz URL's absolute based on the request host
|
||||
func (wfe *WebFrontEndImpl) makeAuthorizations(order *core.Order, request *http.Request) error {
|
||||
var auths []string
|
||||
|
||||
names := make([]string, len(order.ParsedCSR.DNSNames))
|
||||
copy(names, order.ParsedCSR.DNSNames)
|
||||
|
||||
// Create one authz for each name in the CSR
|
||||
for _, name := range names {
|
||||
ident := acme.Identifier{
|
||||
Type: acme.IdentifierDNS,
|
||||
Value: name,
|
||||
}
|
||||
authz := &core.Authorization{
|
||||
ID: newToken(),
|
||||
Authorization: acme.Authorization{
|
||||
Status: acme.StatusPending,
|
||||
Identifier: ident,
|
||||
},
|
||||
}
|
||||
authz.URL = wfe.relativeEndpoint(request, fmt.Sprintf("%s%s", authzPath, authz.ID))
|
||||
// Create the challenges for this authz
|
||||
err := wfe.makeChallenges(authz, request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Save the authorization in memory
|
||||
count, err := wfe.db.addAuthorization(authz)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("There are now %d authorizations in the db\n", count)
|
||||
authzURL := wfe.relativeEndpoint(request, fmt.Sprintf("%s%s", authzPath, authz.ID))
|
||||
auths = append(auths, authzURL)
|
||||
}
|
||||
|
||||
order.Authorizations = auths
|
||||
return nil
|
||||
}
|
||||
|
||||
// makeChallenges populates an authz with new challenges. The request parameter
|
||||
// is required to make the challenge URL's absolute based on the request host
|
||||
func (wfe *WebFrontEndImpl) makeChallenges(authz *core.Authorization, request *http.Request) error {
|
||||
var chals []string
|
||||
|
||||
// TODO(@cpu): construct challenges for DNS-01 and TLS-SNI-02
|
||||
chal := &core.Challenge{
|
||||
ID: newToken(),
|
||||
Challenge: acme.Challenge{
|
||||
Type: acme.ChallengeHTTP01,
|
||||
Token: newToken(),
|
||||
URL: authz.URL,
|
||||
},
|
||||
}
|
||||
count, err := wfe.db.addChallenge(chal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("There are now %d challenges in the db\n", count)
|
||||
chalURL := wfe.relativeEndpoint(request, fmt.Sprintf("%s%s", challengePath, chal.ID))
|
||||
chals = append(chals, chalURL)
|
||||
|
||||
authz.Challenges = chals
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewOrder creates a new Order request and populates its authorizations
|
||||
func (wfe *WebFrontEndImpl) NewOrder(
|
||||
ctx context.Context,
|
||||
logEvent *requestEvent,
|
||||
|
|
@ -419,6 +521,7 @@ func (wfe *WebFrontEndImpl) NewOrder(
|
|||
return
|
||||
}
|
||||
|
||||
// Compute the registration ID for the signer's key
|
||||
regID, err := keyToID(key)
|
||||
if err != nil {
|
||||
wfe.log.Printf("keyToID err: %s\n", err.Error())
|
||||
|
|
@ -427,16 +530,17 @@ func (wfe *WebFrontEndImpl) NewOrder(
|
|||
}
|
||||
wfe.log.Printf("received new-order req from reg ID %s\n", regID)
|
||||
|
||||
var existingReg *acme.Registration
|
||||
// Find the existing registration object for that key ID
|
||||
var existingReg *core.Registration
|
||||
if existingReg = wfe.db.getRegistrationByID(regID); existingReg == nil {
|
||||
wfe.sendError(
|
||||
acme.MalformedProblem(
|
||||
fmt.Sprintf("No existing registration with ID %q", regID)),
|
||||
acme.MalformedProblem("No existing registration for signer's public key"),
|
||||
response)
|
||||
return
|
||||
}
|
||||
|
||||
var newOrder acme.OrderRequest
|
||||
// Unpack the order request body
|
||||
var newOrder acme.Order
|
||||
err = json.Unmarshal(body, &newOrder)
|
||||
if err != nil {
|
||||
wfe.sendError(
|
||||
|
|
@ -444,13 +548,13 @@ func (wfe *WebFrontEndImpl) NewOrder(
|
|||
return
|
||||
}
|
||||
|
||||
// Decode and parse the CSR bytes from the order
|
||||
csrBytes, err := base64.RawURLEncoding.DecodeString(newOrder.CSR)
|
||||
if err != nil {
|
||||
wfe.sendError(
|
||||
acme.MalformedProblem("Error decoding Base64url-encoded CSR: "+err.Error()), response)
|
||||
return
|
||||
}
|
||||
|
||||
parsedCSR, err := x509.ParseCertificateRequest(csrBytes)
|
||||
if err != nil {
|
||||
wfe.sendError(
|
||||
|
|
@ -458,30 +562,43 @@ func (wfe *WebFrontEndImpl) NewOrder(
|
|||
return
|
||||
}
|
||||
|
||||
newOrder.Expires = time.Now().AddDate(0, 0, 1).Format(time.RFC3339)
|
||||
|
||||
order := &acme.Order{
|
||||
OrderRequest: newOrder,
|
||||
ParsedCSR: parsedCSR,
|
||||
order := &core.Order{
|
||||
ID: newToken(),
|
||||
Order: acme.Order{
|
||||
Status: acme.StatusPending,
|
||||
Expires: time.Now().AddDate(0, 0, 1).Format(time.RFC3339),
|
||||
// Only the CSR, NotBefore and NotAfter fields of the client request are
|
||||
// copied as-is
|
||||
CSR: newOrder.CSR,
|
||||
NotBefore: newOrder.NotBefore,
|
||||
NotAfter: newOrder.NotAfter,
|
||||
},
|
||||
ParsedCSR: parsedCSR,
|
||||
}
|
||||
order.ID = acme.NewToken()
|
||||
fmt.Printf("Order: %#v\n", order)
|
||||
|
||||
if err := wfe.validateOrder(order); err != nil {
|
||||
wfe.sendError(
|
||||
// TODO(@cpu) validateOrder should return a problem (e.g.
|
||||
// rejectedIdentifier, unsupportedIdentifier, etc) as appropriate
|
||||
acme.MalformedProblem(
|
||||
fmt.Sprintf("Error validating order: %s", err.Error())), response)
|
||||
// Verify the details of the order before creating authorizations
|
||||
if err := wfe.verifyOrder(order, existingReg); err != nil {
|
||||
wfe.sendError(err, response)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = wfe.db.addOrder(order)
|
||||
// Create the authorizations for the order
|
||||
err = wfe.makeAuthorizations(order, request)
|
||||
if err != nil {
|
||||
wfe.sendError(
|
||||
acme.InternalErrorProblem("Error creating authorizations for order"), response)
|
||||
return
|
||||
}
|
||||
|
||||
// Add the order to the in-memory DB
|
||||
count, err := wfe.db.addOrder(order)
|
||||
if err != nil {
|
||||
wfe.sendError(
|
||||
acme.InternalErrorProblem("Error saving order"), response)
|
||||
return
|
||||
}
|
||||
fmt.Printf("Added order %q to the db\n", order.ID)
|
||||
fmt.Printf("There are now %d orders in the db\n", count)
|
||||
|
||||
orderURL := wfe.relativeEndpoint(request, fmt.Sprintf("%s%s", orderPath, order.ID))
|
||||
response.Header().Add("Location", orderURL)
|
||||
|
|
@ -492,6 +609,7 @@ func (wfe *WebFrontEndImpl) NewOrder(
|
|||
}
|
||||
}
|
||||
|
||||
// Order retrieves the details of an existing order
|
||||
func (wfe *WebFrontEndImpl) Order(
|
||||
ctx context.Context,
|
||||
logEvent *requestEvent,
|
||||
|
|
@ -499,21 +617,63 @@ func (wfe *WebFrontEndImpl) Order(
|
|||
request *http.Request) {
|
||||
|
||||
orderID := strings.TrimPrefix(request.URL.Path, orderPath)
|
||||
fmt.Printf("Order ID: %#v\n", orderID)
|
||||
|
||||
order := wfe.db.getOrderByID(orderID)
|
||||
if order == nil {
|
||||
response.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
err := wfe.writeJsonResponse(response, http.StatusOK, order)
|
||||
// Return only the initial OrderRequest not the internal object with the
|
||||
// parsedCSR
|
||||
orderReq := order.Order
|
||||
|
||||
err := wfe.writeJsonResponse(response, http.StatusOK, orderReq)
|
||||
if err != nil {
|
||||
wfe.sendError(acme.InternalErrorProblem("Error marshalling order"), response)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (wfe *WebFrontEndImpl) Authz(
|
||||
ctx context.Context,
|
||||
logEvent *requestEvent,
|
||||
response http.ResponseWriter,
|
||||
request *http.Request) {
|
||||
|
||||
authzID := strings.TrimPrefix(request.URL.Path, authzPath)
|
||||
authz := wfe.db.getAuthorizationByID(authzID)
|
||||
if authz == nil {
|
||||
response.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
err := wfe.writeJsonResponse(response, http.StatusOK, authz.Authorization)
|
||||
if err != nil {
|
||||
wfe.sendError(acme.InternalErrorProblem("Error marshalling authz"), response)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (wfe *WebFrontEndImpl) Challenge(
|
||||
ctx context.Context,
|
||||
logEvent *requestEvent,
|
||||
response http.ResponseWriter,
|
||||
request *http.Request) {
|
||||
|
||||
chalID := strings.TrimPrefix(request.URL.Path, challengePath)
|
||||
chal := wfe.db.getChallengeByID(chalID)
|
||||
if chal == nil {
|
||||
response.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
err := wfe.writeJsonResponse(response, http.StatusOK, chal.Challenge)
|
||||
if err != nil {
|
||||
wfe.sendError(acme.InternalErrorProblem("Error marshalling challenge"), response)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (wfe *WebFrontEndImpl) writeJsonResponse(response http.ResponseWriter, status int, v interface{}) error {
|
||||
jsonReply, err := marshalIndent(v)
|
||||
if err != nil {
|
||||
|
|
|
|||
Loading…
Reference in New Issue