forked from TrueCloudLab/certificates
261 lines
7.5 KiB
Go
261 lines
7.5 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/url"
|
|
|
|
"github.com/smallstep/certificates/acme"
|
|
)
|
|
|
|
// NewLinker returns a new Directory type.
|
|
func NewLinker(dns, prefix string) Linker {
|
|
return &linker{prefix: prefix, dns: dns}
|
|
}
|
|
|
|
// Linker interface for generating links for ACME resources.
|
|
type Linker interface {
|
|
GetLink(ctx context.Context, typ LinkType, abs bool, inputs ...string) string
|
|
GetLinkExplicit(typ LinkType, provName string, abs bool, baseURL *url.URL, inputs ...string) string
|
|
|
|
LinkOrder(ctx context.Context, o *acme.Order)
|
|
LinkAccount(ctx context.Context, o *acme.Account)
|
|
LinkChallenge(ctx context.Context, o *acme.Challenge)
|
|
LinkAuthorization(ctx context.Context, o *acme.Authorization)
|
|
LinkOrdersByAccountID(ctx context.Context, orders []string)
|
|
}
|
|
|
|
// linker generates ACME links.
|
|
type linker struct {
|
|
prefix string
|
|
dns string
|
|
}
|
|
|
|
// GetLink is a helper for GetLinkExplicit
|
|
func (l *linker) GetLink(ctx context.Context, typ LinkType, abs bool, inputs ...string) string {
|
|
var provName string
|
|
if p, err := provisionerFromContext(ctx); err == nil && p != nil {
|
|
provName = p.GetName()
|
|
}
|
|
return l.GetLinkExplicit(typ, provName, abs, baseURLFromContext(ctx), inputs...)
|
|
}
|
|
|
|
// GetLinkExplicit returns an absolute or partial path to the given resource and a base
|
|
// URL dynamically obtained from the request for which the link is being
|
|
// calculated.
|
|
func (l *linker) GetLinkExplicit(typ LinkType, provisionerName string, abs bool, baseURL *url.URL, inputs ...string) string {
|
|
var link string
|
|
switch typ {
|
|
case NewNonceLinkType, NewAccountLinkType, NewOrderLinkType, NewAuthzLinkType, DirectoryLinkType, KeyChangeLinkType, RevokeCertLinkType:
|
|
link = fmt.Sprintf("/%s/%s", provisionerName, typ)
|
|
case AccountLinkType, OrderLinkType, AuthzLinkType, CertificateLinkType:
|
|
link = fmt.Sprintf("/%s/%s/%s", provisionerName, typ, inputs[0])
|
|
case ChallengeLinkType:
|
|
link = fmt.Sprintf("/%s/%s/%s/%s", provisionerName, typ, inputs[0], inputs[1])
|
|
case OrdersByAccountLinkType:
|
|
link = fmt.Sprintf("/%s/%s/%s/orders", provisionerName, AccountLinkType, inputs[0])
|
|
case FinalizeLinkType:
|
|
link = fmt.Sprintf("/%s/%s/%s/finalize", provisionerName, OrderLinkType, inputs[0])
|
|
}
|
|
|
|
if abs {
|
|
// Copy the baseURL value from the pointer. https://github.com/golang/go/issues/38351
|
|
u := url.URL{}
|
|
if baseURL != nil {
|
|
u = *baseURL
|
|
}
|
|
|
|
// If no Scheme is set, then default to https.
|
|
if u.Scheme == "" {
|
|
u.Scheme = "https"
|
|
}
|
|
|
|
// If no Host is set, then use the default (first DNS attr in the ca.json).
|
|
if u.Host == "" {
|
|
u.Host = l.dns
|
|
}
|
|
|
|
u.Path = l.prefix + link
|
|
return u.String()
|
|
}
|
|
return link
|
|
}
|
|
|
|
// LinkType captures the link type.
|
|
type LinkType int
|
|
|
|
const (
|
|
// NewNonceLinkType new-nonce
|
|
NewNonceLinkType LinkType = iota
|
|
// NewAccountLinkType new-account
|
|
NewAccountLinkType
|
|
// AccountLinkType account
|
|
AccountLinkType
|
|
// OrderLinkType order
|
|
OrderLinkType
|
|
// NewOrderLinkType new-order
|
|
NewOrderLinkType
|
|
// OrdersByAccountLinkType list of orders owned by account
|
|
OrdersByAccountLinkType
|
|
// FinalizeLinkType finalize order
|
|
FinalizeLinkType
|
|
// NewAuthzLinkType authz
|
|
NewAuthzLinkType
|
|
// AuthzLinkType new-authz
|
|
AuthzLinkType
|
|
// ChallengeLinkType challenge
|
|
ChallengeLinkType
|
|
// CertificateLinkType certificate
|
|
CertificateLinkType
|
|
// DirectoryLinkType directory
|
|
DirectoryLinkType
|
|
// RevokeCertLinkType revoke certificate
|
|
RevokeCertLinkType
|
|
// KeyChangeLinkType key rollover
|
|
KeyChangeLinkType
|
|
)
|
|
|
|
func (l LinkType) String() string {
|
|
switch l {
|
|
case NewNonceLinkType:
|
|
return "new-nonce"
|
|
case NewAccountLinkType:
|
|
return "new-account"
|
|
case AccountLinkType:
|
|
return "account"
|
|
case NewOrderLinkType:
|
|
return "new-order"
|
|
case OrderLinkType:
|
|
return "order"
|
|
case NewAuthzLinkType:
|
|
return "new-authz"
|
|
case AuthzLinkType:
|
|
return "authz"
|
|
case ChallengeLinkType:
|
|
return "challenge"
|
|
case CertificateLinkType:
|
|
return "certificate"
|
|
case DirectoryLinkType:
|
|
return "directory"
|
|
case RevokeCertLinkType:
|
|
return "revoke-cert"
|
|
case KeyChangeLinkType:
|
|
return "key-change"
|
|
default:
|
|
return fmt.Sprintf("unexpected LinkType '%d'", int(l))
|
|
}
|
|
}
|
|
|
|
// LinkOrder sets the ACME links required by an ACME order.
|
|
func (l *linker) LinkOrder(ctx context.Context, o *acme.Order) {
|
|
o.AuthorizationURLs = make([]string, len(o.AuthorizationIDs))
|
|
for i, azID := range o.AuthorizationIDs {
|
|
o.AuthorizationURLs[i] = l.GetLink(ctx, AuthzLinkType, true, azID)
|
|
}
|
|
o.FinalizeURL = l.GetLink(ctx, FinalizeLinkType, true, o.ID)
|
|
if o.CertificateID != "" {
|
|
o.CertificateURL = l.GetLink(ctx, CertificateLinkType, true, o.CertificateID)
|
|
}
|
|
}
|
|
|
|
// LinkAccount sets the ACME links required by an ACME account.
|
|
func (l *linker) LinkAccount(ctx context.Context, acc *acme.Account) {
|
|
acc.OrdersURL = l.GetLink(ctx, OrdersByAccountLinkType, true, acc.ID)
|
|
}
|
|
|
|
// LinkChallenge sets the ACME links required by an ACME challenge.
|
|
func (l *linker) LinkChallenge(ctx context.Context, ch *acme.Challenge) {
|
|
ch.URL = l.GetLink(ctx, ChallengeLinkType, true, ch.AuthzID, ch.ID)
|
|
}
|
|
|
|
// LinkAuthorization sets the ACME links required by an ACME authorization.
|
|
func (l *linker) LinkAuthorization(ctx context.Context, az *acme.Authorization) {
|
|
for _, ch := range az.Challenges {
|
|
l.LinkChallenge(ctx, ch)
|
|
}
|
|
}
|
|
|
|
// LinkOrdersByAccountID converts each order ID to an ACME link.
|
|
func (l *linker) LinkOrdersByAccountID(ctx context.Context, orders []string) {
|
|
for i, id := range orders {
|
|
orders[i] = l.GetLink(ctx, OrderLinkType, true, id)
|
|
}
|
|
}
|
|
|
|
// MockLinker implements the Linker interface. Only used for testing.
|
|
type MockLinker struct {
|
|
MockGetLink func(ctx context.Context, typ LinkType, abs bool, inputs ...string) string
|
|
MockGetLinkExplicit func(typ LinkType, provName string, abs bool, baseURL *url.URL, inputs ...string) string
|
|
|
|
MockLinkOrder func(ctx context.Context, o *acme.Order)
|
|
MockLinkAccount func(ctx context.Context, o *acme.Account)
|
|
MockLinkChallenge func(ctx context.Context, o *acme.Challenge)
|
|
MockLinkAuthorization func(ctx context.Context, o *acme.Authorization)
|
|
MockLinkOrdersByAccountID func(ctx context.Context, orders []string)
|
|
|
|
MockError error
|
|
MockRet1 interface{}
|
|
}
|
|
|
|
// GetLink mock.
|
|
func (m *MockLinker) GetLink(ctx context.Context, typ LinkType, abs bool, inputs ...string) string {
|
|
if m.MockGetLink != nil {
|
|
return m.MockGetLink(ctx, typ, abs, inputs...)
|
|
}
|
|
|
|
return m.MockRet1.(string)
|
|
}
|
|
|
|
// GetLinkExplicit mock.
|
|
func (m *MockLinker) GetLinkExplicit(typ LinkType, provName string, abs bool, baseURL *url.URL, inputs ...string) string {
|
|
if m.MockGetLinkExplicit != nil {
|
|
return m.MockGetLinkExplicit(typ, provName, abs, baseURL, inputs...)
|
|
}
|
|
|
|
return m.MockRet1.(string)
|
|
}
|
|
|
|
// LinkOrder mock.
|
|
func (m *MockLinker) LinkOrder(ctx context.Context, o *acme.Order) {
|
|
if m.MockLinkOrder != nil {
|
|
m.MockLinkOrder(ctx, o)
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
// LinkAccount mock.
|
|
func (m *MockLinker) LinkAccount(ctx context.Context, o *acme.Account) {
|
|
if m.MockLinkAccount != nil {
|
|
m.MockLinkAccount(ctx, o)
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
// LinkChallenge mock.
|
|
func (m *MockLinker) LinkChallenge(ctx context.Context, o *acme.Challenge) {
|
|
if m.MockLinkChallenge != nil {
|
|
m.MockLinkChallenge(ctx, o)
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
// LinkAuthorization mock.
|
|
func (m *MockLinker) LinkAuthorization(ctx context.Context, o *acme.Authorization) {
|
|
if m.MockLinkAuthorization != nil {
|
|
m.MockLinkAuthorization(ctx, o)
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
// LinkOrderAccountsByID mock.
|
|
func (m *MockLinker) LinkOrderAccountsByID(ctx context.Context, orders []string) {
|
|
if m.MockLinkOrdersByAccountID != nil {
|
|
m.MockLinkOrdersByAccountID(ctx, orders)
|
|
return
|
|
}
|
|
return
|
|
}
|