forked from TrueCloudLab/certificates
Add initial tests for CreateCertificateAuthority.
This commit is contained in:
parent
062edcdfb4
commit
2611fc04d4
4 changed files with 198 additions and 53 deletions
|
@ -5,25 +5,34 @@ import (
|
|||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
lroauto "cloud.google.com/go/longrunning/autogen"
|
||||
privateca "cloud.google.com/go/security/privateca/apiv1beta1"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
"github.com/google/uuid"
|
||||
gax "github.com/googleapis/gax-go/v2"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/smallstep/certificates/cas/apiv1"
|
||||
"google.golang.org/api/option"
|
||||
pb "google.golang.org/genproto/googleapis/cloud/security/privateca/v1beta1"
|
||||
longrunningpb "google.golang.org/genproto/googleapis/longrunning"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
)
|
||||
|
||||
var (
|
||||
errTest = errors.New("test error")
|
||||
testAuthorityName = "projects/test-project/locations/us-west1/certificateAuthorities/test-ca"
|
||||
testCertificateName = "projects/test-project/locations/us-west1/certificateAuthorities/test-ca/certificates/test-certificate"
|
||||
testProject = "test-project"
|
||||
testLocation = "us-west1"
|
||||
testRootCertificate = `-----BEGIN CERTIFICATE-----
|
||||
MIIBhjCCAS2gAwIBAgIQLbKTuXau4+t3KFbGpJJAADAKBggqhkjOPQQDAjAiMSAw
|
||||
HgYDVQQDExdHb29nbGUgQ0FTIFRlc3QgUm9vdCBDQTAeFw0yMDA5MTQyMjQ4NDla
|
||||
|
@ -72,6 +81,21 @@ ZGNhcxMkZDhkMThhNjgtNTI5Ni00YWYzLWFlNGItMmY4NzdkYTNmYmQ5MAoGCCqG
|
|||
SM49BAMCA0gAMEUCIGxl+pqJ50WYWUqK2l4V1FHoXSi0Nht5kwTxFxnWZu1xAiEA
|
||||
zemu3bhWLFaGg3s8i+HTEhw4RqkHP74vF7AVYp88bAw=
|
||||
-----END CERTIFICATE-----`
|
||||
testIntermediateCsr = `-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIBIjCByQIBADAqMSgwJgYDVQQDEx9Hb29nbGUgQ0FTIFRlc3QgSW50ZXJtZWRp
|
||||
YXRlIENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEqoztio0c4XuaaGxHFiU7
|
||||
UBk3YRGTae9GtlKwyZJDk740hg6ZIoKcaXrzJT5taUpPiQLi7rP1eRui0dhl/bHo
|
||||
o6A9MDsGCSqGSIb3DQEJDjEuMCwwKgYDVR0RBCMwIYIfR29vZ2xlIENBUyBUZXN0
|
||||
IEludGVybWVkaWF0ZSBDQTAKBggqhkjOPQQDAgNIADBFAiEAvRKBPE32scAvsMe8
|
||||
R7ecx91q58ZmeLaRdSzL7stsnJYCIEBu+vQUSTbUpKL2YQNclT9kbilips5pEMr3
|
||||
ojxK6mk3
|
||||
-----END CERTIFICATE REQUEST-----`
|
||||
|
||||
// testIntermediateKey = `-----BEGIN EC PRIVATE KEY-----
|
||||
// MHcCAQEEIMM+DSPChJgcYyqDWs0eRA5BctIo+VSNqRzCTL2ARYAqoAoGCCqGSM49
|
||||
// AwEHoUQDQgAEqoztio0c4XuaaGxHFiU7UBk3YRGTae9GtlKwyZJDk740hg6ZIoKc
|
||||
// aXrzJT5taUpPiQLi7rP1eRui0dhl/bHoow==
|
||||
// -----END EC PRIVATE KEY-----`
|
||||
)
|
||||
|
||||
type testClient struct {
|
||||
|
@ -146,6 +170,18 @@ func (c *testClient) GetCertificateAuthority(ctx context.Context, req *pb.GetCer
|
|||
return c.certificateAuthority, c.err
|
||||
}
|
||||
|
||||
func (c *testClient) CreateCertificateAuthority(ctx context.Context, req *pb.CreateCertificateAuthorityRequest, opts ...gax.CallOption) (*privateca.CreateCertificateAuthorityOperation, error) {
|
||||
return nil, errors.New("use NewMockCertificateAuthorityClient")
|
||||
}
|
||||
|
||||
func (c *testClient) FetchCertificateAuthorityCsr(ctx context.Context, req *pb.FetchCertificateAuthorityCsrRequest, opts ...gax.CallOption) (*pb.FetchCertificateAuthorityCsrResponse, error) {
|
||||
return nil, errors.New("use NewMockCertificateAuthorityClient")
|
||||
}
|
||||
|
||||
func (c *testClient) ActivateCertificateAuthority(ctx context.Context, req *pb.ActivateCertificateAuthorityRequest, opts ...gax.CallOption) (*privateca.ActivateCertificateAuthorityOperation, error) {
|
||||
return nil, errors.New("use NewMockCertificateAuthorityClient")
|
||||
}
|
||||
|
||||
func mustParseCertificate(t *testing.T, pemCert string) *x509.Certificate {
|
||||
t.Helper()
|
||||
crt, err := parseCertificate(pemCert)
|
||||
|
@ -179,17 +215,34 @@ func TestNew(t *testing.T) {
|
|||
}}, &CloudCAS{
|
||||
client: &testClient{},
|
||||
certificateAuthority: testAuthorityName,
|
||||
project: testProject,
|
||||
location: testLocation,
|
||||
}, false},
|
||||
{"ok with credentials", args{context.Background(), apiv1.Options{
|
||||
CertificateAuthority: testAuthorityName, CredentialsFile: "testdata/credentials.json",
|
||||
}}, &CloudCAS{
|
||||
client: &testClient{credentialsFile: "testdata/credentials.json"},
|
||||
certificateAuthority: testAuthorityName,
|
||||
project: testProject,
|
||||
location: testLocation,
|
||||
}, false},
|
||||
{"ok creator", args{context.Background(), apiv1.Options{
|
||||
IsCreator: true, Project: testProject, Location: testLocation,
|
||||
}}, &CloudCAS{
|
||||
client: &testClient{},
|
||||
project: testProject,
|
||||
location: testLocation,
|
||||
}, false},
|
||||
{"fail certificate authority", args{context.Background(), apiv1.Options{}}, nil, true},
|
||||
{"fail with credentials", args{context.Background(), apiv1.Options{
|
||||
CertificateAuthority: testAuthorityName, CredentialsFile: "testdata/error.json",
|
||||
}}, nil, true},
|
||||
{"fail creator project", args{context.Background(), apiv1.Options{
|
||||
IsCreator: true, Project: "", Location: testLocation,
|
||||
}}, nil, true},
|
||||
{"fail creator location", args{context.Background(), apiv1.Options{
|
||||
IsCreator: true, Project: testProject, Location: "",
|
||||
}}, nil, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
@ -217,6 +270,8 @@ func TestNew_register(t *testing.T) {
|
|||
want := &CloudCAS{
|
||||
client: &testClient{credentialsFile: "testdata/credentials.json"},
|
||||
certificateAuthority: testAuthorityName,
|
||||
project: testProject,
|
||||
location: testLocation,
|
||||
}
|
||||
|
||||
newFn, ok := apiv1.LoadCertificateAuthorityServiceNewFunc(apiv1.CloudCAS)
|
||||
|
@ -675,61 +730,150 @@ func Test_getCertificateAndChain(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestCloudCAS(t *testing.T) {
|
||||
cas, err := New(context.Background(), apiv1.Options{
|
||||
Type: "cloudCAS",
|
||||
CertificateAuthority: "projects/smallstep-cas-test/locations/us-west1",
|
||||
CredentialsFile: "/Users/mariano/smallstep-cas-test-8a068f3e4540.json",
|
||||
})
|
||||
func TestCloudCAS_CreateCertificateAuthority(t *testing.T) {
|
||||
must := func(a, b interface{}) interface{} {
|
||||
return a
|
||||
}
|
||||
|
||||
// client, close := mockTestClient()
|
||||
// defer close()
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
mosCtrl := gomock.NewController(t)
|
||||
defer mosCtrl.Finish()
|
||||
|
||||
m := NewMockCertificateAuthorityClient(ctrl)
|
||||
mos := NewMockOperationsServer(mosCtrl)
|
||||
|
||||
// Create operation server
|
||||
lis, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// resp, err := cas.CreateCertificateAuthority(&apiv1.CreateCertificateAuthorityRequest{
|
||||
// Type: apiv1.RootCA,
|
||||
// Template: &x509.Certificate{
|
||||
// Subject: pkix.Name{
|
||||
// CommonName: "Test Mariano Root CA",
|
||||
// },
|
||||
// BasicConstraintsValid: true,
|
||||
// IsCA: true,
|
||||
// MaxPathLen: 1,
|
||||
// MaxPathLenZero: false,
|
||||
// KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
||||
// },
|
||||
// Lifetime: time.Duration(30 * 24 * time.Hour),
|
||||
// Project: "smallstep-cas-test",
|
||||
// Location: "us-west1",
|
||||
// })
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// debug(resp)
|
||||
resp := &apiv1.CreateCertificateAuthorityResponse{
|
||||
Name: "projects/smallstep-cas-test/locations/us-west1/certificateAuthorities/9a593da4-61af-4426-a2f8-0650373b9c8e",
|
||||
srv := grpc.NewServer()
|
||||
longrunningpb.RegisterOperationsServer(srv, mos)
|
||||
|
||||
go srv.Serve(lis)
|
||||
defer srv.Stop()
|
||||
|
||||
// Create fake privateca client
|
||||
conn, err := grpc.Dial(lis.Addr().String(), grpc.WithInsecure())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
resp, err = cas.CreateCertificateAuthority(&apiv1.CreateCertificateAuthorityRequest{
|
||||
Type: apiv1.IntermediateCA,
|
||||
Template: &x509.Certificate{
|
||||
Subject: pkix.Name{
|
||||
Country: []string{"US"},
|
||||
CommonName: "Test Mariano Intermediate CA",
|
||||
},
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: true,
|
||||
MaxPathLen: 0,
|
||||
MaxPathLenZero: true,
|
||||
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
||||
client, err := lroauto.NewOperationsClient(context.Background(), option.WithGRPCConn(conn))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fake := &privateca.CertificateAuthorityClient{
|
||||
LROClient: client,
|
||||
}
|
||||
|
||||
// Configure mocks
|
||||
any := gomock.Any()
|
||||
|
||||
// ok root
|
||||
m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil)
|
||||
mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{
|
||||
Name: "CreateCertificateAuthority",
|
||||
Done: true,
|
||||
Result: &longrunningpb.Operation_Response{
|
||||
Response: must(anypb.New(&pb.CertificateAuthority{
|
||||
Name: testAuthorityName,
|
||||
PemCaCertificates: []string{testRootCertificate},
|
||||
})).(*anypb.Any),
|
||||
},
|
||||
Lifetime: time.Duration(24 * time.Hour),
|
||||
Parent: resp,
|
||||
Project: "smallstep-cas-test",
|
||||
Location: "us-west1",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}, nil)
|
||||
|
||||
// ok intermediate
|
||||
m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil)
|
||||
mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{
|
||||
Name: "CreateCertificateAuthority",
|
||||
Done: true,
|
||||
Result: &longrunningpb.Operation_Response{
|
||||
Response: must(anypb.New(&pb.CertificateAuthority{
|
||||
Name: testAuthorityName,
|
||||
})).(*anypb.Any),
|
||||
},
|
||||
}, nil)
|
||||
m.EXPECT().FetchCertificateAuthorityCsr(any, any).Return(&pb.FetchCertificateAuthorityCsrResponse{
|
||||
PemCsr: testIntermediateCsr,
|
||||
}, nil)
|
||||
m.EXPECT().CreateCertificate(any, any).Return(&pb.Certificate{
|
||||
PemCertificate: testIntermediateCertificate,
|
||||
PemCertificateChain: []string{testRootCertificate},
|
||||
}, nil)
|
||||
m.EXPECT().ActivateCertificateAuthority(any, any).Return(fake.ActivateCertificateAuthorityOperation("ActivateCertificateAuthority"), nil)
|
||||
mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{
|
||||
Name: "ActivateCertificateAuthority",
|
||||
Done: true,
|
||||
Result: &longrunningpb.Operation_Response{
|
||||
Response: must(anypb.New(&pb.CertificateAuthority{
|
||||
Name: testAuthorityName,
|
||||
PemCaCertificates: []string{testIntermediateCertificate, testRootCertificate},
|
||||
})).(*anypb.Any),
|
||||
},
|
||||
}, nil)
|
||||
|
||||
rootCrt := mustParseCertificate(t, testRootCertificate)
|
||||
intCrt := mustParseCertificate(t, testIntermediateCertificate)
|
||||
|
||||
type fields struct {
|
||||
client CertificateAuthorityClient
|
||||
certificateAuthority string
|
||||
project string
|
||||
location string
|
||||
}
|
||||
type args struct {
|
||||
req *apiv1.CreateCertificateAuthorityRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want *apiv1.CreateCertificateAuthorityResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{"ok root", fields{m, "", testProject, testLocation}, args{&apiv1.CreateCertificateAuthorityRequest{
|
||||
Type: apiv1.RootCA,
|
||||
Template: mustParseCertificate(t, testRootCertificate),
|
||||
Lifetime: 24 * time.Hour,
|
||||
}}, &apiv1.CreateCertificateAuthorityResponse{
|
||||
Name: testAuthorityName,
|
||||
Certificate: rootCrt,
|
||||
}, false},
|
||||
{"ok intermediate", fields{m, "", testProject, testLocation}, args{&apiv1.CreateCertificateAuthorityRequest{
|
||||
Type: apiv1.IntermediateCA,
|
||||
Template: mustParseCertificate(t, testIntermediateCertificate),
|
||||
Lifetime: 24 * time.Hour,
|
||||
Parent: &apiv1.CreateCertificateAuthorityResponse{
|
||||
Name: testAuthorityName,
|
||||
Certificate: rootCrt,
|
||||
},
|
||||
}}, &apiv1.CreateCertificateAuthorityResponse{
|
||||
Name: testAuthorityName,
|
||||
Certificate: intCrt,
|
||||
CertificateChain: []*x509.Certificate{rootCrt},
|
||||
}, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &CloudCAS{
|
||||
client: tt.fields.client,
|
||||
certificateAuthority: tt.fields.certificateAuthority,
|
||||
project: tt.fields.project,
|
||||
location: tt.fields.location,
|
||||
}
|
||||
got, err := c.CreateCertificateAuthority(tt.args.req)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("CloudCAS.CreateCertificateAuthority() error = %+v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("CloudCAS.CreateCertificateAuthority() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
// debug(resp)
|
||||
t.Error("foo")
|
||||
}
|
||||
|
|
|
@ -173,9 +173,7 @@ func (c *SoftCAS) CreateCertificateAuthority(req *apiv1.CreateCertificateAuthori
|
|||
var chain []*x509.Certificate
|
||||
if req.Parent != nil {
|
||||
chain = append(chain, req.Parent.Certificate)
|
||||
for _, crt := range req.Parent.CertificateChain {
|
||||
chain = append(chain, crt)
|
||||
}
|
||||
chain = append(chain, req.Parent.CertificateChain...)
|
||||
}
|
||||
|
||||
return &apiv1.CreateCertificateAuthorityResponse{
|
||||
|
|
1
go.mod
1
go.mod
|
@ -8,6 +8,7 @@ require (
|
|||
github.com/aws/aws-sdk-go v1.30.29
|
||||
github.com/go-chi/chi v4.0.2+incompatible
|
||||
github.com/go-piv/piv-go v1.6.0
|
||||
github.com/golang/mock v1.4.4
|
||||
github.com/google/uuid v1.1.2
|
||||
github.com/googleapis/gax-go/v2 v2.0.5
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
||||
|
|
2
go.sum
2
go.sum
|
@ -116,6 +116,7 @@ github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFU
|
|||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
|
@ -134,6 +135,7 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
|
|||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
|
|
Loading…
Reference in a new issue