mirror of
https://github.com/smallstep/certificates.git
synced 2024-11-17 15:29:21 +00:00
600 lines
17 KiB
Go
600 lines
17 KiB
Go
package api
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"crypto/ed25519"
|
|
"crypto/rand"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/asn1"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"encoding/pem"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/smallstep/certificates/acme"
|
|
"github.com/smallstep/certificates/acme/db/nosql"
|
|
"github.com/smallstep/certificates/authority"
|
|
"github.com/smallstep/certificates/authority/provisioner"
|
|
"github.com/smallstep/certificates/authority/provisioner/wire"
|
|
nosqlDB "github.com/smallstep/nosql"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"go.step.sm/crypto/jose"
|
|
"go.step.sm/crypto/minica"
|
|
"go.step.sm/crypto/pemutil"
|
|
"go.step.sm/crypto/x509util"
|
|
)
|
|
|
|
const (
|
|
baseURL = "test.ca.smallstep.com"
|
|
linkerPrefix = "acme"
|
|
)
|
|
|
|
func newWireProvisionerWithOptions(t *testing.T, options *provisioner.Options) *provisioner.ACME {
|
|
p := newProvWithOptions(options)
|
|
a, ok := p.(*provisioner.ACME)
|
|
if !ok {
|
|
t.Fatal("not a valid ACME provisioner")
|
|
}
|
|
a.Challenges = []provisioner.ACMEChallenge{
|
|
provisioner.WIREOIDC_01,
|
|
provisioner.WIREDPOP_01,
|
|
}
|
|
return a
|
|
}
|
|
|
|
// TODO(hs): replace with test CA server + acmez based test client for
|
|
// more realistic integration test?
|
|
func TestWireIntegration(t *testing.T) {
|
|
accessTokenSignerJWK, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
|
require.NoError(t, err)
|
|
|
|
accessTokenSignerPEMBlock, err := pemutil.Serialize(accessTokenSignerJWK.Public().Key)
|
|
require.NoError(t, err)
|
|
accessTokenSignerPEMBytes := pem.EncodeToMemory(accessTokenSignerPEMBlock)
|
|
|
|
accessTokenSigner, err := jose.NewSigner(jose.SigningKey{
|
|
Algorithm: jose.SignatureAlgorithm(accessTokenSignerJWK.Algorithm),
|
|
Key: accessTokenSignerJWK,
|
|
}, new(jose.SignerOptions))
|
|
require.NoError(t, err)
|
|
|
|
oidcTokenSignerJWK, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
|
require.NoError(t, err)
|
|
oidcTokenSigner, err := jose.NewSigner(jose.SigningKey{
|
|
Algorithm: jose.SignatureAlgorithm(oidcTokenSignerJWK.Algorithm),
|
|
Key: oidcTokenSignerJWK,
|
|
}, new(jose.SignerOptions))
|
|
require.NoError(t, err)
|
|
|
|
prov := newWireProvisionerWithOptions(t, &provisioner.Options{
|
|
X509: &provisioner.X509Options{
|
|
Template: `{
|
|
"subject": {
|
|
"organization": "WireTest",
|
|
"commonName": {{ toJson .Oidc.name }}
|
|
},
|
|
"uris": [{{ toJson .Oidc.handle }}, {{ toJson .Dpop.sub }}],
|
|
"keyUsage": ["digitalSignature"],
|
|
"extKeyUsage": ["clientAuth"]
|
|
}`,
|
|
},
|
|
Wire: &wire.Options{
|
|
OIDC: &wire.OIDCOptions{
|
|
Provider: &wire.Provider{
|
|
IssuerURL: "https://issuer.example.com",
|
|
AuthURL: "",
|
|
TokenURL: "",
|
|
JWKSURL: "",
|
|
UserInfoURL: "",
|
|
Algorithms: []string{},
|
|
},
|
|
Config: &wire.Config{
|
|
ClientID: "integration test",
|
|
SignatureAlgorithms: []string{},
|
|
SkipClientIDCheck: true,
|
|
SkipExpiryCheck: true,
|
|
SkipIssuerCheck: true,
|
|
InsecureSkipSignatureCheck: true, // NOTE: this skips actual token verification
|
|
Now: time.Now,
|
|
},
|
|
TransformTemplate: "",
|
|
},
|
|
DPOP: &wire.DPOPOptions{
|
|
SigningKey: accessTokenSignerPEMBytes,
|
|
},
|
|
},
|
|
})
|
|
|
|
// mock provisioner and linker
|
|
ctx := context.Background()
|
|
ctx = acme.NewProvisionerContext(ctx, prov)
|
|
ctx = acme.NewLinkerContext(ctx, acme.NewLinker(baseURL, linkerPrefix))
|
|
|
|
// create temporary BoltDB file
|
|
file, err := os.CreateTemp(os.TempDir(), "integration-db-")
|
|
require.NoError(t, err)
|
|
|
|
t.Log("database file name:", file.Name())
|
|
dbFn := file.Name()
|
|
err = file.Close()
|
|
require.NoError(t, err)
|
|
|
|
// open BoltDB
|
|
rawDB, err := nosqlDB.New(nosqlDB.BBoltDriver, dbFn)
|
|
require.NoError(t, err)
|
|
|
|
// create tables
|
|
db, err := nosql.New(rawDB)
|
|
require.NoError(t, err)
|
|
|
|
// make DB available to handlers
|
|
ctx = acme.NewDatabaseContext(ctx, db)
|
|
|
|
// simulate signed payloads by making the signing key available in ctx
|
|
jwk, err := jose.GenerateJWK("OKP", "", "EdDSA", "sig", "", 0)
|
|
require.NoError(t, err)
|
|
|
|
ed25519PrivKey, ok := jwk.Key.(ed25519.PrivateKey)
|
|
require.True(t, ok)
|
|
|
|
dpopSigner, err := jose.NewSigner(jose.SigningKey{
|
|
Algorithm: jose.SignatureAlgorithm(jwk.Algorithm),
|
|
Key: jwk,
|
|
}, new(jose.SignerOptions))
|
|
require.NoError(t, err)
|
|
|
|
ed25519PubKey, ok := ed25519PrivKey.Public().(ed25519.PublicKey)
|
|
require.True(t, ok)
|
|
|
|
jwk.Key = ed25519PubKey
|
|
ctx = context.WithValue(ctx, jwkContextKey, jwk)
|
|
|
|
// get directory
|
|
dir := func(ctx context.Context) (dir Directory) {
|
|
req := httptest.NewRequest(http.MethodGet, "/foo/bar", http.NoBody)
|
|
req = req.WithContext(ctx)
|
|
w := httptest.NewRecorder()
|
|
|
|
GetDirectory(w, req)
|
|
res := w.Result()
|
|
require.Equal(t, http.StatusOK, res.StatusCode)
|
|
|
|
body, err := io.ReadAll(res.Body)
|
|
require.NoError(t, err)
|
|
|
|
err = json.Unmarshal(bytes.TrimSpace(body), &dir)
|
|
require.NoError(t, err)
|
|
|
|
return
|
|
}(ctx)
|
|
t.Log("directory:", dir)
|
|
|
|
// get nonce
|
|
nonce := func(ctx context.Context) (nonce string) {
|
|
req := httptest.NewRequest(http.MethodGet, dir.NewNonce, http.NoBody).WithContext(ctx)
|
|
w := httptest.NewRecorder()
|
|
addNonce(GetNonce)(w, req)
|
|
res := w.Result()
|
|
require.Equal(t, http.StatusNoContent, res.StatusCode)
|
|
|
|
nonce = res.Header["Replay-Nonce"][0]
|
|
return
|
|
}(ctx)
|
|
t.Log("nonce:", nonce)
|
|
|
|
// create new account
|
|
acc := func(ctx context.Context) (acc *acme.Account) {
|
|
// create payload
|
|
nar := &NewAccountRequest{
|
|
Contact: []string{"foo", "bar"},
|
|
}
|
|
rawNar, err := json.Marshal(nar)
|
|
require.NoError(t, err)
|
|
|
|
// create account
|
|
ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: rawNar})
|
|
req := httptest.NewRequest(http.MethodGet, dir.NewAccount, http.NoBody).WithContext(ctx)
|
|
w := httptest.NewRecorder()
|
|
NewAccount(w, req)
|
|
|
|
res := w.Result()
|
|
require.Equal(t, http.StatusCreated, res.StatusCode)
|
|
|
|
body, err := io.ReadAll(res.Body)
|
|
defer res.Body.Close()
|
|
require.NoError(t, err)
|
|
|
|
err = json.Unmarshal(bytes.TrimSpace(body), &acc)
|
|
require.NoError(t, err)
|
|
|
|
locationParts := strings.Split(res.Header["Location"][0], "/")
|
|
acc, err = db.GetAccount(ctx, locationParts[len(locationParts)-1])
|
|
require.NoError(t, err)
|
|
|
|
return
|
|
}(ctx)
|
|
ctx = context.WithValue(ctx, accContextKey, acc)
|
|
t.Log("account ID:", acc.ID)
|
|
|
|
// new order
|
|
order := func(ctx context.Context) (order *acme.Order) {
|
|
mockMustAuthority(t, &mockCA{})
|
|
nor := &NewOrderRequest{
|
|
Identifiers: []acme.Identifier{
|
|
{
|
|
Type: "wireapp-id",
|
|
Value: `{"name": "Smith, Alice M (QA)", "domain": "example.com", "client-id": "wireapp://lJGYPz0ZRq2kvc_XpdaDlA!ed416ce8ecdd9fad@example.com", "handle": "wireapp://%40alice.smith.qa@example.com"}`,
|
|
},
|
|
},
|
|
}
|
|
b, err := json.Marshal(nor)
|
|
require.NoError(t, err)
|
|
|
|
ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b})
|
|
req := httptest.NewRequest("POST", "https://random.local/", http.NoBody)
|
|
req = req.WithContext(ctx)
|
|
w := httptest.NewRecorder()
|
|
NewOrder(w, req)
|
|
|
|
res := w.Result()
|
|
require.Equal(t, http.StatusCreated, res.StatusCode)
|
|
|
|
body, err := io.ReadAll(res.Body)
|
|
defer res.Body.Close()
|
|
require.NoError(t, err)
|
|
|
|
err = json.Unmarshal(bytes.TrimSpace(body), &order)
|
|
require.NoError(t, err)
|
|
|
|
order, err = db.GetOrder(ctx, order.ID)
|
|
require.NoError(t, err)
|
|
|
|
return
|
|
}(ctx)
|
|
t.Log("authzs IDs:", order.AuthorizationIDs)
|
|
|
|
// get authorization
|
|
getAuthz := func(ctx context.Context, authzID string) (az *acme.Authorization) {
|
|
chiCtx := chi.NewRouteContext()
|
|
chiCtx.URLParams.Add("authzID", authzID)
|
|
ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "https://random.local/", http.NoBody).WithContext(ctx)
|
|
w := httptest.NewRecorder()
|
|
GetAuthorization(w, req)
|
|
|
|
res := w.Result()
|
|
require.Equal(t, http.StatusOK, res.StatusCode)
|
|
|
|
body, err := io.ReadAll(res.Body)
|
|
defer res.Body.Close()
|
|
require.NoError(t, err)
|
|
|
|
err = json.Unmarshal(bytes.TrimSpace(body), &az)
|
|
require.NoError(t, err)
|
|
|
|
az, err = db.GetAuthorization(ctx, authzID)
|
|
require.NoError(t, err)
|
|
|
|
return
|
|
}
|
|
var azs []*acme.Authorization
|
|
for _, azID := range order.AuthorizationIDs {
|
|
az := getAuthz(ctx, azID)
|
|
azs = append(azs, az)
|
|
for _, challenge := range az.Challenges {
|
|
chiCtx := chi.NewRouteContext()
|
|
chiCtx.URLParams.Add("chID", challenge.ID)
|
|
ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx)
|
|
|
|
var payload []byte
|
|
switch challenge.Type {
|
|
case acme.WIREDPOP01:
|
|
dpopBytes, err := json.Marshal(struct {
|
|
jose.Claims
|
|
Challenge string `json:"chal,omitempty"`
|
|
Handle string `json:"handle,omitempty"`
|
|
}{
|
|
Claims: jose.Claims{
|
|
Subject: "wireapp://lJGYPz0ZRq2kvc_XpdaDlA!ed416ce8ecdd9fad@example.com",
|
|
},
|
|
Challenge: "token",
|
|
Handle: "wireapp://%40alice.smith.qa@example.com",
|
|
})
|
|
require.NoError(t, err)
|
|
dpop, err := dpopSigner.Sign(dpopBytes)
|
|
require.NoError(t, err)
|
|
proof, err := dpop.CompactSerialize()
|
|
require.NoError(t, err)
|
|
tokenBytes, err := json.Marshal(struct {
|
|
jose.Claims
|
|
Challenge string `json:"chal,omitempty"`
|
|
Cnf struct {
|
|
Kid string `json:"kid,omitempty"`
|
|
} `json:"cnf"`
|
|
Proof string `json:"proof,omitempty"`
|
|
ClientID string `json:"client_id"`
|
|
APIVersion int `json:"api_version"`
|
|
Scope string `json:"scope"`
|
|
}{
|
|
Claims: jose.Claims{
|
|
Issuer: "http://issuer.example.com",
|
|
Audience: []string{"test"},
|
|
Expiry: jose.NewNumericDate(time.Now().Add(1 * time.Minute)),
|
|
},
|
|
Challenge: "token",
|
|
Cnf: struct {
|
|
Kid string `json:"kid,omitempty"`
|
|
}{
|
|
Kid: jwk.KeyID,
|
|
},
|
|
Proof: proof,
|
|
ClientID: "wireapp://lJGYPz0ZRq2kvc_XpdaDlA!ed416ce8ecdd9fad@example.com",
|
|
APIVersion: 5,
|
|
Scope: "wire_client_id",
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
signed, err := accessTokenSigner.Sign(tokenBytes)
|
|
require.NoError(t, err)
|
|
accessToken, err := signed.CompactSerialize()
|
|
require.NoError(t, err)
|
|
|
|
p, err := json.Marshal(struct {
|
|
AccessToken string `json:"access_token"`
|
|
}{
|
|
AccessToken: accessToken,
|
|
})
|
|
require.NoError(t, err)
|
|
payload = p
|
|
case acme.WIREOIDC01:
|
|
keyAuth, err := acme.KeyAuthorization("token", jwk)
|
|
require.NoError(t, err)
|
|
tokenBytes, err := json.Marshal(struct {
|
|
jose.Claims
|
|
Name string `json:"name,omitempty"`
|
|
PreferredUsername string `json:"preferred_username,omitempty"`
|
|
}{
|
|
Claims: jose.Claims{
|
|
Issuer: "https://issuer.example.com",
|
|
Audience: []string{"test"},
|
|
Expiry: jose.NewNumericDate(time.Now().Add(1 * time.Minute)),
|
|
},
|
|
Name: "Alice Smith",
|
|
PreferredUsername: "wireapp://%40alice_wire@wire.com",
|
|
})
|
|
require.NoError(t, err)
|
|
signed, err := oidcTokenSigner.Sign(tokenBytes)
|
|
require.NoError(t, err)
|
|
idToken, err := signed.CompactSerialize()
|
|
require.NoError(t, err)
|
|
p, err := json.Marshal(struct {
|
|
IDToken string `json:"id_token"`
|
|
KeyAuth string `json:"keyauth"`
|
|
}{
|
|
IDToken: idToken,
|
|
KeyAuth: keyAuth,
|
|
})
|
|
require.NoError(t, err)
|
|
payload = p
|
|
default:
|
|
require.Fail(t, "unexpected challenge payload type")
|
|
}
|
|
|
|
ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: payload})
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "https://random.local/", http.NoBody).WithContext(ctx)
|
|
w := httptest.NewRecorder()
|
|
GetChallenge(w, req)
|
|
|
|
res := w.Result()
|
|
require.Equal(t, http.StatusOK, res.StatusCode)
|
|
|
|
body, err := io.ReadAll(res.Body)
|
|
defer res.Body.Close() //nolint:gocritic // close the body
|
|
require.NoError(t, err)
|
|
|
|
err = json.Unmarshal(bytes.TrimSpace(body), &challenge)
|
|
require.NoError(t, err)
|
|
|
|
t.Log("challenge:", challenge.ID, challenge.Status)
|
|
}
|
|
}
|
|
|
|
// get/validate challenge simulation
|
|
updateAz := func(ctx context.Context, az *acme.Authorization) (updatedAz *acme.Authorization) {
|
|
now := clock.Now().Format(time.RFC3339)
|
|
for _, challenge := range az.Challenges {
|
|
challenge.Status = acme.StatusValid
|
|
challenge.ValidatedAt = now
|
|
err := db.UpdateChallenge(ctx, challenge)
|
|
if err != nil {
|
|
t.Error("updating challenge", challenge.ID, ":", err)
|
|
}
|
|
}
|
|
|
|
updatedAz, err = db.GetAuthorization(ctx, az.ID)
|
|
require.NoError(t, err)
|
|
|
|
return
|
|
}
|
|
for _, az := range azs {
|
|
updatedAz := updateAz(ctx, az)
|
|
for _, challenge := range updatedAz.Challenges {
|
|
t.Log("updated challenge:", challenge.ID, challenge.Status)
|
|
switch challenge.Type {
|
|
case acme.WIREOIDC01:
|
|
err = db.CreateOidcToken(ctx, order.ID, map[string]any{"name": "Smith, Alice M (QA)", "handle": "wireapp://%40alice.smith.qa@example.com"})
|
|
require.NoError(t, err)
|
|
case acme.WIREDPOP01:
|
|
err = db.CreateDpopToken(ctx, order.ID, map[string]any{"sub": "wireapp://lJGYPz0ZRq2kvc_XpdaDlA!ed416ce8ecdd9fad@example.com"})
|
|
require.NoError(t, err)
|
|
default:
|
|
require.Fail(t, "unexpected challenge type")
|
|
}
|
|
}
|
|
}
|
|
|
|
// get order
|
|
updatedOrder := func(ctx context.Context) (updatedOrder *acme.Order) {
|
|
chiCtx := chi.NewRouteContext()
|
|
chiCtx.URLParams.Add("ordID", order.ID)
|
|
ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "https://random.local/", http.NoBody).WithContext(ctx)
|
|
w := httptest.NewRecorder()
|
|
GetOrder(w, req)
|
|
|
|
res := w.Result()
|
|
require.Equal(t, http.StatusOK, res.StatusCode)
|
|
|
|
body, err := io.ReadAll(res.Body)
|
|
defer res.Body.Close()
|
|
require.NoError(t, err)
|
|
|
|
err = json.Unmarshal(bytes.TrimSpace(body), &updatedOrder)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, acme.StatusReady, updatedOrder.Status)
|
|
|
|
return
|
|
}(ctx)
|
|
t.Log("updated order status:", updatedOrder.Status)
|
|
|
|
// finalize order
|
|
finalizedOrder := func(ctx context.Context) (finalizedOrder *acme.Order) {
|
|
ca, err := minica.New(minica.WithName("WireTestCA"))
|
|
require.NoError(t, err)
|
|
mockMustAuthority(t, &mockCASigner{
|
|
signer: func(csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) {
|
|
var (
|
|
certOptions []x509util.Option
|
|
)
|
|
for _, op := range extraOpts {
|
|
if k, ok := op.(provisioner.CertificateOptions); ok {
|
|
certOptions = append(certOptions, k.Options(signOpts)...)
|
|
}
|
|
}
|
|
|
|
x509utilTemplate, err := x509util.NewCertificate(csr, certOptions...)
|
|
require.NoError(t, err)
|
|
|
|
template := x509utilTemplate.GetCertificate()
|
|
require.NotNil(t, template)
|
|
|
|
cert, err := ca.Sign(template)
|
|
require.NoError(t, err)
|
|
|
|
u1, err := url.Parse("wireapp://%40alice.smith.qa@example.com")
|
|
require.NoError(t, err)
|
|
u2, err := url.Parse("wireapp://lJGYPz0ZRq2kvc_XpdaDlA%21ed416ce8ecdd9fad@example.com")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, []*url.URL{u1, u2}, cert.URIs)
|
|
assert.Equal(t, "Smith, Alice M (QA)", cert.Subject.CommonName)
|
|
|
|
return []*x509.Certificate{cert, ca.Intermediate}, nil
|
|
},
|
|
})
|
|
|
|
qUserID, err := url.Parse("wireapp://lJGYPz0ZRq2kvc_XpdaDlA!ed416ce8ecdd9fad@example.com")
|
|
require.NoError(t, err)
|
|
|
|
qUserName, err := url.Parse("wireapp://%40alice.smith.qa@example.com")
|
|
require.NoError(t, err)
|
|
|
|
_, priv, err := ed25519.GenerateKey(rand.Reader)
|
|
require.NoError(t, err)
|
|
|
|
csrTemplate := &x509.CertificateRequest{
|
|
Subject: pkix.Name{
|
|
Organization: []string{"example.com"},
|
|
ExtraNames: []pkix.AttributeTypeAndValue{
|
|
{
|
|
Type: asn1.ObjectIdentifier{2, 16, 840, 1, 113730, 3, 1, 241},
|
|
Value: "Smith, Alice M (QA)",
|
|
},
|
|
},
|
|
},
|
|
URIs: []*url.URL{
|
|
qUserName,
|
|
qUserID,
|
|
},
|
|
SignatureAlgorithm: x509.PureEd25519,
|
|
}
|
|
|
|
csr, err := x509.CreateCertificateRequest(rand.Reader, csrTemplate, priv)
|
|
require.NoError(t, err)
|
|
|
|
fr := FinalizeRequest{CSR: base64.RawURLEncoding.EncodeToString(csr)}
|
|
frRaw, err := json.Marshal(fr)
|
|
require.NoError(t, err)
|
|
|
|
ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: frRaw})
|
|
|
|
chiCtx := chi.NewRouteContext()
|
|
chiCtx.URLParams.Add("ordID", order.ID)
|
|
ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "https://random.local/", http.NoBody).WithContext(ctx)
|
|
w := httptest.NewRecorder()
|
|
FinalizeOrder(w, req)
|
|
|
|
res := w.Result()
|
|
require.Equal(t, http.StatusOK, res.StatusCode)
|
|
|
|
body, err := io.ReadAll(res.Body)
|
|
defer res.Body.Close()
|
|
require.NoError(t, err)
|
|
|
|
err = json.Unmarshal(bytes.TrimSpace(body), &finalizedOrder)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, acme.StatusValid, finalizedOrder.Status)
|
|
|
|
finalizedOrder, err = db.GetOrder(ctx, order.ID)
|
|
require.NoError(t, err)
|
|
|
|
return
|
|
}(ctx)
|
|
t.Log("finalized order status:", finalizedOrder.Status)
|
|
}
|
|
|
|
type mockCASigner struct {
|
|
signer func(*x509.CertificateRequest, provisioner.SignOptions, ...provisioner.SignOption) ([]*x509.Certificate, error)
|
|
}
|
|
|
|
func (m *mockCASigner) Sign(cr *x509.CertificateRequest, opts provisioner.SignOptions, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error) {
|
|
if m.signer == nil {
|
|
return nil, errors.New("unimplemented")
|
|
}
|
|
return m.signer(cr, opts, signOpts...)
|
|
}
|
|
|
|
func (m *mockCASigner) AreSANsAllowed(ctx context.Context, sans []string) error {
|
|
return nil
|
|
}
|
|
|
|
func (m *mockCASigner) IsRevoked(sn string) (bool, error) {
|
|
return false, nil
|
|
}
|
|
|
|
func (m *mockCASigner) Revoke(ctx context.Context, opts *authority.RevokeOptions) error {
|
|
return nil
|
|
}
|
|
|
|
func (m *mockCASigner) LoadProvisionerByName(string) (provisioner.Interface, error) {
|
|
return nil, nil
|
|
}
|