forked from TrueCloudLab/certificates
Add ACME EAB policy
This commit is contained in:
parent
679e2945f2
commit
7df52dbb76
16 changed files with 622 additions and 56 deletions
|
@ -43,6 +43,23 @@ func KeyToID(jwk *jose.JSONWebKey) (string, error) {
|
|||
return base64.RawURLEncoding.EncodeToString(kid), nil
|
||||
}
|
||||
|
||||
// PolicyNames contains ACME account level policy names
|
||||
type PolicyNames struct {
|
||||
DNSNames []string `json:"dns"`
|
||||
IPRanges []string `json:"ips"`
|
||||
}
|
||||
|
||||
// X509Policy contains ACME account level X.509 policy
|
||||
type X509Policy struct {
|
||||
Allowed PolicyNames `json:"allowed"`
|
||||
Denied PolicyNames `json:"denied"`
|
||||
}
|
||||
|
||||
// Policy is an ACME Account level policy
|
||||
type Policy struct {
|
||||
X509 X509Policy `json:"x509"`
|
||||
}
|
||||
|
||||
// ExternalAccountKey is an ACME External Account Binding key.
|
||||
type ExternalAccountKey struct {
|
||||
ID string `json:"id"`
|
||||
|
@ -52,6 +69,7 @@ type ExternalAccountKey struct {
|
|||
KeyBytes []byte `json:"-"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
BoundAt time.Time `json:"boundAt,omitempty"`
|
||||
Policy *Policy `json:"policy,omitempty"`
|
||||
}
|
||||
|
||||
// AlreadyBound returns whether this EAK is already bound to
|
||||
|
|
|
@ -2,6 +2,7 @@ package api
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
|
@ -130,12 +131,14 @@ func (h *Handler) NewAccount(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
fmt.Println("BEFORE EAK BINDING")
|
||||
|
||||
if eak != nil { // means that we have a (valid) External Account Binding key that should be bound, updated and sent in the response
|
||||
err := eak.BindTo(acc)
|
||||
if err != nil {
|
||||
if err := eak.BindTo(acc); err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
fmt.Println("AFTER EAK BINDING")
|
||||
if err := h.db.UpdateExternalAccountKey(ctx, prov.ID, eak); err != nil {
|
||||
render.Error(w, acme.WrapErrorISE(err, "error updating external account binding key"))
|
||||
return
|
||||
|
|
|
@ -60,6 +60,10 @@ func (h *Handler) validateExternalAccountBinding(ctx context.Context, nar *NewAc
|
|||
return nil, acme.NewError(acme.ErrorUnauthorizedType, "external account binding key with id '%s' was already bound to account '%s' on %s", keyID, externalAccountKey.AccountID, externalAccountKey.BoundAt)
|
||||
}
|
||||
|
||||
if len(externalAccountKey.KeyBytes) == 0 {
|
||||
return nil, acme.NewError(acme.ErrorServerInternalType, "no key bytes") // TODO(hs): improve error message
|
||||
}
|
||||
|
||||
payload, err := eabJWS.Verify(externalAccountKey.KeyBytes)
|
||||
if err != nil {
|
||||
return nil, acme.WrapErrorISE(err, "error verifying externalAccountBinding signature")
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
@ -110,6 +111,9 @@ func (h *Handler) NewOrder(w http.ResponseWriter, r *http.Request) {
|
|||
// TODO(hs): gather all errors, so that we can build one response with subproblems; include the nor.Validate()
|
||||
// error here too, like in example?
|
||||
|
||||
eak, err := h.db.GetExternalAccountKeyByAccountID(ctx, prov.GetID(), acc.ID)
|
||||
fmt.Println("EAK: ", eak, err)
|
||||
|
||||
for _, identifier := range nor.Identifiers {
|
||||
// evaluate the provisioner level policy
|
||||
orderIdentifier := provisioner.ACMEIdentifier{Type: provisioner.ACMEIdentifierType(identifier.Type), Value: identifier.Value}
|
||||
|
|
|
@ -782,6 +782,11 @@ func TestHandler_NewOrder(t *testing.T) {
|
|||
assert.Equals(t, ch.Value, "zap.internal")
|
||||
return errors.New("force")
|
||||
},
|
||||
MockGetExternalAccountKeyByAccountID: func(ctx context.Context, provisionerID, accountID string) (*acme.ExternalAccountKey, error) {
|
||||
assert.Equals(t, prov.GetID(), provisionerID)
|
||||
assert.Equals(t, "accID", accountID)
|
||||
return nil, nil
|
||||
},
|
||||
},
|
||||
err: acme.NewErrorISE("error creating challenge: force"),
|
||||
}
|
||||
|
@ -852,6 +857,11 @@ func TestHandler_NewOrder(t *testing.T) {
|
|||
assert.Equals(t, o.AuthorizationIDs, []string{*az1ID})
|
||||
return errors.New("force")
|
||||
},
|
||||
MockGetExternalAccountKeyByAccountID: func(ctx context.Context, provisionerID, accountID string) (*acme.ExternalAccountKey, error) {
|
||||
assert.Equals(t, prov.GetID(), provisionerID)
|
||||
assert.Equals(t, "accID", accountID)
|
||||
return nil, nil
|
||||
},
|
||||
},
|
||||
err: acme.NewErrorISE("error creating order: force"),
|
||||
}
|
||||
|
@ -949,6 +959,11 @@ func TestHandler_NewOrder(t *testing.T) {
|
|||
assert.Equals(t, o.AuthorizationIDs, []string{*az1ID, *az2ID})
|
||||
return nil
|
||||
},
|
||||
MockGetExternalAccountKeyByAccountID: func(ctx context.Context, provisionerID, accountID string) (*acme.ExternalAccountKey, error) {
|
||||
assert.Equals(t, prov.GetID(), provisionerID)
|
||||
assert.Equals(t, "accID", accountID)
|
||||
return nil, nil
|
||||
},
|
||||
},
|
||||
vr: func(t *testing.T, o *acme.Order) {
|
||||
now := clock.Now()
|
||||
|
@ -1042,6 +1057,11 @@ func TestHandler_NewOrder(t *testing.T) {
|
|||
assert.Equals(t, o.AuthorizationIDs, []string{*az1ID})
|
||||
return nil
|
||||
},
|
||||
MockGetExternalAccountKeyByAccountID: func(ctx context.Context, provisionerID, accountID string) (*acme.ExternalAccountKey, error) {
|
||||
assert.Equals(t, prov.GetID(), provisionerID)
|
||||
assert.Equals(t, "accID", accountID)
|
||||
return nil, nil
|
||||
},
|
||||
},
|
||||
vr: func(t *testing.T, o *acme.Order) {
|
||||
now := clock.Now()
|
||||
|
@ -1135,6 +1155,11 @@ func TestHandler_NewOrder(t *testing.T) {
|
|||
assert.Equals(t, o.AuthorizationIDs, []string{*az1ID})
|
||||
return nil
|
||||
},
|
||||
MockGetExternalAccountKeyByAccountID: func(ctx context.Context, provisionerID, accountID string) (*acme.ExternalAccountKey, error) {
|
||||
assert.Equals(t, prov.GetID(), provisionerID)
|
||||
assert.Equals(t, "accID", accountID)
|
||||
return nil, nil
|
||||
},
|
||||
},
|
||||
vr: func(t *testing.T, o *acme.Order) {
|
||||
now := clock.Now()
|
||||
|
@ -1227,6 +1252,11 @@ func TestHandler_NewOrder(t *testing.T) {
|
|||
assert.Equals(t, o.AuthorizationIDs, []string{*az1ID})
|
||||
return nil
|
||||
},
|
||||
MockGetExternalAccountKeyByAccountID: func(ctx context.Context, provisionerID, accountID string) (*acme.ExternalAccountKey, error) {
|
||||
assert.Equals(t, prov.GetID(), provisionerID)
|
||||
assert.Equals(t, "accID", accountID)
|
||||
return nil, nil
|
||||
},
|
||||
},
|
||||
vr: func(t *testing.T, o *acme.Order) {
|
||||
testBufferDur := 5 * time.Second
|
||||
|
@ -1320,6 +1350,11 @@ func TestHandler_NewOrder(t *testing.T) {
|
|||
assert.Equals(t, o.AuthorizationIDs, []string{*az1ID})
|
||||
return nil
|
||||
},
|
||||
MockGetExternalAccountKeyByAccountID: func(ctx context.Context, provisionerID, accountID string) (*acme.ExternalAccountKey, error) {
|
||||
assert.Equals(t, prov.GetID(), provisionerID)
|
||||
assert.Equals(t, "accID", accountID)
|
||||
return nil, nil
|
||||
},
|
||||
},
|
||||
vr: func(t *testing.T, o *acme.Order) {
|
||||
testBufferDur := 5 * time.Second
|
||||
|
|
12
acme/db.go
12
acme/db.go
|
@ -23,6 +23,7 @@ type DB interface {
|
|||
GetExternalAccountKey(ctx context.Context, provisionerID, keyID string) (*ExternalAccountKey, error)
|
||||
GetExternalAccountKeys(ctx context.Context, provisionerID, cursor string, limit int) ([]*ExternalAccountKey, string, error)
|
||||
GetExternalAccountKeyByReference(ctx context.Context, provisionerID, reference string) (*ExternalAccountKey, error)
|
||||
GetExternalAccountKeyByAccountID(ctx context.Context, provisionerID, accountID string) (*ExternalAccountKey, error)
|
||||
DeleteExternalAccountKey(ctx context.Context, provisionerID, keyID string) error
|
||||
UpdateExternalAccountKey(ctx context.Context, provisionerID string, eak *ExternalAccountKey) error
|
||||
|
||||
|
@ -60,6 +61,7 @@ type MockDB struct {
|
|||
MockGetExternalAccountKey func(ctx context.Context, provisionerID, keyID string) (*ExternalAccountKey, error)
|
||||
MockGetExternalAccountKeys func(ctx context.Context, provisionerID, cursor string, limit int) ([]*ExternalAccountKey, string, error)
|
||||
MockGetExternalAccountKeyByReference func(ctx context.Context, provisionerID, reference string) (*ExternalAccountKey, error)
|
||||
MockGetExternalAccountKeyByAccountID func(ctx context.Context, provisionerID, accountID string) (*ExternalAccountKey, error)
|
||||
MockDeleteExternalAccountKey func(ctx context.Context, provisionerID, keyID string) error
|
||||
MockUpdateExternalAccountKey func(ctx context.Context, provisionerID string, eak *ExternalAccountKey) error
|
||||
|
||||
|
@ -168,6 +170,16 @@ func (m *MockDB) GetExternalAccountKeyByReference(ctx context.Context, provision
|
|||
return m.MockRet1.(*ExternalAccountKey), m.MockError
|
||||
}
|
||||
|
||||
// GetExternalAccountKeyByAccountID mock
|
||||
func (m *MockDB) GetExternalAccountKeyByAccountID(ctx context.Context, provisionerID, accountID string) (*ExternalAccountKey, error) {
|
||||
if m.MockGetExternalAccountKeyByAccountID != nil {
|
||||
return m.MockGetExternalAccountKeyByAccountID(ctx, provisionerID, accountID)
|
||||
} else if m.MockError != nil {
|
||||
return nil, m.MockError
|
||||
}
|
||||
return m.MockRet1.(*ExternalAccountKey), m.MockError
|
||||
}
|
||||
|
||||
// DeleteExternalAccountKey mock
|
||||
func (m *MockDB) DeleteExternalAccountKey(ctx context.Context, provisionerID, keyID string) error {
|
||||
if m.MockDeleteExternalAccountKey != nil {
|
||||
|
|
|
@ -226,6 +226,10 @@ func (db *DB) GetExternalAccountKeyByReference(ctx context.Context, provisionerI
|
|||
return db.GetExternalAccountKey(ctx, provisionerID, dbExternalAccountKeyReference.ExternalAccountKeyID)
|
||||
}
|
||||
|
||||
func (db *DB) GetExternalAccountKeyByAccountID(ctx context.Context, provisionerID, accountID string) (*acme.ExternalAccountKey, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (db *DB) UpdateExternalAccountKey(ctx context.Context, provisionerID string, eak *acme.ExternalAccountKey) error {
|
||||
externalAccountKeyMutex.Lock()
|
||||
defer externalAccountKeyMutex.Unlock()
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ func (h *Handler) Route(r api.Router) {
|
|||
}
|
||||
|
||||
acmePolicyMiddleware := func(next http.HandlerFunc) http.HandlerFunc {
|
||||
return authnz(disabledInStandalone(h.loadProvisionerByName(h.requireEABEnabled(next))))
|
||||
return authnz(disabledInStandalone(h.loadProvisionerByName(h.requireEABEnabled(h.loadExternalAccountKey(next)))))
|
||||
}
|
||||
|
||||
// Provisioners
|
||||
|
@ -92,8 +92,13 @@ func (h *Handler) Route(r api.Router) {
|
|||
r.MethodFunc("DELETE", "/provisioners/{provisionerName}/policy", provisionerPolicyMiddleware(h.policyResponder.DeleteProvisionerPolicy))
|
||||
|
||||
// Policy - ACME Account
|
||||
r.MethodFunc("GET", "/acme/policy/{provisionerName}/{accountID}", acmePolicyMiddleware(h.policyResponder.GetACMEAccountPolicy))
|
||||
r.MethodFunc("POST", "/acme/policy/{provisionerName}/{accountID}", acmePolicyMiddleware(h.policyResponder.CreateACMEAccountPolicy))
|
||||
r.MethodFunc("PUT", "/acme/policy/{provisionerName}/{accountID}", acmePolicyMiddleware(h.policyResponder.UpdateACMEAccountPolicy))
|
||||
r.MethodFunc("DELETE", "/acme/policy/{provisionerName}/{accountID}", acmePolicyMiddleware(h.policyResponder.DeleteACMEAccountPolicy))
|
||||
r.MethodFunc("GET", "/acme/policy/{provisionerName}/reference/{reference}", acmePolicyMiddleware(h.policyResponder.GetACMEAccountPolicy))
|
||||
r.MethodFunc("GET", "/acme/policy/{provisionerName}/key/{keyID}", acmePolicyMiddleware(h.policyResponder.GetACMEAccountPolicy))
|
||||
r.MethodFunc("POST", "/acme/policy/{provisionerName}/reference/{reference}", acmePolicyMiddleware(h.policyResponder.CreateACMEAccountPolicy))
|
||||
r.MethodFunc("POST", "/acme/policy/{provisionerName}/key/{keyID}", acmePolicyMiddleware(h.policyResponder.CreateACMEAccountPolicy))
|
||||
r.MethodFunc("PUT", "/acme/policy/{provisionerName}/reference/{reference}", acmePolicyMiddleware(h.policyResponder.UpdateACMEAccountPolicy))
|
||||
r.MethodFunc("PUT", "/acme/policy/{provisionerName}/key/{keyID}", acmePolicyMiddleware(h.policyResponder.UpdateACMEAccountPolicy))
|
||||
r.MethodFunc("DELETE", "/acme/policy/{provisionerName}/reference/{reference}", acmePolicyMiddleware(h.policyResponder.DeleteACMEAccountPolicy))
|
||||
r.MethodFunc("DELETE", "/acme/policy/{provisionerName}/key/{keyID}", acmePolicyMiddleware(h.policyResponder.DeleteACMEAccountPolicy))
|
||||
|
||||
}
|
||||
|
|
|
@ -4,9 +4,11 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"go.step.sm/linkedca"
|
||||
|
||||
"github.com/smallstep/certificates/acme"
|
||||
"github.com/smallstep/certificates/api/render"
|
||||
"github.com/smallstep/certificates/authority/admin"
|
||||
"github.com/smallstep/certificates/authority/admin/db/nosql"
|
||||
|
@ -81,12 +83,12 @@ func (h *Handler) loadProvisionerByName(next http.HandlerFunc) http.HandlerFunc
|
|||
func (h *Handler) checkAction(next http.HandlerFunc, supportedInStandalone bool) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// temporarily only support the admin nosql DB
|
||||
if _, ok := h.adminDB.(*nosql.DB); !ok {
|
||||
render.Error(w, admin.NewError(admin.ErrorNotImplementedType,
|
||||
"operation not supported"))
|
||||
return
|
||||
}
|
||||
// // temporarily only support the admin nosql DB
|
||||
// if _, ok := h.adminDB.(*nosql.DB); !ok {
|
||||
// render.Error(w, admin.NewError(admin.ErrorNotImplementedType,
|
||||
// "operation not supported"))
|
||||
// return
|
||||
// }
|
||||
|
||||
// actions allowed in standalone mode are always supported
|
||||
if supportedInStandalone {
|
||||
|
@ -106,3 +108,118 @@ func (h *Handler) checkAction(next http.HandlerFunc, supportedInStandalone bool)
|
|||
next(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
// loadExternalAccountKey is a middleware that searches for an ACME
|
||||
// External Account Key by accountID, keyID or reference and stores it in the context.
|
||||
func (h *Handler) loadExternalAccountKey(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
prov := linkedca.ProvisionerFromContext(ctx)
|
||||
|
||||
reference := chi.URLParam(r, "reference")
|
||||
keyID := chi.URLParam(r, "keyID")
|
||||
|
||||
var (
|
||||
eak *acme.ExternalAccountKey
|
||||
err error
|
||||
)
|
||||
|
||||
if keyID != "" {
|
||||
eak, err = h.acmeDB.GetExternalAccountKey(ctx, prov.GetId(), keyID)
|
||||
} else {
|
||||
eak, err = h.acmeDB.GetExternalAccountKeyByReference(ctx, prov.GetId(), reference)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
// TODO: handle error; not found vs. some internal server error
|
||||
render.Error(w, admin.WrapErrorISE(err, "error retrieving ACME External Account key"))
|
||||
return
|
||||
}
|
||||
|
||||
if eak == nil {
|
||||
render.Error(w, admin.NewError(admin.ErrorNotFoundType, "ACME External Account Key does not exist"))
|
||||
return
|
||||
}
|
||||
|
||||
linkedEAK := eakToLinked(eak)
|
||||
|
||||
ctx = linkedca.NewContextWithExternalAccountKey(ctx, linkedEAK)
|
||||
|
||||
next(w, r.WithContext(ctx))
|
||||
}
|
||||
}
|
||||
|
||||
func eakToLinked(k *acme.ExternalAccountKey) *linkedca.EABKey {
|
||||
|
||||
if k == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
eak := &linkedca.EABKey{
|
||||
Id: k.ID,
|
||||
HmacKey: k.KeyBytes,
|
||||
Provisioner: k.ProvisionerID,
|
||||
Reference: k.Reference,
|
||||
Account: k.AccountID,
|
||||
CreatedAt: timestamppb.New(k.CreatedAt),
|
||||
BoundAt: timestamppb.New(k.BoundAt),
|
||||
}
|
||||
|
||||
if k.Policy != nil {
|
||||
eak.Policy = &linkedca.Policy{
|
||||
X509: &linkedca.X509Policy{
|
||||
Allow: &linkedca.X509Names{},
|
||||
Deny: &linkedca.X509Names{},
|
||||
},
|
||||
}
|
||||
eak.Policy.X509.Allow.Dns = k.Policy.X509.Allowed.DNSNames
|
||||
eak.Policy.X509.Allow.Ips = k.Policy.X509.Allowed.IPRanges
|
||||
eak.Policy.X509.Deny.Dns = k.Policy.X509.Denied.DNSNames
|
||||
eak.Policy.X509.Deny.Ips = k.Policy.X509.Denied.IPRanges
|
||||
}
|
||||
|
||||
return eak
|
||||
}
|
||||
|
||||
func linkedEAKToCertificates(k *linkedca.EABKey) *acme.ExternalAccountKey {
|
||||
if k == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
eak := &acme.ExternalAccountKey{
|
||||
ID: k.Id,
|
||||
ProvisionerID: k.Provisioner,
|
||||
Reference: k.Reference,
|
||||
AccountID: k.Account,
|
||||
KeyBytes: k.HmacKey,
|
||||
CreatedAt: k.CreatedAt.AsTime(),
|
||||
BoundAt: k.BoundAt.AsTime(),
|
||||
}
|
||||
|
||||
if k.Policy == nil {
|
||||
return eak
|
||||
}
|
||||
|
||||
eak.Policy = &acme.Policy{}
|
||||
|
||||
if k.Policy.X509 == nil {
|
||||
return eak
|
||||
}
|
||||
|
||||
eak.Policy.X509 = acme.X509Policy{
|
||||
Allowed: acme.PolicyNames{},
|
||||
Denied: acme.PolicyNames{},
|
||||
}
|
||||
|
||||
if k.Policy.X509.Allow != nil {
|
||||
eak.Policy.X509.Allowed.DNSNames = k.Policy.X509.Allow.Dns
|
||||
eak.Policy.X509.Allowed.IPRanges = k.Policy.X509.Allow.Ips
|
||||
}
|
||||
|
||||
if k.Policy.X509.Deny != nil {
|
||||
eak.Policy.X509.Denied.DNSNames = k.Policy.X509.Deny.Dns
|
||||
eak.Policy.X509.Denied.IPRanges = k.Policy.X509.Deny.Ips
|
||||
}
|
||||
|
||||
return eak
|
||||
}
|
||||
|
|
|
@ -368,15 +368,15 @@ func TestHandler_checkAction(t *testing.T) {
|
|||
statusCode int
|
||||
}
|
||||
var tests = map[string]func(t *testing.T) test{
|
||||
"standalone-mockdb-supported": func(t *testing.T) test {
|
||||
err := admin.NewError(admin.ErrorNotImplementedType, "operation not supported")
|
||||
err.Message = "operation not supported"
|
||||
return test{
|
||||
adminDB: &admin.MockDB{},
|
||||
statusCode: 501,
|
||||
err: err,
|
||||
}
|
||||
},
|
||||
// "standalone-mockdb-supported": func(t *testing.T) test {
|
||||
// err := admin.NewError(admin.ErrorNotImplementedType, "operation not supported")
|
||||
// err.Message = "operation not supported"
|
||||
// return test{
|
||||
// adminDB: &admin.MockDB{},
|
||||
// statusCode: 501,
|
||||
// err: err,
|
||||
// }
|
||||
// },
|
||||
"standalone-nosql-supported": func(t *testing.T) test {
|
||||
return test{
|
||||
supportedInStandalone: true,
|
||||
|
@ -400,22 +400,21 @@ func TestHandler_checkAction(t *testing.T) {
|
|||
err: err,
|
||||
}
|
||||
},
|
||||
"standalone-no-nosql-not-supported": func(t *testing.T) test {
|
||||
// TODO(hs): temporarily expects an error instead of an OK response
|
||||
err := admin.NewError(admin.ErrorNotImplementedType, "operation not supported")
|
||||
err.Message = "operation not supported"
|
||||
return test{
|
||||
supportedInStandalone: false,
|
||||
adminDB: &admin.MockDB{},
|
||||
next: func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write(nil) // mock response with status 200
|
||||
},
|
||||
statusCode: 501,
|
||||
err: err,
|
||||
// "standalone-no-nosql-not-supported": func(t *testing.T) test {
|
||||
// // TODO(hs): temporarily expects an error instead of an OK response
|
||||
// err := admin.NewError(admin.ErrorNotImplementedType, "operation not supported")
|
||||
// err.Message = "operation not supported"
|
||||
// return test{
|
||||
// supportedInStandalone: false,
|
||||
// adminDB: &admin.MockDB{},
|
||||
// next: func(w http.ResponseWriter, r *http.Request) {
|
||||
// w.Write(nil) // mock response with status 200
|
||||
// },
|
||||
// statusCode: 501,
|
||||
// err: err,
|
||||
// }
|
||||
// },
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
for name, prep := range tests {
|
||||
tc := prep(t)
|
||||
t.Run(name, func(t *testing.T) {
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
"go.step.sm/linkedca"
|
||||
|
||||
"github.com/smallstep/certificates/acme"
|
||||
"github.com/smallstep/certificates/api/read"
|
||||
"github.com/smallstep/certificates/api/render"
|
||||
"github.com/smallstep/certificates/authority"
|
||||
|
@ -31,13 +32,15 @@ type policyAdminResponderInterface interface {
|
|||
type PolicyAdminResponder struct {
|
||||
auth adminAuthority
|
||||
adminDB admin.DB
|
||||
acmeDB acme.DB
|
||||
}
|
||||
|
||||
// NewACMEAdminResponder returns a new ACMEAdminResponder
|
||||
func NewPolicyAdminResponder(auth adminAuthority, adminDB admin.DB) *PolicyAdminResponder {
|
||||
func NewPolicyAdminResponder(auth adminAuthority, adminDB admin.DB, acmeDB acme.DB) *PolicyAdminResponder {
|
||||
return &PolicyAdminResponder{
|
||||
auth: auth,
|
||||
adminDB: adminDB,
|
||||
acmeDB: acmeDB,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,8 +159,7 @@ func (par *PolicyAdminResponder) DeleteAuthorityPolicy(w http.ResponseWriter, r
|
|||
return
|
||||
}
|
||||
|
||||
err = par.auth.RemoveAuthorityPolicy(ctx)
|
||||
if err != nil {
|
||||
if err := par.auth.RemoveAuthorityPolicy(ctx); err != nil {
|
||||
render.Error(w, admin.WrapErrorISE(err, "error deleting authority policy"))
|
||||
return
|
||||
}
|
||||
|
@ -200,8 +202,7 @@ func (par *PolicyAdminResponder) CreateProvisionerPolicy(w http.ResponseWriter,
|
|||
|
||||
prov.Policy = newPolicy
|
||||
|
||||
err := par.auth.UpdateProvisioner(ctx, prov)
|
||||
if err != nil {
|
||||
if err := par.auth.UpdateProvisioner(ctx, prov); err != nil {
|
||||
var pe *authority.PolicyError
|
||||
isPolicyError := errors.As(err, &pe)
|
||||
if isPolicyError && pe.Typ == authority.AdminLockOut || pe.Typ == authority.EvaluationFailure || pe.Typ == authority.ConfigurationFailure {
|
||||
|
@ -233,8 +234,7 @@ func (par *PolicyAdminResponder) UpdateProvisionerPolicy(w http.ResponseWriter,
|
|||
}
|
||||
|
||||
prov.Policy = newPolicy
|
||||
err := par.auth.UpdateProvisioner(ctx, prov)
|
||||
if err != nil {
|
||||
if err := par.auth.UpdateProvisioner(ctx, prov); err != nil {
|
||||
var pe *authority.PolicyError
|
||||
isPolicyError := errors.As(err, &pe)
|
||||
if isPolicyError && pe.Typ == authority.AdminLockOut || pe.Typ == authority.EvaluationFailure || pe.Typ == authority.ConfigurationFailure {
|
||||
|
@ -263,8 +263,7 @@ func (par *PolicyAdminResponder) DeleteProvisionerPolicy(w http.ResponseWriter,
|
|||
// remove the policy
|
||||
prov.Policy = nil
|
||||
|
||||
err := par.auth.UpdateProvisioner(ctx, prov)
|
||||
if err != nil {
|
||||
if err := par.auth.UpdateProvisioner(ctx, prov); err != nil {
|
||||
render.Error(w, admin.WrapErrorISE(err, "error deleting provisioner policy"))
|
||||
return
|
||||
}
|
||||
|
@ -273,17 +272,92 @@ func (par *PolicyAdminResponder) DeleteProvisionerPolicy(w http.ResponseWriter,
|
|||
}
|
||||
|
||||
func (par *PolicyAdminResponder) GetACMEAccountPolicy(w http.ResponseWriter, r *http.Request) {
|
||||
render.JSONStatus(w, "not implemented yet", http.StatusNotImplemented)
|
||||
ctx := r.Context()
|
||||
eak := linkedca.ExternalAccountKeyFromContext(ctx)
|
||||
|
||||
policy := eak.GetPolicy()
|
||||
if policy == nil {
|
||||
render.Error(w, admin.NewError(admin.ErrorNotFoundType, "ACME EAK policy does not exist"))
|
||||
return
|
||||
}
|
||||
|
||||
render.ProtoJSONStatus(w, policy, http.StatusOK)
|
||||
}
|
||||
|
||||
func (par *PolicyAdminResponder) CreateACMEAccountPolicy(w http.ResponseWriter, r *http.Request) {
|
||||
render.JSONStatus(w, "not implemented yet", http.StatusNotImplemented)
|
||||
ctx := r.Context()
|
||||
prov := linkedca.ProvisionerFromContext(ctx)
|
||||
eak := linkedca.ExternalAccountKeyFromContext(ctx)
|
||||
|
||||
policy := eak.GetPolicy()
|
||||
if policy != nil {
|
||||
adminErr := admin.NewError(admin.ErrorBadRequestType, "ACME EAK %s already has a policy", eak.Id)
|
||||
adminErr.Status = http.StatusConflict
|
||||
render.Error(w, adminErr)
|
||||
return
|
||||
}
|
||||
|
||||
var newPolicy = new(linkedca.Policy)
|
||||
if !read.ProtoJSONWithCheck(w, r.Body, newPolicy) {
|
||||
return
|
||||
}
|
||||
|
||||
eak.Policy = newPolicy
|
||||
|
||||
acmeEAK := linkedEAKToCertificates(eak)
|
||||
if err := par.acmeDB.UpdateExternalAccountKey(ctx, prov.GetId(), acmeEAK); err != nil {
|
||||
render.Error(w, admin.WrapErrorISE(err, "error creating ACME EAK policy"))
|
||||
return
|
||||
}
|
||||
|
||||
render.ProtoJSONStatus(w, newPolicy, http.StatusCreated)
|
||||
}
|
||||
|
||||
func (par *PolicyAdminResponder) UpdateACMEAccountPolicy(w http.ResponseWriter, r *http.Request) {
|
||||
render.JSONStatus(w, "not implemented yet", http.StatusNotImplemented)
|
||||
ctx := r.Context()
|
||||
prov := linkedca.ProvisionerFromContext(ctx)
|
||||
eak := linkedca.ExternalAccountKeyFromContext(ctx)
|
||||
|
||||
policy := eak.GetPolicy()
|
||||
if policy == nil {
|
||||
render.Error(w, admin.NewError(admin.ErrorNotFoundType, "ACME EAK policy does not exist"))
|
||||
return
|
||||
}
|
||||
|
||||
var newPolicy = new(linkedca.Policy)
|
||||
if !read.ProtoJSONWithCheck(w, r.Body, newPolicy) {
|
||||
return
|
||||
}
|
||||
|
||||
eak.Policy = newPolicy
|
||||
acmeEAK := linkedEAKToCertificates(eak)
|
||||
if err := par.acmeDB.UpdateExternalAccountKey(ctx, prov.GetId(), acmeEAK); err != nil {
|
||||
render.Error(w, admin.WrapErrorISE(err, "error updating ACME EAK policy"))
|
||||
return
|
||||
}
|
||||
|
||||
render.ProtoJSONStatus(w, newPolicy, http.StatusOK)
|
||||
}
|
||||
|
||||
func (par *PolicyAdminResponder) DeleteACMEAccountPolicy(w http.ResponseWriter, r *http.Request) {
|
||||
render.JSONStatus(w, "not implemented yet", http.StatusNotImplemented)
|
||||
ctx := r.Context()
|
||||
prov := linkedca.ProvisionerFromContext(ctx)
|
||||
eak := linkedca.ExternalAccountKeyFromContext(ctx)
|
||||
|
||||
policy := eak.GetPolicy()
|
||||
if policy == nil {
|
||||
render.Error(w, admin.NewError(admin.ErrorNotFoundType, "ACME EAK policy does not exist"))
|
||||
return
|
||||
}
|
||||
|
||||
// remove the policy
|
||||
eak.Policy = nil
|
||||
|
||||
acmeEAK := linkedEAKToCertificates(eak)
|
||||
if err := par.acmeDB.UpdateExternalAccountKey(ctx, prov.GetId(), acmeEAK); err != nil {
|
||||
render.Error(w, admin.WrapErrorISE(err, "error deleting ACME EAK policy"))
|
||||
return
|
||||
}
|
||||
|
||||
render.JSONStatus(w, DeleteResponse{Status: "ok"}, http.StatusOK)
|
||||
}
|
||||
|
|
|
@ -808,6 +808,290 @@ retry:
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *AdminClient) GetProvisionerPolicy(provisionerName string) (*linkedca.Policy, error) {
|
||||
var retried bool
|
||||
u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "provisioner", provisionerName, "policy")})
|
||||
tok, err := c.generateAdminToken(u.Path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error generating admin token: %w", err)
|
||||
}
|
||||
req, err := http.NewRequest(http.MethodGet, u.String(), http.NoBody)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating GET %s request failed: %w", u, err)
|
||||
}
|
||||
req.Header.Add("Authorization", tok)
|
||||
retry:
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("client GET %s failed: %w", u, err)
|
||||
}
|
||||
if resp.StatusCode >= 400 {
|
||||
if !retried && c.retryOnError(resp) {
|
||||
retried = true
|
||||
goto retry
|
||||
}
|
||||
return nil, readAdminError(resp.Body)
|
||||
}
|
||||
var policy = new(linkedca.Policy)
|
||||
if err := readProtoJSON(resp.Body, policy); err != nil {
|
||||
return nil, fmt.Errorf("error reading %s: %w", u, err)
|
||||
}
|
||||
return policy, nil
|
||||
}
|
||||
|
||||
func (c *AdminClient) CreateProvisionerPolicy(provisionerName string, p *linkedca.Policy) (*linkedca.Policy, error) {
|
||||
var retried bool
|
||||
body, err := protojson.Marshal(p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error marshaling request: %w", err)
|
||||
}
|
||||
u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "provisioner", provisionerName, "policy")})
|
||||
tok, err := c.generateAdminToken(u.Path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error generating admin token: %w", err)
|
||||
}
|
||||
req, err := http.NewRequest(http.MethodPost, u.String(), bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating POST %s request failed: %w", u, err)
|
||||
}
|
||||
req.Header.Add("Authorization", tok)
|
||||
retry:
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("client POST %s failed: %w", u, err)
|
||||
}
|
||||
if resp.StatusCode >= 400 {
|
||||
if !retried && c.retryOnError(resp) {
|
||||
retried = true
|
||||
goto retry
|
||||
}
|
||||
return nil, readAdminError(resp.Body)
|
||||
}
|
||||
var policy = new(linkedca.Policy)
|
||||
if err := readProtoJSON(resp.Body, policy); err != nil {
|
||||
return nil, fmt.Errorf("error reading %s: %w", u, err)
|
||||
}
|
||||
return policy, nil
|
||||
}
|
||||
|
||||
func (c *AdminClient) UpdateProvisionerPolicy(provisionerName string, p *linkedca.Policy) (*linkedca.Policy, error) {
|
||||
var retried bool
|
||||
body, err := protojson.Marshal(p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error marshaling request: %w", err)
|
||||
}
|
||||
u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "provisioner", provisionerName, "policy")})
|
||||
tok, err := c.generateAdminToken(u.Path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error generating admin token: %w", err)
|
||||
}
|
||||
req, err := http.NewRequest(http.MethodPut, u.String(), bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating PUT %s request failed: %w", u, err)
|
||||
}
|
||||
req.Header.Add("Authorization", tok)
|
||||
retry:
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("client PUT %s failed: %w", u, err)
|
||||
}
|
||||
if resp.StatusCode >= 400 {
|
||||
if !retried && c.retryOnError(resp) {
|
||||
retried = true
|
||||
goto retry
|
||||
}
|
||||
return nil, readAdminError(resp.Body)
|
||||
}
|
||||
var policy = new(linkedca.Policy)
|
||||
if err := readProtoJSON(resp.Body, policy); err != nil {
|
||||
return nil, fmt.Errorf("error reading %s: %w", u, err)
|
||||
}
|
||||
return policy, nil
|
||||
}
|
||||
|
||||
func (c *AdminClient) RemoveProvisionerPolicy(provisionerName string) error {
|
||||
var retried bool
|
||||
u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "provisioner", provisionerName, "policy")})
|
||||
tok, err := c.generateAdminToken(u.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error generating admin token: %w", err)
|
||||
}
|
||||
req, err := http.NewRequest(http.MethodDelete, u.String(), http.NoBody)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating DELETE %s request failed: %w", u, err)
|
||||
}
|
||||
req.Header.Add("Authorization", tok)
|
||||
retry:
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("client DELETE %s failed: %w", u, err)
|
||||
}
|
||||
if resp.StatusCode >= 400 {
|
||||
if !retried && c.retryOnError(resp) {
|
||||
retried = true
|
||||
goto retry
|
||||
}
|
||||
return readAdminError(resp.Body)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *AdminClient) GetACMEPolicy(provisionerName, reference, keyID string) (*linkedca.Policy, error) {
|
||||
var retried bool
|
||||
var urlPath string
|
||||
switch {
|
||||
case keyID != "":
|
||||
urlPath = path.Join(adminURLPrefix, "acme", "policy", provisionerName, "key", keyID)
|
||||
default:
|
||||
urlPath = path.Join(adminURLPrefix, "acme", "policy", provisionerName, "reference", reference)
|
||||
}
|
||||
u := c.endpoint.ResolveReference(&url.URL{Path: urlPath})
|
||||
tok, err := c.generateAdminToken(u.Path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error generating admin token: %w", err)
|
||||
}
|
||||
req, err := http.NewRequest(http.MethodGet, u.String(), http.NoBody)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating GET %s request failed: %w", u, err)
|
||||
}
|
||||
req.Header.Add("Authorization", tok)
|
||||
retry:
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("client GET %s failed: %w", u, err)
|
||||
}
|
||||
if resp.StatusCode >= 400 {
|
||||
if !retried && c.retryOnError(resp) {
|
||||
retried = true
|
||||
goto retry
|
||||
}
|
||||
return nil, readAdminError(resp.Body)
|
||||
}
|
||||
var policy = new(linkedca.Policy)
|
||||
if err := readProtoJSON(resp.Body, policy); err != nil {
|
||||
return nil, fmt.Errorf("error reading %s: %w", u, err)
|
||||
}
|
||||
return policy, nil
|
||||
}
|
||||
|
||||
func (c *AdminClient) CreateACMEPolicy(provisionerName, reference, keyID string, p *linkedca.Policy) (*linkedca.Policy, error) {
|
||||
var retried bool
|
||||
body, err := protojson.Marshal(p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error marshaling request: %w", err)
|
||||
}
|
||||
var urlPath string
|
||||
switch {
|
||||
case keyID != "":
|
||||
urlPath = path.Join(adminURLPrefix, "acme", "policy", provisionerName, "key", keyID)
|
||||
default:
|
||||
urlPath = path.Join(adminURLPrefix, "acme", "policy", provisionerName, "reference", reference)
|
||||
}
|
||||
u := c.endpoint.ResolveReference(&url.URL{Path: urlPath})
|
||||
tok, err := c.generateAdminToken(u.Path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error generating admin token: %w", err)
|
||||
}
|
||||
req, err := http.NewRequest(http.MethodPost, u.String(), bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating POST %s request failed: %w", u, err)
|
||||
}
|
||||
req.Header.Add("Authorization", tok)
|
||||
retry:
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("client POST %s failed: %w", u, err)
|
||||
}
|
||||
if resp.StatusCode >= 400 {
|
||||
if !retried && c.retryOnError(resp) {
|
||||
retried = true
|
||||
goto retry
|
||||
}
|
||||
return nil, readAdminError(resp.Body)
|
||||
}
|
||||
var policy = new(linkedca.Policy)
|
||||
if err := readProtoJSON(resp.Body, policy); err != nil {
|
||||
return nil, fmt.Errorf("error reading %s: %w", u, err)
|
||||
}
|
||||
return policy, nil
|
||||
}
|
||||
|
||||
func (c *AdminClient) UpdateACMEPolicy(provisionerName, reference, keyID string, p *linkedca.Policy) (*linkedca.Policy, error) {
|
||||
var retried bool
|
||||
body, err := protojson.Marshal(p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error marshaling request: %w", err)
|
||||
}
|
||||
var urlPath string
|
||||
switch {
|
||||
case keyID != "":
|
||||
urlPath = path.Join(adminURLPrefix, "acme", "policy", provisionerName, "key", keyID)
|
||||
default:
|
||||
urlPath = path.Join(adminURLPrefix, "acme", "policy", provisionerName, "reference", reference)
|
||||
}
|
||||
u := c.endpoint.ResolveReference(&url.URL{Path: urlPath})
|
||||
tok, err := c.generateAdminToken(u.Path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error generating admin token: %w", err)
|
||||
}
|
||||
req, err := http.NewRequest(http.MethodPut, u.String(), bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating PUT %s request failed: %w", u, err)
|
||||
}
|
||||
req.Header.Add("Authorization", tok)
|
||||
retry:
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("client PUT %s failed: %w", u, err)
|
||||
}
|
||||
if resp.StatusCode >= 400 {
|
||||
if !retried && c.retryOnError(resp) {
|
||||
retried = true
|
||||
goto retry
|
||||
}
|
||||
return nil, readAdminError(resp.Body)
|
||||
}
|
||||
var policy = new(linkedca.Policy)
|
||||
if err := readProtoJSON(resp.Body, policy); err != nil {
|
||||
return nil, fmt.Errorf("error reading %s: %w", u, err)
|
||||
}
|
||||
return policy, nil
|
||||
}
|
||||
|
||||
func (c *AdminClient) RemoveACMEPolicy(provisionerName, reference, keyID string) error {
|
||||
var retried bool
|
||||
var urlPath string
|
||||
switch {
|
||||
case keyID != "":
|
||||
urlPath = path.Join(adminURLPrefix, "acme", "policy", provisionerName, "key", keyID)
|
||||
default:
|
||||
urlPath = path.Join(adminURLPrefix, "acme", "policy", provisionerName, "reference", reference)
|
||||
}
|
||||
u := c.endpoint.ResolveReference(&url.URL{Path: urlPath})
|
||||
tok, err := c.generateAdminToken(u.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error generating admin token: %w", err)
|
||||
}
|
||||
req, err := http.NewRequest(http.MethodDelete, u.String(), http.NoBody)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating DELETE %s request failed: %w", u, err)
|
||||
}
|
||||
req.Header.Add("Authorization", tok)
|
||||
retry:
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("client DELETE %s failed: %w", u, err)
|
||||
}
|
||||
if resp.StatusCode >= 400 {
|
||||
if !retried && c.retryOnError(resp) {
|
||||
retried = true
|
||||
goto retry
|
||||
}
|
||||
return readAdminError(resp.Body)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func readAdminError(r io.ReadCloser) error {
|
||||
// TODO: not all errors can be read (i.e. 404); seems to be a bigger issue
|
||||
defer r.Close()
|
||||
|
|
2
ca/ca.go
2
ca/ca.go
|
@ -208,7 +208,7 @@ func (ca *CA) Init(cfg *config.Config) (*CA, error) {
|
|||
adminDB := auth.GetAdminDatabase()
|
||||
if adminDB != nil {
|
||||
acmeAdminResponder := adminAPI.NewACMEAdminResponder()
|
||||
policyAdminResponder := adminAPI.NewPolicyAdminResponder(auth, adminDB)
|
||||
policyAdminResponder := adminAPI.NewPolicyAdminResponder(auth, adminDB, acmeDB)
|
||||
adminHandler := adminAPI.NewHandler(auth, adminDB, acmeDB, acmeAdminResponder, policyAdminResponder)
|
||||
mux.Route("/admin", func(r chi.Router) {
|
||||
adminHandler.Route(r)
|
||||
|
|
8
go.mod
8
go.mod
|
@ -38,12 +38,12 @@ require (
|
|||
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352
|
||||
go.step.sm/cli-utils v0.7.0
|
||||
go.step.sm/crypto v0.16.1
|
||||
go.step.sm/linkedca v0.12.1-0.20220331143637-69bee7065785
|
||||
go.step.sm/linkedca v0.12.1-0.20220405095509-878e3e5f78a3
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3
|
||||
golang.org/x/net v0.0.0-20220325170049-de3da57026de
|
||||
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f // indirect
|
||||
golang.org/x/net v0.0.0-20220403103023-749bd193bc2b
|
||||
golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64 // indirect
|
||||
google.golang.org/api v0.70.0
|
||||
google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7
|
||||
google.golang.org/genproto v0.0.0-20220401170504-314d38edb7de
|
||||
google.golang.org/grpc v1.45.0
|
||||
google.golang.org/protobuf v1.28.0
|
||||
gopkg.in/square/go-jose.v2 v2.6.0
|
||||
|
|
8
go.sum
8
go.sum
|
@ -717,6 +717,8 @@ go.step.sm/linkedca v0.12.0 h1:FA18uJO5P6W2pklcezMs+w+N3dVbpKEE1LP9HLsJgg4=
|
|||
go.step.sm/linkedca v0.12.0/go.mod h1:W59ucS4vFpuR0g4PtkGbbtXAwxbDEnNCg+ovkej1ANM=
|
||||
go.step.sm/linkedca v0.12.1-0.20220331143637-69bee7065785 h1:14HYoAd9P7DNpf8OkXq4OWTzEq5E6iX4hNkYu/NH4Wo=
|
||||
go.step.sm/linkedca v0.12.1-0.20220331143637-69bee7065785/go.mod h1:W59ucS4vFpuR0g4PtkGbbtXAwxbDEnNCg+ovkej1ANM=
|
||||
go.step.sm/linkedca v0.12.1-0.20220405095509-878e3e5f78a3 h1:CIq0rMhfcV3oDRT0h4de2GVpRQnBnLJTTVIdc0eFjUg=
|
||||
go.step.sm/linkedca v0.12.1-0.20220405095509-878e3e5f78a3/go.mod h1:W59ucS4vFpuR0g4PtkGbbtXAwxbDEnNCg+ovkej1ANM=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
|
@ -839,6 +841,8 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacp
|
|||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220325170049-de3da57026de h1:pZB1TWnKi+o4bENlbzAgLrEbY4RMYmUIRobMcSmfeYc=
|
||||
golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220403103023-749bd193bc2b h1:vI32FkLJNAWtGD4BwkThwEy6XS7ZLLMHkSkYfF8M0W0=
|
||||
golang.org/x/net v0.0.0-20220403103023-749bd193bc2b/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
|
@ -954,6 +958,8 @@ golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIj
|
|||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f h1:rlezHXNlxYWvBCzNses9Dlc7nGFaNMJeqLolcmQSSZY=
|
||||
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64 h1:D1v9ucDTYBtbz5vNuBbAhIMAGhQhJ6Ym5ah3maMVNX4=
|
||||
golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
|
@ -1156,6 +1162,8 @@ google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf h1:SVYXkUz2yZS9FWb
|
|||
google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
||||
google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7 h1:HOL66YCI20JvN2hVk6o2YIp9i/3RvzVUz82PqNr7fXw=
|
||||
google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||
google.golang.org/genproto v0.0.0-20220401170504-314d38edb7de h1:9Ti5SG2U4cAcluryUo/sFay3TQKoxiFMfaT0pbizU7k=
|
||||
google.golang.org/genproto v0.0.0-20220401170504-314d38edb7de/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
|
||||
|
|
Loading…
Reference in a new issue