Add uri support initializing cloudkms.

This commit is contained in:
Mariano Cano 2021-02-16 13:11:47 -08:00
parent 4902e45729
commit a947779795
2 changed files with 68 additions and 11 deletions

View file

@ -14,11 +14,15 @@ import (
gax "github.com/googleapis/gax-go/v2" gax "github.com/googleapis/gax-go/v2"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/smallstep/certificates/kms/apiv1" "github.com/smallstep/certificates/kms/apiv1"
"github.com/smallstep/certificates/kms/uri"
"go.step.sm/crypto/pemutil" "go.step.sm/crypto/pemutil"
"google.golang.org/api/option" "google.golang.org/api/option"
kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1" kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1"
) )
// Scheme is the scheme used in uris.
const Scheme = "cloudkms"
const pendingGenerationRetries = 10 const pendingGenerationRetries = 10
// protectionLevelMapping maps step protection levels with cloud kms ones. // protectionLevelMapping maps step protection levels with cloud kms ones.
@ -71,6 +75,10 @@ type KeyManagementClient interface {
CreateCryptoKeyVersion(ctx context.Context, req *kmspb.CreateCryptoKeyVersionRequest, opts ...gax.CallOption) (*kmspb.CryptoKeyVersion, error) CreateCryptoKeyVersion(ctx context.Context, req *kmspb.CreateCryptoKeyVersionRequest, opts ...gax.CallOption) (*kmspb.CryptoKeyVersion, error)
} }
var newKeyManagementClient = func(ctx context.Context, opts ...option.ClientOption) (KeyManagementClient, error) {
return cloudkms.NewKeyManagementClient(ctx, opts...)
}
// CloudKMS implements a KMS using Google's Cloud apiv1. // CloudKMS implements a KMS using Google's Cloud apiv1.
type CloudKMS struct { type CloudKMS struct {
client KeyManagementClient client KeyManagementClient
@ -79,11 +87,23 @@ type CloudKMS struct {
// New creates a new CloudKMS configured with a new client. // New creates a new CloudKMS configured with a new client.
func New(ctx context.Context, opts apiv1.Options) (*CloudKMS, error) { func New(ctx context.Context, opts apiv1.Options) (*CloudKMS, error) {
var cloudOpts []option.ClientOption var cloudOpts []option.ClientOption
if opts.URI != "" {
u, err := uri.ParseWithScheme(Scheme, opts.URI)
if err != nil {
return nil, err
}
if f := u.Get("credentials-file"); f != "" {
cloudOpts = append(cloudOpts, option.WithCredentialsFile(f))
}
}
// Deprecated way to setting configuration parameters.
if opts.CredentialsFile != "" { if opts.CredentialsFile != "" {
cloudOpts = append(cloudOpts, option.WithCredentialsFile(opts.CredentialsFile)) cloudOpts = append(cloudOpts, option.WithCredentialsFile(opts.CredentialsFile))
} }
client, err := cloudkms.NewKeyManagementClient(ctx, cloudOpts...) client, err := newKeyManagementClient(ctx, cloudOpts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -5,13 +5,13 @@ import (
"crypto" "crypto"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os"
"reflect" "reflect"
"testing" "testing"
gax "github.com/googleapis/gax-go/v2" gax "github.com/googleapis/gax-go/v2"
"github.com/smallstep/certificates/kms/apiv1" "github.com/smallstep/certificates/kms/apiv1"
"go.step.sm/crypto/pemutil" "go.step.sm/crypto/pemutil"
"google.golang.org/api/option"
kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1" kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
@ -50,26 +50,63 @@ func TestParent(t *testing.T) {
} }
func TestNew(t *testing.T) { func TestNew(t *testing.T) {
tmp := newKeyManagementClient
t.Cleanup(func() {
newKeyManagementClient = tmp
})
newKeyManagementClient = func(ctx context.Context, opts ...option.ClientOption) (KeyManagementClient, error) {
if len(opts) > 0 {
return nil, fmt.Errorf("test error")
}
return &MockClient{}, nil
}
type args struct { type args struct {
ctx context.Context ctx context.Context
opts apiv1.Options opts apiv1.Options
} }
tests := []struct { tests := []struct {
name string name string
skipOnCI bool
args args args args
want *CloudKMS want *CloudKMS
wantErr bool wantErr bool
}{ }{
{"fail authentication", true, args{context.Background(), apiv1.Options{}}, nil, true}, {"ok", args{context.Background(), apiv1.Options{}}, &CloudKMS{client: &MockClient{}}, false},
{"fail credentials", false, args{context.Background(), apiv1.Options{CredentialsFile: "testdata/missing"}}, nil, true}, {"ok with uri", args{context.Background(), apiv1.Options{URI: "cloudkms:"}}, &CloudKMS{client: &MockClient{}}, false},
{"fail credentials", args{context.Background(), apiv1.Options{CredentialsFile: "testdata/missing"}}, nil, true},
{"fail with uri", args{context.Background(), apiv1.Options{URI: "cloudkms:credentials-file=testdata/missing"}}, nil, true},
{"fail schema", args{context.Background(), apiv1.Options{URI: "pkcs11:"}}, nil, true},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
if tt.skipOnCI && os.Getenv("CI") == "true" { got, err := New(tt.args.ctx, tt.args.opts)
t.SkipNow() if (err != nil) != tt.wantErr {
t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr)
return
} }
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("New() = %v, want %v", got, tt.want)
}
})
}
}
func TestNew_real(t *testing.T) {
type args struct {
ctx context.Context
opts apiv1.Options
}
tests := []struct {
name string
args args
want *CloudKMS
wantErr bool
}{
{"fail credentials", args{context.Background(), apiv1.Options{CredentialsFile: "testdata/missing"}}, nil, true},
{"fail with uri", args{context.Background(), apiv1.Options{URI: "cloudkms:credentials-file=testdata/missing"}}, nil, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := New(tt.args.ctx, tt.args.opts) got, err := New(tt.args.ctx, tt.args.opts)
if (err != nil) != tt.wantErr { if (err != nil) != tt.wantErr {
t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr)