From 823b8cdfc68d389199b3d2d6d9b6c0e0411cf824 Mon Sep 17 00:00:00 2001 From: Haotian Date: Wed, 11 Jan 2023 09:08:22 -0800 Subject: [PATCH] feat(vault): Add CRUDL methods for ssh certificate cred library (#2765) This defines a new credential library type for ssh certificates. It adds new repository methods to support CRUDL of the new ssh certificate credential library. Co-authored-by: Timothy Messier --- internal/credential/credential.go | 1 + internal/credential/vault/fields.go | 12 + internal/credential/vault/options.go | 52 + internal/credential/vault/public_ids.go | 27 +- ...tory_ssh_certificate_credential_library.go | 363 +++ ...ssh_certificate_credential_library_test.go | 1948 +++++++++++++++++ .../credential/vault/setup_manual_test.go | 2 +- .../ssh_certificate_credential_library.go | 120 + internal/credential/vault/store/vault.pb.go | 482 +++- internal/credential/vault/testing.go | 32 + .../39/01_static_ssh_private_key_creds.up.sql | 1 + ...1_credential_vault_ssh_cert_library.up.sql | 170 ++ .../credential_vault_ssh_cert_library.sql | 148 ++ .../credential/vault/store/v1/vault.proto | 101 + 14 files changed, 3351 insertions(+), 108 deletions(-) create mode 100644 internal/credential/vault/repository_ssh_certificate_credential_library.go create mode 100644 internal/credential/vault/repository_ssh_certificate_credential_library_test.go create mode 100644 internal/credential/vault/ssh_certificate_credential_library.go create mode 100644 internal/db/schema/migrations/oss/postgres/63/01_credential_vault_ssh_cert_library.up.sql create mode 100644 internal/db/sqltest/tests/credential/vault/credential_vault_ssh_cert_library.sql diff --git a/internal/credential/credential.go b/internal/credential/credential.go index 0c8726eef..d09e4cc60 100644 --- a/internal/credential/credential.go +++ b/internal/credential/credential.go @@ -30,6 +30,7 @@ const ( UnspecifiedType Type = "unspecified" UsernamePasswordType Type = "username_password" SshPrivateKeyType Type = "ssh_private_key" + SshCertificateType Type = "ssh_certificate" JsonType Type = "json" ) diff --git a/internal/credential/vault/fields.go b/internal/credential/vault/fields.go index a3fab7300..f4e9ec076 100644 --- a/internal/credential/vault/fields.go +++ b/internal/credential/vault/fields.go @@ -9,6 +9,18 @@ const ( httpMethodField = "HttpMethod" httpRequestBodyField = "HttpRequestBody" + usernameField = "Username" + keyTypeField = "KeyType" + keyBitsField = "KeyBits" + ttlField = "Ttl" + keyIdField = "KeyId" + // CriticalOptionsField represents the field mask indicating a critical option + // update has been requested. + CriticalOptionsField = "CriticalOptions" + // ExtensionsField represents the field mask indicating an extension + // update has been requested. + ExtensionsField = "Extensions" + certificateField = "Certificate" certificateKeyField = "CertificateKey" vaultAddressField = "VaultAddress" diff --git a/internal/credential/vault/options.go b/internal/credential/vault/options.go index cc21022fd..c97a46b78 100644 --- a/internal/credential/vault/options.go +++ b/internal/credential/vault/options.go @@ -34,6 +34,13 @@ type options struct { withOverridePrivateKeyAttribute string withOverridePrivateKeyPassphraseAttribute string withMappingOverride MappingOverride + + withKeyType string + withKeyBits uint32 + withTtl string + withKeyId string + withCriticalOptions string + withExtensions string } func getDefaultOptions() options { @@ -174,3 +181,48 @@ func WithMappingOverride(m MappingOverride) Option { o.withMappingOverride = m } } + +// WithKeyType provides an optional ssh private key type to use +// with a ssh certificate credential library. Must be rsa, ed25519, or ecdsa. +func WithKeyType(t string) Option { + return func(o *options) { + o.withKeyType = t + } +} + +// WithKeyBits provides an optional number of bits used to generate an ssh private key. +func WithKeyBits(b uint32) Option { + return func(o *options) { + o.withKeyBits = b + } +} + +// WithTtl provides an optional requested time to live for a generated ssh certificate. +func WithTtl(t string) Option { + return func(o *options) { + o.withTtl = t + } +} + +// WithKeyId provides an optional key id for a created certificate. +func WithKeyId(i string) Option { + return func(o *options) { + o.withKeyId = i + } +} + +// WithCriticalOptions provides an optional map of the critical options +// that the certificate should be signed for. +func WithCriticalOptions(s string) Option { + return func(o *options) { + o.withCriticalOptions = s + } +} + +// WithExtensions provides a optional map of the extensions +// that the certificate should be signed for. +func WithExtensions(s string) Option { + return func(o *options) { + o.withExtensions = s + } +} diff --git a/internal/credential/vault/public_ids.go b/internal/credential/vault/public_ids.go index 4cbec8374..0587c8534 100644 --- a/internal/credential/vault/public_ids.go +++ b/internal/credential/vault/public_ids.go @@ -8,18 +8,27 @@ import ( ) func init() { - if err := subtypes.Register(credential.Domain, Subtype, CredentialStorePrefix, CredentialLibraryPrefix, DynamicCredentialPrefix); err != nil { + if err := subtypes.Register(credential.Domain, Subtype, CredentialStorePrefix, DynamicCredentialPrefix); err != nil { + panic(err) + } + if err := subtypes.Register(credential.Domain, GenericLibrarySubtype, CredentialLibraryPrefix); err != nil { + panic(err) + } + if err := subtypes.Register(credential.Domain, SSHCertificateLibrarySubtype, SSHCertificateCredentialLibraryPrefix); err != nil { panic(err) } } // PublicId prefixes for the resources in the vault package. const ( - CredentialStorePrefix = "csvlt" - CredentialLibraryPrefix = "clvlt" - DynamicCredentialPrefix = "cdvlt" + CredentialStorePrefix = "csvlt" + CredentialLibraryPrefix = "clvlt" + DynamicCredentialPrefix = "cdvlt" + SSHCertificateCredentialLibraryPrefix = "clvsclt" - Subtype = subtypes.Subtype("vault") + Subtype = subtypes.Subtype("vault") + GenericLibrarySubtype = subtypes.Subtype("vault-generic") + SSHCertificateLibrarySubtype = subtypes.Subtype("vault-ssh-certificate") ) func newCredentialStoreId() (string, error) { @@ -45,3 +54,11 @@ func newCredentialLibraryId() (string, error) { } return id, nil } + +func newSSHCertificateCredentialLibraryId() (string, error) { + id, err := db.NewPublicId(SSHCertificateCredentialLibraryPrefix) + if err != nil { + return "", errors.WrapDeprecated(err, "vault.newSSHCertificateCredentialLibraryPrefix") + } + return id, nil +} diff --git a/internal/credential/vault/repository_ssh_certificate_credential_library.go b/internal/credential/vault/repository_ssh_certificate_credential_library.go new file mode 100644 index 000000000..f26b2ef60 --- /dev/null +++ b/internal/credential/vault/repository_ssh_certificate_credential_library.go @@ -0,0 +1,363 @@ +package vault + +import ( + "context" + "fmt" + "strings" + + "github.com/hashicorp/boundary/internal/credential" + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/oplog" + "github.com/hashicorp/go-dbw" +) + +// CreateSSHCertificateCredentialLibrary inserts l into the repository and returns a new +// SSHCertificateCredentialLibrary containing the credential library's PublicId. l is not +// changed. l must contain a valid StoreId. l must not contain a PublicId. +// The PublicId is generated and assigned by this method. +// +// Both l.Name and l.Description are optional. If l.Name is set, it must be +// unique within l.StoreId. +// +// Both l.CreateTime and l.UpdateTime are ignored. +func (r *Repository) CreateSSHCertificateCredentialLibrary(ctx context.Context, projectId string, l *SSHCertificateCredentialLibrary, _ ...Option) (*SSHCertificateCredentialLibrary, error) { + const op = "vault.(Repository).CreateSSHCertificateCredentialLibrary" + if l == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "nil SSHCertificateCredentialLibrary") + } + if l.SSHCertificateCredentialLibrary == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "nil embedded l") + } + if l.StoreId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no store id") + } + if l.VaultPath == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no vault path") + } + if l.PublicId != "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "public id not empty") + } + if projectId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no project id") + } + if l.Username == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no username") + } + + l = l.clone() + + if l.KeyType == "" { + l.KeyType = KeyTypeEd25519 + } + + if l.KeyBits == KeyBitsDefault { + l.KeyBits = l.getDefaultKeyBits() + } + + if l.GetCredentialType() == "" { + l.SSHCertificateCredentialLibrary.CredentialType = string(credential.SshCertificateType) + } + if l.GetCredentialType() != string(credential.SshCertificateType) { + return nil, errors.New(ctx, errors.InvalidParameter, op, "invalid credential type") + } + + id, err := newSSHCertificateCredentialLibraryId() + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + l.setId(id) + + oplogWrapper, err := r.kms.GetWrapper(ctx, projectId, kms.KeyPurposeOplog) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) + } + + var newSSHCertificateCredentialLibrary *SSHCertificateCredentialLibrary + _, err = r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{}, + func(_ db.Reader, w db.Writer) error { + var msgs []*oplog.Message + ticket, err := w.GetTicket(ctx, l) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket")) + } + + // insert credential library + newSSHCertificateCredentialLibrary = l.clone() + var lOplogMsg oplog.Message + if err := w.Create(ctx, newSSHCertificateCredentialLibrary, db.NewOplogMsg(&lOplogMsg)); err != nil { + return errors.Wrap(ctx, err, op) + } + msgs = append(msgs, &lOplogMsg) + + metadata := l.oplog(oplog.OpType_OP_TYPE_CREATE) + if err := w.WriteOplogEntryWith(ctx, oplogWrapper, ticket, metadata, msgs); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write oplog")) + } + return nil + }, + ) + + if err != nil { + if errors.IsUniqueError(err) { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in credential store: %s: name %s already exists", l.StoreId, l.Name))) + } + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in credential store: %s", l.StoreId))) + } + return newSSHCertificateCredentialLibrary, nil +} + +// UpdateSSHCertificateCredentialLibrary updates the repository entry for l.PublicId with +// the values in l for the fields listed in fieldMaskPaths. It returns a +// new SSHCertificateCredentialLibrary containing the updated values and a count of the +// number of records updated. l is not changed. +// +// l must contain a valid PublicId. Name, Description, VaultPath, Username, +// KeyType, KeyBits, Ttl, KeyId, CriticalOptions, and Extensions can be updated. If +// l.Name is set to a non-empty string, it must be unique within l.StoreId. +// +// An attribute of l will be set to NULL in the database if the attribute +// in l is the zero value and it is included in fieldMaskPaths. +func (r *Repository) UpdateSSHCertificateCredentialLibrary(ctx context.Context, projectId string, l *SSHCertificateCredentialLibrary, version uint32, fieldMaskPaths []string, _ ...Option) (*SSHCertificateCredentialLibrary, int, error) { + const op = "vault.(Repository).UpdateSSHCertificateCredentialLibrary" + if l == nil { + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing SSHCertificateCredentialLibrary") + } + if l.SSHCertificateCredentialLibrary == nil { + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing embedded SSHCertificateCredentialLibrary") + } + if l.PublicId == "" { + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidPublicId, op, "missing public id") + } + if version == 0 { + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing version") + } + if projectId == "" { + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing project id") + } + l = l.clone() + + var keyTypeChange, keyBitChangeDefault bool + + for _, f := range fieldMaskPaths { + switch { + case strings.EqualFold(nameField, f): + case strings.EqualFold(descriptionField, f): + case strings.EqualFold(vaultPathField, f): + case strings.EqualFold(usernameField, f): + case strings.EqualFold(keyTypeField, f): + keyTypeChange = true + case strings.EqualFold(keyBitsField, f): + keyBitChangeDefault = l.KeyBits == KeyBitsDefault + case strings.EqualFold(ttlField, f): + case strings.EqualFold(keyIdField, f): + case strings.EqualFold(CriticalOptionsField, f): + case strings.EqualFold(ExtensionsField, f): + + default: + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidFieldMask, op, f) + } + } + + if keyTypeChange && l.KeyType == "" { + l.KeyType = KeyTypeEd25519 + } + + if keyTypeChange && keyBitChangeDefault { + l.KeyBits = l.getDefaultKeyBits() + } + + origLib, err := r.LookupSSHCertificateCredentialLibrary(ctx, l.PublicId) + switch { + case err != nil: + return nil, db.NoRowsAffected, errors.Wrap(ctx, err, op) + case origLib == nil: + return nil, db.NoRowsAffected, errors.New(ctx, errors.RecordNotFound, op, fmt.Sprintf("credential library %s", l.PublicId)) + } + + if keyBitChangeDefault && !keyTypeChange { + l.KeyBits = origLib.getDefaultKeyBits() + } + + var dbMask, nullFields []string + dbMask, nullFields = dbw.BuildUpdatePaths( + map[string]any{ + nameField: l.Name, + descriptionField: l.Description, + vaultPathField: l.VaultPath, + usernameField: l.Username, + keyTypeField: l.KeyType, + keyBitsField: l.KeyBits, + ttlField: l.Ttl, + keyIdField: l.KeyId, + CriticalOptionsField: l.CriticalOptions, + ExtensionsField: l.Extensions, + }, + fieldMaskPaths, + []string{keyBitsField}, + ) + + if len(dbMask) == 0 && len(nullFields) == 0 { + return nil, db.NoRowsAffected, errors.New(ctx, errors.EmptyFieldMask, op, "missing field mask") + } + + oplogWrapper, err := r.kms.GetWrapper(ctx, projectId, kms.KeyPurposeOplog) + if err != nil { + return nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithCode(errors.Encrypt), + errors.WithMsg("unable to get oplog wrapper")) + } + + var rowsUpdated int + var returnedCredentialLibrary *SSHCertificateCredentialLibrary + _, err = r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{}, + func(rr db.Reader, w db.Writer) error { + var msgs []*oplog.Message + ticket, err := w.GetTicket(ctx, l) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket")) + } + + l := l.clone() + var lOplogMsg oplog.Message + + // Update the credential library table + switch { + case len(dbMask) == 0 && len(nullFields) == 0: + // the credential library's fields are not being updated, + // just one of it's child objects, so we just need to + // update the library's version. + l.Version = version + 1 + rowsUpdated, err = w.Update(ctx, l, []string{"Version"}, nil, db.NewOplogMsg(&lOplogMsg), db.WithVersion(&version)) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to update credential library version")) + } + switch rowsUpdated { + case 1: + case 0: + return nil + default: + return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("updated credential library version and %d rows updated", rowsUpdated)) + } + default: + rowsUpdated, err = w.Update(ctx, l, dbMask, nullFields, db.NewOplogMsg(&lOplogMsg), db.WithVersion(&version)) + if err != nil { + if errors.IsUniqueError(err) { + return errors.New(ctx, errors.NotUnique, op, + fmt.Sprintf("name %s already exists: %s", l.Name, l.PublicId)) + } + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to update credential library")) + } + switch rowsUpdated { + case 1: + case 0: + return nil + default: + return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("updated credential library and %d rows updated", rowsUpdated)) + } + } + msgs = append(msgs, &lOplogMsg) + + metadata := l.oplog(oplog.OpType_OP_TYPE_UPDATE) + if err := w.WriteOplogEntryWith(ctx, oplogWrapper, ticket, metadata, msgs); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write oplog")) + } + + accl := allocSSHCertificateCredentialLibrary() + accl.PublicId = l.PublicId + if err = rr.LookupById(ctx, accl); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to retrieve updated credential library")) + } + returnedCredentialLibrary = accl + return nil + }, + ) + + if err != nil { + if errors.IsUniqueError(err) { + return nil, db.NoRowsAffected, errors.New(ctx, errors.NotUnique, op, + fmt.Sprintf("name %s already exists: %s", l.Name, l.PublicId)) + } + return nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(l.PublicId)) + } + + return returnedCredentialLibrary, rowsUpdated, nil +} + +// LookupSSHCertificateCredentialLibrary returns the SSHCertificateCredentialLibrary for publicId. +// Returns nil, nil if no SSHCertificateCredentialLibrary is found for publicId. +func (r *Repository) LookupSSHCertificateCredentialLibrary(ctx context.Context, publicId string, _ ...Option) (*SSHCertificateCredentialLibrary, error) { + const op = "vault.(Repository).LookupCredentialLibrary" + if publicId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no public id") + } + l := allocSSHCertificateCredentialLibrary() + l.PublicId = publicId + if err := r.reader.LookupByPublicId(ctx, l); err != nil { + if errors.IsNotFoundError(err) { + return nil, nil + } + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for: %s", publicId))) + } + return l, nil +} + +// ListSSHCertificateCredentialLibraries returns a slice of SSHCertificateCredentialLibraries for the +// storeId. WithLimit is the only option supported. +func (r *Repository) ListSSHCertificateCredentialLibraries(ctx context.Context, storeId string, opt ...Option) ([]*SSHCertificateCredentialLibrary, error) { + const op = "vault.(Repository).ListSSHCertificateCredentialLibraries" + if storeId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no storeId") + } + opts := getOpts(opt...) + limit := r.defaultLimit + if opts.withLimit != 0 { + // non-zero signals an override of the default limit for the repo. + limit = opts.withLimit + } + var libs []*SSHCertificateCredentialLibrary + err := r.reader.SearchWhere(ctx, &libs, "store_id = ?", []any{storeId}, db.WithLimit(limit)) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + return libs, nil +} + +// DeleteSSHCertificateCredentialLibrary deletes publicId from the repository and returns +// the number of records deleted. +func (r *Repository) DeleteSSHCertificateCredentialLibrary(ctx context.Context, projectId string, publicId string, _ ...Option) (int, error) { + const op = "vault.(Repository).DeleteSSHCertificateCredentialLibrary" + if publicId == "" { + return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "no public id") + } + if projectId == "" { + return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "no project id") + } + + l := allocSSHCertificateCredentialLibrary() + l.PublicId = publicId + + oplogWrapper, err := r.kms.GetWrapper(ctx, projectId, kms.KeyPurposeOplog) + if err != nil { + return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) + } + + var rowsDeleted int + _, err = r.writer.DoTx( + ctx, db.StdRetryCnt, db.ExpBackoff{}, + func(_ db.Reader, w db.Writer) (err error) { + dl := l.clone() + rowsDeleted, err = w.Delete(ctx, dl, db.WithOplog(oplogWrapper, l.oplog(oplog.OpType_OP_TYPE_DELETE))) + if err == nil && rowsDeleted > 1 { + return errors.New(ctx, errors.MultipleRecords, op, "more than 1 CredentialLibrary would have been deleted") + } + return err + }, + ) + + if err != nil { + return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("delete failed for %s", l.PublicId))) + } + + return rowsDeleted, nil +} diff --git a/internal/credential/vault/repository_ssh_certificate_credential_library_test.go b/internal/credential/vault/repository_ssh_certificate_credential_library_test.go new file mode 100644 index 000000000..55898d7b3 --- /dev/null +++ b/internal/credential/vault/repository_ssh_certificate_credential_library_test.go @@ -0,0 +1,1948 @@ +package vault + +import ( + "context" + "testing" + "time" + + "github.com/hashicorp/boundary/internal/credential" + "github.com/hashicorp/boundary/internal/credential/vault/store" + "github.com/hashicorp/boundary/internal/db" + dbassert "github.com/hashicorp/boundary/internal/db/assert" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/oplog" + "github.com/hashicorp/boundary/internal/scheduler" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRepository_CreateSSHCertificateCredentialLibrary(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + cs := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0] + + tests := []struct { + name string + in *SSHCertificateCredentialLibrary + opts []Option + want *SSHCertificateCredentialLibrary + wantErr errors.Code + }{ + { + name: "nil-SSHCertificateCredentialLibrary", + wantErr: errors.InvalidParameter, + }, + { + name: "nil-embedded-SSHCertificateCredentialLibrary", + in: &SSHCertificateCredentialLibrary{}, + wantErr: errors.InvalidParameter, + }, + { + name: "invalid-no-store-id", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary("", "/ssh/sign/foo", "name") + return s + }(), + wantErr: errors.InvalidParameter, + }, + { + name: "invalid-public-id-set", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + cs.GetPublicId(), + "/ssh/sign/foo", + "name", + ) + s.PublicId = "abcd_OOOOOOOOOO" + return s + }(), + wantErr: errors.InvalidParameter, + }, + { + name: "invalid-no-vault-path", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + cs.GetPublicId(), + "", + "name", + ) + return s + }(), + wantErr: errors.InvalidParameter, + }, + { + name: "invalid-vault-path-invalid-endpoint", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + cs.GetPublicId(), + "/ssh/not-sign/foo", + "name", + ) + return s + }(), + wantErr: errors.CheckConstraint, + }, + { + name: "invalid-no-username", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + cs.GetPublicId(), + "/ssh/sign/foo", + "", + ) + return s + }(), + wantErr: errors.InvalidParameter, + }, + { + name: "valid-no-options", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + cs.GetPublicId(), + "/ssh/sign/foo", + "name", + ) + return s + }(), + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + StoreId: cs.GetPublicId(), + VaultPath: "/ssh/sign/foo", + Username: "name", + KeyType: KeyTypeEd25519, + KeyBits: 0, + }, + }, + }, + { + name: "valid-with-name", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + cs.GetPublicId(), + "/ssh/sign/foo", + "name", + WithName("test-name-repo"), + ) + return s + }(), + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + StoreId: cs.GetPublicId(), + Name: "test-name-repo", + VaultPath: "/ssh/sign/foo", + Username: "name", + KeyType: KeyTypeEd25519, + KeyBits: 0, + }, + }, + }, + { + name: "valid-with-description", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + cs.GetPublicId(), + "/ssh/sign/foo", + "name", + WithDescription("test-description-repo"), + ) + return s + }(), + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + StoreId: cs.GetPublicId(), + Description: "test-description-repo", + VaultPath: "/ssh/sign/foo", + Username: "name", + KeyType: KeyTypeEd25519, + KeyBits: 0, + }, + }, + }, + { + name: "valid-key-type-ed25519", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + cs.GetPublicId(), + "/ssh/sign/foo", + "name", + WithKeyType(KeyTypeEd25519), + WithKeyBits(KeyBitsDefault), + ) + return s + }(), + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + StoreId: cs.GetPublicId(), + VaultPath: "/ssh/sign/foo", + Username: "name", + KeyType: KeyTypeEd25519, + KeyBits: KeyBitsDefault, + }, + }, + }, + { + name: "valid-key-type-ecdsa-0", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + cs.GetPublicId(), + "/ssh/sign/foo", + "name", + WithKeyType(KeyTypeEcdsa), + WithKeyBits(KeyBitsDefault), + ) + return s + }(), + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + StoreId: cs.GetPublicId(), + VaultPath: "/ssh/sign/foo", + Username: "name", + KeyType: KeyTypeEcdsa, + KeyBits: KeyBitsEcdsa256, + }, + }, + }, + { + name: "valid-key-type-ecdsa-256", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + cs.GetPublicId(), + "/ssh/sign/foo", + "name", + WithKeyType(KeyTypeEcdsa), + WithKeyBits(KeyBitsEcdsa256), + ) + return s + }(), + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + StoreId: cs.GetPublicId(), + VaultPath: "/ssh/sign/foo", + Username: "name", + KeyType: KeyTypeEcdsa, + KeyBits: KeyBitsEcdsa256, + }, + }, + }, + { + name: "valid-key-type-ecdsa-384", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + cs.GetPublicId(), + "/ssh/sign/foo", + "name", + WithKeyType(KeyTypeEcdsa), + WithKeyBits(KeyBitsEcdsa384), + ) + return s + }(), + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + StoreId: cs.GetPublicId(), + VaultPath: "/ssh/sign/foo", + Username: "name", + KeyType: KeyTypeEcdsa, + KeyBits: KeyBitsEcdsa384, + }, + }, + }, + { + name: "valid-key-type-ecdsa-521", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + cs.GetPublicId(), + "/ssh/sign/foo", + "name", + WithKeyType(KeyTypeEcdsa), + WithKeyBits(KeyBitsEcdsa521), + ) + return s + }(), + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + StoreId: cs.GetPublicId(), + VaultPath: "/ssh/sign/foo", + Username: "name", + KeyType: KeyTypeEcdsa, + KeyBits: KeyBitsEcdsa521, + }, + }, + }, + { + name: "valid-key-type-rsa-0", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + cs.GetPublicId(), + "/ssh/sign/foo", + "name", + WithKeyType(KeyTypeRsa), + WithKeyBits(KeyBitsDefault), + ) + return s + }(), + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + StoreId: cs.GetPublicId(), + VaultPath: "/ssh/sign/foo", + Username: "name", + KeyType: KeyTypeRsa, + KeyBits: KeyBitsRsa2048, + }, + }, + }, + { + name: "valid-key-type-rsa-2048", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + cs.GetPublicId(), + "/ssh/sign/foo", + "name", + WithKeyType(KeyTypeRsa), + WithKeyBits(KeyBitsRsa2048), + ) + return s + }(), + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + StoreId: cs.GetPublicId(), + VaultPath: "/ssh/sign/foo", + Username: "name", + KeyType: KeyTypeRsa, + KeyBits: KeyBitsRsa2048, + }, + }, + }, + { + name: "valid-key-type-rsa-3072", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + cs.GetPublicId(), + "/ssh/sign/foo", + "name", + WithKeyType(KeyTypeRsa), + WithKeyBits(KeyBitsRsa3072), + ) + return s + }(), + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + StoreId: cs.GetPublicId(), + VaultPath: "/ssh/sign/foo", + Username: "name", + KeyType: KeyTypeRsa, + KeyBits: KeyBitsRsa3072, + }, + }, + }, + { + name: "valid-key-type-rsa-4096", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + cs.GetPublicId(), + "/ssh/sign/foo", + "name", + WithKeyType(KeyTypeRsa), + WithKeyBits(KeyBitsRsa4096), + ) + return s + }(), + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + StoreId: cs.GetPublicId(), + VaultPath: "/ssh/sign/foo", + Username: "name", + KeyType: KeyTypeRsa, + KeyBits: KeyBitsRsa4096, + }, + }, + }, + { + name: "invalid-key-type-key-bits-ecdsa-2048", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + cs.GetPublicId(), + "/ssh/sign/foo", + "name", + WithKeyType(KeyTypeEcdsa), + WithKeyBits(KeyBitsRsa2048), + ) + return s + }(), + wantErr: errors.NotSpecificIntegrity, + }, + { + name: "invalid-key-type-key-bits-ecdsa-3072", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + cs.GetPublicId(), + "/ssh/sign/foo", + "name", + WithKeyType(KeyTypeEcdsa), + WithKeyBits(KeyBitsRsa3072), + ) + return s + }(), + wantErr: errors.NotSpecificIntegrity, + }, + { + name: "invalid-key-type-key-bits-ecdsa-4096", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + cs.GetPublicId(), + "/ssh/sign/foo", + "name", + WithKeyType(KeyTypeEcdsa), + WithKeyBits(KeyBitsRsa4096), + ) + return s + }(), + wantErr: errors.NotSpecificIntegrity, + }, + { + name: "invalid-key-type-key-bits-rsa-256", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + cs.GetPublicId(), + "/ssh/sign/foo", + "name", + WithKeyType(KeyTypeRsa), + WithKeyBits(KeyBitsEcdsa256), + ) + return s + }(), + wantErr: errors.NotSpecificIntegrity, + }, + { + name: "invalid-key-type-key-bits-rsa-384", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + cs.GetPublicId(), + "/ssh/sign/foo", + "name", + WithKeyType(KeyTypeRsa), + WithKeyBits(KeyBitsEcdsa384), + ) + return s + }(), + wantErr: errors.NotSpecificIntegrity, + }, + { + name: "invalid-key-type-key-bits-rsa-521", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + cs.GetPublicId(), + "/ssh/sign/foo", + "name", + WithKeyType(KeyTypeRsa), + WithKeyBits(KeyBitsEcdsa521), + ) + return s + }(), + wantErr: errors.NotSpecificIntegrity, + }, + { + name: "invalid-key-type-key-bits-ed25519-256", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + cs.GetPublicId(), + "/ssh/sign/foo", + "name", + WithKeyType(KeyTypeEd25519), + WithKeyBits(KeyBitsEcdsa256), + ) + return s + }(), + wantErr: errors.NotSpecificIntegrity, + }, + { + name: "invalid-key-type-key-bits-ed25519-384", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + cs.GetPublicId(), + "/ssh/sign/foo", + "name", + WithKeyType(KeyTypeEd25519), + WithKeyBits(KeyBitsEcdsa384), + ) + return s + }(), + wantErr: errors.NotSpecificIntegrity, + }, + { + name: "invalid-key-type-key-bits-ed25519-521", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + cs.GetPublicId(), + "/ssh/sign/foo", + "name", + WithKeyType(KeyTypeEd25519), + WithKeyBits(KeyBitsEcdsa521), + ) + return s + }(), + wantErr: errors.NotSpecificIntegrity, + }, + { + name: "invalid-key-type-key-bits-ed25519-2048", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + cs.GetPublicId(), + "/ssh/sign/foo", + "name", + WithKeyType(KeyTypeEd25519), + WithKeyBits(KeyBitsRsa2048), + ) + return s + }(), + wantErr: errors.NotSpecificIntegrity, + }, + { + name: "invalid-key-type-key-bits-ed25519-3072", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + cs.GetPublicId(), + "/ssh/sign/foo", + "name", + WithKeyType(KeyTypeEd25519), + WithKeyBits(KeyBitsRsa3072), + ) + return s + }(), + wantErr: errors.NotSpecificIntegrity, + }, + { + name: "invalid-key-type-key-bits-ed25519-4096", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + cs.GetPublicId(), + "/ssh/sign/foo", + "name", + WithKeyType(KeyTypeEd25519), + WithKeyBits(KeyBitsRsa4096), + ) + return s + }(), + wantErr: errors.NotSpecificIntegrity, + }, + { + name: "valid-critical-options", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + cs.GetPublicId(), + "/ssh/sign/foo", + "name", + WithCriticalOptions(`{"force-command": "/bin/foo"}`), + ) + return s + }(), + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + StoreId: cs.GetPublicId(), + VaultPath: "/ssh/sign/foo", + Username: "name", + KeyType: KeyTypeEd25519, + KeyBits: 0, + CriticalOptions: `{"force-command": "/bin/foo"}`, + }, + }, + }, + { + name: "valid-extensions", + in: func() *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + cs.GetPublicId(), + "/ssh/sign/foo", + "name", + WithExtensions(`{"permit-X11-forwarding": "", "permit-pty": ""}`), + ) + return s + }(), + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + StoreId: cs.GetPublicId(), + VaultPath: "/ssh/sign/foo", + Username: "name", + KeyType: KeyTypeEd25519, + KeyBits: 0, + Extensions: `{"permit-X11-forwarding": "", "permit-pty": ""}`, + }, + }, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + ctx := context.Background() + kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) + repo, err := NewRepository(rw, rw, kms, sche) + require.NoError(err) + require.NotNil(repo) + got, err := repo.CreateSSHCertificateCredentialLibrary(ctx, prj.GetPublicId(), tt.in, tt.opts...) + if tt.wantErr != 0 { + assert.Truef(errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err) + assert.Nil(got) + return + } + require.NoError(err) + assert.Empty(tt.in.PublicId) + require.NotNil(got) + assertPublicId(t, SSHCertificateCredentialLibraryPrefix, got.GetPublicId()) + assert.NotSame(tt.in, got) + assert.Equal(tt.want.Name, got.Name) + assert.Equal(tt.want.Description, got.Description) + assert.Equal(tt.want.KeyType, got.KeyType) + assert.Equal(tt.want.KeyBits, got.KeyBits) + assert.Equal(tt.want.KeyId, got.KeyId) + assert.Equal(tt.want.Ttl, got.Ttl) + assert.Equal(tt.want.CriticalOptions, got.CriticalOptions) + assert.Equal(tt.want.Extensions, got.Extensions) + assert.Equal(got.CredentialType(), credential.SshCertificateType) + assert.Equal(got.CreateTime, got.UpdateTime) + + assert.NoError(db.TestVerifyOplog(t, rw, got.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_CREATE), db.WithCreateNotBefore(10*time.Second))) + }) + } + + t.Run("invalid-duplicate-names", func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + ctx := context.Background() + kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) + repo, err := NewRepository(rw, rw, kms, sche) + require.NoError(err) + require.NotNil(repo) + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + cs := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0] + in := &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + StoreId: cs.GetPublicId(), + KeyType: KeyTypeEd25519, + VaultPath: "/ssh/sign/foo", + Name: "test-name-repo", + Username: "name", + }, + } + + got, err := repo.CreateSSHCertificateCredentialLibrary(ctx, prj.GetPublicId(), in) + require.NoError(err) + require.NotNil(got) + assertPublicId(t, SSHCertificateCredentialLibraryPrefix, got.GetPublicId()) + assert.NotSame(in, got) + assert.Equal(in.Name, got.Name) + assert.Equal(in.Description, got.Description) + assert.Equal(got.CreateTime, got.UpdateTime) + + got2, err := repo.CreateSSHCertificateCredentialLibrary(ctx, prj.GetPublicId(), in) + assert.Truef(errors.Match(errors.T(errors.NotUnique), err), "want err: %q got: %q", errors.NotUnique, err) + assert.Nil(got2) + }) + + t.Run("valid-duplicate-names-diff-stores", func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + ctx := context.Background() + kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) + repo, err := NewRepository(rw, rw, kms, sche) + require.NoError(err) + require.NotNil(repo) + + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + css := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 2) + + csA, csB := css[0], css[1] + + in := &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + KeyType: KeyTypeEd25519, + VaultPath: "/ssh/sign/foo", + Name: "test-name-repo", + Username: "name", + }, + } + in2 := in.clone() + + in.StoreId = csA.GetPublicId() + got, err := repo.CreateSSHCertificateCredentialLibrary(ctx, prj.GetPublicId(), in) + require.NoError(err) + require.NotNil(got) + assertPublicId(t, SSHCertificateCredentialLibraryPrefix, got.GetPublicId()) + assert.NotSame(in, got) + assert.Equal(in.Name, got.Name) + assert.Equal(in.Description, got.Description) + assert.Equal(got.CreateTime, got.UpdateTime) + + in2.StoreId = csB.GetPublicId() + got2, err := repo.CreateSSHCertificateCredentialLibrary(ctx, prj.GetPublicId(), in2) + require.NoError(err) + require.NotNil(got2) + assertPublicId(t, SSHCertificateCredentialLibraryPrefix, got2.GetPublicId()) + assert.NotSame(in2, got2) + assert.Equal(in2.Name, got2.Name) + assert.Equal(in2.Description, got2.Description) + assert.Equal(got2.CreateTime, got2.UpdateTime) + }) +} + +func TestRepository_LookupSSHCertificateCredentialLibrary(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + + { + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + css := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 2) + + cs := css[0] + csWithExpiredToken := css[1] + rows, err := rw.Exec(context.Background(), + "update credential_vault_token set status = ? where token_hmac = ?", + []any{ExpiredToken, csWithExpiredToken.Token().TokenHmac}) + require.NoError(t, err) + require.Equal(t, 1, rows) + + tests := []struct { + name string + in *SSHCertificateCredentialLibrary + }{ + { + name: "valid-no-options", + in: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + StoreId: cs.GetPublicId(), + VaultPath: "/ssh/sign/foo", + Username: "name", + }, + }, + }, + + { + name: "valid-with-expired-cred-store-token", + in: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + StoreId: csWithExpiredToken.GetPublicId(), + VaultPath: "/ssh/sign/foo", + Username: "name", + }, + }, + }, + { + name: "valid-ssh-certificate-credential-type", + in: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + StoreId: cs.GetPublicId(), + VaultPath: "/ssh/sign/foo", + Username: "name", + CredentialType: string(credential.SshCertificateType), + }, + }, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + // setup + assert, require := assert.New(t), require.New(t) + ctx := context.Background() + kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) + repo, err := NewRepository(rw, rw, kms, sche) + assert.NoError(err) + require.NotNil(repo) + orig, err := repo.CreateSSHCertificateCredentialLibrary(ctx, prj.GetPublicId(), tt.in) + assert.NoError(err) + require.NotEmpty(orig) + // test + got, err := repo.LookupSSHCertificateCredentialLibrary(ctx, orig.GetPublicId()) + assert.NoError(err) + require.NotEmpty(got) + assert.Equal(orig.Name, got.Name) + assert.Equal(orig.Description, got.Description) + assert.Equal(orig.CredentialType(), got.CredentialType()) + }) + } + } + + t.Run("empty-public-id", func(t *testing.T) { + // setup + assert, require := assert.New(t), require.New(t) + ctx := context.Background() + kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) + repo, err := NewRepository(rw, rw, kms, sche) + assert.NoError(err) + require.NotNil(repo) + // test + got, err := repo.LookupCredentialLibrary(ctx, "") + wantErr := errors.InvalidParameter + assert.Truef(errors.Match(errors.T(wantErr), err), "want err: %q got: %q", wantErr, err) + assert.Nil(got) + }) + + t.Run("not-found", func(t *testing.T) { + // setup + assert, require := assert.New(t), require.New(t) + ctx := context.Background() + kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) + repo, err := NewRepository(rw, rw, kms, sche) + assert.NoError(err) + require.NotNil(repo) + badId, err := newSSHCertificateCredentialLibraryId() + assert.NoError(err) + require.NotNil(badId) + // test + got, err := repo.LookupCredentialLibrary(ctx, badId) + assert.NoError(err) + assert.Empty(got) + }) +} + +func TestRepository_ListSSHCertificateCredentialLibraries_Limits(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + sche := scheduler.TestScheduler(t, conn, wrapper) + + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + cs := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0] + const count = 10 + libs := TestSSHCertificateCredentialLibraries(t, conn, wrapper, cs.GetPublicId(), count) + + tests := []struct { + name string + repoOpts []Option + listOpts []Option + wantLen int + }{ + { + name: "with-no-limits", + wantLen: count, + }, + { + name: "with-repo-limit", + repoOpts: []Option{WithLimit(3)}, + wantLen: 3, + }, + { + name: "with-negative-repo-limit", + repoOpts: []Option{WithLimit(-1)}, + wantLen: count, + }, + { + name: "with-list-limit", + listOpts: []Option{WithLimit(3)}, + wantLen: 3, + }, + { + name: "with-negative-list-limit", + listOpts: []Option{WithLimit(-1)}, + wantLen: count, + }, + { + name: "with-repo-smaller-than-list-limit", + repoOpts: []Option{WithLimit(2)}, + listOpts: []Option{WithLimit(6)}, + wantLen: 6, + }, + { + name: "with-repo-larger-than-list-limit", + repoOpts: []Option{WithLimit(6)}, + listOpts: []Option{WithLimit(2)}, + wantLen: 2, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + ctx := context.Background() + kms := kms.TestKms(t, conn, wrapper) + repo, err := NewRepository(rw, rw, kms, sche, tt.repoOpts...) + assert.NoError(err) + require.NotNil(repo) + got, err := repo.ListSSHCertificateCredentialLibraries(ctx, libs[0].StoreId, tt.listOpts...) + require.NoError(err) + assert.Len(got, tt.wantLen) + }) + } +} + +func TestRepository_UpdateSSHCertificateCredentialLibrary(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + + changeVaultPath := func(p string) func(*SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary { + return func(l *SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary { + l.VaultPath = p + return l + } + } + + changeName := func(n string) func(*SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary { + return func(l *SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary { + l.Name = n + return l + } + } + + changeDescription := func(d string) func(*SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary { + return func(l *SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary { + l.Description = d + return l + } + } + + changeUsername := func(s string) func(*SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary { + return func(l *SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary { + l.Username = s + return l + } + } + + changeKeyType := func(s string) func(*SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary { + return func(l *SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary { + l.KeyType = s + return l + } + } + + changeKeyBits := func(b uint32) func(*SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary { + return func(l *SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary { + l.KeyBits = b + return l + } + } + + changeTtl := func(s string) func(*SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary { + return func(l *SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary { + l.Ttl = s + return l + } + } + + changeKeyId := func(s string) func(*SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary { + return func(l *SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary { + l.KeyId = s + return l + } + } + + changeCriticalOptions := func(s string) func(*SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary { + return func(l *SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary { + l.CriticalOptions = s + return l + } + } + + changeExtensions := func(s string) func(*SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary { + return func(l *SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary { + l.Extensions = s + return l + } + } + + makeNil := func() func(*SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary { + return func(l *SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary { + return nil + } + } + + makeEmbeddedNil := func() func(*SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary { + return func(l *SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary { + return &SSHCertificateCredentialLibrary{} + } + } + + deletePublicId := func() func(*SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary { + return func(l *SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary { + l.PublicId = "" + return l + } + } + + nonExistentPublicId := func() func(*SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary { + return func(l *SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary { + l.PublicId = "abcd_OOOOOOOOOO" + return l + } + } + + combine := func(fns ...func(l *SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary) func(*SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary { + return func(l *SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary { + for _, fn := range fns { + l = fn(l) + } + return l + } + } + + tests := []struct { + name string + origFn func(string) *SSHCertificateCredentialLibrary + chgFn func(*SSHCertificateCredentialLibrary) *SSHCertificateCredentialLibrary + masks []string + want *SSHCertificateCredentialLibrary + wantCount int + wantErr errors.Code + }{ + { + name: "nil-credential-library", + origFn: func(csId string) *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + csId, + "/ssh/sign/foo", + "name", + ) + return s + }, + chgFn: makeNil(), + masks: []string{nameField, descriptionField}, + wantErr: errors.InvalidParameter, + }, + { + name: "nil-embedded-credential-library", + origFn: func(csId string) *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + csId, + "/ssh/sign/foo", + "name", + ) + return s + }, + chgFn: makeEmbeddedNil(), + masks: []string{nameField, descriptionField}, + wantErr: errors.InvalidParameter, + }, + { + name: "no-public-id", + origFn: func(csId string) *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + csId, + "/ssh/sign/foo", + "name", + ) + return s + }, + chgFn: deletePublicId(), + masks: []string{nameField, descriptionField}, + wantErr: errors.InvalidPublicId, + }, + { + name: "updating-non-existent-credential-library", + origFn: func(csId string) *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + csId, + "/ssh/sign/foo", + "name", + WithName("test-name-repo"), + ) + return s + }, + chgFn: combine(nonExistentPublicId(), changeName("test-update-name-repo")), + masks: []string{nameField}, + wantErr: errors.RecordNotFound, + }, + { + name: "empty-field-mask", + origFn: func(csId string) *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + csId, + "/ssh/sign/foo", + "name", + WithName("test-name-repo"), + ) + return s + }, + chgFn: changeName("test-update-name-repo"), + wantErr: errors.EmptyFieldMask, + }, + { + name: "read-only-fields-in-field-mask", + origFn: func(csId string) *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + csId, + "/ssh/sign/foo", + "name", + WithName("test-name-repo"), + ) + return s + }, + chgFn: changeName("test-update-name-repo"), + masks: []string{"PublicId", "CreateTime", "UpdateTime", "StoreId"}, + wantErr: errors.InvalidFieldMask, + }, + { + name: "unknown-field-in-field-mask", + origFn: func(csId string) *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + csId, + "/ssh/sign/foo", + "name", + WithName("test-name-repo"), + ) + return s + }, + chgFn: changeName("test-update-name-repo"), + masks: []string{"Bilbo"}, + wantErr: errors.InvalidFieldMask, + }, + { + name: "change-name", + origFn: func(csId string) *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + csId, + "/ssh/sign/foo", + "name", + WithName("test-name-repo"), + ) + return s + }, + chgFn: changeName("test-update-name-repo"), + masks: []string{nameField}, + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + VaultPath: "/ssh/sign/foo", + Username: "name", + Name: "test-update-name-repo", + }, + }, + wantCount: 1, + }, + { + name: "change-description", + origFn: func(csId string) *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + csId, + "/ssh/sign/foo", + "name", + WithDescription("test-description-repo"), + ) + return s + }, + chgFn: changeDescription("test-update-description-repo"), + masks: []string{descriptionField}, + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + VaultPath: "/ssh/sign/foo", + Username: "name", + Description: "test-update-description-repo", + }, + }, + wantCount: 1, + }, + { + name: "change-name-and-description", + origFn: func(csId string) *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + csId, + "/ssh/sign/foo", + "name", + WithName("test-name-repo"), + WithDescription("test-description-repo"), + ) + return s + }, + chgFn: combine(changeDescription("test-update-description-repo"), changeName("test-update-name-repo")), + masks: []string{nameField, descriptionField}, + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + VaultPath: "/ssh/sign/foo", + Username: "name", + Name: "test-update-name-repo", + Description: "test-update-description-repo", + }, + }, + wantCount: 1, + }, + { + name: "change-username", + origFn: func(csId string) *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + csId, + "/ssh/sign/foo", + "name", + ) + return s + }, + chgFn: changeUsername("update-name"), + masks: []string{usernameField}, + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + VaultPath: "/ssh/sign/foo", + Username: "update-name", + }, + }, + wantCount: 1, + }, + { + name: "change-keytype", + origFn: func(csId string) *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + csId, + "/ssh/sign/foo", + "name", + WithKeyType(KeyTypeEd25519), + ) + return s + }, + chgFn: combine(changeKeyType(KeyTypeEcdsa), changeKeyBits(KeyBitsDefault)), + masks: []string{keyTypeField, keyBitsField}, + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + KeyType: KeyTypeEcdsa, + KeyBits: KeyBitsEcdsa256, + VaultPath: "/ssh/sign/foo", + Username: "name", + }, + }, + wantCount: 1, + }, + { + name: "change-keybits", + origFn: func(csId string) *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + csId, + "/ssh/sign/foo", + "name", + WithKeyType(KeyTypeEcdsa), + WithKeyBits(KeyBitsEcdsa256), + ) + return s + }, + chgFn: changeKeyBits(KeyBitsEcdsa384), + masks: []string{keyBitsField}, + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + KeyType: KeyTypeEcdsa, + KeyBits: KeyBitsEcdsa384, + VaultPath: "/ssh/sign/foo", + Username: "name", + }, + }, + wantCount: 1, + }, + { + name: "change-keybits-to-default-ecdsa", + origFn: func(csId string) *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + csId, + "/ssh/sign/foo", + "name", + WithKeyType(KeyTypeEcdsa), + WithKeyBits(KeyBitsEcdsa384), + ) + return s + }, + chgFn: changeKeyBits(KeyBitsDefault), + masks: []string{keyBitsField}, + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + KeyType: KeyTypeEcdsa, + KeyBits: KeyBitsEcdsa256, + VaultPath: "/ssh/sign/foo", + Username: "name", + }, + }, + wantCount: 1, + }, + { + name: "change-keybits-to-default-rsa", + origFn: func(csId string) *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + csId, + "/ssh/sign/foo", + "name", + WithKeyType(KeyTypeRsa), + WithKeyBits(KeyBitsRsa3072), + ) + return s + }, + chgFn: changeKeyBits(KeyBitsDefault), + masks: []string{keyBitsField}, + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + KeyType: KeyTypeRsa, + KeyBits: KeyBitsRsa2048, + VaultPath: "/ssh/sign/foo", + Username: "name", + }, + }, + wantCount: 1, + }, + { + name: "change-ttl", + origFn: func(csId string) *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + csId, + "/ssh/sign/foo", + "name", + WithTtl("10s"), + ) + return s + }, + chgFn: changeTtl("1h"), + masks: []string{ttlField}, + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + Ttl: "1h", + VaultPath: "/ssh/sign/foo", + Username: "name", + }, + }, + wantCount: 1, + }, + { + name: "change-keyid", + origFn: func(csId string) *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + csId, + "/ssh/sign/foo", + "name", + WithKeyId("id"), + ) + return s + }, + chgFn: changeKeyId("update-id"), + masks: []string{keyIdField}, + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + KeyId: "update-id", + VaultPath: "/ssh/sign/foo", + Username: "name", + }, + }, + wantCount: 1, + }, + { + name: "change-critical-options", + origFn: func(csId string) *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + csId, + "/ssh/sign/foo", + "name", + WithCriticalOptions("options"), + ) + return s + }, + chgFn: changeCriticalOptions("update-options"), + masks: []string{CriticalOptionsField}, + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + CriticalOptions: "update-options", + VaultPath: "/ssh/sign/foo", + Username: "name", + }, + }, + wantCount: 1, + }, + { + name: "change-extensions", + origFn: func(csId string) *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + csId, + "/ssh/sign/foo", + "name", + WithExtensions("extensions"), + ) + return s + }, + chgFn: changeExtensions("update-extensions"), + masks: []string{ExtensionsField}, + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + Extensions: "update-extensions", + VaultPath: "/ssh/sign/foo", + Username: "name", + }, + }, + wantCount: 1, + }, + { + name: "delete-name", + origFn: func(csId string) *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + csId, + "/ssh/sign/foo", + "name", + WithName("test-name-repo"), + WithDescription("test-description-repo"), + ) + return s + }, + masks: []string{nameField}, + chgFn: combine(changeDescription("test-update-description-repo"), changeName("")), + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + VaultPath: "/ssh/sign/foo", + Username: "name", + Description: "test-description-repo", + }, + }, + wantCount: 1, + }, + { + name: "delete-description", + origFn: func(csId string) *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + csId, + "/ssh/sign/foo", + "name", + WithName("test-name-repo"), + WithDescription("test-description-repo"), + ) + return s + }, + masks: []string{descriptionField}, + chgFn: combine(changeDescription(""), changeName("test-update-name-repo")), + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + VaultPath: "/ssh/sign/foo", + Username: "name", + Name: "test-name-repo", + }, + }, + wantCount: 1, + }, + { + name: "do-not-delete-name", + origFn: func(csId string) *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + csId, + "/ssh/sign/foo", + "name", + WithName("test-name-repo"), + WithDescription("test-description-repo"), + ) + return s + }, + masks: []string{descriptionField}, + chgFn: combine(changeDescription("test-update-description-repo"), changeName("")), + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + VaultPath: "/ssh/sign/foo", + Username: "name", + Name: "test-name-repo", + Description: "test-update-description-repo", + }, + }, + wantCount: 1, + }, + { + name: "do-not-delete-description", + origFn: func(csId string) *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + csId, + "/ssh/sign/foo", + "name", + WithName("test-name-repo"), + WithDescription("test-description-repo"), + ) + return s + }, + masks: []string{nameField}, + chgFn: combine(changeDescription(""), changeName("test-update-name-repo")), + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + VaultPath: "/ssh/sign/foo", + Username: "name", + Name: "test-update-name-repo", + Description: "test-description-repo", + }, + }, + wantCount: 1, + }, + { + name: "change-vault-path", + origFn: func(csId string) *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + csId, + "/ssh/sign/foo", + "name", + ) + return s + }, + chgFn: changeVaultPath("/ssh/issue/foo"), + masks: []string{vaultPathField}, + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + VaultPath: "/ssh/issue/foo", + Username: "name", + }, + }, + wantCount: 1, + }, + { + name: "change-vault-path-invalid", + origFn: func(csId string) *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + csId, + "/ssh/sign/foo", + "name", + ) + return s + }, + chgFn: changeVaultPath("/ssh/not-sign/foo"), + masks: []string{vaultPathField}, + wantErr: errors.CheckConstraint, + }, + { + name: "delete-vault-path", + origFn: func(csId string) *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + csId, + "/ssh/sign/foo", + "name", + ) + return s + }, + chgFn: changeVaultPath(""), + masks: []string{vaultPathField}, + wantErr: errors.NotNull, + }, + { + name: "change-key-type-invalid", + origFn: func(csId string) *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + csId, + "/ssh/sign/foo", + "name", + WithKeyType(KeyTypeEcdsa), + WithKeyBits(KeyBitsEcdsa256), + ) + return s + }, + chgFn: changeKeyType(KeyTypeEd25519), + masks: []string{keyTypeField}, + wantErr: errors.NotSpecificIntegrity, + }, + { + name: "change-key-type-bits-valid", + origFn: func(csId string) *SSHCertificateCredentialLibrary { + s, _ := NewSSHCertificateCredentialLibrary( + csId, + "/ssh/sign/foo", + "name", + WithKeyType(KeyTypeEcdsa), + WithKeyBits(KeyBitsEcdsa256), + ) + return s + }, + chgFn: combine(changeKeyType(KeyTypeRsa), changeKeyBits(KeyBitsRsa2048)), + masks: []string{keyTypeField, keyBitsField}, + want: &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + VaultPath: "/ssh/sign/foo", + Username: "name", + KeyType: KeyTypeRsa, + KeyBits: KeyBitsRsa2048, + }, + }, + wantCount: 1, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + ctx := context.Background() + kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) + repo, err := NewRepository(rw, rw, kms, sche) + assert.NoError(err) + require.NotNil(repo) + + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + cs := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0] + + ttOrig := tt.origFn(cs.GetPublicId()) + orig, err := repo.CreateSSHCertificateCredentialLibrary(ctx, prj.GetPublicId(), ttOrig) + assert.NoError(err) + require.NotNil(orig) + + if tt.chgFn != nil { + orig = tt.chgFn(orig) + } + got, gotCount, err := repo.UpdateSSHCertificateCredentialLibrary(ctx, prj.GetPublicId(), orig, 1, tt.masks) + if tt.wantErr != 0 { + assert.Truef(errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err) + assert.Equal(tt.wantCount, gotCount, "row count") + assert.Nil(got) + return + } + require.NoError(err) + assert.Empty(ttOrig.PublicId) + require.NotNil(got) + assertPublicId(t, SSHCertificateCredentialLibraryPrefix, got.GetPublicId()) + assert.Equal(tt.wantCount, gotCount, "row count") + assert.NotSame(ttOrig, got) + assert.Equal(ttOrig.StoreId, got.StoreId) + underlyingDB, err := conn.SqlDB(ctx) + require.NoError(err) + dbassert := dbassert.New(t, underlyingDB) + + switch tt.want.Name { + case "": + dbassert.IsNull(got, "name") + default: + assert.Equal(tt.want.Name, got.Name) + } + + switch tt.want.Description { + case "": + dbassert.IsNull(got, "description") + default: + assert.Equal(tt.want.Description, got.Description) + } + + switch tt.want.Username { + case "": + dbassert.IsNull(got, "Username") + default: + assert.Equal(tt.want.Username, got.Username) + } + + switch tt.want.KeyType { + case "": + assert.Equal(got.KeyType, KeyTypeEd25519) + default: + assert.Equal(tt.want.KeyType, got.KeyType) + } + + switch tt.want.KeyBits { + case (0): + assert.Zero(got.KeyBits) + default: + assert.Equal(tt.want.KeyBits, got.KeyBits) + } + + switch tt.want.Ttl { + case "": + dbassert.IsNull(got, "Ttl") + default: + assert.Equal(tt.want.Ttl, got.Ttl) + } + + switch tt.want.KeyId { + case "": + dbassert.IsNull(got, "KeyId") + default: + assert.Equal(tt.want.KeyId, got.KeyId) + } + + switch tt.want.CriticalOptions { + case "": + dbassert.IsNull(got, "CriticalOptions") + default: + assert.Equal(tt.want.CriticalOptions, got.CriticalOptions) + } + + switch tt.want.Extensions { + case "": + dbassert.IsNull(got, "Extensions") + default: + assert.Equal(tt.want.Extensions, got.Extensions) + } + + if tt.wantCount > 0 { + assert.NoError(db.TestVerifyOplog(t, rw, got.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second))) + } + }) + } + + t.Run("invalid-duplicate-names", func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + ctx := context.Background() + kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) + repo, err := NewRepository(rw, rw, kms, sche) + assert.NoError(err) + require.NotNil(repo) + + name := "test-dup-name" + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + cs := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0] + libs := TestSSHCertificateCredentialLibraries(t, conn, wrapper, cs.GetPublicId(), 2) + + lA, lB := libs[0], libs[1] + + lA.Name = name + got1, gotCount1, err := repo.UpdateSSHCertificateCredentialLibrary(ctx, prj.GetPublicId(), lA, 1, []string{"name"}) + assert.NoError(err) + require.NotNil(got1) + assert.Equal(name, got1.Name) + assert.Equal(1, gotCount1, "row count") + assert.NoError(db.TestVerifyOplog(t, rw, lA.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second))) + + lB.Name = name + got2, gotCount2, err := repo.UpdateSSHCertificateCredentialLibrary(ctx, prj.GetPublicId(), lB, 1, []string{"name"}) + assert.Truef(errors.Match(errors.T(errors.NotUnique), err), "want err code: %v got err: %v", errors.NotUnique, err) + assert.Nil(got2) + assert.Equal(db.NoRowsAffected, gotCount2, "row count") + err = db.TestVerifyOplog(t, rw, lB.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second)) + assert.Error(err) + assert.True(errors.IsNotFoundError(err)) + }) + + t.Run("valid-duplicate-names-diff-CredentialStores", func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + ctx := context.Background() + kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) + repo, err := NewRepository(rw, rw, kms, sche) + assert.NoError(err) + require.NotNil(repo) + + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + css := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 2) + + csA, csB := css[0], css[1] + + in := &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + Username: "name", + VaultPath: "/ssh/sign/foo", + Name: "test-name-repo", + }, + } + in2 := in.clone() + + in.StoreId = csA.GetPublicId() + got, err := repo.CreateSSHCertificateCredentialLibrary(ctx, prj.GetPublicId(), in) + assert.NoError(err) + require.NotNil(got) + assertPublicId(t, SSHCertificateCredentialLibraryPrefix, got.GetPublicId()) + assert.NotSame(in, got) + assert.Equal(in.Name, got.Name) + assert.Equal(in.Description, got.Description) + + in2.StoreId = csB.GetPublicId() + in2.Name = "first-name" + got2, err := repo.CreateSSHCertificateCredentialLibrary(ctx, prj.GetPublicId(), in2) + assert.NoError(err) + require.NotNil(got2) + got2.Name = got.Name + got3, gotCount3, err := repo.UpdateSSHCertificateCredentialLibrary(ctx, prj.GetPublicId(), got2, 1, []string{"name"}) + assert.NoError(err) + require.NotNil(got3) + assert.NotSame(got2, got3) + assert.Equal(got.Name, got3.Name) + assert.Equal(got2.Description, got3.Description) + assert.Equal(1, gotCount3, "row count") + assert.NoError(db.TestVerifyOplog(t, rw, got2.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second))) + }) + + t.Run("valid-update-with-expired-store-token", func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + ctx := context.Background() + kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) + repo, err := NewRepository(rw, rw, kms, sche) + assert.NoError(err) + require.NotNil(repo) + + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + css := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1) + cs := css[0] + + in := &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + Username: "name", + VaultPath: "/ssh/sign/foo", + Name: "test-name-repo", + }, + } + + in.StoreId = cs.GetPublicId() + got, err := repo.CreateSSHCertificateCredentialLibrary(ctx, prj.GetPublicId(), in) + assert.NoError(err) + require.NotNil(got) + + // Expire the credential store Vault token + rows, err := rw.Exec(context.Background(), + "update credential_vault_token set status = ? where token_hmac = ?", + []any{ExpiredToken, cs.Token().TokenHmac}) + require.NoError(err) + require.Equal(1, rows) + + got.Name = "new-name" + updated, gotCount, err := repo.UpdateSSHCertificateCredentialLibrary(ctx, prj.GetPublicId(), got, 1, []string{"name"}) + assert.NoError(err) + require.NotNil(updated) + assert.Equal("new-name", updated.Name) + assert.Equal(1, gotCount) + }) + + t.Run("change-project-id", func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + ctx := context.Background() + kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) + repo, err := NewRepository(rw, rw, kms, sche) + assert.NoError(err) + require.NotNil(repo) + + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + css := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 2) + + csA, csB := css[0], css[1] + + lA := TestSSHCertificateCredentialLibraries(t, conn, wrapper, csA.GetPublicId(), 1)[0] + lB := TestSSHCertificateCredentialLibraries(t, conn, wrapper, csB.GetPublicId(), 1)[0] + + assert.NotEqual(lA.StoreId, lB.StoreId) + orig := lA.clone() + + lA.StoreId = lB.StoreId + assert.Equal(lA.StoreId, lB.StoreId) + + got1, gotCount1, err := repo.UpdateSSHCertificateCredentialLibrary(ctx, prj.GetPublicId(), lA, 1, []string{"name"}) + + assert.NoError(err) + require.NotNil(got1) + assert.Equal(orig.StoreId, got1.StoreId) + assert.Equal(1, gotCount1, "row count") + assert.NoError(db.TestVerifyOplog(t, rw, lA.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second))) + }) + + t.Run("change-key-type-bits-consecutive", func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + ctx := context.Background() + kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) + repo, err := NewRepository(rw, rw, kms, sche) + assert.NoError(err) + require.NotNil(repo) + + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + cs := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0] + + in := &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + Username: "name", + VaultPath: "/ssh/sign/foo", + KeyType: KeyTypeEcdsa, + KeyBits: KeyBitsEcdsa521, + }, + } + in.StoreId = cs.GetPublicId() + + orig, err := repo.CreateSSHCertificateCredentialLibrary(ctx, prj.GetPublicId(), in) + assert.NoError(err) + require.NotNil(orig) + + orig.KeyBits = KeyBitsEcdsa256 + + got, gotCount, err := repo.UpdateSSHCertificateCredentialLibrary(ctx, prj.GetPublicId(), orig, 1, []string{keyBitsField}) + require.NoError(err) + assert.Empty(in.PublicId) + require.NotNil(got) + assertPublicId(t, SSHCertificateCredentialLibraryPrefix, got.GetPublicId()) + assert.Equal(1, gotCount, "row count") + assert.NotSame(orig, got) + assert.Equal(orig.StoreId, got.StoreId) + + orig.KeyType = KeyTypeRsa + orig.KeyBits = KeyBitsDefault + + got, gotCount, err = repo.UpdateSSHCertificateCredentialLibrary(ctx, prj.GetPublicId(), orig, 2, []string{keyTypeField, keyBitsField}) + require.NoError(err) + require.NotNil(got) + assertPublicId(t, SSHCertificateCredentialLibraryPrefix, got.GetPublicId()) + assert.Equal(1, gotCount, "row count") + assert.NotSame(orig, got) + assert.Equal(orig.StoreId, got.StoreId) + + orig.KeyBits = KeyBitsRsa3072 + + got, gotCount, err = repo.UpdateSSHCertificateCredentialLibrary(ctx, prj.GetPublicId(), orig, 3, []string{keyTypeField}) + require.NoError(err) + require.NotNil(got) + assertPublicId(t, SSHCertificateCredentialLibraryPrefix, got.GetPublicId()) + assert.Equal(1, gotCount, "row count") + assert.NotSame(orig, got) + assert.Equal(orig.StoreId, got.StoreId) + + assert.Equal(KeyTypeRsa, orig.KeyType) + assert.Equal(uint32(KeyBitsRsa3072), orig.KeyBits) + }) +} + +func TestRepository_DeleteSSHCertificateCredentialLibrary(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + + { + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + cs := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0] + l := TestSSHCertificateCredentialLibraries(t, conn, wrapper, cs.GetPublicId(), 1)[0] + + badId, err := newSSHCertificateCredentialLibraryId() + require.NoError(t, err) + require.NotNil(t, badId) + + tests := []struct { + name string + in string + want int + wantErr errors.Code + }{ + { + name: "found", + in: l.GetPublicId(), + want: 1, + }, + { + name: "not-found", + in: badId, + }, + { + name: "empty-public-id", + in: "", + wantErr: errors.InvalidParameter, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + ctx := context.Background() + kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) + repo, err := NewRepository(rw, rw, kms, sche) + assert.NoError(err) + require.NotNil(repo) + + got, err := repo.DeleteSSHCertificateCredentialLibrary(ctx, prj.GetPublicId(), tt.in) + if tt.wantErr != 0 { + assert.Truef(errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err) + return + } + assert.NoError(err) + assert.Equal(tt.want, got, "row count") + + cl, err := repo.LookupSSHCertificateCredentialLibrary(ctx, tt.in) + assert.Empty(err) + assert.Empty(cl) + }) + } + } +} diff --git a/internal/credential/vault/setup_manual_test.go b/internal/credential/vault/setup_manual_test.go index aa4fff3fc..def2593c9 100644 --- a/internal/credential/vault/setup_manual_test.go +++ b/internal/credential/vault/setup_manual_test.go @@ -74,7 +74,7 @@ func TestSetupSleepyDevEnvironment(t *testing.T) { require.NoError(t, err) require.NotNil(t, store) libClient := credentiallibraries.NewClient(client) - lib, err := libClient.Create(ctx, store.Item.Id, credentiallibraries.WithVaultCredentialLibraryPath(path.Join("database", "creds", "opened")), + lib, err := libClient.Create(ctx, store.Item.Type, store.Item.Id, credentiallibraries.WithVaultCredentialLibraryPath(path.Join("database", "creds", "opened")), credentiallibraries.WithVaultCredentialLibraryHttpMethod("GET"), ) require.NoError(t, err) diff --git a/internal/credential/vault/ssh_certificate_credential_library.go b/internal/credential/vault/ssh_certificate_credential_library.go new file mode 100644 index 000000000..9d78bf5c4 --- /dev/null +++ b/internal/credential/vault/ssh_certificate_credential_library.go @@ -0,0 +1,120 @@ +package vault + +import ( + "github.com/hashicorp/boundary/internal/credential" + "github.com/hashicorp/boundary/internal/credential/vault/store" + "github.com/hashicorp/boundary/internal/oplog" + "google.golang.org/protobuf/proto" +) + +const ( + KeyTypeEcdsa = "ecdsa" + KeyTypeEd25519 = "ed25519" + KeyTypeRsa = "rsa" + + KeyBitsDefault = 0 + + KeyBitsEcdsa256 = 256 + KeyBitsEcdsa384 = 384 + KeyBitsEcdsa521 = 521 + + KeyBitsRsa2048 = 2048 + KeyBitsRsa3072 = 3072 + KeyBitsRsa4096 = 4096 +) + +// SSHCertificateCredentialLibrary is a credential library that issues +// ssh certificate using the vault ssh secret engine. +// See: https://developer.hashicorp.com/vault/api-docs/secret/ssh#sign-ssh-key +type SSHCertificateCredentialLibrary struct { + *store.SSHCertificateCredentialLibrary + tableName string `gorm:"-"` +} + +// NewSSHCertificateCredentialLibrary creates a new in memory SSHCertificateCredentialLibrary +// for a Vault backend at vaultPath assigned to storeId. The SSH username field must be set. +// Name, description, key type, key bits, ttl, key id, critical options, and extensions +// are the only valid options. All other options are ignored. +func NewSSHCertificateCredentialLibrary(storeId string, vaultPath string, username string, opt ...Option) (*SSHCertificateCredentialLibrary, error) { + const op = "vault.NewSSHCertificateCredentialLibrary" + opts := getOpts(opt...) + + l := &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{ + StoreId: storeId, + Name: opts.withName, + Description: opts.withDescription, + VaultPath: vaultPath, + Username: username, + KeyType: opts.withKeyType, + KeyBits: opts.withKeyBits, + Ttl: opts.withTtl, + KeyId: opts.withKeyId, + CriticalOptions: opts.withCriticalOptions, + Extensions: opts.withExtensions, + CredentialType: string(credential.SshCertificateType), + }, + } + + return l, nil +} + +func allocSSHCertificateCredentialLibrary() *SSHCertificateCredentialLibrary { + return &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{}, + } +} + +func (l *SSHCertificateCredentialLibrary) clone() *SSHCertificateCredentialLibrary { + cp := proto.Clone(l.SSHCertificateCredentialLibrary) + return &SSHCertificateCredentialLibrary{ + SSHCertificateCredentialLibrary: cp.(*store.SSHCertificateCredentialLibrary), + } +} + +func (l *SSHCertificateCredentialLibrary) setId(i string) { + l.PublicId = i +} + +// TableName returns the table name. +func (l *SSHCertificateCredentialLibrary) TableName() string { + if l.tableName != "" { + return l.tableName + } + return "credential_vault_ssh_cert_library" +} + +// SetTableName sets the table name. +func (l *SSHCertificateCredentialLibrary) SetTableName(n string) { + l.tableName = n +} + +func (l *SSHCertificateCredentialLibrary) oplog(op oplog.OpType) oplog.Metadata { + metadata := oplog.Metadata{ + "resource-public-id": []string{l.PublicId}, + "resource-type": []string{"credential-vault-ssh-cert-library"}, + "op-type": []string{op.String()}, + } + if l.StoreId != "" { + metadata["store-id"] = []string{l.StoreId} + } + return metadata +} + +func (l *SSHCertificateCredentialLibrary) getDefaultKeyBits() uint32 { + switch l.KeyType { + case KeyTypeEcdsa: + return KeyBitsEcdsa256 + case KeyTypeRsa: + return KeyBitsRsa2048 + default: + return KeyBitsDefault + } +} + +// CredentialType returns the type of credential the library retrieves. +func (l *SSHCertificateCredentialLibrary) CredentialType() credential.Type { + return credential.Type(l.SSHCertificateCredentialLibrary.CredentialType) +} + +var _ credential.Library = (*SSHCertificateCredentialLibrary)(nil) diff --git a/internal/credential/vault/store/vault.pb.go b/internal/credential/vault/store/vault.pb.go index 7a198ac30..06bd6a14e 100644 --- a/internal/credential/vault/store/vault.pb.go +++ b/internal/credential/vault/store/vault.pb.go @@ -629,6 +629,210 @@ func (x *CredentialLibrary) GetCredentialType() string { return "" } +type SSHCertificateCredentialLibrary struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // public_id is a surrogate key suitable for use in a public API. + // @inject_tag: `gorm:"primary_key"` + PublicId string `protobuf:"bytes,1,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"` + // create_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + CreateTime *timestamp.Timestamp `protobuf:"bytes,2,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"` + // update_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + UpdateTime *timestamp.Timestamp `protobuf:"bytes,3,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty" gorm:"default:current_timestamp"` + // name is optional. If set, it must be unique within project_id. + // @inject_tag: `gorm:"default:null"` + Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"` + // description is optional. + // @inject_tag: `gorm:"default:null"` + Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty" gorm:"default:null"` + // store_id of the owning vault credential store. + // It must be set. + // @inject_tag: `gorm:"not_null"` + StoreId string `protobuf:"bytes,6,opt,name=store_id,json=storeId,proto3" json:"store_id,omitempty" gorm:"not_null"` + // version allows optimistic locking of the resource. + // @inject_tag: `gorm:"default:null"` + Version uint32 `protobuf:"varint,7,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"` + // vault_path is the path in Vault to request credentials from. + // It must be set. + // @inject_tag: `gorm:"not_null"` + VaultPath string `protobuf:"bytes,8,opt,name=vault_path,json=vaultPath,proto3" json:"vault_path,omitempty" gorm:"not_null"` + // username is the username to use when making an SSH connection. + // It must be set. + // @inject_tag: `gorm:"not_null"` + Username string `protobuf:"bytes,9,opt,name=username,proto3" json:"username,omitempty" gorm:"not_null"` + // key_type specifies the key type to use when generating an SSH private key. + // Values must be "rsa", "ed25519", or "ecdsa". + // @inject_tag: `gorm:"not_null"` + KeyType string `protobuf:"bytes,10,opt,name=key_type,json=keyType,proto3" json:"key_type,omitempty" gorm:"not_null"` + // key_bits specifies the number of bits to use to generate an SSH private key. + // Not used if key_type is ed25519. + // @inject_tag: `gorm:"not_null"` + KeyBits uint32 `protobuf:"varint,11,opt,name=key_bits,json=keyBits,proto3" json:"key_bits,omitempty" gorm:"not_null"` + // ttl specifies the requested time to live for the certificate. + // @inject_tag: `gorm:"default:null"` + Ttl string `protobuf:"bytes,12,opt,name=ttl,proto3" json:"ttl,omitempty" gorm:"default:null"` + // key_id specifies the key id that the created certificate should have. + // @inject_tag: `gorm:"default:null"` + KeyId string `protobuf:"bytes,13,opt,name=key_id,json=keyId,proto3" json:"key_id,omitempty" gorm:"default:null"` + // critical_options specifies a map of the critical options that the certificate should be signed for. + // @inject_tag: `gorm:"default:null"` + CriticalOptions string `protobuf:"bytes,14,opt,name=critical_options,json=criticalOptions,proto3" json:"critical_options,omitempty" gorm:"default:null"` + // extensions specifies a map of the extensions that the certificate should be signed for. + // @inject_tag: `gorm:"default:null"` + Extensions string `protobuf:"bytes,15,opt,name=extensions,proto3" json:"extensions,omitempty" gorm:"default:null"` + // credential_type is always ssh_certificate + // @inject_tag: `gorm:"default:null"` + CredentialType string `protobuf:"bytes,16,opt,name=credential_type,json=credentialType,proto3" json:"credential_type,omitempty" gorm:"default:null"` +} + +func (x *SSHCertificateCredentialLibrary) Reset() { + *x = SSHCertificateCredentialLibrary{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SSHCertificateCredentialLibrary) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SSHCertificateCredentialLibrary) ProtoMessage() {} + +func (x *SSHCertificateCredentialLibrary) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SSHCertificateCredentialLibrary.ProtoReflect.Descriptor instead. +func (*SSHCertificateCredentialLibrary) Descriptor() ([]byte, []int) { + return file_controller_storage_credential_vault_store_v1_vault_proto_rawDescGZIP(), []int{4} +} + +func (x *SSHCertificateCredentialLibrary) GetPublicId() string { + if x != nil { + return x.PublicId + } + return "" +} + +func (x *SSHCertificateCredentialLibrary) GetCreateTime() *timestamp.Timestamp { + if x != nil { + return x.CreateTime + } + return nil +} + +func (x *SSHCertificateCredentialLibrary) GetUpdateTime() *timestamp.Timestamp { + if x != nil { + return x.UpdateTime + } + return nil +} + +func (x *SSHCertificateCredentialLibrary) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *SSHCertificateCredentialLibrary) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *SSHCertificateCredentialLibrary) GetStoreId() string { + if x != nil { + return x.StoreId + } + return "" +} + +func (x *SSHCertificateCredentialLibrary) GetVersion() uint32 { + if x != nil { + return x.Version + } + return 0 +} + +func (x *SSHCertificateCredentialLibrary) GetVaultPath() string { + if x != nil { + return x.VaultPath + } + return "" +} + +func (x *SSHCertificateCredentialLibrary) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *SSHCertificateCredentialLibrary) GetKeyType() string { + if x != nil { + return x.KeyType + } + return "" +} + +func (x *SSHCertificateCredentialLibrary) GetKeyBits() uint32 { + if x != nil { + return x.KeyBits + } + return 0 +} + +func (x *SSHCertificateCredentialLibrary) GetTtl() string { + if x != nil { + return x.Ttl + } + return "" +} + +func (x *SSHCertificateCredentialLibrary) GetKeyId() string { + if x != nil { + return x.KeyId + } + return "" +} + +func (x *SSHCertificateCredentialLibrary) GetCriticalOptions() string { + if x != nil { + return x.CriticalOptions + } + return "" +} + +func (x *SSHCertificateCredentialLibrary) GetExtensions() string { + if x != nil { + return x.Extensions + } + return "" +} + +func (x *SSHCertificateCredentialLibrary) GetCredentialType() string { + if x != nil { + return x.CredentialType + } + return "" +} + type Credential struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -688,7 +892,7 @@ type Credential struct { func (x *Credential) Reset() { *x = Credential{} if protoimpl.UnsafeEnabled { - mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[4] + mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -701,7 +905,7 @@ func (x *Credential) String() string { func (*Credential) ProtoMessage() {} func (x *Credential) ProtoReflect() protoreflect.Message { - mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[4] + mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -714,7 +918,7 @@ func (x *Credential) ProtoReflect() protoreflect.Message { // Deprecated: Use Credential.ProtoReflect.Descriptor instead. func (*Credential) Descriptor() ([]byte, []int) { - return file_controller_storage_credential_vault_store_v1_vault_proto_rawDescGZIP(), []int{4} + return file_controller_storage_credential_vault_store_v1_vault_proto_rawDescGZIP(), []int{5} } func (x *Credential) GetPublicId() string { @@ -832,7 +1036,7 @@ type UsernamePasswordOverride struct { func (x *UsernamePasswordOverride) Reset() { *x = UsernamePasswordOverride{} if protoimpl.UnsafeEnabled { - mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[5] + mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -845,7 +1049,7 @@ func (x *UsernamePasswordOverride) String() string { func (*UsernamePasswordOverride) ProtoMessage() {} func (x *UsernamePasswordOverride) ProtoReflect() protoreflect.Message { - mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[5] + mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -858,7 +1062,7 @@ func (x *UsernamePasswordOverride) ProtoReflect() protoreflect.Message { // Deprecated: Use UsernamePasswordOverride.ProtoReflect.Descriptor instead. func (*UsernamePasswordOverride) Descriptor() ([]byte, []int) { - return file_controller_storage_credential_vault_store_v1_vault_proto_rawDescGZIP(), []int{5} + return file_controller_storage_credential_vault_store_v1_vault_proto_rawDescGZIP(), []int{6} } func (x *UsernamePasswordOverride) GetLibraryId() string { @@ -923,7 +1127,7 @@ type SshPrivateKeyOverride struct { func (x *SshPrivateKeyOverride) Reset() { *x = SshPrivateKeyOverride{} if protoimpl.UnsafeEnabled { - mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[6] + mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -936,7 +1140,7 @@ func (x *SshPrivateKeyOverride) String() string { func (*SshPrivateKeyOverride) ProtoMessage() {} func (x *SshPrivateKeyOverride) ProtoReflect() protoreflect.Message { - mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[6] + mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -949,7 +1153,7 @@ func (x *SshPrivateKeyOverride) ProtoReflect() protoreflect.Message { // Deprecated: Use SshPrivateKeyOverride.ProtoReflect.Descriptor instead. func (*SshPrivateKeyOverride) Descriptor() ([]byte, []int) { - return file_controller_storage_credential_vault_store_v1_vault_proto_rawDescGZIP(), []int{6} + return file_controller_storage_credential_vault_store_v1_vault_proto_rawDescGZIP(), []int{7} } func (x *SshPrivateKeyOverride) GetLibraryId() string { @@ -1146,72 +1350,131 @@ var file_controller_storage_credential_vault_store_v1_vault_proto_rawDesc = []by 0x0f, 0x68, 0x74, 0x74, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x72, 0x65, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x22, 0xc3, 0x04, 0x0a, 0x0a, 0x43, 0x72, - 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, - 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, - 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, - 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6c, 0x69, 0x62, 0x72, 0x61, - 0x72, 0x79, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, - 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x68, 0x6d, 0x61, - 0x63, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x48, 0x6d, - 0x61, 0x63, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, - 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, + 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x22, 0xaa, 0x07, 0x0a, 0x1f, 0x53, 0x53, + 0x48, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x12, 0x1b, 0x0a, + 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, + 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, + 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x54, 0x69, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x10, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x40, 0x0a, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x1e, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x3f, 0x0a, 0x0a, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x09, 0x42, 0x20, 0xc2, 0xdd, 0x29, 0x1c, 0x0a, 0x09, 0x56, 0x61, 0x75, + 0x6c, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x0f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x73, 0x2e, 0x70, 0x61, 0x74, 0x68, 0x52, 0x09, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x50, 0x61, + 0x74, 0x68, 0x12, 0x3f, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x23, 0xc2, 0xdd, 0x29, 0x1f, 0x0a, 0x08, 0x55, 0x73, 0x65, 0x72, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x13, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, + 0x2e, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x3d, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x09, 0x42, 0x22, 0xc2, 0xdd, 0x29, 0x1e, 0x0a, 0x07, 0x4b, 0x65, 0x79, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x13, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, + 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x3d, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x18, 0x0b, + 0x20, 0x01, 0x28, 0x0d, 0x42, 0x22, 0xc2, 0xdd, 0x29, 0x1e, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x42, + 0x69, 0x74, 0x73, 0x12, 0x13, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, + 0x6b, 0x65, 0x79, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x42, 0x69, 0x74, + 0x73, 0x12, 0x2b, 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x42, 0x19, + 0xc2, 0xdd, 0x29, 0x15, 0x0a, 0x03, 0x54, 0x74, 0x6c, 0x12, 0x0e, 0x61, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x74, 0x74, 0x6c, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x12, 0x35, + 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1e, + 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x05, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x11, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x52, 0x05, + 0x6b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x5d, 0x0a, 0x10, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, + 0x6c, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x32, 0xc2, 0xdd, 0x29, 0x2e, 0x0a, 0x0f, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x4f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1b, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x73, 0x2e, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x52, 0x0f, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x4f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x47, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x42, 0x27, 0xc2, 0xdd, 0x29, 0x23, 0x0a, 0x0a, + 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x15, 0x61, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, + 0x73, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x27, 0x0a, + 0x0f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x61, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x22, 0xc3, 0x04, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x49, + 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, + 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x68, 0x6d, 0x61, 0x63, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x48, 0x6d, 0x61, 0x63, 0x12, + 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x56, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, - 0x72, 0x65, 0x6e, 0x65, 0x77, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, - 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0f, - 0x6c, 0x61, 0x73, 0x74, 0x52, 0x65, 0x6e, 0x65, 0x77, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x12, - 0x53, 0x0a, 0x0f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, - 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x54, 0x69, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x6e, 0x65, 0x77, - 0x61, 0x62, 0x6c, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x73, 0x52, 0x65, - 0x6e, 0x65, 0x77, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, - 0x97, 0x01, 0x0a, 0x18, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x50, 0x61, 0x73, 0x73, - 0x77, 0x6f, 0x72, 0x64, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a, - 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x75, - 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, - 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x2d, 0x0a, 0x12, 0x70, 0x61, - 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, - 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x22, 0xe2, 0x01, 0x0a, 0x15, 0x53, 0x73, - 0x68, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x4f, 0x76, 0x65, 0x72, 0x72, - 0x69, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, - 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x61, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, - 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, - 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x13, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x47, 0x0a, 0x20, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, - 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x5f, - 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x1d, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x73, 0x73, 0x70, - 0x68, 0x72, 0x61, 0x73, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x42, 0x45, - 0x5a, 0x43, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, - 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x61, 0x6c, 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, - 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, + 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, + 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x49, 0x64, 0x12, 0x56, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x72, 0x65, 0x6e, + 0x65, 0x77, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, + 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0f, 0x6c, 0x61, 0x73, + 0x74, 0x52, 0x65, 0x6e, 0x65, 0x77, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x53, 0x0a, 0x0f, + 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x52, 0x0e, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, + 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x6e, 0x65, 0x77, 0x61, 0x62, 0x6c, + 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x73, 0x52, 0x65, 0x6e, 0x65, 0x77, + 0x61, 0x62, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0c, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x97, 0x01, 0x0a, + 0x18, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, + 0x64, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x69, 0x62, + 0x72, 0x61, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6c, + 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x75, 0x73, 0x65, 0x72, + 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x41, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x2d, 0x0a, 0x12, 0x70, 0x61, 0x73, 0x73, 0x77, + 0x6f, 0x72, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x11, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x41, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x22, 0xe2, 0x01, 0x0a, 0x15, 0x53, 0x73, 0x68, 0x50, 0x72, + 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, + 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x49, 0x64, 0x12, + 0x2d, 0x0a, 0x12, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x75, 0x73, 0x65, + 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x32, + 0x0a, 0x15, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70, + 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x12, 0x47, 0x0a, 0x20, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, + 0x79, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x5f, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1d, 0x70, 0x72, + 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, + 0x73, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x42, 0x45, 0x5a, 0x43, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, + 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, + 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1226,36 +1489,39 @@ func file_controller_storage_credential_vault_store_v1_vault_proto_rawDescGZIP() return file_controller_storage_credential_vault_store_v1_vault_proto_rawDescData } -var file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes = make([]protoimpl.MessageInfo, 8) var file_controller_storage_credential_vault_store_v1_vault_proto_goTypes = []interface{}{ - (*CredentialStore)(nil), // 0: controller.storage.credential.vault.store.v1.CredentialStore - (*Token)(nil), // 1: controller.storage.credential.vault.store.v1.Token - (*ClientCertificate)(nil), // 2: controller.storage.credential.vault.store.v1.ClientCertificate - (*CredentialLibrary)(nil), // 3: controller.storage.credential.vault.store.v1.CredentialLibrary - (*Credential)(nil), // 4: controller.storage.credential.vault.store.v1.Credential - (*UsernamePasswordOverride)(nil), // 5: controller.storage.credential.vault.store.v1.UsernamePasswordOverride - (*SshPrivateKeyOverride)(nil), // 6: controller.storage.credential.vault.store.v1.SshPrivateKeyOverride - (*timestamp.Timestamp)(nil), // 7: controller.storage.timestamp.v1.Timestamp + (*CredentialStore)(nil), // 0: controller.storage.credential.vault.store.v1.CredentialStore + (*Token)(nil), // 1: controller.storage.credential.vault.store.v1.Token + (*ClientCertificate)(nil), // 2: controller.storage.credential.vault.store.v1.ClientCertificate + (*CredentialLibrary)(nil), // 3: controller.storage.credential.vault.store.v1.CredentialLibrary + (*SSHCertificateCredentialLibrary)(nil), // 4: controller.storage.credential.vault.store.v1.SSHCertificateCredentialLibrary + (*Credential)(nil), // 5: controller.storage.credential.vault.store.v1.Credential + (*UsernamePasswordOverride)(nil), // 6: controller.storage.credential.vault.store.v1.UsernamePasswordOverride + (*SshPrivateKeyOverride)(nil), // 7: controller.storage.credential.vault.store.v1.SshPrivateKeyOverride + (*timestamp.Timestamp)(nil), // 8: controller.storage.timestamp.v1.Timestamp } var file_controller_storage_credential_vault_store_v1_vault_proto_depIdxs = []int32{ - 7, // 0: controller.storage.credential.vault.store.v1.CredentialStore.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 7, // 1: controller.storage.credential.vault.store.v1.CredentialStore.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 7, // 2: controller.storage.credential.vault.store.v1.CredentialStore.delete_time:type_name -> controller.storage.timestamp.v1.Timestamp - 7, // 3: controller.storage.credential.vault.store.v1.Token.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 7, // 4: controller.storage.credential.vault.store.v1.Token.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 7, // 5: controller.storage.credential.vault.store.v1.Token.last_renewal_time:type_name -> controller.storage.timestamp.v1.Timestamp - 7, // 6: controller.storage.credential.vault.store.v1.Token.expiration_time:type_name -> controller.storage.timestamp.v1.Timestamp - 7, // 7: controller.storage.credential.vault.store.v1.CredentialLibrary.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 7, // 8: controller.storage.credential.vault.store.v1.CredentialLibrary.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 7, // 9: controller.storage.credential.vault.store.v1.Credential.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 7, // 10: controller.storage.credential.vault.store.v1.Credential.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 7, // 11: controller.storage.credential.vault.store.v1.Credential.last_renewal_time:type_name -> controller.storage.timestamp.v1.Timestamp - 7, // 12: controller.storage.credential.vault.store.v1.Credential.expiration_time:type_name -> controller.storage.timestamp.v1.Timestamp - 13, // [13:13] is the sub-list for method output_type - 13, // [13:13] is the sub-list for method input_type - 13, // [13:13] is the sub-list for extension type_name - 13, // [13:13] is the sub-list for extension extendee - 0, // [0:13] is the sub-list for field type_name + 8, // 0: controller.storage.credential.vault.store.v1.CredentialStore.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 8, // 1: controller.storage.credential.vault.store.v1.CredentialStore.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 8, // 2: controller.storage.credential.vault.store.v1.CredentialStore.delete_time:type_name -> controller.storage.timestamp.v1.Timestamp + 8, // 3: controller.storage.credential.vault.store.v1.Token.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 8, // 4: controller.storage.credential.vault.store.v1.Token.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 8, // 5: controller.storage.credential.vault.store.v1.Token.last_renewal_time:type_name -> controller.storage.timestamp.v1.Timestamp + 8, // 6: controller.storage.credential.vault.store.v1.Token.expiration_time:type_name -> controller.storage.timestamp.v1.Timestamp + 8, // 7: controller.storage.credential.vault.store.v1.CredentialLibrary.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 8, // 8: controller.storage.credential.vault.store.v1.CredentialLibrary.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 8, // 9: controller.storage.credential.vault.store.v1.SSHCertificateCredentialLibrary.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 8, // 10: controller.storage.credential.vault.store.v1.SSHCertificateCredentialLibrary.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 8, // 11: controller.storage.credential.vault.store.v1.Credential.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 8, // 12: controller.storage.credential.vault.store.v1.Credential.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 8, // 13: controller.storage.credential.vault.store.v1.Credential.last_renewal_time:type_name -> controller.storage.timestamp.v1.Timestamp + 8, // 14: controller.storage.credential.vault.store.v1.Credential.expiration_time:type_name -> controller.storage.timestamp.v1.Timestamp + 15, // [15:15] is the sub-list for method output_type + 15, // [15:15] is the sub-list for method input_type + 15, // [15:15] is the sub-list for extension type_name + 15, // [15:15] is the sub-list for extension extendee + 0, // [0:15] is the sub-list for field type_name } func init() { file_controller_storage_credential_vault_store_v1_vault_proto_init() } @@ -1313,7 +1579,7 @@ func file_controller_storage_credential_vault_store_v1_vault_proto_init() { } } file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Credential); i { + switch v := v.(*SSHCertificateCredentialLibrary); i { case 0: return &v.state case 1: @@ -1325,7 +1591,7 @@ func file_controller_storage_credential_vault_store_v1_vault_proto_init() { } } file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UsernamePasswordOverride); i { + switch v := v.(*Credential); i { case 0: return &v.state case 1: @@ -1337,6 +1603,18 @@ func file_controller_storage_credential_vault_store_v1_vault_proto_init() { } } file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UsernamePasswordOverride); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SshPrivateKeyOverride); i { case 0: return &v.state @@ -1355,7 +1633,7 @@ func file_controller_storage_credential_vault_store_v1_vault_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controller_storage_credential_vault_store_v1_vault_proto_rawDesc, NumEnums: 0, - NumMessages: 7, + NumMessages: 8, NumExtensions: 0, NumServices: 0, }, diff --git a/internal/credential/vault/testing.go b/internal/credential/vault/testing.go index 817492655..d87171f84 100644 --- a/internal/credential/vault/testing.go +++ b/internal/credential/vault/testing.go @@ -140,6 +140,38 @@ func TestCredentialLibraries(t testing.TB, conn *db.DB, _ wrapping.Wrapper, stor return libs } +// TestSSHCertificateCredentialLibraries creates count number of vault credential +// libraries in the provided DB with the provided store id. If any errors +// are encountered during the creation of the credential libraries, the +// test will fail. +func TestSSHCertificateCredentialLibraries(t testing.TB, conn *db.DB, _ wrapping.Wrapper, storeId string, count int) []*SSHCertificateCredentialLibrary { + t.Helper() + assert, require := assert.New(t), require.New(t) + w := db.New(conn) + var libs []*SSHCertificateCredentialLibrary + + for i := 0; i < count; i++ { + lib, err := NewSSHCertificateCredentialLibrary(storeId, fmt.Sprintf("vault/path%d", i), "username", withKeyType(KeyTypeEd25519)) + assert.NoError(err) + require.NotNil(lib) + id, err := newSSHCertificateCredentialLibraryId() + assert.NoError(err) + require.NotEmpty(id) + lib.PublicId = id + + ctx := context.Background() + _, err2 := w.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{}, + func(_ db.Reader, iw db.Writer) error { + return iw.Create(ctx, lib) + }, + ) + + require.NoError(err2) + libs = append(libs, lib) + } + return libs +} + // TestCredentials creates count number of vault credentials in the provided DB with // the provided library id and session id. If any errors are encountered // during the creation of the credentials, the test will fail. diff --git a/internal/db/schema/migrations/oss/postgres/39/01_static_ssh_private_key_creds.up.sql b/internal/db/schema/migrations/oss/postgres/39/01_static_ssh_private_key_creds.up.sql index 6b5673ad2..b19952e69 100644 --- a/internal/db/schema/migrations/oss/postgres/39/01_static_ssh_private_key_creds.up.sql +++ b/internal/db/schema/migrations/oss/postgres/39/01_static_ssh_private_key_creds.up.sql @@ -6,6 +6,7 @@ begin; -- Add new constraint that only allows known types -- This replaces the constraint defined in 36/01_credential_type_up + -- Replaced in 63/01_credential_vault_ssh_cert_library alter table credential_type_enm add constraint only_predefined_credential_types_allowed check ( diff --git a/internal/db/schema/migrations/oss/postgres/63/01_credential_vault_ssh_cert_library.up.sql b/internal/db/schema/migrations/oss/postgres/63/01_credential_vault_ssh_cert_library.up.sql new file mode 100644 index 000000000..6ec697947 --- /dev/null +++ b/internal/db/schema/migrations/oss/postgres/63/01_credential_vault_ssh_cert_library.up.sql @@ -0,0 +1,170 @@ +begin; + + -- drop constraint so we can add ssh_certificate + alter table credential_type_enm + drop constraint only_predefined_credential_types_allowed; + + -- Add new constraint that only allows known types + -- This replaces the constraint defined in 39/01_static_ssh_private_key_creds_up + alter table credential_type_enm + add constraint only_predefined_credential_types_allowed + check ( + name in ( + 'unspecified', + 'username_password', + 'ssh_private_key', + 'ssh_certificate' + ) + ); + + insert into credential_type_enm (name) + values ('ssh_certificate'); + + create table credential_vault_ssh_cert_key_type_enm ( + name text primary key + constraint only_predefined_key_types_allowed + check ( + name in ( + 'ed25519', + 'ecdsa', + 'rsa' + ) + ) + ); + comment on table credential_vault_ssh_cert_key_type_enm is + 'credential_vault_ssh_cert_key_type_enm is an enumeration table for the ssh key type. '; + + insert into credential_vault_ssh_cert_key_type_enm (name) + values + ('ed25519'), + ('ecdsa'), + ('rsa'); + + create table credential_vault_ssh_cert_key_bits_enm ( + bits int primary key + constraint only_predefined_key_bits_allowed + check ( + bits in ( + 0, + 2048, + 3072, + 4096, + 256, + 384, + 521 + ) + ) + ); + comment on table credential_vault_ssh_cert_key_bits_enm is + 'credential_vault_ssh_cert_key_bits_enm is an enumeration table for the ssh key bits. '; + + insert into credential_vault_ssh_cert_key_bits_enm (bits) + values + (0), + (2048), + (3072), + (4096), + (256), + (384), + (521); + + create table credential_vault_ssh_cert_valid_key_type_key_bits ( + key_type text not null + constraint credential_vault_ssh_cert_key_type_enm_fkey + references credential_vault_ssh_cert_key_type_enm (name), + key_bits int not null + constraint credential_vault_ssh_cert_key_bits_enm_fkey + references credential_vault_ssh_cert_key_bits_enm (bits), + constraint credential_vault_ssh_cert_valid_key_type_key_bits_uq + unique(key_type, key_bits) + ); + + insert into credential_vault_ssh_cert_valid_key_type_key_bits (key_type, key_bits) + values + ('ed25519', 0), + ('ecdsa', 256), + ('ecdsa', 384), + ('ecdsa', 521), + ('rsa', 2048), + ('rsa', 3072), + ('rsa', 4096); + + create table credential_vault_ssh_cert_library ( + public_id wt_public_id primary key, + store_id wt_public_id not null + constraint credential_vault_store_fkey + references credential_vault_store (public_id) + on delete cascade + on update cascade, + name wt_name, + description wt_description, + create_time wt_timestamp, + update_time wt_timestamp, + version wt_version, + vault_path text not null + constraint vault_path_must_not_be_empty + check(length(trim(vault_path)) > 0) + constraint vault_path_must_be_sign_or_issue + check(vault_path ~ '^.+\/(sign|issue)\/[^\/\\\s]+$'), + username text not null + constraint username_must_not_be_empty + check(length(trim(username)) > 0), + key_type text not null, + key_bits int not null, + ttl text, + key_id text, + critical_options bytea, + extensions bytea, + credential_type text, + project_id wt_public_id not null, + constraint credential_vault_ssh_cert_library_store_id_name_uq + unique(store_id, name), + constraint credential_vault_ssh_cert_library_store_id_public_id_uq + unique(store_id, public_id), + constraint credential_library_fkey + foreign key (project_id, store_id, public_id, credential_type) + references credential_library (project_id, store_id, public_id, credential_type) + on delete cascade + on update cascade, + constraint credential_vault_ssh_cert_valid_key_type_key_bits_fkey + foreign key (key_type, key_bits) + references credential_vault_ssh_cert_valid_key_type_key_bits(key_type, key_bits) + ); + comment on table credential_vault_ssh_cert_library is + 'credential_vault_ssh_cert_library a credential library that issues credentials from a vault ssh secret backend.'; + + create function default_ssh_certificate_credential_type() returns trigger + as $$ + begin + if new.credential_type is distinct from 'ssh_certificate' then + raise warning 'credential_vault_ssh_cert_library only supports ssh_certificate credentials'; + new.credential_type = 'ssh_certificate'; + end if; + return new; + end; + $$ language plpgsql; + comment on function default_ssh_certificate_credential_type is + 'default_ssh_certificate_credential_type ensures the credential_type is set to ssh_certificate'; + + create trigger default_ssh_certificate_credential_type before insert on credential_vault_ssh_cert_library + for each row execute procedure default_ssh_certificate_credential_type(); + create trigger insert_credential_library_subtype before insert on credential_vault_ssh_cert_library + for each row execute procedure insert_credential_library_subtype(); + create trigger default_create_time_column before insert on credential_vault_ssh_cert_library + for each row execute procedure default_create_time(); + create trigger delete_credential_library_subtype after delete on credential_vault_ssh_cert_library + for each row execute procedure delete_credential_library_subtype(); + create trigger immutable_columns before update on credential_vault_ssh_cert_library + for each row execute procedure immutable_columns('public_id', 'store_id', 'project_id', 'credential_type', 'create_time'); + create trigger update_time_column before update on credential_vault_ssh_cert_library + for each row execute procedure update_time_column(); + create trigger update_version_column after update on credential_vault_ssh_cert_library + for each row execute procedure update_version_column(); + create trigger before_insert_credential_vault_library before insert on credential_vault_ssh_cert_library + for each row execute procedure before_insert_credential_vault_library(); + + insert into oplog_ticket (name, version) + values + ('credential_vault_ssh_cert_library', 1); + +commit; diff --git a/internal/db/sqltest/tests/credential/vault/credential_vault_ssh_cert_library.sql b/internal/db/sqltest/tests/credential/vault/credential_vault_ssh_cert_library.sql new file mode 100644 index 000000000..f26ca74f0 --- /dev/null +++ b/internal/db/sqltest/tests/credential/vault/credential_vault_ssh_cert_library.sql @@ -0,0 +1,148 @@ +begin; + select plan(16); + select wtt_load('widgets', 'iam', 'kms', 'auth', 'hosts', 'targets', 'credentials'); + + -- validate default values + prepare insert_valid as + insert into credential_vault_ssh_cert_library + (store_id, public_id, vault_path, username, key_type, key_bits) + values + ('vs_______wvs', 'vl______vsc1', '/ssh/sign/foo', 'bar', 'ed25519', 0), + ('vs_______wvs', 'vl______vsc2', '/ssh/sign/foo', 'bar', 'ecdsa', 256), + ('vs_______wvs', 'vl______vsc3', '/ssh/sign/foo', 'bar', 'ecdsa', 384), + ('vs_______wvs', 'vl______vsc4', '/ssh/sign/foo', 'bar', 'ecdsa', 521), + ('vs_______wvs', 'vl______vsc5', '/ssh/sign/foo', 'bar', 'rsa', 2048), + ('vs_______wvs', 'vl______vsc6', '/ssh/sign/foo', 'bar', 'rsa', 3072), + ('vs_______wvs', 'vl______vsc7', '/ssh/sign/foo', 'bar', 'rsa', 4096); + + prepare select_vault_ssh_cert_libraries as + select public_id::text, store_id::text, name::text, description::text, vault_path, username, key_type, key_bits, ttl, key_id, critical_options, extensions, credential_type, project_id::text + from credential_vault_ssh_cert_library + where public_id like 'vl______vsc%' + order by public_id; + + prepare select_libraries as + select public_id::text, store_id::text, credential_type, project_id::text + from credential_library + where public_id in ('vl______vsc1', 'vl______vsc3') + order by public_id; + + select lives_ok('insert_valid'); + select results_eq( + 'select_vault_ssh_cert_libraries', + $$VALUES + ('vl______vsc1', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'ed25519', 0, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'), + ('vl______vsc2', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'ecdsa', 256, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'), + ('vl______vsc3', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'ecdsa', 384, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'), + ('vl______vsc4', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'ecdsa', 521, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'), + ('vl______vsc5', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'rsa', 2048, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'), + ('vl______vsc6', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'rsa', 3072, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'), + ('vl______vsc7', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'rsa', 4096, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget')$$ + ); + + + prepare insert_invalid_key_type as + insert into credential_vault_ssh_cert_library + (store_id, public_id, vault_path, username, key_type, key_bits) + values + ('vs_______wvs', 'vl______vsc8', '/ssh/sign/foo', 'bar', 'unknown', 256); + select throws_ok('insert_invalid_key_type', 'insert or update on table "credential_vault_ssh_cert_library" violates foreign key constraint "credential_vault_ssh_cert_valid_key_type_key_bits_fkey"'); + + prepare insert_invalid_key_bits as + insert into credential_vault_ssh_cert_library + (store_id, public_id, vault_path, username, key_type, key_bits) + values + ('vs_______wvs', 'vl______vsc8', '/ssh/sign/foo', 'bar', 'rsa', 99); + select throws_ok('insert_invalid_key_bits', 'insert or update on table "credential_vault_ssh_cert_library" violates foreign key constraint "credential_vault_ssh_cert_valid_key_type_key_bits_fkey"'); + + prepare insert_invalid_rsa_key_type_bits_combo as + insert into credential_vault_ssh_cert_library + (store_id, public_id, vault_path, username, key_type, key_bits) + values + ('vs_______wvs', 'vl______vsc8', '/ssh/sign/foo', 'bar', 'rsa', 384); + select throws_ok('insert_invalid_rsa_key_type_bits_combo', 'insert or update on table "credential_vault_ssh_cert_library" violates foreign key constraint "credential_vault_ssh_cert_valid_key_type_key_bits_fkey"'); + + prepare insert_invalid_ecdsa_key_type_bits_combo as + insert into credential_vault_ssh_cert_library + (store_id, public_id, vault_path, username, key_type, key_bits) + values + ('vs_______wvs', 'vl______vsc8', '/ssh/sign/foo', 'bar', 'ecdsa', 2048); + select throws_ok('insert_invalid_ecdsa_key_type_bits_combo', 'insert or update on table "credential_vault_ssh_cert_library" violates foreign key constraint "credential_vault_ssh_cert_valid_key_type_key_bits_fkey"'); + + prepare insert_invalid_ed25519_key_type_bits_combo as + insert into credential_vault_ssh_cert_library + (store_id, public_id, vault_path, username, key_type, key_bits) + values + ('vs_______wvs', 'vl______vsc8', '/ssh/sign/foo', 'bar', 'ed25519', 2048); + select throws_ok('insert_invalid_ed25519_key_type_bits_combo', 'insert or update on table "credential_vault_ssh_cert_library" violates foreign key constraint "credential_vault_ssh_cert_valid_key_type_key_bits_fkey"'); + + prepare insert_invalid_vault_path as + insert into credential_vault_ssh_cert_library + (store_id, public_id, vault_path, username, key_type, key_bits) + values + ('vs_______wvs', 'vl______vsc8', '/ssh/not-sign/foo', 'bar', 'ed25519', 0); + select throws_ok('insert_invalid_vault_path', 'new row for relation "credential_vault_ssh_cert_library" violates check constraint "vault_path_must_be_sign_or_issue"'); + + prepare insert_invalid_vault_path_empty as + insert into credential_vault_ssh_cert_library + (store_id, public_id, vault_path, username, key_type, key_bits) + values + ('vs_______wvs', 'vl______vsc8', '', 'bar', 'ed25519', 0); + select throws_ok('insert_invalid_vault_path', 'new row for relation "credential_vault_ssh_cert_library" violates check constraint "vault_path_must_be_sign_or_issue"'); + + prepare insert_invalid_username as + insert into credential_vault_ssh_cert_library + (store_id, public_id, vault_path, username, key_type, key_bits) + values + ('vs_______wvs', 'vl______vsc8', '/ssh/sign/foo', '', 'ed25519', 0); + select throws_ok('insert_invalid_username', 'new row for relation "credential_vault_ssh_cert_library" violates check constraint "username_must_not_be_empty"'); + + prepare insert_invalid_credential_type as + insert into credential_vault_ssh_cert_library + (store_id, public_id, vault_path, username, credential_type, key_type, key_bits) + values + ('vs_______wvs', 'vl______vsc8', '/ssh/sign/foo', 'bar', 'username_password', 'ed25519', 0); + select lives_ok('insert_invalid_credential_type'); + select results_eq( + 'select_vault_ssh_cert_libraries', + $$VALUES + ('vl______vsc1', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'ed25519', 0, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'), + ('vl______vsc2', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'ecdsa', 256, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'), + ('vl______vsc3', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'ecdsa', 384, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'), + ('vl______vsc4', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'ecdsa', 521, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'), + ('vl______vsc5', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'rsa', 2048, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'), + ('vl______vsc6', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'rsa', 3072, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'), + ('vl______vsc7', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'rsa', 4096, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'), + ('vl______vsc8', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'ed25519', 0, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget')$$ + ); + + select results_eq( + 'select_libraries', + $$VALUES + ('vl______vsc1', 'vs_______wvs', 'ssh_certificate', 'p____bwidget'), + ('vl______vsc3', 'vs_______wvs', 'ssh_certificate', 'p____bwidget')$$ + ); + + prepare delete_ssh_cred_library as + delete from credential_vault_ssh_cert_library where public_id = 'vl______vsc3'; + + select lives_ok('delete_ssh_cred_library'); + select results_eq( + 'select_vault_ssh_cert_libraries', + $$VALUES + ('vl______vsc1', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'ed25519', 0, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'), + ('vl______vsc2', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'ecdsa', 256, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'), + ('vl______vsc4', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'ecdsa', 521, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'), + ('vl______vsc5', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'rsa', 2048, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'), + ('vl______vsc6', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'rsa', 3072, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'), + ('vl______vsc7', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'rsa', 4096, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'), + ('vl______vsc8', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'ed25519', 0, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget')$$ + ); + + select results_eq( + 'select_libraries', + $$VALUES + ('vl______vsc1', 'vs_______wvs', 'ssh_certificate', 'p____bwidget')$$ + ); + +rollback; diff --git a/internal/proto/controller/storage/credential/vault/store/v1/vault.proto b/internal/proto/controller/storage/credential/vault/store/v1/vault.proto index 1022bf44b..965ffb53d 100644 --- a/internal/proto/controller/storage/credential/vault/store/v1/vault.proto +++ b/internal/proto/controller/storage/credential/vault/store/v1/vault.proto @@ -265,6 +265,107 @@ message CredentialLibrary { string credential_type = 11; } +message SSHCertificateCredentialLibrary { + // public_id is a surrogate key suitable for use in a public API. + // @inject_tag: `gorm:"primary_key"` + string public_id = 1; + + // create_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp create_time = 2; + + // update_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp update_time = 3; + + // name is optional. If set, it must be unique within project_id. + // @inject_tag: `gorm:"default:null"` + string name = 4 [(custom_options.v1.mask_mapping) = { + this: "Name" + that: "name" + }]; + + // description is optional. + // @inject_tag: `gorm:"default:null"` + string description = 5 [(custom_options.v1.mask_mapping) = { + this: "Description" + that: "description" + }]; + + // store_id of the owning vault credential store. + // It must be set. + // @inject_tag: `gorm:"not_null"` + string store_id = 6; + + // version allows optimistic locking of the resource. + // @inject_tag: `gorm:"default:null"` + uint32 version = 7; + + // vault_path is the path in Vault to request credentials from. + // It must be set. + // @inject_tag: `gorm:"not_null"` + string vault_path = 8 [(custom_options.v1.mask_mapping) = { + this: "VaultPath" + that: "attributes.path" + }]; + + // username is the username to use when making an SSH connection. + // It must be set. + // @inject_tag: `gorm:"not_null"` + string username = 9 [(custom_options.v1.mask_mapping) = { + this: "Username" + that: "attributes.username" + }]; + + // key_type specifies the key type to use when generating an SSH private key. + // Values must be "rsa", "ed25519", or "ecdsa". + // @inject_tag: `gorm:"not_null"` + string key_type = 10 [(custom_options.v1.mask_mapping) = { + this: "KeyType" + that: "attributes.key_type" + }]; + + // key_bits specifies the number of bits to use to generate an SSH private key. + // Not used if key_type is ed25519. + // @inject_tag: `gorm:"not_null"` + uint32 key_bits = 11 [(custom_options.v1.mask_mapping) = { + this: "KeyBits" + that: "attributes.key_bits" + }]; + + // ttl specifies the requested time to live for the certificate. + // @inject_tag: `gorm:"default:null"` + string ttl = 12 [(custom_options.v1.mask_mapping) = { + this: "Ttl" + that: "attributes.ttl" + }]; + + // key_id specifies the key id that the created certificate should have. + // @inject_tag: `gorm:"default:null"` + string key_id = 13 [(custom_options.v1.mask_mapping) = { + this: "KeyId" + that: "attributes.key_id" + }]; + + // critical_options specifies a map of the critical options that the certificate should be signed for. + // @inject_tag: `gorm:"default:null"` + string critical_options = 14 [(custom_options.v1.mask_mapping) = { + this: "CriticalOptions" + that: "attributes.critical_options" + }]; + + // extensions specifies a map of the extensions that the certificate should be signed for. + // @inject_tag: `gorm:"default:null"` + string extensions = 15 [(custom_options.v1.mask_mapping) = { + this: "Extensions" + that: "attributes.extensions" + }]; + + // credential_type is always ssh_certificate + // @inject_tag: `gorm:"default:null"` + string credential_type = 16; +} + message Credential { // public_id is a surrogate key suitable for use in a public API. // @inject_tag: `gorm:"primary_key"`