Merge pull request #6207 from hashicorp/llb-password-credential-type
Password credential type
This commit is contained in:
66 files changed
+5784
-947
No files matched your search
@@ -179,6 +179,18 @@ func WithJsonCredentialObject(inObject map[string]interface{}) Option {
|
||||
}
|
||||
}
|
||||
|
||||
func WithPasswordCredentialPassword(inPassword string) Option {
|
||||
return func(o *options) {
|
||||
raw, ok := o.postMap["attributes"]
|
||||
if !ok {
|
||||
raw = any(map[string]any{})
|
||||
}
|
||||
val := raw.(map[string]any)
|
||||
val["password"] = inPassword
|
||||
o.postMap["attributes"] = val
|
||||
}
|
||||
}
|
||||
|
||||
func WithUsernamePasswordCredentialPassword(inPassword string) Option {
|
||||
return func(o *options) {
|
||||
raw, ok := o.postMap["attributes"]
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
// Code generated by "make api"; DO NOT EDIT.
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package credentials
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
type PasswordAttributes struct {
|
||||
Password string `json:"password,omitempty"`
|
||||
PasswordHmac string `json:"password_hmac,omitempty"`
|
||||
}
|
||||
|
||||
func AttributesMapToPasswordAttributes(in map[string]any) (*PasswordAttributes, error) {
|
||||
if in == nil {
|
||||
return nil, fmt.Errorf("nil input map")
|
||||
}
|
||||
var out PasswordAttributes
|
||||
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
||||
Result: &out,
|
||||
TagName: "json",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating mapstructure decoder: %w", err)
|
||||
}
|
||||
if err := dec.Decode(in); err != nil {
|
||||
return nil, fmt.Errorf("error decoding: %w", err)
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
func (pt *Credential) GetPasswordAttributes() (*PasswordAttributes, error) {
|
||||
if pt.Type != "password" {
|
||||
return nil, fmt.Errorf("asked to fetch %s-type attributes but credential is of type %s", "password", pt.Type)
|
||||
}
|
||||
return AttributesMapToPasswordAttributes(pt.Attributes)
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
const (
|
||||
usernamePasswordCredentialType = "username_password"
|
||||
passwordCredentialType = "password"
|
||||
sshPrivateKeyCredentialType = "ssh_private_key"
|
||||
)
|
||||
|
||||
@@ -26,6 +27,16 @@ type UsernamePassword struct {
|
||||
Consumed bool
|
||||
}
|
||||
|
||||
// Password contains a password credential
|
||||
type Password struct {
|
||||
Password string `mapstructure:"password"`
|
||||
|
||||
Raw *targets.SessionCredential
|
||||
// Consumed can be set by the caller to indicate that the credential has
|
||||
// been used, e.g. displayed to the user
|
||||
Consumed bool
|
||||
}
|
||||
|
||||
// SshPrivateKey contains the username and private key with optional passphrase
|
||||
// for the key
|
||||
type SshPrivateKey struct {
|
||||
@@ -41,6 +52,7 @@ type SshPrivateKey struct {
|
||||
|
||||
type Credentials struct {
|
||||
UsernamePassword []UsernamePassword
|
||||
Password []Password
|
||||
SshPrivateKey []SshPrivateKey
|
||||
// Unspecified are credentials that do not match one of the types above
|
||||
Unspecified []*targets.SessionCredential
|
||||
@@ -62,6 +74,11 @@ func (c Credentials) UnconsumedSessionCredentials() []*targets.SessionCredential
|
||||
out = append(out, c.Raw)
|
||||
}
|
||||
}
|
||||
for _, c := range c.Password {
|
||||
if !c.Consumed {
|
||||
out = append(out, c.Raw)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -76,6 +93,7 @@ func ParseCredentials(creds []*targets.SessionCredential) (Credentials, error) {
|
||||
}
|
||||
|
||||
var upCred UsernamePassword
|
||||
var pCred Password
|
||||
var spkCred SshPrivateKey
|
||||
switch cred.CredentialSource.CredentialType {
|
||||
case usernamePasswordCredentialType:
|
||||
@@ -90,6 +108,18 @@ func ParseCredentials(creds []*targets.SessionCredential) (Credentials, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
case passwordCredentialType:
|
||||
// Decode attributes from credential struct
|
||||
if err := mapstructure.Decode(cred.Credential, &pCred); err != nil {
|
||||
return Credentials{}, err
|
||||
}
|
||||
|
||||
if pCred.Password != "" {
|
||||
pCred.Raw = cred
|
||||
out.Password = append(out.Password, pCred)
|
||||
continue
|
||||
}
|
||||
|
||||
case sshPrivateKeyCredentialType:
|
||||
// Decode attributes from credential struct
|
||||
if err := mapstructure.Decode(cred.Credential, &spkCred); err != nil {
|
||||
@@ -116,6 +146,16 @@ func ParseCredentials(creds []*targets.SessionCredential) (Credentials, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Attempt unmarshaling into password creds
|
||||
if err := mapstructure.Decode(cred.Secret.Decoded, &pCred); err != nil {
|
||||
return Credentials{}, err
|
||||
}
|
||||
if pCred.Password != "" {
|
||||
pCred.Raw = cred
|
||||
out.Password = append(out.Password, pCred)
|
||||
continue
|
||||
}
|
||||
|
||||
// Attempt unmarshaling into ssh private key creds
|
||||
if err := mapstructure.Decode(cred.Secret.Decoded, &spkCred); err != nil {
|
||||
return Credentials{}, err
|
||||
|
||||
@@ -29,6 +29,15 @@ var (
|
||||
},
|
||||
}
|
||||
|
||||
typedPassword = &targets.SessionCredential{
|
||||
CredentialSource: &targets.CredentialSource{
|
||||
CredentialType: passwordCredentialType,
|
||||
},
|
||||
Credential: map[string]any{
|
||||
"password": "pass",
|
||||
},
|
||||
}
|
||||
|
||||
typedSshPrivateKey = &targets.SessionCredential{
|
||||
CredentialSource: &targets.CredentialSource{
|
||||
CredentialType: sshPrivateKeyCredentialType,
|
||||
@@ -75,6 +84,17 @@ var (
|
||||
},
|
||||
}
|
||||
|
||||
vaultPassword = &targets.SessionCredential{
|
||||
CredentialSource: &targets.CredentialSource{
|
||||
Type: vaultGenericLibrarySubtype,
|
||||
},
|
||||
Secret: &targets.SessionSecret{
|
||||
Decoded: map[string]any{
|
||||
"password": "vault-decoded-pass",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
vaultSshPrivateKey = &targets.SessionCredential{
|
||||
CredentialSource: &targets.CredentialSource{
|
||||
Type: vaultGenericLibrarySubtype,
|
||||
@@ -119,6 +139,17 @@ var (
|
||||
},
|
||||
}
|
||||
|
||||
staticPassword = &targets.SessionCredential{
|
||||
CredentialSource: &targets.CredentialSource{
|
||||
Type: staticSubtype,
|
||||
},
|
||||
Secret: &targets.SessionSecret{
|
||||
Decoded: map[string]any{
|
||||
"password": "static-decoded-pass",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
staticSshPrivateKey = &targets.SessionCredential{
|
||||
CredentialSource: &targets.CredentialSource{
|
||||
Type: staticSubtype,
|
||||
@@ -215,6 +246,21 @@ func Test_parseCredentials(t *testing.T) {
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "password-typed",
|
||||
creds: []*targets.SessionCredential{
|
||||
typedPassword,
|
||||
},
|
||||
wantCreds: Credentials{
|
||||
Password: []Password{
|
||||
{
|
||||
Password: "pass",
|
||||
Raw: typedPassword,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ssh-private-key-typed",
|
||||
creds: []*targets.SessionCredential{
|
||||
@@ -247,6 +293,21 @@ func Test_parseCredentials(t *testing.T) {
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "vault-password-decoded",
|
||||
creds: []*targets.SessionCredential{
|
||||
vaultPassword,
|
||||
},
|
||||
wantCreds: Credentials{
|
||||
Password: []Password{
|
||||
{
|
||||
Password: "vault-decoded-pass",
|
||||
Raw: vaultPassword,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "vault-private-key-decoded",
|
||||
creds: []*targets.SessionCredential{
|
||||
@@ -343,6 +404,21 @@ func Test_parseCredentials(t *testing.T) {
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "static-password-decoded",
|
||||
creds: []*targets.SessionCredential{
|
||||
staticPassword,
|
||||
},
|
||||
wantCreds: Credentials{
|
||||
Password: []Password{
|
||||
{
|
||||
Password: "static-decoded-pass",
|
||||
Raw: staticPassword,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "static-private-key-decoded",
|
||||
creds: []*targets.SessionCredential{
|
||||
@@ -388,7 +464,7 @@ func Test_parseCredentials(t *testing.T) {
|
||||
creds: []*targets.SessionCredential{
|
||||
staticSshPrivateKey, UnspecifiedCred1, vaultSshPrivateKey, typedUsernamePassword,
|
||||
UnspecifiedCred, vaultUsernamePassword, typedSshPrivateKey, staticUsernamePassword,
|
||||
staticKv,
|
||||
staticKv, typedPassword, vaultPassword, staticPassword,
|
||||
},
|
||||
wantCreds: Credentials{
|
||||
SshPrivateKey: []SshPrivateKey{
|
||||
@@ -425,6 +501,20 @@ func Test_parseCredentials(t *testing.T) {
|
||||
Raw: typedUsernamePassword,
|
||||
},
|
||||
},
|
||||
Password: []Password{
|
||||
{
|
||||
Password: "static-decoded-pass",
|
||||
Raw: staticPassword,
|
||||
},
|
||||
{
|
||||
Password: "vault-decoded-pass",
|
||||
Raw: vaultPassword,
|
||||
},
|
||||
{
|
||||
Password: "pass",
|
||||
Raw: typedPassword,
|
||||
},
|
||||
},
|
||||
Unspecified: []*targets.SessionCredential{
|
||||
UnspecifiedCred, UnspecifiedCred1, staticKv,
|
||||
},
|
||||
@@ -447,6 +537,7 @@ func Test_parseCredentials(t *testing.T) {
|
||||
assert.ElementsMatch(tt.wantCreds.UsernamePassword, creds.UsernamePassword)
|
||||
assert.ElementsMatch(tt.wantCreds.SshPrivateKey, creds.SshPrivateKey)
|
||||
assert.ElementsMatch(tt.wantCreds.Unspecified, creds.Unspecified)
|
||||
assert.ElementsMatch(tt.wantCreds.Password, creds.Password)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -507,6 +598,29 @@ func Test_unconsumedSessionCredentials(t *testing.T) {
|
||||
},
|
||||
wantCreds: nil,
|
||||
},
|
||||
{
|
||||
name: "p",
|
||||
creds: Credentials{
|
||||
Password: []Password{
|
||||
{
|
||||
Raw: vaultPassword,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantCreds: []*targets.SessionCredential{vaultPassword},
|
||||
},
|
||||
{
|
||||
name: "p-consumed",
|
||||
creds: Credentials{
|
||||
Password: []Password{
|
||||
{
|
||||
Raw: vaultPassword,
|
||||
Consumed: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantCreds: nil,
|
||||
},
|
||||
{
|
||||
name: "Unspecified",
|
||||
creds: Credentials{
|
||||
@@ -542,10 +656,23 @@ func Test_unconsumedSessionCredentials(t *testing.T) {
|
||||
Consumed: true,
|
||||
},
|
||||
},
|
||||
Password: []Password{
|
||||
{
|
||||
Raw: staticPassword,
|
||||
},
|
||||
{
|
||||
Raw: vaultPassword,
|
||||
},
|
||||
{
|
||||
Raw: typedPassword,
|
||||
Consumed: true,
|
||||
},
|
||||
},
|
||||
Unspecified: []*targets.SessionCredential{UnspecifiedCred, UnspecifiedCred1},
|
||||
},
|
||||
wantCreds: []*targets.SessionCredential{
|
||||
vaultSshPrivateKey, typedSshPrivateKey, vaultUsernamePassword, UnspecifiedCred, UnspecifiedCred1,
|
||||
staticPassword, vaultPassword,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -579,6 +706,20 @@ func Test_unconsumedSessionCredentials(t *testing.T) {
|
||||
Consumed: true,
|
||||
},
|
||||
},
|
||||
Password: []Password{
|
||||
{
|
||||
Raw: staticPassword,
|
||||
Consumed: true,
|
||||
},
|
||||
{
|
||||
Raw: vaultPassword,
|
||||
Consumed: true,
|
||||
},
|
||||
{
|
||||
Raw: typedPassword,
|
||||
Consumed: true,
|
||||
},
|
||||
},
|
||||
Unspecified: []*targets.SessionCredential{UnspecifiedCred, UnspecifiedCred1},
|
||||
},
|
||||
wantCreds: []*targets.SessionCredential{
|
||||
@@ -610,11 +751,23 @@ func Test_unconsumedSessionCredentials(t *testing.T) {
|
||||
Raw: typedUsernamePassword,
|
||||
},
|
||||
},
|
||||
Password: []Password{
|
||||
{
|
||||
Raw: staticPassword,
|
||||
},
|
||||
{
|
||||
Raw: vaultPassword,
|
||||
},
|
||||
{
|
||||
Raw: typedPassword,
|
||||
},
|
||||
},
|
||||
Unspecified: []*targets.SessionCredential{UnspecifiedCred, UnspecifiedCred1},
|
||||
},
|
||||
wantCreds: []*targets.SessionCredential{
|
||||
staticSshPrivateKey, UnspecifiedCred1, vaultSshPrivateKey, typedUsernamePassword,
|
||||
UnspecifiedCred, vaultUsernamePassword, typedSshPrivateKey, staticUsernamePassword,
|
||||
staticPassword, vaultPassword, typedPassword,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
// Code generated by "make api"; DO NOT EDIT.
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package sessionrecordings
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
type PasswordCredentialAttributes struct {
|
||||
PasswordHmac string `json:"password_hmac,omitempty"`
|
||||
}
|
||||
|
||||
func AttributesMapToPasswordCredentialAttributes(in map[string]any) (*PasswordCredentialAttributes, error) {
|
||||
if in == nil {
|
||||
return nil, fmt.Errorf("nil input map")
|
||||
}
|
||||
var out PasswordCredentialAttributes
|
||||
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
||||
Result: &out,
|
||||
TagName: "json",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating mapstructure decoder: %w", err)
|
||||
}
|
||||
if err := dec.Decode(in); err != nil {
|
||||
return nil, fmt.Errorf("error decoding: %w", err)
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
func (pt *Credential) GetPasswordCredentialAttributes() (*PasswordCredentialAttributes, error) {
|
||||
if pt.Type != "password" {
|
||||
return nil, fmt.Errorf("asked to fetch %s-type attributes but credential is of type %s", "password", pt.Type)
|
||||
}
|
||||
return AttributesMapToPasswordCredentialAttributes(pt.Attributes)
|
||||
}
|
||||
@@ -11,6 +11,7 @@ const (
|
||||
UnspecifiedCredentialType CredentialType = "unspecified"
|
||||
UsernamePasswordCredentialType CredentialType = "username_password"
|
||||
UsernamePasswordDomainCredentialType CredentialType = "username_password_domain"
|
||||
PasswordCredentialType CredentialType = "password"
|
||||
SshPrivateKeyCredentialType CredentialType = "ssh_private_key"
|
||||
SshCertificateCredentialType CredentialType = "ssh_certificate"
|
||||
JsonCredentialType CredentialType = "json"
|
||||
|
||||
@@ -83,6 +83,8 @@ const (
|
||||
// UsernamePasswordCredentialPreviousPrefix is the previous prefix for
|
||||
// username/password creds
|
||||
UsernamePasswordCredentialPreviousPrefix = "cred"
|
||||
// PasswordCredentialPrefix is the prefix for password creds
|
||||
PasswordCredentialPrefix = "credp"
|
||||
// SshPrivateKeyCredentialPrefix is the prefix for SSH private key creds
|
||||
SshPrivateKeyCredentialPrefix = "credspk"
|
||||
// JsonCredentialPrefix is the prefix for generic JSON creds
|
||||
@@ -266,6 +268,10 @@ var prefixToResourceType = map[string]ResourceInfo{
|
||||
Type: resource.Credential,
|
||||
Subtype: UnknownSubtype,
|
||||
},
|
||||
PasswordCredentialPrefix: {
|
||||
Type: resource.Credential,
|
||||
Subtype: UnknownSubtype,
|
||||
},
|
||||
SshPrivateKeyCredentialPrefix: {
|
||||
Type: resource.Credential,
|
||||
Subtype: UnknownSubtype,
|
||||
|
||||
@@ -692,6 +692,22 @@ var inputStructs = []*structInfo{
|
||||
versionEnabled: true,
|
||||
createResponseTypes: []string{CreateResponseType, ReadResponseType, UpdateResponseType, DeleteResponseType, ListResponseType},
|
||||
},
|
||||
{
|
||||
inProto: &credentials.PasswordAttributes{},
|
||||
outFile: "credentials/password_attributes.gen.go",
|
||||
subtypeName: "PasswordCredential",
|
||||
subtype: "password",
|
||||
fieldOverrides: []fieldInfo{
|
||||
{
|
||||
Name: "Password",
|
||||
SkipDefault: true,
|
||||
},
|
||||
},
|
||||
parentTypeName: "Credential",
|
||||
templates: []*template.Template{
|
||||
mapstructureConversionTemplate,
|
||||
},
|
||||
},
|
||||
{
|
||||
inProto: &credentials.UsernamePasswordAttributes{},
|
||||
outFile: "credentials/username_password_attributes.gen.go",
|
||||
@@ -1278,6 +1294,15 @@ var inputStructs = []*structInfo{
|
||||
inProto: &session_recordings.Credential{},
|
||||
outFile: "sessionrecordings/credential.gen.go",
|
||||
},
|
||||
{
|
||||
inProto: &session_recordings.PasswordCredentialAttributes{},
|
||||
outFile: "sessionrecordings/password_credential_attributes.gen.go",
|
||||
subtype: "password",
|
||||
parentTypeName: "Credential",
|
||||
templates: []*template.Template{
|
||||
mapstructureConversionTemplate,
|
||||
},
|
||||
},
|
||||
{
|
||||
inProto: &session_recordings.UsernamePasswordCredentialAttributes{},
|
||||
outFile: "sessionrecordings/username_password_credential_attributes.gen.go",
|
||||
|
||||
@@ -621,6 +621,12 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
||||
Func: "create",
|
||||
}
|
||||
}),
|
||||
"credentials create password": wrapper.Wrap(func() wrapper.WrappableCommand {
|
||||
return &credentialscmd.PasswordCommand{
|
||||
Command: base.NewCommand(ui, opts...),
|
||||
Func: "create",
|
||||
}
|
||||
}),
|
||||
"credentials create username-password": wrapper.Wrap(func() wrapper.WrappableCommand {
|
||||
return &credentialscmd.UsernamePasswordCommand{
|
||||
Command: base.NewCommand(ui, opts...),
|
||||
@@ -651,6 +657,12 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
||||
Func: "update",
|
||||
}
|
||||
}),
|
||||
"credentials update password": wrapper.Wrap(func() wrapper.WrappableCommand {
|
||||
return &credentialscmd.PasswordCommand{
|
||||
Command: base.NewCommand(ui, opts...),
|
||||
Func: "update",
|
||||
}
|
||||
}),
|
||||
"credentials update username-password": wrapper.Wrap(func() wrapper.WrappableCommand {
|
||||
return &credentialscmd.UsernamePasswordCommand{
|
||||
Command: base.NewCommand(ui, opts...),
|
||||
|
||||
@@ -49,13 +49,21 @@ func (r *redisFlags) buildArgs(c *Command, port, ip, _ string, creds proxy.Crede
|
||||
var username, password string
|
||||
|
||||
retCreds = creds
|
||||
if len(retCreds.UsernamePassword) > 0 {
|
||||
switch {
|
||||
case len(retCreds.UsernamePassword) > 0:
|
||||
// Mark credential as consumed, such that it is not printed to the user
|
||||
retCreds.UsernamePassword[0].Consumed = true
|
||||
|
||||
// Grab the first available username/password credential brokered
|
||||
username = retCreds.UsernamePassword[0].Username
|
||||
password = retCreds.UsernamePassword[0].Password
|
||||
|
||||
case len(retCreds.Password) > 0:
|
||||
// Mark credential as consumed, such that it is not printed to the user
|
||||
retCreds.Password[0].Consumed = true
|
||||
|
||||
// Grab the first available password credential brokered
|
||||
password = retCreds.Password[0].Password
|
||||
}
|
||||
|
||||
switch r.flagRedisStyle {
|
||||
@@ -69,12 +77,14 @@ func (r *redisFlags) buildArgs(c *Command, port, ip, _ string, creds proxy.Crede
|
||||
case username != "":
|
||||
args = append(args, "--user", username)
|
||||
case c.flagUsername != "":
|
||||
args = append(args, "--user", c.flagUsername, "--askpass")
|
||||
args = append(args, "--user", c.flagUsername)
|
||||
}
|
||||
|
||||
// Password is read by redis-cli via environment variable. The password disappears after the command exits.
|
||||
if password != "" {
|
||||
envs = append(envs, fmt.Sprintf("REDISCLI_AUTH=%s", password))
|
||||
} else {
|
||||
// prompt for password if it wasn't provided
|
||||
envs = append(envs, "--askpass")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,278 @@
|
||||
// Code generated by "make cli"; DO NOT EDIT.
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package credentialscmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/api"
|
||||
"github.com/hashicorp/boundary/api/credentials"
|
||||
"github.com/hashicorp/boundary/internal/cmd/base"
|
||||
"github.com/hashicorp/boundary/internal/cmd/common"
|
||||
"github.com/hashicorp/go-secure-stdlib/strutil"
|
||||
"github.com/mitchellh/cli"
|
||||
"github.com/posener/complete"
|
||||
)
|
||||
|
||||
func initPasswordFlags() {
|
||||
flagsOnce.Do(func() {
|
||||
extraFlags := extraPasswordActionsFlagsMapFunc()
|
||||
for k, v := range extraFlags {
|
||||
flagsPasswordMap[k] = append(flagsPasswordMap[k], v...)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
var (
|
||||
_ cli.Command = (*PasswordCommand)(nil)
|
||||
_ cli.CommandAutocomplete = (*PasswordCommand)(nil)
|
||||
)
|
||||
|
||||
type PasswordCommand struct {
|
||||
*base.Command
|
||||
|
||||
Func string
|
||||
|
||||
plural string
|
||||
|
||||
extraPasswordCmdVars
|
||||
}
|
||||
|
||||
func (c *PasswordCommand) AutocompleteArgs() complete.Predictor {
|
||||
initPasswordFlags()
|
||||
return complete.PredictAnything
|
||||
}
|
||||
|
||||
func (c *PasswordCommand) AutocompleteFlags() complete.Flags {
|
||||
initPasswordFlags()
|
||||
return c.Flags().Completions()
|
||||
}
|
||||
|
||||
func (c *PasswordCommand) Synopsis() string {
|
||||
if extra := extraPasswordSynopsisFunc(c); extra != "" {
|
||||
return extra
|
||||
}
|
||||
|
||||
synopsisStr := "credential"
|
||||
|
||||
synopsisStr = fmt.Sprintf("%s %s", "password-type", synopsisStr)
|
||||
|
||||
return common.SynopsisFunc(c.Func, synopsisStr)
|
||||
}
|
||||
|
||||
func (c *PasswordCommand) Help() string {
|
||||
initPasswordFlags()
|
||||
|
||||
var helpStr string
|
||||
helpMap := common.HelpMap("credential")
|
||||
|
||||
switch c.Func {
|
||||
|
||||
default:
|
||||
|
||||
helpStr = c.extraPasswordHelpFunc(helpMap)
|
||||
|
||||
}
|
||||
|
||||
// Keep linter from complaining if we don't actually generate code using it
|
||||
_ = helpMap
|
||||
return helpStr
|
||||
}
|
||||
|
||||
var flagsPasswordMap = map[string][]string{
|
||||
|
||||
"create": {"credential-store-id", "name", "description"},
|
||||
|
||||
"update": {"id", "name", "description", "version"},
|
||||
}
|
||||
|
||||
func (c *PasswordCommand) Flags() *base.FlagSets {
|
||||
if len(flagsPasswordMap[c.Func]) == 0 {
|
||||
return c.FlagSet(base.FlagSetNone)
|
||||
}
|
||||
|
||||
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
|
||||
f := set.NewFlagSet("Command Options")
|
||||
common.PopulateCommonFlags(c.Command, f, "password-type credential", flagsPasswordMap, c.Func)
|
||||
|
||||
extraPasswordFlagsFunc(c, set, f)
|
||||
|
||||
return set
|
||||
}
|
||||
|
||||
func (c *PasswordCommand) Run(args []string) int {
|
||||
initPasswordFlags()
|
||||
|
||||
switch c.Func {
|
||||
case "":
|
||||
return cli.RunResultHelp
|
||||
|
||||
}
|
||||
|
||||
c.plural = "password-type credential"
|
||||
switch c.Func {
|
||||
case "list":
|
||||
c.plural = "password-type credentials"
|
||||
}
|
||||
|
||||
f := c.Flags()
|
||||
|
||||
if err := f.Parse(args); err != nil {
|
||||
c.PrintCliError(err)
|
||||
return base.CommandUserError
|
||||
}
|
||||
|
||||
if strutil.StrListContains(flagsPasswordMap[c.Func], "id") && c.FlagId == "" {
|
||||
c.PrintCliError(errors.New("ID is required but not passed in via -id"))
|
||||
return base.CommandUserError
|
||||
}
|
||||
|
||||
var opts []credentials.Option
|
||||
|
||||
if strutil.StrListContains(flagsPasswordMap[c.Func], "credential-store-id") {
|
||||
switch c.Func {
|
||||
|
||||
case "create":
|
||||
if c.FlagCredentialStoreId == "" {
|
||||
c.PrintCliError(errors.New("CredentialStore ID must be passed in via -credential-store-id or BOUNDARY_CREDENTIAL_STORE_ID"))
|
||||
return base.CommandUserError
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
client, err := c.Client()
|
||||
if c.WrapperCleanupFunc != nil {
|
||||
defer func() {
|
||||
if err := c.WrapperCleanupFunc(); err != nil {
|
||||
c.PrintCliError(fmt.Errorf("Error cleaning kms wrapper: %w", err))
|
||||
}
|
||||
}()
|
||||
}
|
||||
if err != nil {
|
||||
c.PrintCliError(fmt.Errorf("Error creating API client: %w", err))
|
||||
return base.CommandCliError
|
||||
}
|
||||
credentialsClient := credentials.NewClient(client)
|
||||
|
||||
switch c.FlagName {
|
||||
case "":
|
||||
case "null":
|
||||
opts = append(opts, credentials.DefaultName())
|
||||
default:
|
||||
opts = append(opts, credentials.WithName(c.FlagName))
|
||||
}
|
||||
|
||||
switch c.FlagDescription {
|
||||
case "":
|
||||
case "null":
|
||||
opts = append(opts, credentials.DefaultDescription())
|
||||
default:
|
||||
opts = append(opts, credentials.WithDescription(c.FlagDescription))
|
||||
}
|
||||
|
||||
if c.FlagFilter != "" {
|
||||
opts = append(opts, credentials.WithFilter(c.FlagFilter))
|
||||
}
|
||||
|
||||
var version uint32
|
||||
|
||||
switch c.Func {
|
||||
|
||||
case "update":
|
||||
switch c.FlagVersion {
|
||||
case 0:
|
||||
opts = append(opts, credentials.WithAutomaticVersioning(true))
|
||||
default:
|
||||
version = uint32(c.FlagVersion)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ok := extraPasswordFlagsHandlingFunc(c, f, &opts); !ok {
|
||||
return base.CommandUserError
|
||||
}
|
||||
|
||||
var resp *api.Response
|
||||
var item *credentials.Credential
|
||||
|
||||
var createResult *credentials.CredentialCreateResult
|
||||
|
||||
var updateResult *credentials.CredentialUpdateResult
|
||||
|
||||
switch c.Func {
|
||||
|
||||
case "create":
|
||||
createResult, err = credentialsClient.Create(c.Context, "password", c.FlagCredentialStoreId, opts...)
|
||||
if exitCode := c.checkFuncError(err); exitCode > 0 {
|
||||
return exitCode
|
||||
}
|
||||
resp = createResult.GetResponse()
|
||||
item = createResult.GetItem()
|
||||
|
||||
case "update":
|
||||
updateResult, err = credentialsClient.Update(c.Context, c.FlagId, version, opts...)
|
||||
if exitCode := c.checkFuncError(err); exitCode > 0 {
|
||||
return exitCode
|
||||
}
|
||||
resp = updateResult.GetResponse()
|
||||
item = updateResult.GetItem()
|
||||
|
||||
}
|
||||
|
||||
resp, item, err = executeExtraPasswordActions(c, resp, item, err, credentialsClient, version, opts)
|
||||
if exitCode := c.checkFuncError(err); exitCode > 0 {
|
||||
return exitCode
|
||||
}
|
||||
|
||||
output, err := printCustomPasswordActionOutput(c)
|
||||
if err != nil {
|
||||
c.PrintCliError(err)
|
||||
return base.CommandUserError
|
||||
}
|
||||
if output {
|
||||
return base.CommandSuccess
|
||||
}
|
||||
|
||||
switch c.Func {
|
||||
|
||||
}
|
||||
|
||||
switch base.Format(c.UI) {
|
||||
case "table":
|
||||
c.UI.Output(printItemTable(item, resp))
|
||||
|
||||
case "json":
|
||||
if ok := c.PrintJsonItem(resp); !ok {
|
||||
return base.CommandCliError
|
||||
}
|
||||
}
|
||||
|
||||
return base.CommandSuccess
|
||||
}
|
||||
|
||||
func (c *PasswordCommand) checkFuncError(err error) int {
|
||||
if err == nil {
|
||||
return 0
|
||||
}
|
||||
if apiErr := api.AsServerError(err); apiErr != nil {
|
||||
c.PrintApiError(apiErr, fmt.Sprintf("Error from controller when performing %s on %s", c.Func, c.plural))
|
||||
return base.CommandApiError
|
||||
}
|
||||
c.PrintCliError(fmt.Errorf("Error trying to %s %s: %s", c.Func, c.plural, err.Error()))
|
||||
return base.CommandCliError
|
||||
}
|
||||
|
||||
var (
|
||||
extraPasswordActionsFlagsMapFunc = func() map[string][]string { return nil }
|
||||
extraPasswordSynopsisFunc = func(*PasswordCommand) string { return "" }
|
||||
extraPasswordFlagsFunc = func(*PasswordCommand, *base.FlagSets, *base.FlagSet) {}
|
||||
extraPasswordFlagsHandlingFunc = func(*PasswordCommand, *base.FlagSets, *[]credentials.Option) bool { return true }
|
||||
executeExtraPasswordActions = func(_ *PasswordCommand, inResp *api.Response, inItem *credentials.Credential, inErr error, _ *credentials.Client, _ uint32, _ []credentials.Option) (*api.Response, *credentials.Credential, error) {
|
||||
return inResp, inItem, inErr
|
||||
}
|
||||
printCustomPasswordActionOutput = func(*PasswordCommand) (bool, error) { return false, nil }
|
||||
)
|
||||
@@ -0,0 +1,96 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package credentialscmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/api/credentials"
|
||||
"github.com/hashicorp/boundary/internal/cmd/base"
|
||||
"github.com/hashicorp/go-secure-stdlib/parseutil"
|
||||
)
|
||||
|
||||
func init() {
|
||||
extraPasswordFlagsFunc = extraPasswordFlagsFuncImpl
|
||||
extraPasswordActionsFlagsMapFunc = extraPasswordActionsFlagsMapFuncImpl
|
||||
extraPasswordFlagsHandlingFunc = extraPasswordFlagHandlingFuncImpl
|
||||
}
|
||||
|
||||
type extraPasswordCmdVars struct {
|
||||
flagPassword string
|
||||
}
|
||||
|
||||
func extraPasswordActionsFlagsMapFuncImpl() map[string][]string {
|
||||
flags := map[string][]string{
|
||||
"create": {
|
||||
passwordFlagName,
|
||||
},
|
||||
}
|
||||
flags["update"] = flags["create"]
|
||||
return flags
|
||||
}
|
||||
|
||||
func extraPasswordFlagsFuncImpl(c *PasswordCommand, set *base.FlagSets, _ *base.FlagSet) {
|
||||
f := set.NewFlagSet("Password Credential Options")
|
||||
|
||||
for _, name := range flagsPasswordMap[c.Func] {
|
||||
switch name {
|
||||
case passwordFlagName:
|
||||
f.StringVar(&base.StringVar{
|
||||
Name: passwordFlagName,
|
||||
Target: &c.flagPassword,
|
||||
Usage: "The password associated with the credential. This can be a file on disk (file://) from which the value will be read, or an env var (env://) from which the value will be read.",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func extraPasswordFlagHandlingFuncImpl(c *PasswordCommand, _ *base.FlagSets, opts *[]credentials.Option) bool {
|
||||
switch c.flagPassword {
|
||||
case "":
|
||||
default:
|
||||
password, err := parseutil.MustParsePath(c.flagPassword)
|
||||
switch {
|
||||
case err == nil:
|
||||
case errors.Is(err, parseutil.ErrNotParsed):
|
||||
c.UI.Error("Password flag must be used with env:// or file:// syntax")
|
||||
return false
|
||||
default:
|
||||
c.UI.Error(fmt.Sprintf("Error parsing password flag: %v", err))
|
||||
return false
|
||||
}
|
||||
*opts = append(*opts, credentials.WithPasswordCredentialPassword(password))
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *PasswordCommand) extraPasswordHelpFunc(_ map[string]func() string) string {
|
||||
var helpStr string
|
||||
switch c.Func {
|
||||
case "create":
|
||||
helpStr = base.WrapForHelpText([]string{
|
||||
"Usage: boundary credentials create password -credential-store-id [options] [args]",
|
||||
"",
|
||||
" Create a password credential. Example:",
|
||||
"",
|
||||
` $ boundary credentials create password -credential-store-id csvlt_1234567890 -password pass`,
|
||||
"",
|
||||
"",
|
||||
})
|
||||
|
||||
case "update":
|
||||
helpStr = base.WrapForHelpText([]string{
|
||||
"Usage: boundary credentials update password [options] [args]",
|
||||
"",
|
||||
" Update a password credential given its ID. Example:",
|
||||
"",
|
||||
` $ boundary credentials update password -id clvlt_1234567890 -name devops -description "For DevOps usage"`,
|
||||
"",
|
||||
"",
|
||||
})
|
||||
}
|
||||
return helpStr + c.Flags().Help()
|
||||
}
|
||||
@@ -383,6 +383,22 @@ var inputStructs = map[string][]*cmdInfo{
|
||||
Container: "CredentialStore",
|
||||
HasId: true,
|
||||
},
|
||||
{
|
||||
ResourceType: resource.Credential.String(),
|
||||
Pkg: "credentials",
|
||||
StdActions: []string{"create", "update"},
|
||||
SubActionPrefix: "password",
|
||||
HasExtraCommandVars: true,
|
||||
SkipNormalHelp: true,
|
||||
HasExtraHelpFunc: true,
|
||||
HasId: true,
|
||||
HasName: true,
|
||||
HasDescription: true,
|
||||
Container: "CredentialStore",
|
||||
VersionedActions: []string{"update"},
|
||||
NeedsSubtypeInCreate: true,
|
||||
PrefixAttributeFieldErrorsWithSubactionPrefix: true,
|
||||
},
|
||||
{
|
||||
ResourceType: resource.Credential.String(),
|
||||
Pkg: "credentials",
|
||||
|
||||
@@ -143,6 +143,13 @@ type UsernamePasswordDomain interface {
|
||||
Domain() string
|
||||
}
|
||||
|
||||
// PasswordOnly is a credential containing a password.
|
||||
// Does not follow naming convention to avoid conflict with existing Password type.
|
||||
type PasswordOnly interface {
|
||||
Credential
|
||||
Password() Password
|
||||
}
|
||||
|
||||
// SshPrivateKey is a credential containing a username an SSH private key and
|
||||
// an optional private key passphrase.
|
||||
type SshPrivateKey interface {
|
||||
|
||||
@@ -16,6 +16,7 @@ func init() {
|
||||
globals.RegisterPrefixToResourceInfo(globals.UsernamePasswordCredentialPrefix, resource.Credential, Domain, UsernamePasswordSubtype)
|
||||
globals.RegisterPrefixToResourceInfo(globals.UsernamePasswordDomainCredentialPrefix, resource.Credential, Domain, UsernamePasswordDomainSubtype)
|
||||
globals.RegisterPrefixToResourceInfo(globals.UsernamePasswordCredentialPreviousPrefix, resource.Credential, Domain, UsernamePasswordSubtype)
|
||||
globals.RegisterPrefixToResourceInfo(globals.PasswordCredentialPrefix, resource.Credential, Domain, PasswordSubtype)
|
||||
globals.RegisterPrefixToResourceInfo(globals.SshPrivateKeyCredentialPrefix, resource.Credential, Domain, SshPrivateKeySubtype)
|
||||
globals.RegisterPrefixToResourceInfo(globals.JsonCredentialPrefix, resource.Credential, Domain, JsonSubtype)
|
||||
}
|
||||
@@ -25,6 +26,8 @@ const (
|
||||
|
||||
UsernamePasswordDomainSubtype = globals.Subtype("username_password_domain")
|
||||
|
||||
PasswordSubtype = globals.Subtype("password")
|
||||
|
||||
SshPrivateKeySubtype = globals.Subtype("ssh_private_key")
|
||||
|
||||
JsonSubtype = globals.Subtype("json")
|
||||
@@ -48,6 +51,15 @@ func NewUsernamePasswordDomainCredentialId(ctx context.Context) (string, error)
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// NewPasswordCredentialId generates a new public ID for a password credential.
|
||||
func NewPasswordCredentialId(ctx context.Context) (string, error) {
|
||||
id, err := db.NewPublicId(ctx, globals.PasswordCredentialPrefix)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(ctx, err, "credential.NewPasswordCredentialId")
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// New SshPrivateKeyCredentialId generates a new public ID for an SSH private key credential.
|
||||
func NewSshPrivateKeyCredentialId(ctx context.Context) (string, error) {
|
||||
id, err := db.NewPublicId(ctx, globals.SshPrivateKeyCredentialPrefix)
|
||||
|
||||
@@ -92,6 +92,24 @@ func (c *listCredentialResult) toCredential(ctx context.Context) (credential.Sta
|
||||
cred.PasswordHmac = []byte(c.Hmac1)
|
||||
}
|
||||
return cred, nil
|
||||
case "p":
|
||||
cred := &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
PublicId: c.PublicId,
|
||||
StoreId: c.StoreId,
|
||||
Name: c.Name,
|
||||
Description: c.Description,
|
||||
CreateTime: c.CreateTime,
|
||||
UpdateTime: c.UpdateTime,
|
||||
Version: uint32(c.Version),
|
||||
KeyId: c.KeyId,
|
||||
},
|
||||
}
|
||||
// Assign byte slices only if the string isn't empty
|
||||
if c.Hmac1 != "" {
|
||||
cred.PasswordHmac = []byte(c.Hmac1)
|
||||
}
|
||||
return cred, nil
|
||||
case "ssh":
|
||||
cred := &SshPrivateKeyCredential{
|
||||
SshPrivateKeyCredential: &store.SshPrivateKeyCredential{
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package static
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/credential"
|
||||
"github.com/hashicorp/boundary/internal/credential/static/store"
|
||||
"github.com/hashicorp/boundary/internal/db/timestamp"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/hashicorp/boundary/internal/libs/crypto"
|
||||
"github.com/hashicorp/boundary/internal/oplog"
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
|
||||
"github.com/hashicorp/go-kms-wrapping/v2/extras/structwrapping"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
var _ credential.Static = (*PasswordCredential)(nil)
|
||||
|
||||
// PasswordCredential contains the credential with a password.
|
||||
// It is owned by a credential store.
|
||||
type PasswordCredential struct {
|
||||
*store.PasswordCredential
|
||||
tableName string `gorm:"-"`
|
||||
}
|
||||
|
||||
// NewPasswordCredential creates a new in memory static Credential containing a
|
||||
// password that is assigned to storeId. Name and description are the only
|
||||
// valid options. All other options are ignored.
|
||||
func NewPasswordCredential(
|
||||
storeId string,
|
||||
password credential.Password,
|
||||
opt ...Option,
|
||||
) (*PasswordCredential, error) {
|
||||
opts := getOpts(opt...)
|
||||
l := &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
StoreId: storeId,
|
||||
Name: opts.withName,
|
||||
Description: opts.withDescription,
|
||||
Password: []byte(password),
|
||||
},
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func allocPasswordCredential() *PasswordCredential {
|
||||
return &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *PasswordCredential) clone() *PasswordCredential {
|
||||
cp := proto.Clone(c.PasswordCredential)
|
||||
return &PasswordCredential{
|
||||
PasswordCredential: cp.(*store.PasswordCredential),
|
||||
}
|
||||
}
|
||||
|
||||
// TableName returns the table name.
|
||||
func (c *PasswordCredential) TableName() string {
|
||||
if c.tableName != "" {
|
||||
return c.tableName
|
||||
}
|
||||
return "credential_static_password_credential"
|
||||
}
|
||||
|
||||
// SetTableName sets the table name.
|
||||
func (c *PasswordCredential) SetTableName(n string) {
|
||||
c.tableName = n
|
||||
}
|
||||
|
||||
// GetResourceType returns the resource type of the Credential
|
||||
func (c *PasswordCredential) GetResourceType() resource.Type {
|
||||
return resource.Credential
|
||||
}
|
||||
|
||||
func (c *PasswordCredential) oplog(op oplog.OpType) oplog.Metadata {
|
||||
metadata := oplog.Metadata{
|
||||
"resource-public-id": []string{c.PublicId},
|
||||
"resource-type": []string{"credential-static-password"},
|
||||
"op-type": []string{op.String()},
|
||||
}
|
||||
if c.StoreId != "" {
|
||||
metadata["store-id"] = []string{c.StoreId}
|
||||
}
|
||||
return metadata
|
||||
}
|
||||
|
||||
func (c *PasswordCredential) encrypt(ctx context.Context, cipher wrapping.Wrapper) error {
|
||||
const op = "static.(PasswordCredential).encrypt"
|
||||
if len(c.Password) == 0 {
|
||||
return errors.New(ctx, errors.InvalidParameter, op, "no password defined")
|
||||
}
|
||||
if err := structwrapping.WrapStruct(ctx, cipher, c.PasswordCredential, nil); err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithCode(errors.Encrypt))
|
||||
}
|
||||
keyId, err := cipher.KeyId(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithCode(errors.Encrypt), errors.WithMsg("error reading cipher key id"))
|
||||
}
|
||||
c.KeyId = keyId
|
||||
if err := c.hmacPassword(ctx, cipher); err != nil {
|
||||
return errors.Wrap(ctx, err, op)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *PasswordCredential) decrypt(ctx context.Context, cipher wrapping.Wrapper) error {
|
||||
const op = "static.(PasswordCredential).decrypt"
|
||||
if err := structwrapping.UnwrapStruct(ctx, cipher, c.PasswordCredential, nil); err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithCode(errors.Decrypt))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *PasswordCredential) hmacPassword(ctx context.Context, cipher wrapping.Wrapper) error {
|
||||
const op = "static.(PasswordCredential).hmacPassword"
|
||||
if cipher == nil {
|
||||
return errors.New(ctx, errors.InvalidParameter, op, "missing cipher")
|
||||
}
|
||||
hm, err := crypto.HmacSha256(ctx, c.Password, cipher, []byte(c.StoreId), nil, crypto.WithEd25519())
|
||||
if err != nil {
|
||||
return errors.Wrap(ctx, err, op)
|
||||
}
|
||||
c.PasswordHmac = []byte(hm)
|
||||
return nil
|
||||
}
|
||||
|
||||
type deletedPasswordCredential struct {
|
||||
PublicId string `gorm:"primary_key"`
|
||||
DeleteTime *timestamp.Timestamp
|
||||
}
|
||||
|
||||
// TableName returns the tablename to override the default gorm table name
|
||||
func (s *deletedPasswordCredential) TableName() string {
|
||||
return "credential_static_password_credential_deleted"
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package static
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/boundary/internal/credential"
|
||||
"github.com/hashicorp/boundary/internal/credential/static/store"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/libs/crypto"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/testing/protocmp"
|
||||
)
|
||||
|
||||
func TestPasswordCredential_New(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
kkms := kms.TestKms(t, conn, wrapper)
|
||||
rw := db.New(conn)
|
||||
|
||||
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
cs := TestCredentialStore(t, conn, wrapper, prj.PublicId)
|
||||
|
||||
type args struct {
|
||||
password credential.Password
|
||||
storeId string
|
||||
options []Option
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *PasswordCredential
|
||||
wantCreateErr bool
|
||||
wantEncryptErr bool
|
||||
}{
|
||||
{
|
||||
name: "missing-password",
|
||||
args: args{
|
||||
storeId: cs.PublicId,
|
||||
},
|
||||
want: allocPasswordCredential(),
|
||||
wantEncryptErr: true,
|
||||
},
|
||||
{
|
||||
name: "missing-store-id",
|
||||
args: args{
|
||||
password: "test-pass",
|
||||
},
|
||||
want: allocPasswordCredential(),
|
||||
wantCreateErr: true,
|
||||
},
|
||||
{
|
||||
name: "valid-no-options",
|
||||
args: args{
|
||||
password: "test-pass",
|
||||
storeId: cs.PublicId,
|
||||
},
|
||||
want: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Password: []byte("test-pass"),
|
||||
StoreId: cs.PublicId,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-with-name",
|
||||
args: args{
|
||||
password: "test-pass",
|
||||
storeId: cs.PublicId,
|
||||
options: []Option{WithName("my-credential")},
|
||||
},
|
||||
want: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Password: []byte("test-pass"),
|
||||
StoreId: cs.PublicId,
|
||||
Name: "my-credential",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-with-description",
|
||||
args: args{
|
||||
password: "test-pass",
|
||||
storeId: cs.PublicId,
|
||||
options: []Option{WithDescription("my-credential-description")},
|
||||
},
|
||||
want: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Password: []byte("test-pass"),
|
||||
StoreId: cs.PublicId,
|
||||
Description: "my-credential-description",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
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()
|
||||
|
||||
got, err := NewPasswordCredential(tt.args.storeId, tt.args.password, tt.args.options...)
|
||||
|
||||
require.NoError(err)
|
||||
require.NotNil(got)
|
||||
assert.Emptyf(got.PublicId, "PublicId set")
|
||||
id, err := credential.NewPasswordCredentialId(ctx)
|
||||
require.NoError(err)
|
||||
|
||||
tt.want.PublicId = id
|
||||
got.PublicId = id
|
||||
|
||||
databaseWrapper, err := kkms.GetWrapper(context.Background(), prj.PublicId, kms.KeyPurposeDatabase)
|
||||
require.NoError(err)
|
||||
|
||||
err = got.encrypt(ctx, databaseWrapper)
|
||||
if tt.wantEncryptErr {
|
||||
require.Error(err)
|
||||
return
|
||||
}
|
||||
assert.NoError(err)
|
||||
|
||||
err = rw.Create(context.Background(), got)
|
||||
if tt.wantCreateErr {
|
||||
require.Error(err)
|
||||
return
|
||||
}
|
||||
assert.NoError(err)
|
||||
|
||||
got2 := allocPasswordCredential()
|
||||
got2.PublicId = id
|
||||
assert.Equal(id, got2.GetPublicId())
|
||||
require.NoError(rw.LookupById(ctx, got2))
|
||||
|
||||
err = got2.decrypt(ctx, databaseWrapper)
|
||||
require.NoError(err)
|
||||
|
||||
// Timestamps and version are automatically set
|
||||
tt.want.CreateTime = got2.CreateTime
|
||||
tt.want.UpdateTime = got2.UpdateTime
|
||||
tt.want.Version = got2.Version
|
||||
|
||||
// KeyId is allocated via kms no need to validate in this test
|
||||
tt.want.KeyId = got2.KeyId
|
||||
got2.CtPassword = nil
|
||||
|
||||
// encrypt also calculates the hmac, validate it is correct
|
||||
hm, err := crypto.HmacSha256(ctx, got.Password, databaseWrapper, []byte(got.StoreId), nil, crypto.WithEd25519())
|
||||
require.NoError(err)
|
||||
tt.want.PasswordHmac = []byte(hm)
|
||||
|
||||
assert.Empty(cmp.Diff(tt.want, got2.clone(), protocmp.Transform()))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,16 @@ select distinct upd.public_id,
|
||||
and upd.key_id = ?;
|
||||
`
|
||||
|
||||
credStaticPasswordRewrapQuery = `
|
||||
select distinct pass.public_id,
|
||||
pass.password_encrypted,
|
||||
pass.key_id
|
||||
from credential_static_password_credential pass
|
||||
inner join credential_static_store store
|
||||
on store.public_id = pass.store_id
|
||||
where store.project_id = ?
|
||||
and pass.key_id = ?;
|
||||
`
|
||||
credStaticSshPrivKeyRewrapQuery = `
|
||||
select distinct ssh.public_id,
|
||||
ssh.private_key_encrypted,
|
||||
@@ -56,6 +66,7 @@ select sum(reltuples::bigint) as estimate
|
||||
'credential_static_json_credential'::regclass,
|
||||
'credential_static_username_password_credential'::regclass,
|
||||
'credential_static_username_password_domain_credential'::regclass,
|
||||
'credential_static_password_credential'::regclass,
|
||||
'credential_static_ssh_private_key_credential'::regclass
|
||||
)
|
||||
`
|
||||
@@ -83,6 +94,11 @@ upd_creds as (
|
||||
from credential_static_username_password_domain_credential
|
||||
where public_id in (select public_id from credentials)
|
||||
),
|
||||
p_creds as (
|
||||
select *
|
||||
from credential_static_password_credential
|
||||
where public_id in (select public_id from credentials)
|
||||
),
|
||||
ssh_creds as (
|
||||
select *
|
||||
from credential_static_ssh_private_key_credential
|
||||
@@ -137,6 +153,22 @@ final as (
|
||||
'upd' as type
|
||||
from upd_creds
|
||||
union
|
||||
select public_id,
|
||||
store_id,
|
||||
project_id,
|
||||
name,
|
||||
description,
|
||||
create_time,
|
||||
update_time,
|
||||
version,
|
||||
null as username, -- Add this to make the union uniform
|
||||
null as domain, -- Add this to make the union uniform
|
||||
key_id,
|
||||
password_hmac as hmac1,
|
||||
null::bytea as hmac2, -- Add this to make the union uniform
|
||||
'p' as type
|
||||
from p_creds
|
||||
union
|
||||
select public_id,
|
||||
store_id,
|
||||
project_id,
|
||||
@@ -182,6 +214,11 @@ upd_creds as (
|
||||
from credential_static_username_password_domain_credential
|
||||
where public_id in (select public_id from credentials)
|
||||
),
|
||||
p_creds as (
|
||||
select *
|
||||
from credential_static_password_credential
|
||||
where public_id in (select public_id from credentials)
|
||||
),
|
||||
ssh_creds as (
|
||||
select *
|
||||
from credential_static_ssh_private_key_credential
|
||||
@@ -236,6 +273,22 @@ final as (
|
||||
'upd' as type
|
||||
from upd_creds
|
||||
union
|
||||
select public_id,
|
||||
store_id,
|
||||
project_id,
|
||||
name,
|
||||
description,
|
||||
create_time,
|
||||
update_time,
|
||||
version,
|
||||
null as username, -- Add this to make the union uniform
|
||||
null as domain, -- Add this to make the union uniform
|
||||
key_id,
|
||||
password_hmac as hmac1,
|
||||
null::bytea as hmac2, -- Add this to make the union uniform
|
||||
'p' as type
|
||||
from p_creds
|
||||
union
|
||||
select public_id,
|
||||
store_id,
|
||||
project_id,
|
||||
@@ -281,6 +334,11 @@ upd_creds as (
|
||||
from credential_static_username_password_domain_credential
|
||||
where public_id in (select public_id from credentials)
|
||||
),
|
||||
p_creds as (
|
||||
select *
|
||||
from credential_static_password_credential
|
||||
where public_id in (select public_id from credentials)
|
||||
),
|
||||
ssh_creds as (
|
||||
select *
|
||||
from credential_static_ssh_private_key_credential
|
||||
@@ -335,6 +393,22 @@ final as (
|
||||
'upd' as type
|
||||
from upd_creds
|
||||
union
|
||||
select public_id,
|
||||
store_id,
|
||||
project_id,
|
||||
name,
|
||||
description,
|
||||
create_time,
|
||||
update_time,
|
||||
version,
|
||||
null as username, -- Add this to make the union uniform
|
||||
null as domain, -- Add this to make the union uniform
|
||||
key_id,
|
||||
password_hmac as hmac1,
|
||||
null::bytea as hmac2, -- Add this to make the union uniform
|
||||
'p' as type
|
||||
from p_creds
|
||||
union
|
||||
select public_id,
|
||||
store_id,
|
||||
project_id,
|
||||
@@ -381,6 +455,11 @@ upd_creds as (
|
||||
from credential_static_username_password_domain_credential
|
||||
where public_id in (select public_id from credentials)
|
||||
),
|
||||
p_creds as (
|
||||
select *
|
||||
from credential_static_password_credential
|
||||
where public_id in (select public_id from credentials)
|
||||
),
|
||||
ssh_creds as (
|
||||
select *
|
||||
from credential_static_ssh_private_key_credential
|
||||
@@ -435,6 +514,22 @@ final as (
|
||||
'upd' as type
|
||||
from upd_creds
|
||||
union
|
||||
select public_id,
|
||||
store_id,
|
||||
project_id,
|
||||
name,
|
||||
description,
|
||||
create_time,
|
||||
update_time,
|
||||
version,
|
||||
null as username, -- Add this to make the union uniform
|
||||
null as domain, -- Add this to make the union uniform
|
||||
key_id,
|
||||
password_hmac as hmac1,
|
||||
null::bytea as hmac2, -- Add this to make the union uniform
|
||||
'p' as type
|
||||
from p_creds
|
||||
union
|
||||
select public_id,
|
||||
store_id,
|
||||
project_id,
|
||||
|
||||
@@ -196,6 +196,89 @@ func (r *Repository) CreateUsernamePasswordDomainCredential(
|
||||
return newCred, nil
|
||||
}
|
||||
|
||||
// CreatePasswordCredential inserts c into the repository and returns a new
|
||||
// PasswordCredential containing the credential's PublicId. c is not
|
||||
// changed. c must not contain a PublicId. The PublicId is generated and
|
||||
// assigned by this method. c must contain a valid StoreId.
|
||||
//
|
||||
// The password is encrypted and a HmacSha256 of the password is calculated. Only the
|
||||
// PasswordHmac is returned, the plain-text and encrypted password is not returned.
|
||||
//
|
||||
// Both c.Name and c.Description are optional. If c.Name is set, it must
|
||||
// be unique within c.ProjectId. Both c.CreateTime and c.UpdateTime are
|
||||
// ignored.
|
||||
func (r *Repository) CreatePasswordCredential(
|
||||
ctx context.Context,
|
||||
projectId string,
|
||||
c *PasswordCredential,
|
||||
_ ...Option,
|
||||
) (*PasswordCredential, error) {
|
||||
const op = "static.(Repository).CreatePasswordCredential"
|
||||
if c == nil {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing credential")
|
||||
}
|
||||
if c.PasswordCredential == nil {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing embedded credential")
|
||||
}
|
||||
if projectId == "" {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing project id")
|
||||
}
|
||||
if c.Password == nil {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing password")
|
||||
}
|
||||
if c.StoreId == "" {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing store id")
|
||||
}
|
||||
if c.PublicId != "" {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "public id not empty")
|
||||
}
|
||||
|
||||
c = c.clone()
|
||||
id, err := credential.NewPasswordCredentialId(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
c.PublicId = 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"))
|
||||
}
|
||||
|
||||
// encrypt
|
||||
databaseWrapper, err := r.kms.GetWrapper(ctx, projectId, kms.KeyPurposeDatabase)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get database wrapper"))
|
||||
}
|
||||
if err := c.encrypt(ctx, databaseWrapper); err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
var newCred *PasswordCredential
|
||||
_, err = r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{},
|
||||
func(_ db.Reader, w db.Writer) error {
|
||||
newCred = c.clone()
|
||||
if err := w.Create(ctx, newCred,
|
||||
db.WithOplog(oplogWrapper, newCred.oplog(oplog.OpType_OP_TYPE_CREATE))); err != nil {
|
||||
return errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if errors.IsUniqueError(err) {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in store: %s: name %s already exists", c.StoreId, c.Name)))
|
||||
}
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in store: %s", c.StoreId)))
|
||||
}
|
||||
|
||||
// Clear password fields, only PasswordHmac should be returned
|
||||
newCred.CtPassword = nil
|
||||
newCred.Password = nil
|
||||
|
||||
return newCred, nil
|
||||
}
|
||||
|
||||
// CreateSshPrivateKeyCredential inserts c into the repository and returns a new
|
||||
// SshPrivateKeyCredential containing the credential's PublicId. c is not
|
||||
// changed. c must not contain a PublicId. The PublicId is generated and
|
||||
@@ -411,6 +494,20 @@ func (r *Repository) LookupCredential(ctx context.Context, publicId string, _ ..
|
||||
updCred.Password = nil
|
||||
cred = updCred
|
||||
|
||||
case credential.PasswordSubtype:
|
||||
pCred := allocPasswordCredential()
|
||||
pCred.PublicId = publicId
|
||||
if err := r.reader.LookupByPublicId(ctx, pCred); err != nil {
|
||||
if errors.IsNotFoundError(err) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for: %s", publicId)))
|
||||
}
|
||||
// Clear password fields, only passwordHmac should be returned
|
||||
pCred.CtPassword = nil
|
||||
pCred.Password = nil
|
||||
cred = pCred
|
||||
|
||||
case credential.SshPrivateKeySubtype:
|
||||
spkCred := allocSshPrivateKeyCredential()
|
||||
spkCred.PublicId = publicId
|
||||
@@ -676,6 +773,118 @@ func (r *Repository) UpdateUsernamePasswordDomainCredential(ctx context.Context,
|
||||
return returnedCredential, rowsUpdated, nil
|
||||
}
|
||||
|
||||
// UpdatePasswordCredential updates the repository entry for c.PublicId with
|
||||
// the values in c for the fields listed in fieldMaskPaths. It returns a
|
||||
// new PasswordCredential containing the updated values and a count of the
|
||||
// number of records updated. c is not changed.
|
||||
//
|
||||
// c must contain a valid PublicId. Only Name, Description and Password can be
|
||||
// changed. If c.Name is set to a non-empty string, it must be unique within c.ProjectId.
|
||||
//
|
||||
// An attribute of c will be set to NULL in the database if the attribute
|
||||
// in c is the zero value and it is included in fieldMaskPaths.
|
||||
func (r *Repository) UpdatePasswordCredential(ctx context.Context,
|
||||
projectId string,
|
||||
c *PasswordCredential,
|
||||
version uint32,
|
||||
fieldMaskPaths []string,
|
||||
_ ...Option,
|
||||
) (*PasswordCredential, int, error) {
|
||||
const op = "static.(Repository).UpdatePasswordCredential"
|
||||
if c == nil {
|
||||
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing credential")
|
||||
}
|
||||
if c.PasswordCredential == nil {
|
||||
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing embedded credential")
|
||||
}
|
||||
if c.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")
|
||||
}
|
||||
if c.StoreId == "" {
|
||||
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing store id")
|
||||
}
|
||||
c = c.clone()
|
||||
|
||||
for _, f := range fieldMaskPaths {
|
||||
switch {
|
||||
case strings.EqualFold(nameField, f):
|
||||
case strings.EqualFold(descriptionField, f):
|
||||
case strings.EqualFold(passwordField, f):
|
||||
default:
|
||||
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidFieldMask, op, f)
|
||||
}
|
||||
}
|
||||
dbMask, nullFields := dbw.BuildUpdatePaths(
|
||||
map[string]any{
|
||||
nameField: c.Name,
|
||||
descriptionField: c.Description,
|
||||
passwordField: c.Password,
|
||||
},
|
||||
fieldMaskPaths,
|
||||
nil,
|
||||
)
|
||||
if len(dbMask) == 0 && len(nullFields) == 0 {
|
||||
return nil, db.NoRowsAffected, errors.New(ctx, errors.EmptyFieldMask, op, "missing field mask")
|
||||
}
|
||||
|
||||
for _, f := range fieldMaskPaths {
|
||||
if strings.EqualFold(passwordField, f) {
|
||||
// Password has been updated, re-encrypt and recalculate hmac
|
||||
databaseWrapper, err := r.kms.GetWrapper(ctx, projectId, kms.KeyPurposeDatabase)
|
||||
if err != nil {
|
||||
return nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get database wrapper"))
|
||||
}
|
||||
if err := c.encrypt(ctx, databaseWrapper); err != nil {
|
||||
return nil, db.NoRowsAffected, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
// Set PasswordHmac and CtPassword masks for update.
|
||||
dbMask = append(dbMask, "PasswordHmac", "CtPassword", "KeyId")
|
||||
}
|
||||
}
|
||||
|
||||
oplogWrapper, err := r.kms.GetWrapper(ctx, projectId, kms.KeyPurposeOplog)
|
||||
if err != nil {
|
||||
return nil, db.NoRowsAffected,
|
||||
errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper"))
|
||||
}
|
||||
|
||||
var rowsUpdated int
|
||||
var returnedCredential *PasswordCredential
|
||||
_, err = r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{},
|
||||
func(_ db.Reader, w db.Writer) error {
|
||||
returnedCredential = c.clone()
|
||||
var err error
|
||||
rowsUpdated, err = w.Update(ctx, returnedCredential,
|
||||
dbMask, nullFields,
|
||||
db.WithOplog(oplogWrapper, returnedCredential.oplog(oplog.OpType_OP_TYPE_UPDATE)),
|
||||
db.WithVersion(&version))
|
||||
if err != nil {
|
||||
return errors.Wrap(ctx, err, op)
|
||||
}
|
||||
if rowsUpdated > 1 {
|
||||
return errors.New(ctx, errors.MultipleRecords, op, "more than 1 resource would have been updated")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, db.NoRowsAffected, err
|
||||
}
|
||||
|
||||
// Clear password fields, only PasswordHmac should be returned
|
||||
returnedCredential.CtPassword = nil
|
||||
returnedCredential.Password = nil
|
||||
|
||||
return returnedCredential, rowsUpdated, nil
|
||||
}
|
||||
|
||||
// UpdateSshPrivateKeyCredential updates the repository entry for c.PublicId
|
||||
// with the values in c for the fields listed in fieldMaskPaths. It returns a
|
||||
// new SshPrivateKeyCredential containing the updated values and a count of the
|
||||
@@ -1089,6 +1298,11 @@ func (r *Repository) DeleteCredential(ctx context.Context, projectId, id string,
|
||||
c.PublicId = id
|
||||
input = c
|
||||
md = c.oplog(oplog.OpType_OP_TYPE_DELETE)
|
||||
case credential.PasswordSubtype:
|
||||
c := allocPasswordCredential()
|
||||
c.PublicId = id
|
||||
input = c
|
||||
md = c.oplog(oplog.OpType_OP_TYPE_DELETE)
|
||||
case credential.SshPrivateKeySubtype:
|
||||
c := allocSshPrivateKeyCredential()
|
||||
c.PublicId = id
|
||||
@@ -1177,6 +1391,13 @@ func (r *Repository) ListDeletedCredentialIds(ctx context.Context, since time.Ti
|
||||
for _, cl := range deletedUsernamePasswordDomainCredentials {
|
||||
credentialStoreIds = append(credentialStoreIds, cl.PublicId)
|
||||
}
|
||||
var deletedPasswordCredentials []*deletedPasswordCredential
|
||||
if err := r.SearchWhere(ctx, &deletedPasswordCredentials, "delete_time >= ?", []any{since}); err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithMsg("failed to query deleted password credentials"))
|
||||
}
|
||||
for _, cl := range deletedPasswordCredentials {
|
||||
credentialStoreIds = append(credentialStoreIds, cl.PublicId)
|
||||
}
|
||||
var deletedSSHPrivateKeyCredentials []*deletedSSHPrivateKeyCredential
|
||||
if err := r.SearchWhere(ctx, &deletedSSHPrivateKeyCredentials, "delete_time >= ?", []any{since}); err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithMsg("failed to query deleted ssh private key credentials"))
|
||||
|
||||
@@ -391,6 +391,162 @@ func TestRepository_CreateUsernamePasswordDomainCredential(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestRepository_CreatePasswordCredential(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 := TestCredentialStore(t, conn, wrapper, prj.PublicId)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
projectId string
|
||||
cred *PasswordCredential
|
||||
wantErr bool
|
||||
wantErrCode errors.Code
|
||||
}{
|
||||
{
|
||||
name: "missing-store",
|
||||
wantErr: true,
|
||||
wantErrCode: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "missing-embedded-cred",
|
||||
cred: &PasswordCredential{},
|
||||
wantErr: true,
|
||||
wantErrCode: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "missing-project-id",
|
||||
cred: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Password: []byte("secret"),
|
||||
StoreId: cs.PublicId,
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrCode: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "missing-password",
|
||||
projectId: prj.PublicId,
|
||||
cred: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
StoreId: cs.PublicId,
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrCode: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "missing-store-id",
|
||||
projectId: prj.PublicId,
|
||||
cred: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Password: []byte("secret"),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrCode: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "valid",
|
||||
projectId: prj.PublicId,
|
||||
cred: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Password: []byte("secret"),
|
||||
StoreId: cs.PublicId,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
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()
|
||||
kkms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := NewRepository(ctx, rw, rw, kkms)
|
||||
require.NoError(err)
|
||||
require.NotNil(repo)
|
||||
|
||||
got, err := repo.CreatePasswordCredential(ctx, tt.projectId, tt.cred)
|
||||
if tt.wantErr {
|
||||
assert.Truef(errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err)
|
||||
assert.Nil(got)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assertPublicId(t, globals.PasswordCredentialPrefix, got.PublicId)
|
||||
assert.Nil(got.Password)
|
||||
assert.Nil(got.CtPassword)
|
||||
|
||||
// Validate password
|
||||
lookupCred := allocPasswordCredential()
|
||||
lookupCred.PublicId = got.PublicId
|
||||
require.NoError(rw.LookupById(ctx, lookupCred))
|
||||
|
||||
databaseWrapper, err := kkms.GetWrapper(context.Background(), tt.projectId, kms.KeyPurposeDatabase)
|
||||
require.NoError(err)
|
||||
require.NoError(lookupCred.decrypt(ctx, databaseWrapper))
|
||||
assert.Equal(tt.cred.Password, lookupCred.Password)
|
||||
|
||||
assert.Empty(got.Password)
|
||||
assert.Empty(got.CtPassword)
|
||||
assert.NotEmpty(got.PasswordHmac)
|
||||
|
||||
// Validate hmac
|
||||
hm, err := crypto.HmacSha256(ctx, tt.cred.Password, databaseWrapper, []byte(tt.cred.StoreId), nil, crypto.WithEd25519())
|
||||
require.NoError(err)
|
||||
assert.Equal([]byte(hm), got.PasswordHmac)
|
||||
|
||||
// Validate oplog
|
||||
assert.NoError(db.TestVerifyOplog(t, rw, got.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_CREATE), db.WithCreateNotBefore(10*time.Second)))
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("duplicate-names", func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
ctx := context.Background()
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := NewRepository(ctx, rw, rw, kms)
|
||||
require.NoError(err)
|
||||
require.NotNil(repo)
|
||||
org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
prj2 := iam.TestProject(t, iam.TestRepo(t, conn, wrapper), org.GetPublicId())
|
||||
require.NoError(err)
|
||||
|
||||
prjCs := TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
|
||||
prj2Cs := TestCredentialStore(t, conn, wrapper, prj2.GetPublicId())
|
||||
|
||||
in, err := NewPasswordCredential(prjCs.GetPublicId(), "pass", WithName("my-name"), WithDescription("original"))
|
||||
assert.NoError(err)
|
||||
|
||||
got, err := repo.CreatePasswordCredential(ctx, prj.PublicId, in)
|
||||
require.NoError(err)
|
||||
assert.Equal(in.Name, got.Name)
|
||||
assert.Equal(in.Description, got.Description)
|
||||
|
||||
in2, err := NewPasswordCredential(prjCs.GetPublicId(), "pass", WithName("my-name"), WithDescription("different"))
|
||||
require.NoError(err)
|
||||
got2, err := repo.CreatePasswordCredential(ctx, prj.GetPublicId(), in2)
|
||||
assert.Truef(errors.Match(errors.T(errors.NotUnique), err), "want err code: %v got err: %v", errors.NotUnique, err)
|
||||
assert.Nil(got2)
|
||||
|
||||
// Creating credential in different project should not conflict
|
||||
in3, err := NewPasswordCredential(prj2Cs.GetPublicId(), "pass", WithName("my-name"), WithDescription("different"))
|
||||
require.NoError(err)
|
||||
got3, err := repo.CreatePasswordCredential(ctx, prj2.GetPublicId(), in3)
|
||||
require.NoError(err)
|
||||
assert.Equal(in3.Name, got3.Name)
|
||||
assert.Equal(in3.Description, got3.Description)
|
||||
|
||||
assert.NotEqual(got.PublicId, got3.PublicId)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRepository_CreateSshPrivateKeyCredential(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
@@ -766,6 +922,7 @@ func TestRepository_LookupCredential(t *testing.T) {
|
||||
store := TestCredentialStore(t, conn, wrapper, prj.PublicId)
|
||||
upCred := TestUsernamePasswordCredential(t, conn, wrapper, "username", "password", store.PublicId, prj.PublicId)
|
||||
updCred := TestUsernamePasswordDomainCredential(t, conn, wrapper, "username", "password", "domain.com", store.PublicId, prj.PublicId)
|
||||
pCred := TestPasswordCredential(t, conn, wrapper, "password", store.PublicId, prj.PublicId)
|
||||
spkCred := TestSshPrivateKeyCredential(t, conn, wrapper, "username", TestSshPrivateKeyPem, store.PublicId, prj.PublicId)
|
||||
spkCredWithPass := TestSshPrivateKeyCredential(t, conn, wrapper, "username", string(testdata.PEMEncryptedKeys[0].PEMBytes),
|
||||
store.PublicId, prj.PublicId, WithPrivateKeyPassphrase([]byte(testdata.PEMEncryptedKeys[0].EncryptionKey)))
|
||||
@@ -790,6 +947,11 @@ func TestRepository_LookupCredential(t *testing.T) {
|
||||
id: updCred.GetPublicId(),
|
||||
want: updCred,
|
||||
},
|
||||
{
|
||||
name: "p-valid",
|
||||
id: pCred.GetPublicId(),
|
||||
want: pCred,
|
||||
},
|
||||
{
|
||||
name: "spk-valid",
|
||||
id: spkCred.GetPublicId(),
|
||||
@@ -849,6 +1011,10 @@ func TestRepository_LookupCredential(t *testing.T) {
|
||||
assert.Empty(v.Password)
|
||||
assert.Empty(v.CtPassword)
|
||||
assert.NotEmpty(v.PasswordHmac)
|
||||
case *PasswordCredential:
|
||||
assert.Empty(v.Password)
|
||||
assert.Empty(v.CtPassword)
|
||||
assert.NotEmpty(v.PasswordHmac)
|
||||
case *SshPrivateKeyCredential:
|
||||
assert.Empty(v.PrivateKey)
|
||||
assert.Empty(v.PrivateKeyEncrypted)
|
||||
@@ -884,6 +1050,7 @@ func TestRepository_ListCredentials(t *testing.T) {
|
||||
store := TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
|
||||
TestUsernamePasswordCredentials(t, conn, wrapper, "user", "pass", store.GetPublicId(), prj.GetPublicId(), total/4)
|
||||
TestUsernamePasswordDomainCredentials(t, conn, wrapper, "user", "pass", "domain.com", store.GetPublicId(), prj.GetPublicId(), total/4)
|
||||
TestPasswordCredentials(t, conn, wrapper, "pass", store.GetPublicId(), prj.GetPublicId(), total/4)
|
||||
TestSshPrivateKeyCredentials(t, conn, wrapper, "user", TestSshPrivateKeyPem, store.GetPublicId(), prj.GetPublicId(), total/4)
|
||||
|
||||
obj, _ := TestJsonObject(t)
|
||||
@@ -948,6 +1115,10 @@ func TestRepository_ListCredentials(t *testing.T) {
|
||||
assert.Empty(v.Password)
|
||||
assert.Empty(v.CtPassword)
|
||||
assert.NotEmpty(v.PasswordHmac)
|
||||
case *PasswordCredential:
|
||||
assert.Empty(v.Password)
|
||||
assert.Empty(v.CtPassword)
|
||||
assert.NotEmpty(v.PasswordHmac)
|
||||
case *SshPrivateKeyCredential:
|
||||
assert.Empty(v.PrivateKey)
|
||||
assert.Empty(v.PrivateKeyEncrypted)
|
||||
@@ -979,6 +1150,7 @@ func TestRepository_ListCredentials_Pagination(t *testing.T) {
|
||||
_ = TestSshPrivateKeyCredentials(t, conn, wrapper, "username", TestSshPrivateKeyPem, store.GetPublicId(), prj.GetPublicId(), 2)
|
||||
_ = TestUsernamePasswordCredentials(t, conn, wrapper, "username", "testpassword", store.GetPublicId(), prj.GetPublicId(), 2)
|
||||
_ = TestUsernamePasswordDomainCredentials(t, conn, wrapper, "username", "testpassword", "domain.com", store.GetPublicId(), prj.GetPublicId(), 1)
|
||||
_ = TestPasswordCredentials(t, conn, wrapper, "testpassword", store.GetPublicId(), prj.GetPublicId(), 1)
|
||||
|
||||
repo, err := NewRepository(ctx, rw, rw, kms)
|
||||
require.NoError(err)
|
||||
@@ -1015,15 +1187,16 @@ func TestRepository_ListCredentials_Pagination(t *testing.T) {
|
||||
|
||||
page4, ttime, err := repo.ListCredentials(ctx, store.GetPublicId(), credential.WithLimit(2), credential.WithStartPageAfterItem(page3[1]))
|
||||
require.NoError(err)
|
||||
require.Len(page4, 1)
|
||||
require.Len(page4, 2)
|
||||
pages = append(pages, page3...)
|
||||
for _, item := range pages {
|
||||
assert.NotEqual(item.GetPublicId(), page4[0].GetPublicId())
|
||||
assert.NotEqual(item.GetPublicId(), page4[1].GetPublicId())
|
||||
}
|
||||
assert.True(time.Now().Before(ttime.Add(10 * time.Second)))
|
||||
assert.True(time.Now().After(ttime.Add(-10 * time.Second)))
|
||||
|
||||
page5, ttime, err := repo.ListCredentials(ctx, store.GetPublicId(), credential.WithLimit(2), credential.WithStartPageAfterItem(page4[0]))
|
||||
page5, ttime, err := repo.ListCredentials(ctx, store.GetPublicId(), credential.WithLimit(2), credential.WithStartPageAfterItem(page4[1]))
|
||||
require.NoError(err)
|
||||
require.Empty(page5)
|
||||
assert.True(time.Now().Before(ttime.Add(10 * time.Second)))
|
||||
@@ -2292,6 +2465,432 @@ func TestRepository_UpdateUsernamePasswordDomainCredential(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepository_UpdatePasswordCredential(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
|
||||
changeName := func(n string) func(credential *PasswordCredential) *PasswordCredential {
|
||||
return func(c *PasswordCredential) *PasswordCredential {
|
||||
c.Name = n
|
||||
return c
|
||||
}
|
||||
}
|
||||
|
||||
changeDescription := func(d string) func(*PasswordCredential) *PasswordCredential {
|
||||
return func(c *PasswordCredential) *PasswordCredential {
|
||||
c.Description = d
|
||||
return c
|
||||
}
|
||||
}
|
||||
|
||||
makeNil := func() func(*PasswordCredential) *PasswordCredential {
|
||||
return func(_ *PasswordCredential) *PasswordCredential {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
makeEmbeddedNil := func() func(*PasswordCredential) *PasswordCredential {
|
||||
return func(_ *PasswordCredential) *PasswordCredential {
|
||||
return &PasswordCredential{}
|
||||
}
|
||||
}
|
||||
|
||||
setPublicId := func(n string) func(*PasswordCredential) *PasswordCredential {
|
||||
return func(c *PasswordCredential) *PasswordCredential {
|
||||
c.PublicId = n
|
||||
return c
|
||||
}
|
||||
}
|
||||
|
||||
deleteStoreId := func() func(*PasswordCredential) *PasswordCredential {
|
||||
return func(c *PasswordCredential) *PasswordCredential {
|
||||
c.StoreId = ""
|
||||
return c
|
||||
}
|
||||
}
|
||||
|
||||
deleteVersion := func() func(*PasswordCredential) *PasswordCredential {
|
||||
return func(c *PasswordCredential) *PasswordCredential {
|
||||
c.Version = 0
|
||||
return c
|
||||
}
|
||||
}
|
||||
|
||||
changePassword := func(d string) func(*PasswordCredential) *PasswordCredential {
|
||||
return func(c *PasswordCredential) *PasswordCredential {
|
||||
c.Password = []byte(d)
|
||||
return c
|
||||
}
|
||||
}
|
||||
|
||||
combine := func(fns ...func(cs *PasswordCredential) *PasswordCredential) func(*PasswordCredential) *PasswordCredential {
|
||||
return func(c *PasswordCredential) *PasswordCredential {
|
||||
for _, fn := range fns {
|
||||
c = fn(c)
|
||||
}
|
||||
return c
|
||||
}
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
orig *PasswordCredential
|
||||
chgFn func(*PasswordCredential) *PasswordCredential
|
||||
masks []string
|
||||
want *PasswordCredential
|
||||
wantCount int
|
||||
wantErr errors.Code
|
||||
}{
|
||||
{
|
||||
name: "nil-credential",
|
||||
orig: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Password: []byte("pass"),
|
||||
},
|
||||
},
|
||||
chgFn: makeNil(),
|
||||
masks: []string{"Name", "Description"},
|
||||
wantErr: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "nil-embedded-credential",
|
||||
orig: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Password: []byte("pass"),
|
||||
},
|
||||
},
|
||||
chgFn: makeEmbeddedNil(),
|
||||
masks: []string{"Name", "Description"},
|
||||
wantErr: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "no-public-id",
|
||||
orig: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Password: []byte("pass"),
|
||||
},
|
||||
},
|
||||
chgFn: setPublicId(""),
|
||||
masks: []string{"Name", "Description"},
|
||||
wantErr: errors.InvalidPublicId,
|
||||
},
|
||||
{
|
||||
name: "no-store-id",
|
||||
orig: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Password: []byte("pass"),
|
||||
},
|
||||
},
|
||||
chgFn: deleteStoreId(),
|
||||
masks: []string{"Name", "Description"},
|
||||
wantErr: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "no-version",
|
||||
orig: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Password: []byte("pass"),
|
||||
},
|
||||
},
|
||||
chgFn: deleteVersion(),
|
||||
masks: []string{"Name", "Description"},
|
||||
wantErr: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "updating-non-existent-credential",
|
||||
orig: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Name: "test-name-repo",
|
||||
Password: []byte("pass"),
|
||||
},
|
||||
},
|
||||
chgFn: combine(setPublicId("abcd_OOOOOOOOOO"), changeName("test-update-name-repo")),
|
||||
masks: []string{"Name"},
|
||||
wantErr: errors.RecordNotFound,
|
||||
},
|
||||
{
|
||||
name: "empty-field-mask",
|
||||
orig: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Name: "test-name-repo",
|
||||
Password: []byte("pass"),
|
||||
},
|
||||
},
|
||||
chgFn: changeName("test-update-name-repo"),
|
||||
wantErr: errors.EmptyFieldMask,
|
||||
},
|
||||
{
|
||||
name: "read-only-fields-in-field-mask",
|
||||
orig: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Name: "test-name-repo",
|
||||
Password: []byte("pass"),
|
||||
},
|
||||
},
|
||||
chgFn: changeName("test-update-name-repo"),
|
||||
masks: []string{"PublicId", "CreateTime", "UpdateTime", "ProjectId"},
|
||||
wantErr: errors.InvalidFieldMask,
|
||||
},
|
||||
{
|
||||
name: "unknown-field-in-field-mask",
|
||||
orig: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Name: "test-name-repo",
|
||||
Password: []byte("pass"),
|
||||
},
|
||||
},
|
||||
chgFn: changeName("test-update-name-repo"),
|
||||
masks: []string{"Bilbo"},
|
||||
wantErr: errors.InvalidFieldMask,
|
||||
},
|
||||
{
|
||||
name: "change-name",
|
||||
orig: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Name: "test-name-repo",
|
||||
Password: []byte("pass"),
|
||||
},
|
||||
},
|
||||
chgFn: changeName("test-update-name-repo"),
|
||||
masks: []string{"Name"},
|
||||
want: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Name: "test-update-name-repo",
|
||||
Password: []byte("pass"),
|
||||
},
|
||||
},
|
||||
wantCount: 1,
|
||||
},
|
||||
{
|
||||
name: "change-description",
|
||||
orig: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Description: "test-description-repo",
|
||||
Password: []byte("pass"),
|
||||
},
|
||||
},
|
||||
chgFn: changeDescription("test-update-description-repo"),
|
||||
masks: []string{"Description"},
|
||||
want: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Description: "test-update-description-repo",
|
||||
Password: []byte("pass"),
|
||||
},
|
||||
},
|
||||
wantCount: 1,
|
||||
},
|
||||
{
|
||||
name: "change-name-and-description",
|
||||
orig: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Name: "test-name-repo",
|
||||
Description: "test-description-repo",
|
||||
Password: []byte("pass"),
|
||||
},
|
||||
},
|
||||
chgFn: combine(changeDescription("test-update-description-repo"), changeName("test-update-name-repo")),
|
||||
masks: []string{"Name", "Description"},
|
||||
want: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Name: "test-update-name-repo",
|
||||
Description: "test-update-description-repo",
|
||||
Password: []byte("pass"),
|
||||
},
|
||||
},
|
||||
wantCount: 1,
|
||||
},
|
||||
{
|
||||
name: "change-password",
|
||||
orig: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Password: []byte("pass"),
|
||||
},
|
||||
},
|
||||
chgFn: changePassword("test-update-pass"),
|
||||
masks: []string{"Password"},
|
||||
want: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Password: []byte("test-update-pass"),
|
||||
},
|
||||
},
|
||||
wantCount: 1,
|
||||
},
|
||||
{
|
||||
name: "do-not-delete-password",
|
||||
orig: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Password: []byte("pass"),
|
||||
},
|
||||
},
|
||||
masks: []string{"Name"},
|
||||
chgFn: combine(changeName("new-name"), changePassword("")),
|
||||
want: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Name: "new-name",
|
||||
Password: []byte("pass"),
|
||||
},
|
||||
},
|
||||
wantCount: 1,
|
||||
},
|
||||
{
|
||||
name: "delete-name",
|
||||
orig: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Name: "test-name-repo",
|
||||
Description: "test-description-repo",
|
||||
Password: []byte("pass"),
|
||||
},
|
||||
},
|
||||
masks: []string{"Name"},
|
||||
chgFn: combine(changeDescription("test-update-description-repo"), changeName("")),
|
||||
want: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Description: "test-description-repo",
|
||||
Password: []byte("pass"),
|
||||
},
|
||||
},
|
||||
wantCount: 1,
|
||||
},
|
||||
{
|
||||
name: "delete-description",
|
||||
orig: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Name: "test-name-repo",
|
||||
Description: "test-description-repo",
|
||||
Password: []byte("pass"),
|
||||
},
|
||||
},
|
||||
masks: []string{"Description"},
|
||||
chgFn: combine(changeDescription(""), changeName("test-update-name-repo")),
|
||||
want: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Name: "test-name-repo",
|
||||
Password: []byte("pass"),
|
||||
},
|
||||
},
|
||||
wantCount: 1,
|
||||
},
|
||||
{
|
||||
name: "do-not-delete-name",
|
||||
orig: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Name: "test-name-repo",
|
||||
Description: "test-description-repo",
|
||||
Password: []byte("pass"),
|
||||
},
|
||||
},
|
||||
masks: []string{"Description"},
|
||||
chgFn: combine(changeDescription("test-update-description-repo"), changeName("")),
|
||||
want: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Name: "test-name-repo",
|
||||
Description: "test-update-description-repo",
|
||||
Password: []byte("pass"),
|
||||
},
|
||||
},
|
||||
wantCount: 1,
|
||||
},
|
||||
{
|
||||
name: "do-not-delete-description",
|
||||
orig: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Name: "test-name-repo",
|
||||
Description: "test-description-repo",
|
||||
Password: []byte("pass"),
|
||||
},
|
||||
},
|
||||
masks: []string{"Name"},
|
||||
chgFn: combine(changeDescription(""), changeName("test-update-name-repo")),
|
||||
want: &PasswordCredential{
|
||||
PasswordCredential: &store.PasswordCredential{
|
||||
Name: "test-update-name-repo",
|
||||
Description: "test-description-repo",
|
||||
Password: []byte("pass"),
|
||||
},
|
||||
},
|
||||
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()
|
||||
kkms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := NewRepository(ctx, rw, rw, kkms)
|
||||
assert.NoError(err)
|
||||
require.NotNil(repo)
|
||||
|
||||
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
store := TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
|
||||
tt.orig.StoreId = store.PublicId
|
||||
|
||||
orig, err := repo.CreatePasswordCredential(ctx, prj.GetPublicId(), tt.orig)
|
||||
assert.NoError(err)
|
||||
require.NotNil(orig)
|
||||
|
||||
if tt.chgFn != nil {
|
||||
orig = tt.chgFn(orig)
|
||||
}
|
||||
var version uint32
|
||||
if orig != nil {
|
||||
version = orig.GetVersion()
|
||||
}
|
||||
got, gotCount, err := repo.UpdatePasswordCredential(ctx, prj.GetPublicId(), orig, version, 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
|
||||
}
|
||||
assert.NoError(err)
|
||||
assert.Empty(tt.orig.PublicId)
|
||||
require.NotNil(got)
|
||||
assertPublicId(t, globals.PasswordCredentialPrefix, got.PublicId)
|
||||
assert.Equal(tt.wantCount, gotCount, "row count")
|
||||
assert.NotSame(tt.orig, got)
|
||||
assert.Equal(tt.orig.StoreId, got.StoreId)
|
||||
underlyingDB, err := conn.SqlDB(ctx)
|
||||
require.NoError(err)
|
||||
dbassert := dbassert.New(t, underlyingDB)
|
||||
if tt.want.Name == "" {
|
||||
got := got.clone()
|
||||
dbassert.IsNull(got, "name")
|
||||
} else {
|
||||
assert.Equal(tt.want.Name, got.Name)
|
||||
}
|
||||
|
||||
if tt.want.Description == "" {
|
||||
got := got.clone()
|
||||
dbassert.IsNull(got, "description")
|
||||
} else {
|
||||
assert.Equal(tt.want.Description, got.Description)
|
||||
}
|
||||
|
||||
assert.Equal(tt.want.Name, got.Name)
|
||||
|
||||
// Validate only passwordHmac is returned
|
||||
assert.Empty(got.Password)
|
||||
assert.Empty(got.CtPassword)
|
||||
assert.NotEmpty(got.PasswordHmac)
|
||||
|
||||
// Validate hmac
|
||||
databaseWrapper, err := kkms.GetWrapper(context.Background(), prj.GetPublicId(), kms.KeyPurposeDatabase)
|
||||
require.NoError(err)
|
||||
hm, err := crypto.HmacSha256(ctx, tt.want.Password, databaseWrapper, []byte(store.GetPublicId()), nil, crypto.WithEd25519())
|
||||
require.NoError(err)
|
||||
assert.Equal([]byte(hm), got.PasswordHmac)
|
||||
|
||||
if tt.wantCount > 0 {
|
||||
assert.NoError(db.TestVerifyOplog(t, rw, got.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second)))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepository_UpdatePasswordCredentialKeyUpdate(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
@@ -3407,6 +4006,7 @@ func TestRepository_ListDeletedCredentialIds(t *testing.T) {
|
||||
sshCreds := TestSshPrivateKeyCredentials(t, conn, wrapper, "username", TestSshPrivateKeyPem, store.GetPublicId(), prj.GetPublicId(), 2)
|
||||
pwCreds := TestUsernamePasswordCredentials(t, conn, wrapper, "username", "testpassword", store.GetPublicId(), prj.GetPublicId(), 2)
|
||||
updCreds := TestUsernamePasswordDomainCredentials(t, conn, wrapper, "username", "testpassword", "domain", store.GetPublicId(), prj.GetPublicId(), 2)
|
||||
pCreds := TestPasswordCredentials(t, conn, wrapper, "testpassword", store.GetPublicId(), prj.GetPublicId(), 2)
|
||||
|
||||
repo, err := NewRepository(ctx, rw, rw, kms)
|
||||
require.NoError(err)
|
||||
@@ -3490,6 +4090,23 @@ func TestRepository_ListDeletedCredentialIds(t *testing.T) {
|
||||
require.True(time.Now().Before(ttime.Add(10 * time.Second)))
|
||||
require.True(time.Now().After(ttime.Add(-10 * time.Second)))
|
||||
|
||||
// Delete a p credential
|
||||
_, err = staticRepo.DeleteCredential(ctx, prj.GetPublicId(), pCreds[0].GetPublicId())
|
||||
require.NoError(err)
|
||||
|
||||
// Expect five entries
|
||||
deletedIds, ttime, err = repo.ListDeletedCredentialIds(ctx, time.Now().AddDate(-1, 0, 0))
|
||||
require.NoError(err)
|
||||
assert.Empty(
|
||||
cmp.Diff(
|
||||
[]string{jsonCreds[0].GetPublicId(), sshCreds[0].GetPublicId(), pwCreds[0].GetPublicId(), updCreds[0].GetPublicId(), pCreds[0].GetPublicId()},
|
||||
deletedIds,
|
||||
cmpopts.SortSlices(func(i, j string) bool { return i < j }),
|
||||
),
|
||||
)
|
||||
require.True(time.Now().Before(ttime.Add(10 * time.Second)))
|
||||
require.True(time.Now().After(ttime.Add(-10 * time.Second)))
|
||||
|
||||
// Try again with the time set to now, expect no entries
|
||||
deletedIds, ttime, err = repo.ListDeletedCredentialIds(ctx, time.Now())
|
||||
require.NoError(err)
|
||||
@@ -3529,13 +4146,14 @@ func TestRepository_EstimatedCredentialCount(t *testing.T) {
|
||||
sshCreds := TestSshPrivateKeyCredentials(t, conn, wrapper, "username", TestSshPrivateKeyPem, staticStore.GetPublicId(), prj.GetPublicId(), 2)
|
||||
pwCreds := TestUsernamePasswordCredentials(t, conn, wrapper, "username", "testpassword", staticStore.GetPublicId(), prj.GetPublicId(), 2)
|
||||
updCreds := TestUsernamePasswordDomainCredentials(t, conn, wrapper, "username", "testpassword", "domain", staticStore.GetPublicId(), prj.GetPublicId(), 2)
|
||||
pCreds := TestPasswordCredentials(t, conn, wrapper, "testpassword", staticStore.GetPublicId(), prj.GetPublicId(), 2)
|
||||
// Run analyze to update postgres meta tables
|
||||
_, err = sqlDb.ExecContext(ctx, "analyze")
|
||||
require.NoError(err)
|
||||
|
||||
numItems, err = repo.EstimatedCredentialCount(ctx)
|
||||
require.NoError(err)
|
||||
assert.Equal(8, numItems)
|
||||
assert.Equal(10, numItems)
|
||||
|
||||
// Delete a json credential
|
||||
_, err = staticRepo.DeleteCredential(ctx, prj.GetPublicId(), jsonCreds[0].GetPublicId())
|
||||
@@ -3545,7 +4163,7 @@ func TestRepository_EstimatedCredentialCount(t *testing.T) {
|
||||
|
||||
numItems, err = repo.EstimatedCredentialCount(ctx)
|
||||
require.NoError(err)
|
||||
assert.Equal(7, numItems)
|
||||
assert.Equal(9, numItems)
|
||||
|
||||
// Delete a ssh credential
|
||||
_, err = staticRepo.DeleteCredential(ctx, prj.GetPublicId(), sshCreds[0].GetPublicId())
|
||||
@@ -3555,7 +4173,7 @@ func TestRepository_EstimatedCredentialCount(t *testing.T) {
|
||||
|
||||
numItems, err = repo.EstimatedCredentialCount(ctx)
|
||||
require.NoError(err)
|
||||
assert.Equal(6, numItems)
|
||||
assert.Equal(8, numItems)
|
||||
|
||||
// Delete a pw credential
|
||||
_, err = staticRepo.DeleteCredential(ctx, prj.GetPublicId(), pwCreds[0].GetPublicId())
|
||||
@@ -3565,7 +4183,7 @@ func TestRepository_EstimatedCredentialCount(t *testing.T) {
|
||||
|
||||
numItems, err = repo.EstimatedCredentialCount(ctx)
|
||||
require.NoError(err)
|
||||
assert.Equal(5, numItems)
|
||||
assert.Equal(7, numItems)
|
||||
|
||||
// Delete a upd credential
|
||||
_, err = staticRepo.DeleteCredential(ctx, prj.GetPublicId(), updCreds[0].GetPublicId())
|
||||
@@ -3575,5 +4193,15 @@ func TestRepository_EstimatedCredentialCount(t *testing.T) {
|
||||
|
||||
numItems, err = repo.EstimatedCredentialCount(ctx)
|
||||
require.NoError(err)
|
||||
assert.Equal(4, numItems)
|
||||
assert.Equal(6, numItems)
|
||||
|
||||
// Delete a p credential
|
||||
_, err = staticRepo.DeleteCredential(ctx, prj.GetPublicId(), pCreds[0].GetPublicId())
|
||||
require.NoError(err)
|
||||
_, err = sqlDb.ExecContext(ctx, "analyze")
|
||||
require.NoError(err)
|
||||
|
||||
numItems, err = repo.EstimatedCredentialCount(ctx)
|
||||
require.NoError(err)
|
||||
assert.Equal(5, numItems)
|
||||
}
|
||||
@@ -30,6 +30,11 @@ func (r *Repository) Retrieve(ctx context.Context, projectId string, ids []strin
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
var pCreds []*PasswordCredential
|
||||
err = r.reader.SearchWhere(ctx, &pCreds, "public_id in (?)", []any{ids})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
var spkCreds []*SshPrivateKeyCredential
|
||||
err = r.reader.SearchWhere(ctx, &spkCreds, "public_id in (?)", []any{ids})
|
||||
if err != nil {
|
||||
@@ -41,9 +46,9 @@ func (r *Repository) Retrieve(ctx context.Context, projectId string, ids []strin
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
if len(upCreds)+len(updCreds)+len(spkCreds)+len(jsonCreds) != len(ids) {
|
||||
if len(upCreds)+len(updCreds)+len(pCreds)+len(spkCreds)+len(jsonCreds) != len(ids) {
|
||||
return nil, errors.New(ctx, errors.NotSpecificIntegrity, op,
|
||||
fmt.Sprintf("mismatch between creds and number of ids requested, expected %d got %d", len(ids), len(upCreds)+len(spkCreds)+len(jsonCreds)))
|
||||
fmt.Sprintf("mismatch between creds and number of ids requested, expected %d got %d", len(ids), len(upCreds)+len(updCreds)+len(pCreds)+len(spkCreds)+len(jsonCreds)))
|
||||
}
|
||||
|
||||
out := make([]credential.Static, 0, len(ids))
|
||||
@@ -73,6 +78,19 @@ func (r *Repository) Retrieve(ctx context.Context, projectId string, ids []strin
|
||||
out = append(out, c)
|
||||
}
|
||||
|
||||
for _, c := range pCreds {
|
||||
// decrypt credential
|
||||
databaseWrapper, err := r.kms.GetWrapper(ctx, projectId, kms.KeyPurposeDatabase)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get database wrapper"))
|
||||
}
|
||||
if err := c.decrypt(ctx, databaseWrapper); err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
out = append(out, c)
|
||||
}
|
||||
|
||||
for _, c := range spkCreds {
|
||||
// decrypt credential
|
||||
databaseWrapper, err := r.kms.GetWrapper(ctx, projectId, kms.KeyPurposeDatabase)
|
||||
|
||||
@@ -39,6 +39,8 @@ func TestRepository_Retrieve(t *testing.T) {
|
||||
upCred2 := TestUsernamePasswordCredential(t, conn, wrapper, "different user", "better password", staticStore.GetPublicId(), prj.GetPublicId())
|
||||
updCred1 := TestUsernamePasswordDomainCredential(t, conn, wrapper, "user", "pass", "domain.com", staticStore.GetPublicId(), prj.GetPublicId())
|
||||
updCred2 := TestUsernamePasswordDomainCredential(t, conn, wrapper, "different user", "better password", "new-domain.com", staticStore.GetPublicId(), prj.GetPublicId())
|
||||
pCred1 := TestPasswordCredential(t, conn, wrapper, "better password", staticStore.GetPublicId(), prj.GetPublicId())
|
||||
pCred2 := TestPasswordCredential(t, conn, wrapper, "another password", staticStore.GetPublicId(), prj.GetPublicId())
|
||||
spkCred1 := TestSshPrivateKeyCredential(t, conn, wrapper, "final user", string(testdata.PEMBytes["ed25519"]), staticStore.GetPublicId(), prj.GetPublicId())
|
||||
spkCred2 := TestSshPrivateKeyCredential(t, conn, wrapper, "last user", string(testdata.PEMBytes["rsa-openssh-format"]), staticStore.GetPublicId(), prj.GetPublicId())
|
||||
spkCredWithPass := TestSshPrivateKeyCredential(t, conn, wrapper, "another last user",
|
||||
@@ -125,6 +127,26 @@ func TestRepository_Retrieve(t *testing.T) {
|
||||
updCred1, updCred2,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-one-p-cred",
|
||||
args: args{
|
||||
projectId: prj.GetPublicId(),
|
||||
credIds: []string{pCred1.GetPublicId()},
|
||||
},
|
||||
wantCreds: []credential.Static{
|
||||
pCred1,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-multiple-p-creds",
|
||||
args: args{
|
||||
projectId: prj.GetPublicId(),
|
||||
credIds: []string{pCred1.GetPublicId(), pCred2.GetPublicId()},
|
||||
},
|
||||
wantCreds: []credential.Static{
|
||||
pCred1, pCred2,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-ssh-pk-cred",
|
||||
args: args{
|
||||
@@ -169,10 +191,10 @@ func TestRepository_Retrieve(t *testing.T) {
|
||||
name: "valid-mixed-creds",
|
||||
args: args{
|
||||
projectId: prj.GetPublicId(),
|
||||
credIds: []string{upCred1.GetPublicId(), spkCred1.GetPublicId(), spkCredWithPass.GetPublicId(), spkCred2.GetPublicId(), upCred2.GetPublicId(), jsonCred1.GetPublicId(), jsonCred2.GetPublicId(), updCred1.GetPublicId(), updCred2.GetPublicId()},
|
||||
credIds: []string{upCred1.GetPublicId(), spkCred1.GetPublicId(), spkCredWithPass.GetPublicId(), spkCred2.GetPublicId(), upCred2.GetPublicId(), jsonCred1.GetPublicId(), jsonCred2.GetPublicId(), updCred1.GetPublicId(), updCred2.GetPublicId(), pCred1.GetPublicId(), pCred2.GetPublicId()},
|
||||
},
|
||||
wantCreds: []credential.Static{
|
||||
upCred1, spkCred1, spkCredWithPass, spkCred2, upCred2, jsonCred1, jsonCred2, updCred1, updCred2,
|
||||
upCred1, spkCred1, spkCredWithPass, spkCred2, upCred2, jsonCred1, jsonCred2, updCred1, updCred2, pCred1, pCred2,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -193,6 +215,7 @@ func TestRepository_Retrieve(t *testing.T) {
|
||||
cmpopts.IgnoreUnexported(
|
||||
UsernamePasswordCredential{}, store.UsernamePasswordCredential{},
|
||||
UsernamePasswordDomainCredential{}, store.UsernamePasswordDomainCredential{},
|
||||
PasswordCredential{}, store.PasswordCredential{},
|
||||
SshPrivateKeyCredential{}, store.SshPrivateKeyCredential{},
|
||||
JsonCredential{}, store.JsonCredential{}),
|
||||
cmpopts.IgnoreTypes(×tamp.Timestamp{}),
|
||||
|
||||
@@ -17,6 +17,7 @@ func init() {
|
||||
kms.RegisterTableRewrapFn("credential_static_ssh_private_key_credential", credStaticSshPrivKeyRewrapFn)
|
||||
kms.RegisterTableRewrapFn("credential_static_json_credential", credStaticJsonRewrapFn)
|
||||
kms.RegisterTableRewrapFn("credential_static_username_password_domain_credential", credStaticUsernamePasswordDomainRewrapFn)
|
||||
kms.RegisterTableRewrapFn("credential_static_password_credential", credStaticPasswordRewrapFn)
|
||||
}
|
||||
|
||||
func rewrapParameterChecks(ctx context.Context, dataKeyVersionId string, scopeId string, reader db.Reader, writer db.Writer, kmsRepo kms.GetWrapperer) string {
|
||||
@@ -128,6 +129,51 @@ func credStaticUsernamePasswordDomainRewrapFn(ctx context.Context, dataKeyVersio
|
||||
return nil
|
||||
}
|
||||
|
||||
func credStaticPasswordRewrapFn(ctx context.Context, dataKeyVersionId, scopeId string, reader db.Reader, writer db.Writer, kmsRepo kms.GetWrapperer) error {
|
||||
const op = "static.credStaticPasswordRewrapFn"
|
||||
if errStr := rewrapParameterChecks(ctx, dataKeyVersionId, scopeId, reader, writer, kmsRepo); errStr != "" {
|
||||
return errors.New(ctx, errors.InvalidParameter, op, errStr)
|
||||
}
|
||||
var creds []*PasswordCredential
|
||||
// Indexes exist on (store_id, etc), so we can query static stores via scope and refine with key id.
|
||||
// This is the fastest query we can use without creating a new index on key_id.
|
||||
rows, err := reader.Query(ctx, credStaticPasswordRewrapQuery, []any{scopeId, dataKeyVersionId})
|
||||
if err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithMsg("failed to query sql for rows that need rewrapping"))
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
cred := allocPasswordCredential()
|
||||
if err := rows.Scan(
|
||||
&cred.PublicId,
|
||||
&cred.CtPassword,
|
||||
&cred.KeyId,
|
||||
); err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithMsg("failed to failed to scan row"))
|
||||
}
|
||||
creds = append(creds, cred)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithMsg("failed to iterate over retrieved rows"))
|
||||
}
|
||||
wrapper, err := kmsRepo.GetWrapper(ctx, scopeId, kms.KeyPurposeDatabase)
|
||||
if err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithMsg("failed to fetch kms wrapper for rewrapping"))
|
||||
}
|
||||
for _, cred := range creds {
|
||||
if err := cred.decrypt(ctx, wrapper); err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithMsg("failed to decrypt password credential"))
|
||||
}
|
||||
if err := cred.encrypt(ctx, wrapper); err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithMsg("failed to re-encrypt password credential"))
|
||||
}
|
||||
if _, err := writer.Update(ctx, cred, []string{"CtPassword", "KeyId"}, nil); err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithMsg("failed to update password credential row with rewrapped fields"))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func credStaticSshPrivKeyRewrapFn(ctx context.Context, dataKeyVersionId, scopeId string, reader db.Reader, writer db.Writer, kmsRepo kms.GetWrapperer) error {
|
||||
const op = "static.credStaticSshPrivKeyRewrapFn"
|
||||
if errStr := rewrapParameterChecks(ctx, dataKeyVersionId, scopeId, reader, writer, kmsRepo); errStr != "" {
|
||||
|
||||
@@ -321,3 +321,67 @@ func TestRewrap_credStaticUsernamePasswordDomainRewrapFn(t *testing.T) {
|
||||
assert.Equal(t, cred.GetPasswordHmac(), got.GetPasswordHmac())
|
||||
})
|
||||
}
|
||||
|
||||
func TestRewrap_credStaticPasswordRewrapFn(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
t.Run("errors-on-query-error", func(t *testing.T) {
|
||||
conn, mock := db.TestSetupWithMock(t)
|
||||
wrapper := db.TestWrapper(t)
|
||||
mock.ExpectQuery(
|
||||
`SELECT \* FROM "kms_schema_version" WHERE 1=1 ORDER BY "kms_schema_version"\."version" LIMIT \$1`,
|
||||
).WillReturnRows(sqlmock.NewRows([]string{"version", "create_time"}).AddRow(migrations.Version, time.Now()))
|
||||
mock.ExpectQuery(
|
||||
`SELECT \* FROM "kms_oplog_schema_version" WHERE 1=1 ORDER BY "kms_oplog_schema_version"."version" LIMIT \$1`,
|
||||
).WillReturnRows(sqlmock.NewRows([]string{"version", "create_time"}).AddRow(migrations.Version, time.Now()))
|
||||
kmsCache := kms.TestKms(t, conn, wrapper)
|
||||
rw := db.New(conn)
|
||||
mock.ExpectQuery(
|
||||
`select distinct pass\.public_id, pass\.password_encrypted, pass\.key_id from credential_static_password_credential pass inner join credential_static_store store on store\.public_id = pass\.store_id where store\.project_id = \$1 and pass\.key_id = \$2;`,
|
||||
).WillReturnError(errors.New("Query error"))
|
||||
err := credStaticPasswordRewrapFn(ctx, "some_id", "some_scope", rw, rw, kmsCache)
|
||||
require.Error(t, err)
|
||||
})
|
||||
t.Run("success", func(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
kmsCache := kms.TestKms(t, conn, wrapper)
|
||||
rw := db.New(conn)
|
||||
|
||||
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
cs := TestCredentialStore(t, conn, wrapper, prj.PublicId)
|
||||
cred, err := NewPasswordCredential(cs.GetPublicId(), "password")
|
||||
assert.NoError(t, err)
|
||||
|
||||
cred.PublicId, err = credential.NewPasswordCredentialId(ctx)
|
||||
assert.NoError(t, err)
|
||||
|
||||
kmsWrapper, err := kmsCache.GetWrapper(context.Background(), prj.PublicId, kms.KeyPurposeDatabase)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.NoError(t, cred.encrypt(ctx, kmsWrapper))
|
||||
assert.NoError(t, rw.Create(context.Background(), cred))
|
||||
|
||||
// now things are stored in the db, we can rotate and rewrap
|
||||
assert.NoError(t, kmsCache.RotateKeys(ctx, prj.PublicId))
|
||||
assert.NoError(t, credStaticPasswordRewrapFn(ctx, cred.GetKeyId(), prj.PublicId, rw, rw, kmsCache))
|
||||
|
||||
// now we pull the credential back from the db, decrypt it with the new key, and ensure things match
|
||||
got := allocPasswordCredential()
|
||||
got.PublicId = cred.PublicId
|
||||
assert.NoError(t, rw.LookupById(ctx, got))
|
||||
|
||||
kmsWrapper2, err := kmsCache.GetWrapper(context.Background(), prj.PublicId, kms.KeyPurposeDatabase, kms.WithKeyId(got.GetKeyId()))
|
||||
assert.NoError(t, err)
|
||||
newKeyVersionId, err := kmsWrapper2.KeyId(ctx)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// decrypt with the new key version and check to make sure things match
|
||||
assert.NoError(t, got.decrypt(ctx, kmsWrapper2))
|
||||
assert.NotEmpty(t, got.GetKeyId())
|
||||
assert.NotEqual(t, cred.GetKeyId(), got.GetKeyId())
|
||||
assert.Equal(t, newKeyVersionId, got.GetKeyId())
|
||||
assert.Equal(t, "password", string(got.GetPassword()))
|
||||
assert.NotEmpty(t, got.GetPasswordHmac())
|
||||
assert.Equal(t, cred.GetPasswordHmac(), got.GetPasswordHmac())
|
||||
})
|
||||
}
|
||||
@@ -136,6 +136,157 @@ func (x *CredentialStore) GetVersion() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
type PasswordCredential struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// 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 static 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"`
|
||||
// password is the plain-text of the password associated with the credential. We are
|
||||
// not storing this plain-text password in the database.
|
||||
// @inject_tag: `gorm:"-" wrapping:"pt,password_data"`
|
||||
Password []byte `protobuf:"bytes,8,opt,name=password,proto3" json:"password,omitempty" gorm:"-" wrapping:"pt,password_data"`
|
||||
// ct_password is the ciphertext of the password. It
|
||||
// is stored in the database.
|
||||
// @inject_tag: `gorm:"column:password_encrypted;not_null" wrapping:"ct,password_data"`
|
||||
CtPassword []byte `protobuf:"bytes,9,opt,name=ct_password,json=ctPassword,proto3" json:"ct_password,omitempty" gorm:"column:password_encrypted;not_null" wrapping:"ct,password_data"`
|
||||
// password_hmac is a sha256-hmac of the unencrypted password. It is recalculated
|
||||
// everytime the password is updated.
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
PasswordHmac []byte `protobuf:"bytes,10,opt,name=password_hmac,json=passwordHmac,proto3" json:"password_hmac,omitempty" gorm:"not_null"`
|
||||
// The key_id of the kms database key used for encrypting this entry.
|
||||
// It must be set.
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
KeyId string `protobuf:"bytes,11,opt,name=key_id,json=keyId,proto3" json:"key_id,omitempty" gorm:"not_null"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *PasswordCredential) Reset() {
|
||||
*x = PasswordCredential{}
|
||||
mi := &file_controller_storage_credential_static_store_v1_static_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *PasswordCredential) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*PasswordCredential) ProtoMessage() {}
|
||||
|
||||
func (x *PasswordCredential) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_storage_credential_static_store_v1_static_proto_msgTypes[1]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use PasswordCredential.ProtoReflect.Descriptor instead.
|
||||
func (*PasswordCredential) Descriptor() ([]byte, []int) {
|
||||
return file_controller_storage_credential_static_store_v1_static_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *PasswordCredential) GetPublicId() string {
|
||||
if x != nil {
|
||||
return x.PublicId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *PasswordCredential) GetCreateTime() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.CreateTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *PasswordCredential) GetUpdateTime() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.UpdateTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *PasswordCredential) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *PasswordCredential) GetDescription() string {
|
||||
if x != nil {
|
||||
return x.Description
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *PasswordCredential) GetStoreId() string {
|
||||
if x != nil {
|
||||
return x.StoreId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *PasswordCredential) GetVersion() uint32 {
|
||||
if x != nil {
|
||||
return x.Version
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *PasswordCredential) GetPassword() []byte {
|
||||
if x != nil {
|
||||
return x.Password
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *PasswordCredential) GetCtPassword() []byte {
|
||||
if x != nil {
|
||||
return x.CtPassword
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *PasswordCredential) GetPasswordHmac() []byte {
|
||||
if x != nil {
|
||||
return x.PasswordHmac
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *PasswordCredential) GetKeyId() string {
|
||||
if x != nil {
|
||||
return x.KeyId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type UsernamePasswordCredential struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// public_id is a surrogate key suitable for use in a public API.
|
||||
@@ -186,7 +337,7 @@ type UsernamePasswordCredential struct {
|
||||
|
||||
func (x *UsernamePasswordCredential) Reset() {
|
||||
*x = UsernamePasswordCredential{}
|
||||
mi := &file_controller_storage_credential_static_store_v1_static_proto_msgTypes[1]
|
||||
mi := &file_controller_storage_credential_static_store_v1_static_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -198,7 +349,7 @@ func (x *UsernamePasswordCredential) String() string {
|
||||
func (*UsernamePasswordCredential) ProtoMessage() {}
|
||||
|
||||
func (x *UsernamePasswordCredential) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_storage_credential_static_store_v1_static_proto_msgTypes[1]
|
||||
mi := &file_controller_storage_credential_static_store_v1_static_proto_msgTypes[2]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -211,7 +362,7 @@ func (x *UsernamePasswordCredential) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use UsernamePasswordCredential.ProtoReflect.Descriptor instead.
|
||||
func (*UsernamePasswordCredential) Descriptor() ([]byte, []int) {
|
||||
return file_controller_storage_credential_static_store_v1_static_proto_rawDescGZIP(), []int{1}
|
||||
return file_controller_storage_credential_static_store_v1_static_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *UsernamePasswordCredential) GetPublicId() string {
|
||||
@@ -352,7 +503,7 @@ type UsernamePasswordDomainCredential struct {
|
||||
|
||||
func (x *UsernamePasswordDomainCredential) Reset() {
|
||||
*x = UsernamePasswordDomainCredential{}
|
||||
mi := &file_controller_storage_credential_static_store_v1_static_proto_msgTypes[2]
|
||||
mi := &file_controller_storage_credential_static_store_v1_static_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -364,7 +515,7 @@ func (x *UsernamePasswordDomainCredential) String() string {
|
||||
func (*UsernamePasswordDomainCredential) ProtoMessage() {}
|
||||
|
||||
func (x *UsernamePasswordDomainCredential) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_storage_credential_static_store_v1_static_proto_msgTypes[2]
|
||||
mi := &file_controller_storage_credential_static_store_v1_static_proto_msgTypes[3]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -377,7 +528,7 @@ func (x *UsernamePasswordDomainCredential) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use UsernamePasswordDomainCredential.ProtoReflect.Descriptor instead.
|
||||
func (*UsernamePasswordDomainCredential) Descriptor() ([]byte, []int) {
|
||||
return file_controller_storage_credential_static_store_v1_static_proto_rawDescGZIP(), []int{2}
|
||||
return file_controller_storage_credential_static_store_v1_static_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *UsernamePasswordDomainCredential) GetPublicId() string {
|
||||
@@ -534,7 +685,7 @@ type SshPrivateKeyCredential struct {
|
||||
|
||||
func (x *SshPrivateKeyCredential) Reset() {
|
||||
*x = SshPrivateKeyCredential{}
|
||||
mi := &file_controller_storage_credential_static_store_v1_static_proto_msgTypes[3]
|
||||
mi := &file_controller_storage_credential_static_store_v1_static_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -546,7 +697,7 @@ func (x *SshPrivateKeyCredential) String() string {
|
||||
func (*SshPrivateKeyCredential) ProtoMessage() {}
|
||||
|
||||
func (x *SshPrivateKeyCredential) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_storage_credential_static_store_v1_static_proto_msgTypes[3]
|
||||
mi := &file_controller_storage_credential_static_store_v1_static_proto_msgTypes[4]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -559,7 +710,7 @@ func (x *SshPrivateKeyCredential) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use SshPrivateKeyCredential.ProtoReflect.Descriptor instead.
|
||||
func (*SshPrivateKeyCredential) Descriptor() ([]byte, []int) {
|
||||
return file_controller_storage_credential_static_store_v1_static_proto_rawDescGZIP(), []int{3}
|
||||
return file_controller_storage_credential_static_store_v1_static_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *SshPrivateKeyCredential) GetPublicId() string {
|
||||
@@ -713,7 +864,7 @@ type JsonCredential struct {
|
||||
|
||||
func (x *JsonCredential) Reset() {
|
||||
*x = JsonCredential{}
|
||||
mi := &file_controller_storage_credential_static_store_v1_static_proto_msgTypes[4]
|
||||
mi := &file_controller_storage_credential_static_store_v1_static_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -725,7 +876,7 @@ func (x *JsonCredential) String() string {
|
||||
func (*JsonCredential) ProtoMessage() {}
|
||||
|
||||
func (x *JsonCredential) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_storage_credential_static_store_v1_static_proto_msgTypes[4]
|
||||
mi := &file_controller_storage_credential_static_store_v1_static_proto_msgTypes[5]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -738,7 +889,7 @@ func (x *JsonCredential) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use JsonCredential.ProtoReflect.Descriptor instead.
|
||||
func (*JsonCredential) Descriptor() ([]byte, []int) {
|
||||
return file_controller_storage_credential_static_store_v1_static_proto_rawDescGZIP(), []int{4}
|
||||
return file_controller_storage_credential_static_store_v1_static_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *JsonCredential) GetPublicId() string {
|
||||
@@ -835,7 +986,27 @@ const file_controller_storage_credential_static_store_v1_static_proto_rawDesc =
|
||||
"\vDescription\x12\vdescriptionR\vdescription\x12\x1d\n" +
|
||||
"\n" +
|
||||
"project_id\x18\x06 \x01(\tR\tprojectId\x12\x18\n" +
|
||||
"\aversion\x18\a \x01(\rR\aversion\"\xfd\x04\n" +
|
||||
"\aversion\x18\a \x01(\rR\aversion\"\xb4\x04\n" +
|
||||
"\x12PasswordCredential\x12\x1b\n" +
|
||||
"\tpublic_id\x18\x01 \x01(\tR\bpublicId\x12K\n" +
|
||||
"\vcreate_time\x18\x02 \x01(\v2*.controller.storage.timestamp.v1.TimestampR\n" +
|
||||
"createTime\x12K\n" +
|
||||
"\vupdate_time\x18\x03 \x01(\v2*.controller.storage.timestamp.v1.TimestampR\n" +
|
||||
"updateTime\x12$\n" +
|
||||
"\x04name\x18\x04 \x01(\tB\x10\xc2\xdd)\f\n" +
|
||||
"\x04Name\x12\x04nameR\x04name\x12@\n" +
|
||||
"\vdescription\x18\x05 \x01(\tB\x1e\xc2\xdd)\x1a\n" +
|
||||
"\vDescription\x12\vdescriptionR\vdescription\x12\x19\n" +
|
||||
"\bstore_id\x18\x06 \x01(\tR\astoreId\x12\x18\n" +
|
||||
"\aversion\x18\a \x01(\rR\aversion\x12?\n" +
|
||||
"\bpassword\x18\b \x01(\fB#\xc2\xdd)\x1f\n" +
|
||||
"\bPassword\x12\x13attributes.passwordR\bpassword\x12\x1f\n" +
|
||||
"\vct_password\x18\t \x01(\fR\n" +
|
||||
"ctPassword\x12Q\n" +
|
||||
"\rpassword_hmac\x18\n" +
|
||||
" \x01(\fB,\xc2\xdd)(\n" +
|
||||
"\fPasswordHmac\x12\x18attributes.password_hmacR\fpasswordHmac\x12\x15\n" +
|
||||
"\x06key_id\x18\v \x01(\tR\x05keyId\"\xfd\x04\n" +
|
||||
"\x1aUsernamePasswordCredential\x12\x1b\n" +
|
||||
"\tpublic_id\x18\x01 \x01(\tR\bpublicId\x12K\n" +
|
||||
"\vcreate_time\x18\x02 \x01(\v2*.controller.storage.timestamp.v1.TimestampR\n" +
|
||||
@@ -944,31 +1115,34 @@ func file_controller_storage_credential_static_store_v1_static_proto_rawDescGZIP
|
||||
return file_controller_storage_credential_static_store_v1_static_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_controller_storage_credential_static_store_v1_static_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
|
||||
var file_controller_storage_credential_static_store_v1_static_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
|
||||
var file_controller_storage_credential_static_store_v1_static_proto_goTypes = []any{
|
||||
(*CredentialStore)(nil), // 0: controller.storage.credential.static.store.v1.CredentialStore
|
||||
(*UsernamePasswordCredential)(nil), // 1: controller.storage.credential.static.store.v1.UsernamePasswordCredential
|
||||
(*UsernamePasswordDomainCredential)(nil), // 2: controller.storage.credential.static.store.v1.UsernamePasswordDomainCredential
|
||||
(*SshPrivateKeyCredential)(nil), // 3: controller.storage.credential.static.store.v1.SshPrivateKeyCredential
|
||||
(*JsonCredential)(nil), // 4: controller.storage.credential.static.store.v1.JsonCredential
|
||||
(*timestamp.Timestamp)(nil), // 5: controller.storage.timestamp.v1.Timestamp
|
||||
(*PasswordCredential)(nil), // 1: controller.storage.credential.static.store.v1.PasswordCredential
|
||||
(*UsernamePasswordCredential)(nil), // 2: controller.storage.credential.static.store.v1.UsernamePasswordCredential
|
||||
(*UsernamePasswordDomainCredential)(nil), // 3: controller.storage.credential.static.store.v1.UsernamePasswordDomainCredential
|
||||
(*SshPrivateKeyCredential)(nil), // 4: controller.storage.credential.static.store.v1.SshPrivateKeyCredential
|
||||
(*JsonCredential)(nil), // 5: controller.storage.credential.static.store.v1.JsonCredential
|
||||
(*timestamp.Timestamp)(nil), // 6: controller.storage.timestamp.v1.Timestamp
|
||||
}
|
||||
var file_controller_storage_credential_static_store_v1_static_proto_depIdxs = []int32{
|
||||
5, // 0: controller.storage.credential.static.store.v1.CredentialStore.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
5, // 1: controller.storage.credential.static.store.v1.CredentialStore.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
5, // 2: controller.storage.credential.static.store.v1.UsernamePasswordCredential.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
5, // 3: controller.storage.credential.static.store.v1.UsernamePasswordCredential.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
5, // 4: controller.storage.credential.static.store.v1.UsernamePasswordDomainCredential.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
5, // 5: controller.storage.credential.static.store.v1.UsernamePasswordDomainCredential.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
5, // 6: controller.storage.credential.static.store.v1.SshPrivateKeyCredential.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
5, // 7: controller.storage.credential.static.store.v1.SshPrivateKeyCredential.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
5, // 8: controller.storage.credential.static.store.v1.JsonCredential.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
5, // 9: controller.storage.credential.static.store.v1.JsonCredential.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // [10:10] is the sub-list for method output_type
|
||||
10, // [10:10] is the sub-list for method input_type
|
||||
10, // [10:10] is the sub-list for extension type_name
|
||||
10, // [10:10] is the sub-list for extension extendee
|
||||
0, // [0:10] is the sub-list for field type_name
|
||||
6, // 0: controller.storage.credential.static.store.v1.CredentialStore.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
6, // 1: controller.storage.credential.static.store.v1.CredentialStore.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
6, // 2: controller.storage.credential.static.store.v1.PasswordCredential.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
6, // 3: controller.storage.credential.static.store.v1.PasswordCredential.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
6, // 4: controller.storage.credential.static.store.v1.UsernamePasswordCredential.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
6, // 5: controller.storage.credential.static.store.v1.UsernamePasswordCredential.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
6, // 6: controller.storage.credential.static.store.v1.UsernamePasswordDomainCredential.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
6, // 7: controller.storage.credential.static.store.v1.UsernamePasswordDomainCredential.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
6, // 8: controller.storage.credential.static.store.v1.SshPrivateKeyCredential.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
6, // 9: controller.storage.credential.static.store.v1.SshPrivateKeyCredential.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
6, // 10: controller.storage.credential.static.store.v1.JsonCredential.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
6, // 11: controller.storage.credential.static.store.v1.JsonCredential.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
12, // [12:12] is the sub-list for method output_type
|
||||
12, // [12:12] is the sub-list for method input_type
|
||||
12, // [12:12] is the sub-list for extension type_name
|
||||
12, // [12:12] is the sub-list for extension extendee
|
||||
0, // [0:12] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_controller_storage_credential_static_store_v1_static_proto_init() }
|
||||
@@ -982,7 +1156,7 @@ func file_controller_storage_credential_static_store_v1_static_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_controller_storage_credential_static_store_v1_static_proto_rawDesc), len(file_controller_storage_credential_static_store_v1_static_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 5,
|
||||
NumMessages: 6,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
||||
@@ -284,6 +284,77 @@ func TestUsernamePasswordDomainCredentials(
|
||||
return creds
|
||||
}
|
||||
|
||||
// TestPasswordCredential creates a password credential in the provided DB with
|
||||
// the provided project id and any values passed in through.
|
||||
// If any errors are encountered during the creation of the store, the test will fail.
|
||||
func TestPasswordCredential(
|
||||
t testing.TB,
|
||||
conn *db.DB,
|
||||
wrapper wrapping.Wrapper,
|
||||
password, storeId, projectId string,
|
||||
opts ...Option,
|
||||
) *PasswordCredential {
|
||||
t.Helper()
|
||||
ctx := context.Background()
|
||||
kmsCache := kms.TestKms(t, conn, wrapper)
|
||||
w := db.New(conn)
|
||||
|
||||
opt := getOpts(opts...)
|
||||
|
||||
databaseWrapper, err := kmsCache.GetWrapper(ctx, projectId, kms.KeyPurposeDatabase)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, databaseWrapper)
|
||||
|
||||
cred, err := NewPasswordCredential(storeId, credential.Password(password), opts...)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, cred)
|
||||
|
||||
id := opt.withPublicId
|
||||
if id == "" {
|
||||
id, err = credential.NewPasswordCredentialId(ctx)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
cred.PublicId = id
|
||||
|
||||
err = cred.encrypt(ctx, databaseWrapper)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err2 := w.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{},
|
||||
func(_ db.Reader, iw db.Writer) error {
|
||||
require.NoError(t, iw.Create(ctx, cred))
|
||||
return nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err2)
|
||||
|
||||
return cred
|
||||
}
|
||||
|
||||
// TestPasswordCredentials creates count number of password credentials in
|
||||
// the provided DB with the provided project id. If any errors are
|
||||
// encountered during the creation of the credentials, the test will fail.
|
||||
func TestPasswordCredentials(
|
||||
t testing.TB,
|
||||
conn *db.DB,
|
||||
wrapper wrapping.Wrapper,
|
||||
password, storeId, projectId string,
|
||||
count int,
|
||||
) []*PasswordCredential {
|
||||
t.Helper()
|
||||
ctx := context.Background()
|
||||
kmsCache := kms.TestKms(t, conn, wrapper)
|
||||
|
||||
databaseWrapper, err := kmsCache.GetWrapper(ctx, projectId, kms.KeyPurposeDatabase)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, databaseWrapper)
|
||||
|
||||
creds := make([]*PasswordCredential, 0, count)
|
||||
for i := 0; i < count; i++ {
|
||||
creds = append(creds, TestPasswordCredential(t, conn, wrapper, password, storeId, projectId))
|
||||
}
|
||||
return creds
|
||||
}
|
||||
|
||||
// TestSshPrivateKeyCredential creates an ssh private key credential in the
|
||||
// provided DB with the provided project and any values passed in through. If any
|
||||
// errors are encountered during the creation of the store, the test will fail.
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
// Package password provides access to the password
|
||||
// stored in a Vault secret.
|
||||
package password
|
||||
@@ -0,0 +1,118 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package password
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/mitchellh/pointerstructure"
|
||||
)
|
||||
|
||||
type (
|
||||
data map[string]any
|
||||
|
||||
// extractFunc attempts to extract the password
|
||||
// from sd using the provided attribute names, using a known
|
||||
// Vault data response format.
|
||||
extractFunc func(sd data, passwordAttr string) string
|
||||
)
|
||||
|
||||
// Extract attempts to extract the values of the password
|
||||
// stored within the provided data using the given attribute names.
|
||||
//
|
||||
// Extract does not return partial results, i.e. if one of the attributes
|
||||
// were extracted but not the other ("") will be returned.
|
||||
func Extract(d data, passwordAttr string) string {
|
||||
for _, f := range []extractFunc{
|
||||
defaultExtract,
|
||||
kv2Extract,
|
||||
} {
|
||||
password := f(d, passwordAttr)
|
||||
if password != "" {
|
||||
// got valid password from secret
|
||||
return password
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// defaultExtract looks for the passwordAttr in the data map
|
||||
func defaultExtract(sd data, passwordAttr string) (password string) {
|
||||
if sd == nil {
|
||||
// nothing to do return early
|
||||
return ""
|
||||
}
|
||||
|
||||
var p any
|
||||
switch {
|
||||
case strings.HasPrefix(passwordAttr, "/"):
|
||||
var err error
|
||||
p, err = pointerstructure.Get(sd, passwordAttr)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
default:
|
||||
p = sd[passwordAttr]
|
||||
}
|
||||
|
||||
if p, ok := p.(string); ok {
|
||||
password = p
|
||||
}
|
||||
|
||||
return password
|
||||
}
|
||||
|
||||
// kv2Extract looks for the the passwordAttr in the embedded
|
||||
// 'data' field within the data map.
|
||||
//
|
||||
// Additionally it validates the data is in the expected KV-v2 format:
|
||||
//
|
||||
// {
|
||||
// "data": {},
|
||||
// "metadata: {}
|
||||
// }
|
||||
//
|
||||
// If the format does not match, it returns (""). See:
|
||||
// https://www.vaultproject.io/api/secret/kv/kv-v2#sample-response-1
|
||||
func kv2Extract(sd data, passwordAttr string) (password string) {
|
||||
if sd == nil {
|
||||
// nothing to do return early
|
||||
return ""
|
||||
}
|
||||
|
||||
var data, metadata map[string]any
|
||||
for k, v := range sd {
|
||||
switch k {
|
||||
case "data":
|
||||
var ok bool
|
||||
if data, ok = v.(map[string]any); !ok {
|
||||
// data field should be of type map[string]interface{} in KV-v2
|
||||
return ""
|
||||
}
|
||||
case "metadata":
|
||||
var ok bool
|
||||
if metadata, ok = v.(map[string]any); !ok {
|
||||
// metadata field should be of type map[string]interface{} in KV-v2
|
||||
return ""
|
||||
}
|
||||
default:
|
||||
// secretData contains a non valid KV-v2 top level field
|
||||
return ""
|
||||
}
|
||||
}
|
||||
if data == nil || metadata == nil {
|
||||
// missing required KV-v2 field
|
||||
return ""
|
||||
}
|
||||
|
||||
if p, ok := data[passwordAttr]; ok {
|
||||
if p, ok := p.(string); ok {
|
||||
password = p
|
||||
}
|
||||
}
|
||||
|
||||
return password
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package password
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestBaseToPass(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type args struct {
|
||||
s data
|
||||
pAttr string
|
||||
}
|
||||
type pass struct {
|
||||
pass string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
given args
|
||||
want pass
|
||||
}{
|
||||
{
|
||||
name: "nil-input",
|
||||
want: pass{pass: ""},
|
||||
},
|
||||
{
|
||||
name: "no-input",
|
||||
given: args{},
|
||||
want: pass{pass: ""},
|
||||
},
|
||||
{
|
||||
name: "no-secret",
|
||||
given: args{
|
||||
pAttr: "password",
|
||||
},
|
||||
want: pass{pass: ""},
|
||||
},
|
||||
{
|
||||
name: "no-match-password-secret",
|
||||
given: args{
|
||||
s: data{
|
||||
"password-wrong": "pass",
|
||||
},
|
||||
pAttr: "password",
|
||||
},
|
||||
want: pass{pass: ""},
|
||||
},
|
||||
{
|
||||
name: "valid-default",
|
||||
given: args{
|
||||
s: data{
|
||||
"password": "pass",
|
||||
},
|
||||
pAttr: "password",
|
||||
},
|
||||
want: pass{pass: "pass"},
|
||||
},
|
||||
{
|
||||
name: "no-match-password-secret-kv2",
|
||||
given: args{
|
||||
s: data{
|
||||
"metadata": map[string]any{},
|
||||
"data": map[string]any{
|
||||
"password-wrong": "pass",
|
||||
},
|
||||
},
|
||||
pAttr: "password",
|
||||
},
|
||||
want: pass{pass: ""},
|
||||
},
|
||||
{
|
||||
name: "valid-kv2",
|
||||
given: args{
|
||||
s: data{
|
||||
"metadata": map[string]any{},
|
||||
"data": map[string]any{
|
||||
"password": "pass",
|
||||
},
|
||||
},
|
||||
pAttr: "password",
|
||||
},
|
||||
want: pass{pass: "pass"},
|
||||
},
|
||||
{
|
||||
name: "no-metadata-kv2",
|
||||
given: args{
|
||||
s: data{
|
||||
"data": map[string]any{
|
||||
"password": "pass",
|
||||
},
|
||||
},
|
||||
pAttr: "password",
|
||||
},
|
||||
want: pass{pass: ""},
|
||||
},
|
||||
{
|
||||
name: "invalid-metadata-kv2",
|
||||
given: args{
|
||||
s: data{
|
||||
"metadata": "string",
|
||||
"data": map[string]any{
|
||||
"password": "pass",
|
||||
},
|
||||
},
|
||||
pAttr: "password",
|
||||
},
|
||||
want: pass{pass: ""},
|
||||
},
|
||||
{
|
||||
name: "invalid-field-kv2",
|
||||
given: args{
|
||||
s: data{
|
||||
"invalid": map[string]any{},
|
||||
"metadata": map[string]any{},
|
||||
"data": map[string]any{
|
||||
"password": "pass",
|
||||
},
|
||||
},
|
||||
pAttr: "password",
|
||||
},
|
||||
want: pass{pass: ""},
|
||||
},
|
||||
{
|
||||
name: "valid-order-default-first",
|
||||
given: args{
|
||||
s: data{
|
||||
"password": "default-pass",
|
||||
"metadata": map[string]any{},
|
||||
"data": map[string]any{
|
||||
"password": "kv2-pass",
|
||||
},
|
||||
},
|
||||
pAttr: "password",
|
||||
},
|
||||
want: pass{pass: "default-pass"},
|
||||
},
|
||||
{
|
||||
name: "json-pointer-password",
|
||||
given: args{
|
||||
s: data{
|
||||
"testing": map[string]any{
|
||||
"my-password": "secret",
|
||||
},
|
||||
},
|
||||
pAttr: "/testing/my-password",
|
||||
},
|
||||
want: pass{pass: "secret"},
|
||||
},
|
||||
{
|
||||
name: "deep-json-pointer",
|
||||
given: args{
|
||||
s: data{
|
||||
"first-path": map[string]any{
|
||||
"deeper-path": map[string]any{
|
||||
"my-password": "deeper-secret",
|
||||
},
|
||||
},
|
||||
},
|
||||
pAttr: "/first-path/deeper-path/my-password",
|
||||
},
|
||||
want: pass{pass: "deeper-secret"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
pass := Extract(tt.given.s, tt.given.pAttr)
|
||||
assert.Equal(tt.want.pass, pass)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,8 @@ func validMappingOverride(m MappingOverride, ct globals.CredentialType) bool {
|
||||
return ct == globals.UsernamePasswordCredentialType
|
||||
case *UsernamePasswordDomainOverride:
|
||||
return ct == globals.UsernamePasswordDomainCredentialType
|
||||
case *PasswordOverride:
|
||||
return ct == globals.PasswordCredentialType
|
||||
case *SshPrivateKeyOverride:
|
||||
return ct == globals.SshPrivateKeyCredentialType
|
||||
default:
|
||||
@@ -171,6 +173,65 @@ func (o *UsernamePasswordDomainOverride) SetTableName(n string) {
|
||||
o.tableName = n
|
||||
}
|
||||
|
||||
// A PasswordOverride contains optional values for overriding the
|
||||
// default mappings used to map a Vault secret to a Password credential
|
||||
// type for the credential library that owns it.
|
||||
type PasswordOverride struct {
|
||||
*store.PasswordOverride
|
||||
tableName string `gorm:"-"`
|
||||
}
|
||||
|
||||
var _ MappingOverride = (*PasswordOverride)(nil)
|
||||
|
||||
// NewPasswordOverride creates a new in memory PasswordOverride.
|
||||
// WithOverrideAttribute and WithOverridePasswordAttribute are the
|
||||
// only valid options. All other options are ignored.
|
||||
func NewPasswordOverride(opt ...Option) *PasswordOverride {
|
||||
opts := getOpts(opt...)
|
||||
o := &PasswordOverride{
|
||||
PasswordOverride: &store.PasswordOverride{
|
||||
PasswordAttribute: sanitize.String(opts.withOverridePasswordAttribute),
|
||||
},
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
func allocPasswordOverride() *PasswordOverride {
|
||||
return &PasswordOverride{
|
||||
PasswordOverride: &store.PasswordOverride{},
|
||||
}
|
||||
}
|
||||
|
||||
func (o *PasswordOverride) clone() MappingOverride {
|
||||
cp := proto.Clone(o.PasswordOverride)
|
||||
return &PasswordOverride{
|
||||
PasswordOverride: cp.(*store.PasswordOverride),
|
||||
}
|
||||
}
|
||||
|
||||
func (o *PasswordOverride) setLibraryId(i string) {
|
||||
o.LibraryId = i
|
||||
}
|
||||
|
||||
func (o *PasswordOverride) sanitize() {
|
||||
if sentinel.Is(o.PasswordAttribute) {
|
||||
o.PasswordAttribute = ""
|
||||
}
|
||||
}
|
||||
|
||||
// TableName returns the table name.
|
||||
func (o *PasswordOverride) TableName() string {
|
||||
if o.tableName != "" {
|
||||
return o.tableName
|
||||
}
|
||||
return "credential_vault_generic_library_password_mapping_override"
|
||||
}
|
||||
|
||||
// SetTableName sets the table name.
|
||||
func (o *PasswordOverride) SetTableName(n string) {
|
||||
o.tableName = n
|
||||
}
|
||||
|
||||
// A SshPrivateKeyOverride contains optional values for overriding the
|
||||
// default mappings used to map a Vault secret to a SshPrivateKey credential
|
||||
// type for the credential library that owns it.
|
||||
|
||||
@@ -45,6 +45,11 @@ func TestValidMappingOverrides(t *testing.T) {
|
||||
ct: globals.UsernamePasswordDomainCredentialType,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
m: nil,
|
||||
ct: globals.PasswordCredentialType,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
m: unknownMapper(1),
|
||||
ct: globals.UnspecifiedCredentialType,
|
||||
@@ -60,6 +65,11 @@ func TestValidMappingOverrides(t *testing.T) {
|
||||
ct: globals.UsernamePasswordDomainCredentialType,
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
m: unknownMapper(1),
|
||||
ct: globals.PasswordCredentialType,
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
m: allocUsernamePasswordOverride(),
|
||||
ct: globals.UnspecifiedCredentialType,
|
||||
@@ -80,6 +90,16 @@ func TestValidMappingOverrides(t *testing.T) {
|
||||
ct: globals.UsernamePasswordDomainCredentialType,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
m: allocPasswordOverride(),
|
||||
ct: globals.UnspecifiedCredentialType,
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
m: allocPasswordOverride(),
|
||||
ct: globals.PasswordCredentialType,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
m: allocSshPrivateKeyOverride(),
|
||||
ct: globals.UnspecifiedCredentialType,
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
ldapv3 "github.com/go-ldap/ldap/v3"
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/credential"
|
||||
"github.com/hashicorp/boundary/internal/credential/vault/internal/password"
|
||||
"github.com/hashicorp/boundary/internal/credential/vault/internal/sshprivatekey"
|
||||
"github.com/hashicorp/boundary/internal/credential/vault/internal/usernamepassword"
|
||||
"github.com/hashicorp/boundary/internal/credential/vault/internal/usernamepassworddomain"
|
||||
@@ -66,6 +67,8 @@ func convert(ctx context.Context, bc *baseCred) (dynamicCred, error) {
|
||||
return baseToSshPriKey(ctx, bc)
|
||||
case globals.UsernamePasswordDomainCredentialType:
|
||||
return baseToUsrPassDomain(ctx, bc)
|
||||
case globals.PasswordCredentialType:
|
||||
return baseToPass(ctx, bc)
|
||||
}
|
||||
return bc, nil
|
||||
}
|
||||
@@ -166,6 +169,45 @@ func baseToUsrPassDomain(ctx context.Context, bc *baseCred) (*usrPassDomainCred,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var _ credential.PasswordOnly = (*passCred)(nil)
|
||||
|
||||
type passCred struct {
|
||||
*baseCred
|
||||
password credential.Password
|
||||
}
|
||||
|
||||
func (c *passCred) Password() credential.Password { return c.password }
|
||||
|
||||
func baseToPass(ctx context.Context, bc *baseCred) (*passCred, error) {
|
||||
switch {
|
||||
case bc == nil:
|
||||
return nil, errors.E(ctx, errors.WithCode(errors.InvalidParameter), errors.WithMsg("nil baseCred"))
|
||||
case bc.lib == nil:
|
||||
return nil, errors.E(ctx, errors.WithCode(errors.InvalidParameter), errors.WithMsg("nil baseCred.lib"))
|
||||
case bc.Library().CredentialType() != globals.PasswordCredentialType:
|
||||
return nil, errors.E(ctx, errors.WithCode(errors.InvalidParameter), errors.WithMsg("invalid credential type"))
|
||||
}
|
||||
|
||||
lib, ok := bc.lib.(*genericIssuingCredentialLibrary)
|
||||
if !ok {
|
||||
return nil, errors.E(ctx, errors.WithCode(errors.InvalidParameter), errors.WithMsg("baseCred.lib is not of type genericIssuingCredentialLibrary"))
|
||||
}
|
||||
|
||||
pAttr := lib.PasswordAttribute
|
||||
if pAttr == "" {
|
||||
pAttr = "password"
|
||||
}
|
||||
password := password.Extract(bc.secretData, pAttr)
|
||||
if password == "" {
|
||||
return nil, errors.E(ctx, errors.WithCode(errors.VaultInvalidCredentialMapping))
|
||||
}
|
||||
|
||||
return &passCred{
|
||||
baseCred: bc,
|
||||
password: credential.Password(password),
|
||||
}, nil
|
||||
}
|
||||
|
||||
var _ credential.SshPrivateKey = (*sshPrivateKeyCred)(nil)
|
||||
|
||||
type sshPrivateKeyCred struct {
|
||||
|
||||
@@ -344,6 +344,37 @@ func TestRepository_getPrivateLibraries(t *testing.T) {
|
||||
req := credential.Request{SourceId: lib.GetPublicId(), Purpose: credential.BrokeredPurpose}
|
||||
requests = append(requests, req)
|
||||
}
|
||||
{
|
||||
opts := []Option{
|
||||
WithCredentialType(globals.PasswordCredentialType),
|
||||
}
|
||||
libIn, err := NewCredentialLibrary(origStore.GetPublicId(), "/vault/path", opts...)
|
||||
assert.NoError(err)
|
||||
require.NotNil(libIn)
|
||||
lib, err := repo.CreateCredentialLibrary(ctx, prj.GetPublicId(), libIn)
|
||||
assert.NoError(err)
|
||||
require.NotNil(lib)
|
||||
libs[lib.GetPublicId()] = lib
|
||||
req := credential.Request{SourceId: lib.GetPublicId(), Purpose: credential.BrokeredPurpose}
|
||||
requests = append(requests, req)
|
||||
}
|
||||
{
|
||||
opts := []Option{
|
||||
WithCredentialType(globals.PasswordCredentialType),
|
||||
WithMappingOverride(NewPasswordOverride(
|
||||
WithOverridePasswordAttribute("test-password"),
|
||||
)),
|
||||
}
|
||||
libIn, err := NewCredentialLibrary(origStore.GetPublicId(), "/vault/path", opts...)
|
||||
assert.NoError(err)
|
||||
require.NotNil(libIn)
|
||||
lib, err := repo.CreateCredentialLibrary(ctx, prj.GetPublicId(), libIn)
|
||||
assert.NoError(err)
|
||||
require.NotNil(lib)
|
||||
libs[lib.GetPublicId()] = lib
|
||||
req := credential.Request{SourceId: lib.GetPublicId(), Purpose: credential.BrokeredPurpose}
|
||||
requests = append(requests, req)
|
||||
}
|
||||
{
|
||||
opts := []Option{
|
||||
WithCredentialType(globals.SshPrivateKeyCredentialType),
|
||||
@@ -460,6 +491,8 @@ func TestRepository_getPrivateLibraries(t *testing.T) {
|
||||
assert.Equal(w.UsernameAttribute, got.UsernameAttribute)
|
||||
assert.Equal(w.PasswordAttribute, got.PasswordAttribute)
|
||||
assert.Equal(w.DomainAttribute, got.DomainAttribute)
|
||||
case *PasswordOverride:
|
||||
assert.Equal(w.PasswordAttribute, got.PasswordAttribute)
|
||||
case *SshPrivateKeyOverride:
|
||||
assert.Equal(w.UsernameAttribute, got.UsernameAttribute)
|
||||
assert.Equal(w.PrivateKeyAttribute, got.PrivateKeyAttribute)
|
||||
@@ -1424,6 +1457,211 @@ func TestBaseToUsrPassDomain(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBaseToPass(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
given *baseCred
|
||||
want *passCred
|
||||
wantErr errors.Code
|
||||
}{
|
||||
{
|
||||
name: "nil-input",
|
||||
wantErr: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "nil-library",
|
||||
given: &baseCred{},
|
||||
wantErr: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "library-not-password-type",
|
||||
given: &baseCred{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(globals.UnspecifiedCredentialType),
|
||||
},
|
||||
},
|
||||
wantErr: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "invalid-no-password-attribute",
|
||||
given: &baseCred{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(globals.PasswordCredentialType),
|
||||
},
|
||||
secretData: map[string]any{},
|
||||
},
|
||||
wantErr: errors.VaultInvalidCredentialMapping,
|
||||
},
|
||||
{
|
||||
name: "valid-default-attributes",
|
||||
given: &baseCred{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(globals.PasswordCredentialType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
"password": "my-password",
|
||||
},
|
||||
},
|
||||
want: &passCred{
|
||||
password: credential.Password("my-password"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-override-attributes",
|
||||
given: &baseCred{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(globals.PasswordCredentialType),
|
||||
PasswordAttribute: "test-password",
|
||||
},
|
||||
secretData: map[string]any{
|
||||
"password": "default-password",
|
||||
"test-password": "override-password",
|
||||
},
|
||||
},
|
||||
want: &passCred{
|
||||
password: credential.Password("override-password"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid-password-override",
|
||||
given: &baseCred{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(globals.PasswordCredentialType),
|
||||
PasswordAttribute: "missing-password",
|
||||
},
|
||||
secretData: map[string]any{
|
||||
"password": "default-password",
|
||||
"test-password": "override-password",
|
||||
},
|
||||
},
|
||||
wantErr: errors.VaultInvalidCredentialMapping,
|
||||
},
|
||||
{
|
||||
name: "invalid-kv2-no-metadata-field",
|
||||
given: &baseCred{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(globals.PasswordCredentialType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
"data": map[string]any{
|
||||
"password": "my-password",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: errors.VaultInvalidCredentialMapping,
|
||||
},
|
||||
{
|
||||
name: "invalid-kv2-no-data-field",
|
||||
given: &baseCred{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(globals.PasswordCredentialType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
"metadata": map[string]any{},
|
||||
},
|
||||
},
|
||||
wantErr: errors.VaultInvalidCredentialMapping,
|
||||
},
|
||||
{
|
||||
name: "invalid-kv2-invalid-metadata-type",
|
||||
given: &baseCred{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(globals.PasswordCredentialType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
"metadata": "hello",
|
||||
"data": map[string]any{
|
||||
"password": "my-password",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: errors.VaultInvalidCredentialMapping,
|
||||
},
|
||||
{
|
||||
name: "invalid-kv2-invalid-metadata-type",
|
||||
given: &baseCred{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(globals.PasswordCredentialType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
"metadata": map[string]any{},
|
||||
"data": "hello",
|
||||
},
|
||||
},
|
||||
wantErr: errors.VaultInvalidCredentialMapping,
|
||||
},
|
||||
{
|
||||
name: "invalid-kv2-additional-field",
|
||||
given: &baseCred{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(globals.PasswordCredentialType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
"bad-field": "hello",
|
||||
"metadata": map[string]any{},
|
||||
"data": map[string]any{
|
||||
"password": "my-password",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: errors.VaultInvalidCredentialMapping,
|
||||
},
|
||||
{
|
||||
name: "valid-kv2-default-password-attribute",
|
||||
given: &baseCred{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(globals.PasswordCredentialType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
"metadata": map[string]any{},
|
||||
"data": map[string]any{
|
||||
"password": "my-password",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &passCred{
|
||||
password: credential.Password("my-password"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-kv2-override-password-attribute",
|
||||
given: &baseCred{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(globals.PasswordCredentialType),
|
||||
PasswordAttribute: "test-password",
|
||||
},
|
||||
secretData: map[string]any{
|
||||
"metadata": map[string]any{},
|
||||
"data": map[string]any{
|
||||
"password": "default-password",
|
||||
"test-password": "override-password",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &passCred{
|
||||
password: credential.Password("override-password"),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
got, err := baseToPass(context.Background(), tt.given)
|
||||
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)
|
||||
want := tt.want
|
||||
want.baseCred = tt.given
|
||||
assert.Equal(want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBaseToSshPriKey(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -406,6 +406,14 @@ func (pl *listLookupLibrary) toCredentialLibrary() *CredentialLibrary {
|
||||
upd.sanitize()
|
||||
cl.MappingOverride = upd
|
||||
}
|
||||
case string(globals.PasswordCredentialType):
|
||||
if pl.PasswordAttribute != "" {
|
||||
p := allocPasswordOverride()
|
||||
p.LibraryId = pl.PublicId
|
||||
p.PasswordAttribute = pl.PasswordAttribute
|
||||
p.sanitize()
|
||||
cl.MappingOverride = p
|
||||
}
|
||||
case string(globals.SshPrivateKeyCredentialType):
|
||||
if pl.UsernameAttribute != "" || pl.PrivateKeyAttribute != "" || pl.PrivateKeyPassphraseAttribute != "" {
|
||||
pk := allocSshPrivateKeyOverride()
|
||||
|
||||
@@ -293,6 +293,76 @@ func TestRepository_CreateCredentialLibrary(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-password-credential-type",
|
||||
in: &CredentialLibrary{
|
||||
CredentialLibrary: &store.CredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
HttpMethod: "GET",
|
||||
VaultPath: "/some/path",
|
||||
CredentialType: string(globals.PasswordCredentialType),
|
||||
},
|
||||
},
|
||||
want: &CredentialLibrary{
|
||||
CredentialLibrary: &store.CredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
HttpMethod: "GET",
|
||||
VaultPath: "/some/path",
|
||||
CredentialType: string(globals.PasswordCredentialType),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "unknown-password-mapping-override-type",
|
||||
in: &CredentialLibrary{
|
||||
MappingOverride: unknownMapper(1),
|
||||
CredentialLibrary: &store.CredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
HttpMethod: "GET",
|
||||
VaultPath: "/some/path",
|
||||
CredentialType: string(globals.PasswordCredentialType),
|
||||
},
|
||||
},
|
||||
wantErr: errors.VaultInvalidMappingOverride,
|
||||
},
|
||||
{
|
||||
name: "invalid-password-mapping-override-type",
|
||||
in: &CredentialLibrary{
|
||||
MappingOverride: NewUsernamePasswordOverride(WithOverrideUsernameAttribute("test")),
|
||||
CredentialLibrary: &store.CredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
HttpMethod: "GET",
|
||||
VaultPath: "/some/path",
|
||||
CredentialType: string(globals.PasswordCredentialType),
|
||||
},
|
||||
},
|
||||
wantErr: errors.VaultInvalidMappingOverride,
|
||||
},
|
||||
{
|
||||
name: "valid-password-credential-type-with-password-override",
|
||||
in: &CredentialLibrary{
|
||||
MappingOverride: NewPasswordOverride(
|
||||
WithOverridePasswordAttribute("ptest"),
|
||||
),
|
||||
CredentialLibrary: &store.CredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
HttpMethod: "GET",
|
||||
VaultPath: "/some/path",
|
||||
CredentialType: string(globals.PasswordCredentialType),
|
||||
},
|
||||
},
|
||||
want: &CredentialLibrary{
|
||||
MappingOverride: NewPasswordOverride(
|
||||
WithOverridePasswordAttribute("ptest"),
|
||||
),
|
||||
CredentialLibrary: &store.CredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
HttpMethod: "GET",
|
||||
VaultPath: "/some/path",
|
||||
CredentialType: string(globals.PasswordCredentialType),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-ssh-private-key-credential-type",
|
||||
in: &CredentialLibrary{
|
||||
@@ -339,7 +409,7 @@ func TestRepository_CreateCredentialLibrary(t *testing.T) {
|
||||
wantErr: errors.VaultInvalidMappingOverride,
|
||||
},
|
||||
{
|
||||
name: "valid-ssh-prviate-key-credential-type-with-username-override",
|
||||
name: "valid-ssh-private-key-credential-type-with-username-override",
|
||||
in: &CredentialLibrary{
|
||||
MappingOverride: NewSshPrivateKeyOverride(
|
||||
WithOverrideUsernameAttribute("utest"),
|
||||
@@ -486,6 +556,15 @@ func TestRepository_CreateCredentialLibrary(t *testing.T) {
|
||||
override := allocUsernamePasswordOverride()
|
||||
assert.NoError(rw.LookupWhere(ctx, &override, "library_id = ?", []any{got.GetPublicId()}))
|
||||
|
||||
case *PasswordOverride:
|
||||
g, ok := got.MappingOverride.(*PasswordOverride)
|
||||
require.True(ok)
|
||||
assert.Equal(w.PasswordAttribute, g.PasswordAttribute)
|
||||
|
||||
// verify it was persisted in the database
|
||||
override := allocPasswordOverride()
|
||||
assert.NoError(rw.LookupWhere(ctx, &override, "library_id = ?", []any{got.GetPublicId()}))
|
||||
|
||||
case *SshPrivateKeyOverride:
|
||||
g, ok := got.MappingOverride.(*SshPrivateKeyOverride)
|
||||
require.True(ok)
|
||||
@@ -1273,6 +1352,94 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) {
|
||||
masks: []string{"MappingOverride"},
|
||||
wantErr: errors.VaultInvalidMappingOverride,
|
||||
},
|
||||
{
|
||||
name: "password-attribute-change-password-attribute",
|
||||
orig: &CredentialLibrary{
|
||||
MappingOverride: NewPasswordOverride(
|
||||
WithOverridePasswordAttribute("orig-password"),
|
||||
),
|
||||
CredentialLibrary: &store.CredentialLibrary{
|
||||
HttpMethod: "GET",
|
||||
VaultPath: "/some/path",
|
||||
Name: "test-password-repo",
|
||||
CredentialType: string(globals.PasswordCredentialType),
|
||||
},
|
||||
},
|
||||
chgFn: changeMappingOverride(
|
||||
NewPasswordOverride(
|
||||
WithOverridePasswordAttribute("changed-password"),
|
||||
),
|
||||
),
|
||||
masks: []string{"MappingOverride"},
|
||||
want: &CredentialLibrary{
|
||||
MappingOverride: NewPasswordOverride(
|
||||
WithOverridePasswordAttribute("changed-password"),
|
||||
),
|
||||
CredentialLibrary: &store.CredentialLibrary{
|
||||
HttpMethod: "GET",
|
||||
VaultPath: "/some/path",
|
||||
Name: "test-password-repo",
|
||||
CredentialType: string(globals.PasswordCredentialType),
|
||||
},
|
||||
},
|
||||
wantCount: 1,
|
||||
},
|
||||
{
|
||||
name: "no-mapping-override-change-password-attributes",
|
||||
orig: &CredentialLibrary{
|
||||
CredentialLibrary: &store.CredentialLibrary{
|
||||
HttpMethod: "GET",
|
||||
VaultPath: "/some/path",
|
||||
Name: "test-password-repo",
|
||||
CredentialType: string(globals.PasswordCredentialType),
|
||||
},
|
||||
},
|
||||
chgFn: changeMappingOverride(
|
||||
NewPasswordOverride(
|
||||
WithOverridePasswordAttribute("changed-password"),
|
||||
),
|
||||
),
|
||||
masks: []string{"MappingOverride"},
|
||||
want: &CredentialLibrary{
|
||||
MappingOverride: NewPasswordOverride(
|
||||
WithOverridePasswordAttribute("changed-password"),
|
||||
),
|
||||
CredentialLibrary: &store.CredentialLibrary{
|
||||
HttpMethod: "GET",
|
||||
VaultPath: "/some/path",
|
||||
Name: "test-password-repo",
|
||||
CredentialType: string(globals.PasswordCredentialType),
|
||||
},
|
||||
},
|
||||
wantCount: 1,
|
||||
},
|
||||
{
|
||||
name: "password-attributes-delete-mapping-override",
|
||||
orig: &CredentialLibrary{
|
||||
MappingOverride: NewPasswordOverride(
|
||||
WithOverridePasswordAttribute("orig-password"),
|
||||
WithOverridePrivateKeyAttribute("orig-private-key"),
|
||||
WithOverridePrivateKeyPassphraseAttribute("orig-passphrase"),
|
||||
),
|
||||
CredentialLibrary: &store.CredentialLibrary{
|
||||
HttpMethod: "GET",
|
||||
VaultPath: "/some/path",
|
||||
Name: "test-password-repo",
|
||||
CredentialType: string(globals.PasswordCredentialType),
|
||||
},
|
||||
},
|
||||
chgFn: changeMappingOverride(nil),
|
||||
masks: []string{"MappingOverride"},
|
||||
want: &CredentialLibrary{
|
||||
CredentialLibrary: &store.CredentialLibrary{
|
||||
HttpMethod: "GET",
|
||||
VaultPath: "/some/path",
|
||||
Name: "test-password-repo",
|
||||
CredentialType: string(globals.PasswordCredentialType),
|
||||
},
|
||||
},
|
||||
wantCount: 1,
|
||||
},
|
||||
{
|
||||
name: "ssh-private-key-attributes-change-username-attribute",
|
||||
orig: &CredentialLibrary{
|
||||
@@ -1535,6 +1702,10 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) {
|
||||
require.True(ok)
|
||||
assert.Equal(w.UsernameAttribute, g.UsernameAttribute)
|
||||
assert.Equal(w.PasswordAttribute, g.PasswordAttribute)
|
||||
case *PasswordOverride:
|
||||
g, ok := got.MappingOverride.(*PasswordOverride)
|
||||
require.True(ok)
|
||||
assert.Equal(w.PasswordAttribute, g.PasswordAttribute)
|
||||
case *SshPrivateKeyOverride:
|
||||
g, ok := got.MappingOverride.(*SshPrivateKeyOverride)
|
||||
require.True(ok)
|
||||
@@ -1800,6 +1971,31 @@ func TestRepository_LookupCredentialLibrary(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-password-credential-type",
|
||||
in: &CredentialLibrary{
|
||||
CredentialLibrary: &store.CredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
HttpMethod: "GET",
|
||||
VaultPath: "/some/path",
|
||||
CredentialType: string(globals.PasswordCredentialType),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-password-credential-type-with-password-override",
|
||||
in: &CredentialLibrary{
|
||||
MappingOverride: NewPasswordOverride(
|
||||
WithOverridePasswordAttribute("ptest"),
|
||||
),
|
||||
CredentialLibrary: &store.CredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
HttpMethod: "GET",
|
||||
VaultPath: "/some/path",
|
||||
CredentialType: string(globals.PasswordCredentialType),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-ssh-private-key-credential-type",
|
||||
in: &CredentialLibrary{
|
||||
@@ -1901,6 +2097,10 @@ func TestRepository_LookupCredentialLibrary(t *testing.T) {
|
||||
require.True(ok)
|
||||
assert.Equal(w.UsernameAttribute, g.UsernameAttribute)
|
||||
assert.Equal(w.PasswordAttribute, g.PasswordAttribute)
|
||||
case *PasswordOverride:
|
||||
g, ok := got.MappingOverride.(*PasswordOverride)
|
||||
require.True(ok)
|
||||
assert.Equal(w.PasswordAttribute, g.PasswordAttribute)
|
||||
case *SshPrivateKeyOverride:
|
||||
g, ok := got.MappingOverride.(*SshPrivateKeyOverride)
|
||||
require.True(ok)
|
||||
|
||||
@@ -1174,6 +1174,68 @@ func (x *UsernamePasswordDomainOverride) GetDomainAttribute() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
type PasswordOverride struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// library_id of the owning vault credential library.
|
||||
// @inject_tag: `gorm:"primary_key"`
|
||||
LibraryId string `protobuf:"bytes,1,opt,name=library_id,json=libraryId,proto3" json:"library_id,omitempty" gorm:"primary_key"`
|
||||
// password_attribute is the name of the attribute in the Data field of a
|
||||
// Vault api.Secret that maps to a password.
|
||||
// If set, it overrides any default attribute names the system uses to
|
||||
// find a password attribute.
|
||||
//
|
||||
// See https://github.com/hashicorp/vault/blob/5e505ec039177e8212cbbab74ccb644c46e62e63/api/secret.go#L25
|
||||
//
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
PasswordAttribute string `protobuf:"bytes,2,opt,name=password_attribute,json=passwordAttribute,proto3" json:"password_attribute,omitempty" gorm:"default:null"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *PasswordOverride) Reset() {
|
||||
*x = PasswordOverride{}
|
||||
mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[8]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *PasswordOverride) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*PasswordOverride) ProtoMessage() {}
|
||||
|
||||
func (x *PasswordOverride) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[8]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use PasswordOverride.ProtoReflect.Descriptor instead.
|
||||
func (*PasswordOverride) Descriptor() ([]byte, []int) {
|
||||
return file_controller_storage_credential_vault_store_v1_vault_proto_rawDescGZIP(), []int{8}
|
||||
}
|
||||
|
||||
func (x *PasswordOverride) GetLibraryId() string {
|
||||
if x != nil {
|
||||
return x.LibraryId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *PasswordOverride) GetPasswordAttribute() string {
|
||||
if x != nil {
|
||||
return x.PasswordAttribute
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type SshPrivateKeyOverride struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// library_id of the owning vault credential library.
|
||||
@@ -1213,7 +1275,7 @@ type SshPrivateKeyOverride struct {
|
||||
|
||||
func (x *SshPrivateKeyOverride) Reset() {
|
||||
*x = SshPrivateKeyOverride{}
|
||||
mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[8]
|
||||
mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[9]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1225,7 +1287,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[8]
|
||||
mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[9]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1238,7 +1300,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{8}
|
||||
return file_controller_storage_credential_vault_store_v1_vault_proto_rawDescGZIP(), []int{9}
|
||||
}
|
||||
|
||||
func (x *SshPrivateKeyOverride) GetLibraryId() string {
|
||||
@@ -1306,7 +1368,7 @@ type LdapCredentialLibrary struct {
|
||||
|
||||
func (x *LdapCredentialLibrary) Reset() {
|
||||
*x = LdapCredentialLibrary{}
|
||||
mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[9]
|
||||
mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[10]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1318,7 +1380,7 @@ func (x *LdapCredentialLibrary) String() string {
|
||||
func (*LdapCredentialLibrary) ProtoMessage() {}
|
||||
|
||||
func (x *LdapCredentialLibrary) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[9]
|
||||
mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[10]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1331,7 +1393,7 @@ func (x *LdapCredentialLibrary) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use LdapCredentialLibrary.ProtoReflect.Descriptor instead.
|
||||
func (*LdapCredentialLibrary) Descriptor() ([]byte, []int) {
|
||||
return file_controller_storage_credential_vault_store_v1_vault_proto_rawDescGZIP(), []int{9}
|
||||
return file_controller_storage_credential_vault_store_v1_vault_proto_rawDescGZIP(), []int{10}
|
||||
}
|
||||
|
||||
func (x *LdapCredentialLibrary) GetPublicId() string {
|
||||
@@ -1546,7 +1608,11 @@ const file_controller_storage_credential_vault_store_v1_vault_proto_rawDesc = ""
|
||||
"library_id\x18\x01 \x01(\tR\tlibraryId\x12-\n" +
|
||||
"\x12username_attribute\x18\x02 \x01(\tR\x11usernameAttribute\x12-\n" +
|
||||
"\x12password_attribute\x18\x03 \x01(\tR\x11passwordAttribute\x12)\n" +
|
||||
"\x10domain_attribute\x18\x04 \x01(\tR\x0fdomainAttribute\"\xe2\x01\n" +
|
||||
"\x10domain_attribute\x18\x04 \x01(\tR\x0fdomainAttribute\"`\n" +
|
||||
"\x10PasswordOverride\x12\x1d\n" +
|
||||
"\n" +
|
||||
"library_id\x18\x01 \x01(\tR\tlibraryId\x12-\n" +
|
||||
"\x12password_attribute\x18\x02 \x01(\tR\x11passwordAttribute\"\xe2\x01\n" +
|
||||
"\x15SshPrivateKeyOverride\x12\x1d\n" +
|
||||
"\n" +
|
||||
"library_id\x18\x01 \x01(\tR\tlibraryId\x12-\n" +
|
||||
@@ -1582,7 +1648,7 @@ 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, 10)
|
||||
var file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes = make([]protoimpl.MessageInfo, 11)
|
||||
var file_controller_storage_credential_vault_store_v1_vault_proto_goTypes = []any{
|
||||
(*CredentialStore)(nil), // 0: controller.storage.credential.vault.store.v1.CredentialStore
|
||||
(*Token)(nil), // 1: controller.storage.credential.vault.store.v1.Token
|
||||
@@ -1592,28 +1658,29 @@ var file_controller_storage_credential_vault_store_v1_vault_proto_goTypes = []an
|
||||
(*Credential)(nil), // 5: controller.storage.credential.vault.store.v1.Credential
|
||||
(*UsernamePasswordOverride)(nil), // 6: controller.storage.credential.vault.store.v1.UsernamePasswordOverride
|
||||
(*UsernamePasswordDomainOverride)(nil), // 7: controller.storage.credential.vault.store.v1.UsernamePasswordDomainOverride
|
||||
(*SshPrivateKeyOverride)(nil), // 8: controller.storage.credential.vault.store.v1.SshPrivateKeyOverride
|
||||
(*LdapCredentialLibrary)(nil), // 9: controller.storage.credential.vault.store.v1.LdapCredentialLibrary
|
||||
(*timestamp.Timestamp)(nil), // 10: controller.storage.timestamp.v1.Timestamp
|
||||
(*PasswordOverride)(nil), // 8: controller.storage.credential.vault.store.v1.PasswordOverride
|
||||
(*SshPrivateKeyOverride)(nil), // 9: controller.storage.credential.vault.store.v1.SshPrivateKeyOverride
|
||||
(*LdapCredentialLibrary)(nil), // 10: controller.storage.credential.vault.store.v1.LdapCredentialLibrary
|
||||
(*timestamp.Timestamp)(nil), // 11: controller.storage.timestamp.v1.Timestamp
|
||||
}
|
||||
var file_controller_storage_credential_vault_store_v1_vault_proto_depIdxs = []int32{
|
||||
10, // 0: controller.storage.credential.vault.store.v1.CredentialStore.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 1: controller.storage.credential.vault.store.v1.CredentialStore.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 2: controller.storage.credential.vault.store.v1.CredentialStore.delete_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 3: controller.storage.credential.vault.store.v1.Token.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 4: controller.storage.credential.vault.store.v1.Token.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 5: controller.storage.credential.vault.store.v1.Token.last_renewal_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 6: controller.storage.credential.vault.store.v1.Token.expiration_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 7: controller.storage.credential.vault.store.v1.CredentialLibrary.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 8: controller.storage.credential.vault.store.v1.CredentialLibrary.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 9: controller.storage.credential.vault.store.v1.SSHCertificateCredentialLibrary.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 10: controller.storage.credential.vault.store.v1.SSHCertificateCredentialLibrary.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 11: controller.storage.credential.vault.store.v1.Credential.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 12: controller.storage.credential.vault.store.v1.Credential.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 13: controller.storage.credential.vault.store.v1.Credential.last_renewal_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 14: controller.storage.credential.vault.store.v1.Credential.expiration_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 15: controller.storage.credential.vault.store.v1.LdapCredentialLibrary.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 16: controller.storage.credential.vault.store.v1.LdapCredentialLibrary.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
11, // 0: controller.storage.credential.vault.store.v1.CredentialStore.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
11, // 1: controller.storage.credential.vault.store.v1.CredentialStore.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
11, // 2: controller.storage.credential.vault.store.v1.CredentialStore.delete_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
11, // 3: controller.storage.credential.vault.store.v1.Token.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
11, // 4: controller.storage.credential.vault.store.v1.Token.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
11, // 5: controller.storage.credential.vault.store.v1.Token.last_renewal_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
11, // 6: controller.storage.credential.vault.store.v1.Token.expiration_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
11, // 7: controller.storage.credential.vault.store.v1.CredentialLibrary.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
11, // 8: controller.storage.credential.vault.store.v1.CredentialLibrary.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
11, // 9: controller.storage.credential.vault.store.v1.SSHCertificateCredentialLibrary.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
11, // 10: controller.storage.credential.vault.store.v1.SSHCertificateCredentialLibrary.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
11, // 11: controller.storage.credential.vault.store.v1.Credential.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
11, // 12: controller.storage.credential.vault.store.v1.Credential.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
11, // 13: controller.storage.credential.vault.store.v1.Credential.last_renewal_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
11, // 14: controller.storage.credential.vault.store.v1.Credential.expiration_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
11, // 15: controller.storage.credential.vault.store.v1.LdapCredentialLibrary.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
11, // 16: controller.storage.credential.vault.store.v1.LdapCredentialLibrary.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
17, // [17:17] is the sub-list for method output_type
|
||||
17, // [17:17] is the sub-list for method input_type
|
||||
17, // [17:17] is the sub-list for extension type_name
|
||||
@@ -1632,7 +1699,7 @@ func file_controller_storage_credential_vault_store_v1_vault_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_controller_storage_credential_vault_store_v1_vault_proto_rawDesc), len(file_controller_storage_credential_vault_store_v1_vault_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 10,
|
||||
NumMessages: 11,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
||||
@@ -83,6 +83,7 @@ var (
|
||||
globals.SshPrivateKeyCredentialType,
|
||||
globals.UnspecifiedCredentialType,
|
||||
globals.UsernamePasswordDomainCredentialType,
|
||||
globals.PasswordCredentialType,
|
||||
}
|
||||
|
||||
validKeyTypes = []string{
|
||||
@@ -851,6 +852,11 @@ func toProto(ctx context.Context, in credential.Library, opt ...handlers.Option)
|
||||
m[domainAttribute] = mapping.DomainAttribute
|
||||
}
|
||||
|
||||
case *vault.PasswordOverride:
|
||||
if mapping.PasswordAttribute != "" {
|
||||
m[passwordAttribute] = mapping.PasswordAttribute
|
||||
}
|
||||
|
||||
case *vault.SshPrivateKeyOverride:
|
||||
if mapping.UsernameAttribute != "" {
|
||||
m[usernameAttribute] = mapping.UsernameAttribute
|
||||
@@ -1006,6 +1012,17 @@ func toStorageVaultLibrary(ctx context.Context, storeId string, in *pb.Credentia
|
||||
opts = append(opts, vault.WithMappingOverride(vault.NewUsernamePasswordDomainOverride(mapOpts...)))
|
||||
}
|
||||
|
||||
case globals.PasswordCredentialType:
|
||||
opts = append(opts, vault.WithCredentialType(credentialType))
|
||||
overrides := in.CredentialMappingOverrides.AsMap()
|
||||
var mapOpts []vault.Option
|
||||
if password := overrides[passwordAttribute]; password != nil {
|
||||
mapOpts = append(mapOpts, vault.WithOverridePasswordAttribute(password.(string)))
|
||||
}
|
||||
if len(mapOpts) > 0 {
|
||||
opts = append(opts, vault.WithMappingOverride(vault.NewPasswordOverride(mapOpts...)))
|
||||
}
|
||||
|
||||
case globals.SshPrivateKeyCredentialType:
|
||||
opts = append(opts, vault.WithCredentialType(credentialType))
|
||||
overrides := in.CredentialMappingOverrides.AsMap()
|
||||
@@ -1340,6 +1357,8 @@ func validateMapping(badFields map[string]string, credentialType globals.Credent
|
||||
validFields[usernameAttribute] = true
|
||||
validFields[passwordAttribute] = true
|
||||
validFields[domainAttribute] = true
|
||||
case globals.PasswordCredentialType:
|
||||
validFields[passwordAttribute] = true
|
||||
default:
|
||||
badFields[globals.CredentialTypeField] = fmt.Sprintf("Unknown credential type %q", credentialType)
|
||||
return
|
||||
@@ -1457,6 +1476,18 @@ func getMappingUpdates(credentialType globals.CredentialType, current vault.Mapp
|
||||
default:
|
||||
ret[domainAttribute] = currentDomain
|
||||
}
|
||||
case globals.PasswordCredentialType:
|
||||
var currentPass any
|
||||
if overrides, ok := current.(*vault.PasswordOverride); ok {
|
||||
currentPass = overrides.PasswordAttribute
|
||||
}
|
||||
|
||||
switch {
|
||||
case masks[passwordAttribute]:
|
||||
ret[passwordAttribute] = new[passwordAttribute]
|
||||
default:
|
||||
ret[passwordAttribute] = currentPass
|
||||
}
|
||||
case globals.SshPrivateKeyCredentialType:
|
||||
var currentUser, currentpPass, currentPk any
|
||||
if overrides, ok := current.(*vault.SshPrivateKeyOverride); ok {
|
||||
|
||||
+308
-453
@@ -14,7 +14,6 @@ import (
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/auth/password"
|
||||
"github.com/hashicorp/boundary/internal/authtoken"
|
||||
@@ -87,25 +86,6 @@ func sshCredentialLibraryToProto(credLib *vault.SSHCertificateCredentialLibrary,
|
||||
}
|
||||
}
|
||||
|
||||
func ldapCredentialLibraryToProto(credLib *vault.LdapCredentialLibrary, project *iam.Scope) *pb.CredentialLibrary {
|
||||
return &pb.CredentialLibrary{
|
||||
Id: credLib.GetPublicId(),
|
||||
CredentialStoreId: credLib.GetStoreId(),
|
||||
CredentialType: credLib.GetCredentialType(),
|
||||
Scope: &scopepb.ScopeInfo{Id: project.GetPublicId(), Type: scope.Project.String(), ParentScopeId: project.GetParentId()},
|
||||
CreatedTime: credLib.GetCreateTime().GetTimestamp(),
|
||||
UpdatedTime: credLib.GetUpdateTime().GetTimestamp(),
|
||||
Version: credLib.GetVersion(),
|
||||
Type: vault.LdapCredentialLibrarySubtype.String(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Attrs: &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
|
||||
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String(credLib.GetVaultPath()),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
@@ -134,9 +114,6 @@ func TestList(t *testing.T) {
|
||||
for _, l := range vault.TestSSHCertificateCredentialLibraries(t, conn, wrapper, store.GetPublicId(), 10) {
|
||||
wantLibraries = append(wantLibraries, sshCredentialLibraryToProto(l, prj))
|
||||
}
|
||||
for _, l := range vault.TestLdapCredentialLibraries(t, conn, wrapper, store.GetPublicId(), 10) {
|
||||
wantLibraries = append(wantLibraries, ldapCredentialLibraryToProto(l, prj))
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
@@ -154,7 +131,7 @@ func TestList(t *testing.T) {
|
||||
SortBy: "created_time",
|
||||
SortDir: "desc",
|
||||
RemovedIds: nil,
|
||||
EstItemCount: 30,
|
||||
EstItemCount: 20,
|
||||
},
|
||||
anonRes: &pbs.ListCredentialLibrariesResponse{
|
||||
Items: wantLibraries,
|
||||
@@ -162,7 +139,7 @@ func TestList(t *testing.T) {
|
||||
SortBy: "created_time",
|
||||
SortDir: "desc",
|
||||
RemovedIds: nil,
|
||||
EstItemCount: 30,
|
||||
EstItemCount: 20,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -287,20 +264,16 @@ func TestList_Attributes(t *testing.T) {
|
||||
|
||||
_, prj := iam.TestScopes(t, iamRepo)
|
||||
|
||||
ts := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 3)
|
||||
storeGeneric, storeSSHCertificate, storeLdap := ts[0], ts[1], ts[2]
|
||||
ts := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 2)
|
||||
storeGeneric, storeSSHCertificate := ts[0], ts[1]
|
||||
var wantLibrariesGeneric []*pb.CredentialLibrary
|
||||
var wantLibrariesSSHCertificate []*pb.CredentialLibrary
|
||||
var wantLibrariesLdap []*pb.CredentialLibrary
|
||||
for _, l := range vault.TestCredentialLibraries(t, conn, wrapper, storeGeneric.GetPublicId(), globals.UnspecifiedCredentialType, 5) {
|
||||
wantLibrariesGeneric = append(wantLibrariesGeneric, vaultCredentialLibraryToProto(l, prj))
|
||||
}
|
||||
for _, l := range vault.TestSSHCertificateCredentialLibraries(t, conn, wrapper, storeSSHCertificate.GetPublicId(), 10) {
|
||||
wantLibrariesSSHCertificate = append(wantLibrariesSSHCertificate, sshCredentialLibraryToProto(l, prj))
|
||||
}
|
||||
for _, l := range vault.TestLdapCredentialLibraries(t, conn, wrapper, storeLdap.GetPublicId(), 10) {
|
||||
wantLibrariesLdap = append(wantLibrariesLdap, ldapCredentialLibraryToProto(l, prj))
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
@@ -345,24 +318,6 @@ func TestList_Attributes(t *testing.T) {
|
||||
RemovedIds: nil,
|
||||
}, // anonymous user does not have access to attributes
|
||||
},
|
||||
{
|
||||
name: "Filter on Attribute Ldap Library",
|
||||
req: &pbs.ListCredentialLibrariesRequest{CredentialStoreId: storeLdap.GetPublicId(), Filter: fmt.Sprintf(`"/item/attributes/path"==%q`, wantLibrariesLdap[2].GetVaultLdapCredentialLibraryAttributes().GetPath().Value)},
|
||||
res: &pbs.ListCredentialLibrariesResponse{
|
||||
Items: wantLibrariesLdap[2:3],
|
||||
ResponseType: "complete",
|
||||
SortBy: "created_time",
|
||||
SortDir: "desc",
|
||||
RemovedIds: nil,
|
||||
EstItemCount: 1,
|
||||
},
|
||||
anonRes: &pbs.ListCredentialLibrariesResponse{
|
||||
ResponseType: "complete",
|
||||
SortBy: "created_time",
|
||||
SortDir: "desc",
|
||||
RemovedIds: nil,
|
||||
}, // anonymous user does not have access to attributes
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
@@ -632,47 +587,26 @@ func TestCreate(t *testing.T) {
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "No Vault LDAP path",
|
||||
req: &pbs.CreateCredentialLibraryRequest{
|
||||
Item: &pb.CredentialLibrary{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Type: vault.LdapCredentialLibrarySubtype.String(),
|
||||
Attrs: &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
|
||||
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String(""),
|
||||
},
|
||||
name: "Invalid password mapping",
|
||||
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
||||
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String("something"),
|
||||
},
|
||||
},
|
||||
},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "Invalid Vault LDAP credential type",
|
||||
req: &pbs.CreateCredentialLibraryRequest{
|
||||
Item: &pb.CredentialLibrary{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Type: vault.LdapCredentialLibrarySubtype.String(),
|
||||
CredentialType: string(globals.SshPrivateKeyCredentialType),
|
||||
Attrs: &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
|
||||
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String("ldap/invalid/path"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "Invalid credential library type",
|
||||
req: &pbs.CreateCredentialLibraryRequest{
|
||||
Item: &pb.CredentialLibrary{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Type: "bla-invalid",
|
||||
CredentialType: "bla-invalid",
|
||||
},
|
||||
},
|
||||
CredentialType: string(globals.PasswordCredentialType),
|
||||
CredentialMappingOverrides: func() *structpb.Struct {
|
||||
v := map[string]any{
|
||||
passwordAttribute: "password-test",
|
||||
"invalid": "invalid-test",
|
||||
}
|
||||
ret, err := structpb.NewStruct(v)
|
||||
require.NoError(t, err)
|
||||
return ret
|
||||
}(),
|
||||
}},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
@@ -1073,6 +1007,90 @@ func TestCreate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Create a valid vault CredentialLibrary password type",
|
||||
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
||||
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String("something"),
|
||||
},
|
||||
},
|
||||
CredentialType: string(globals.PasswordCredentialType),
|
||||
}},
|
||||
idPrefix: globals.VaultCredentialLibraryPrefix + "_",
|
||||
res: &pbs.CreateCredentialLibraryResponse{
|
||||
Uri: fmt.Sprintf("credential-libraries/%s_", globals.VaultCredentialLibraryPrefix),
|
||||
Item: &pb.CredentialLibrary{
|
||||
Id: store.GetPublicId(),
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
CreatedTime: store.GetCreateTime().GetTimestamp(),
|
||||
UpdatedTime: store.GetUpdateTime().GetTimestamp(),
|
||||
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: prj.GetType(), ParentScopeId: prj.GetParentId()},
|
||||
Version: 1,
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
||||
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String("something"),
|
||||
HttpMethod: wrapperspb.String("GET"),
|
||||
},
|
||||
},
|
||||
CredentialType: string(globals.PasswordCredentialType),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Create a valid vault CredentialLibrary password type with password mapping",
|
||||
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
||||
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String("something"),
|
||||
},
|
||||
},
|
||||
CredentialMappingOverrides: func() *structpb.Struct {
|
||||
v := map[string]any{
|
||||
passwordAttribute: "password-test",
|
||||
}
|
||||
ret, err := structpb.NewStruct(v)
|
||||
require.NoError(t, err)
|
||||
return ret
|
||||
}(),
|
||||
CredentialType: string(globals.PasswordCredentialType),
|
||||
}},
|
||||
idPrefix: globals.VaultCredentialLibraryPrefix + "_",
|
||||
res: &pbs.CreateCredentialLibraryResponse{
|
||||
Uri: fmt.Sprintf("credential-libraries/%s_", globals.VaultCredentialLibraryPrefix),
|
||||
Item: &pb.CredentialLibrary{
|
||||
Id: store.GetPublicId(),
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
CreatedTime: store.GetCreateTime().GetTimestamp(),
|
||||
UpdatedTime: store.GetUpdateTime().GetTimestamp(),
|
||||
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: prj.GetType(), ParentScopeId: prj.GetParentId()},
|
||||
Version: 1,
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
||||
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String("something"),
|
||||
HttpMethod: wrapperspb.String("GET"),
|
||||
},
|
||||
},
|
||||
CredentialType: string(globals.PasswordCredentialType),
|
||||
CredentialMappingOverrides: func() *structpb.Struct {
|
||||
v := map[string]any{
|
||||
passwordAttribute: "password-test",
|
||||
}
|
||||
ret, err := structpb.NewStruct(v)
|
||||
require.NoError(t, err)
|
||||
return ret
|
||||
}(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Create a valid vault CredentialLibrary ssh_private_key type",
|
||||
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
||||
@@ -1193,40 +1211,6 @@ func TestCreate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Create a valid Vault LDAP Credential library",
|
||||
req: &pbs.CreateCredentialLibraryRequest{
|
||||
Item: &pb.CredentialLibrary{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Type: vault.LdapCredentialLibrarySubtype.String(),
|
||||
Attrs: &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
|
||||
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String("ldap/creds/hi"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
idPrefix: globals.VaultLdapCredentialLibraryPrefix + "_",
|
||||
res: &pbs.CreateCredentialLibraryResponse{
|
||||
Uri: fmt.Sprintf("credential-libraries/%s_", globals.VaultLdapCredentialLibraryPrefix),
|
||||
Item: &pb.CredentialLibrary{
|
||||
Id: store.GetPublicId(),
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
CreatedTime: store.GetCreateTime().GetTimestamp(),
|
||||
UpdatedTime: store.GetUpdateTime().GetTimestamp(),
|
||||
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: prj.GetType(), ParentScopeId: prj.GetParentId()},
|
||||
Version: 1,
|
||||
Type: vault.LdapCredentialLibrarySubtype.String(),
|
||||
CredentialType: string(globals.UsernamePasswordDomainCredentialType),
|
||||
Attrs: &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
|
||||
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String("ldap/creds/hi"),
|
||||
},
|
||||
},
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
@@ -1324,6 +1308,17 @@ func TestGet(t *testing.T) {
|
||||
userPassDomainLib, err := repo.CreateCredentialLibrary(context.Background(), prj.GetPublicId(), libDomain)
|
||||
require.NoError(t, err)
|
||||
|
||||
libPassword, err := vault.NewCredentialLibrary(store.GetPublicId(), "vault/path",
|
||||
vault.WithCredentialType("password"),
|
||||
vault.WithMappingOverride(
|
||||
vault.NewPasswordOverride(
|
||||
vault.WithOverridePasswordAttribute("pass"),
|
||||
)))
|
||||
|
||||
require.NoError(t, err)
|
||||
passwordLib, err := repo.CreateCredentialLibrary(context.Background(), prj.GetPublicId(), libPassword)
|
||||
require.NoError(t, err)
|
||||
|
||||
lib1, err := vault.NewCredentialLibrary(store.GetPublicId(), "vault/path/ssh",
|
||||
vault.WithCredentialType("ssh_private_key"),
|
||||
vault.WithMappingOverride(
|
||||
@@ -1341,13 +1336,6 @@ func TestGet(t *testing.T) {
|
||||
sshCertLib, err := repo.CreateSSHCertificateCredentialLibrary(context.Background(), prj.GetPublicId(), lib2)
|
||||
require.NoError(t, err)
|
||||
|
||||
libLdap1, err := vault.NewLdapCredentialLibrary(store.GetPublicId(), "ldap/creds/test")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, libLdap1)
|
||||
libLdap1, err = repo.CreateLdapCredentialLibrary(t.Context(), prj.GetPublicId(), libLdap1)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, libLdap1)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
id string
|
||||
@@ -1441,6 +1429,37 @@ func TestGet(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "success-password",
|
||||
id: passwordLib.GetPublicId(),
|
||||
res: &pbs.GetCredentialLibraryResponse{
|
||||
Item: &pb.CredentialLibrary{
|
||||
Id: passwordLib.GetPublicId(),
|
||||
CredentialStoreId: passwordLib.GetStoreId(),
|
||||
Scope: &scopepb.ScopeInfo{Id: store.GetProjectId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
CreatedTime: passwordLib.CreateTime.GetTimestamp(),
|
||||
UpdatedTime: passwordLib.UpdateTime.GetTimestamp(),
|
||||
Version: 1,
|
||||
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
||||
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String(passwordLib.GetVaultPath()),
|
||||
HttpMethod: wrapperspb.String(passwordLib.GetHttpMethod()),
|
||||
},
|
||||
},
|
||||
CredentialType: "password",
|
||||
CredentialMappingOverrides: func() *structpb.Struct {
|
||||
v := map[string]any{
|
||||
passwordAttribute: "pass",
|
||||
}
|
||||
ret, err := structpb.NewStruct(v)
|
||||
require.NoError(t, err)
|
||||
return ret
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "success-ssh-private-key",
|
||||
id: sshPkLib.GetPublicId(),
|
||||
@@ -1498,28 +1517,6 @@ func TestGet(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "success-ldap",
|
||||
id: libLdap1.GetPublicId(),
|
||||
res: &pbs.GetCredentialLibraryResponse{
|
||||
Item: &pb.CredentialLibrary{
|
||||
Id: libLdap1.GetPublicId(),
|
||||
CredentialStoreId: libLdap1.GetStoreId(),
|
||||
CredentialType: libLdap1.GetCredentialType(),
|
||||
Scope: &scopepb.ScopeInfo{Id: store.GetProjectId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
|
||||
Type: vault.LdapCredentialLibrarySubtype.String(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
CreatedTime: libLdap1.CreateTime.GetTimestamp(),
|
||||
UpdatedTime: libLdap1.UpdateTime.GetTimestamp(),
|
||||
Version: 1,
|
||||
Attrs: &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
|
||||
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String(libLdap1.GetVaultPath()),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "not found error",
|
||||
id: fmt.Sprintf("%s_1234567890", globals.VaultCredentialLibraryPrefix),
|
||||
@@ -1582,7 +1579,6 @@ func TestDelete(t *testing.T) {
|
||||
store := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0]
|
||||
vl := vault.TestCredentialLibraries(t, conn, wrapper, store.GetPublicId(), globals.UnspecifiedCredentialType, 1)[0]
|
||||
vl2 := vault.TestSSHCertificateCredentialLibraries(t, conn, wrapper, store.GetPublicId(), 1)[0]
|
||||
vl3 := vault.TestLdapCredentialLibraries(t, conn, wrapper, store.GetPublicId(), 1)[0]
|
||||
s, err := NewService(ctx, iamRepoFn, repoFn, 1000)
|
||||
require.NoError(t, err)
|
||||
cases := []struct {
|
||||
@@ -1599,10 +1595,6 @@ func TestDelete(t *testing.T) {
|
||||
name: "success-ssh-cert",
|
||||
id: vl2.GetPublicId(),
|
||||
},
|
||||
{
|
||||
name: "success-ldap",
|
||||
id: vl3.GetPublicId(),
|
||||
},
|
||||
{
|
||||
name: "not found error",
|
||||
id: fmt.Sprintf("%s_1234567890", globals.VaultCredentialLibraryPrefix),
|
||||
@@ -2118,6 +2110,84 @@ func TestUpdate(t *testing.T) {
|
||||
return out
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "password-attribute-change-password-attribute",
|
||||
opts: []vault.Option{
|
||||
vault.WithCredentialType("password"),
|
||||
vault.WithMappingOverride(
|
||||
vault.NewPasswordOverride(
|
||||
vault.WithOverridePasswordAttribute("orig-pass"),
|
||||
)),
|
||||
},
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
UpdateMask: fieldmask(passwordAttrField),
|
||||
Item: &pb.CredentialLibrary{
|
||||
CredentialMappingOverrides: func() *structpb.Struct {
|
||||
v := map[string]any{
|
||||
passwordAttribute: "changed-pass",
|
||||
}
|
||||
ret, err := structpb.NewStruct(v)
|
||||
require.NoError(t, err)
|
||||
return ret
|
||||
}(),
|
||||
},
|
||||
},
|
||||
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
||||
out := proto.Clone(in).(*pb.CredentialLibrary)
|
||||
out.CredentialMappingOverrides.Fields[passwordAttribute] = structpb.NewStringValue("changed-pass")
|
||||
return out
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "password-no-mapping-override-change-password-attribute",
|
||||
opts: []vault.Option{
|
||||
vault.WithCredentialType("password"),
|
||||
},
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
UpdateMask: fieldmask(passwordAttrField),
|
||||
Item: &pb.CredentialLibrary{
|
||||
CredentialMappingOverrides: func() *structpb.Struct {
|
||||
v := map[string]any{
|
||||
passwordAttribute: "new-pass",
|
||||
}
|
||||
ret, err := structpb.NewStruct(v)
|
||||
require.NoError(t, err)
|
||||
return ret
|
||||
}(),
|
||||
},
|
||||
},
|
||||
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
||||
out := proto.Clone(in).(*pb.CredentialLibrary)
|
||||
v := map[string]any{
|
||||
passwordAttribute: "new-pass",
|
||||
}
|
||||
var err error
|
||||
out.CredentialMappingOverrides, err = structpb.NewStruct(v)
|
||||
require.NoError(t, err)
|
||||
return out
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "password-attribute-delete-mapping-override",
|
||||
opts: []vault.Option{
|
||||
vault.WithCredentialType("password"),
|
||||
vault.WithMappingOverride(
|
||||
vault.NewPasswordOverride(
|
||||
vault.WithOverridePasswordAttribute("orig-pass"),
|
||||
)),
|
||||
},
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
UpdateMask: fieldmask(credentialMappingPathField),
|
||||
Item: &pb.CredentialLibrary{
|
||||
CredentialMappingOverrides: nil,
|
||||
},
|
||||
},
|
||||
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
||||
out := proto.Clone(in).(*pb.CredentialLibrary)
|
||||
out.CredentialMappingOverrides = nil
|
||||
return out
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no-mapping-override-delete-mapping-override",
|
||||
opts: []vault.Option{
|
||||
@@ -2166,7 +2236,7 @@ func TestUpdate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "username-password-attributes-delete-mapping-override-field-specific",
|
||||
name: "username-password-domain-attributes-delete-mapping-override-field-specific",
|
||||
opts: []vault.Option{
|
||||
vault.WithCredentialType("username_password_domain"),
|
||||
vault.WithMappingOverride(
|
||||
@@ -2248,6 +2318,75 @@ func TestUpdate(t *testing.T) {
|
||||
return out
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "password-no-mapping-override-delete-mapping-override",
|
||||
opts: []vault.Option{
|
||||
vault.WithCredentialType("password"),
|
||||
},
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
UpdateMask: fieldmask(credentialMappingPathField),
|
||||
Item: &pb.CredentialLibrary{
|
||||
CredentialMappingOverrides: nil,
|
||||
},
|
||||
},
|
||||
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
||||
out := proto.Clone(in).(*pb.CredentialLibrary)
|
||||
out.CredentialMappingOverrides = nil
|
||||
return out
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "password-no-mapping-override-delete-mapping-override-field-specific",
|
||||
opts: []vault.Option{
|
||||
vault.WithCredentialType("password"),
|
||||
},
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
UpdateMask: fieldmask(passwordAttrField),
|
||||
Item: &pb.CredentialLibrary{
|
||||
CredentialMappingOverrides: func() *structpb.Struct {
|
||||
v := map[string]any{
|
||||
passwordAttribute: nil,
|
||||
}
|
||||
ret, err := structpb.NewStruct(v)
|
||||
require.NoError(t, err)
|
||||
return ret
|
||||
}(),
|
||||
},
|
||||
},
|
||||
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
||||
out := proto.Clone(in).(*pb.CredentialLibrary)
|
||||
out.CredentialMappingOverrides = nil
|
||||
return out
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "password-delete-mapping-override-field-specific",
|
||||
opts: []vault.Option{
|
||||
vault.WithCredentialType("password"),
|
||||
vault.WithMappingOverride(
|
||||
vault.NewPasswordOverride(
|
||||
vault.WithOverridePasswordAttribute("orig-pass"),
|
||||
)),
|
||||
},
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
UpdateMask: fieldmask(passwordAttrField),
|
||||
Item: &pb.CredentialLibrary{
|
||||
CredentialMappingOverrides: func() *structpb.Struct {
|
||||
v := map[string]any{
|
||||
passwordAttribute: nil,
|
||||
}
|
||||
ret, err := structpb.NewStruct(v)
|
||||
require.NoError(t, err)
|
||||
return ret
|
||||
}(),
|
||||
},
|
||||
},
|
||||
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
||||
out := proto.Clone(in).(*pb.CredentialLibrary)
|
||||
out.CredentialMappingOverrides = nil
|
||||
return out
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ssh-private-key-attributes-change-username-attribute",
|
||||
opts: []vault.Option{
|
||||
@@ -3402,287 +3541,6 @@ func TestUpdate_SSHCertificateCredentialLibrary(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdate_LdapCredentialLibrary(t *testing.T) {
|
||||
testCtx := context.Background()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
rw := db.New(conn)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
repoFn := func() (*vault.Repository, error) {
|
||||
return vault.NewRepository(testCtx, rw, rw, kms, sche)
|
||||
}
|
||||
|
||||
_, prj := iam.TestScopes(t, iamRepo)
|
||||
ctx := auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId())
|
||||
|
||||
s, err := NewService(testCtx, iamRepoFn, repoFn, 1000)
|
||||
require.NoError(t, err)
|
||||
cs := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)
|
||||
store := cs[0]
|
||||
|
||||
freshLibrary := func(opt ...vault.Option) (*vault.LdapCredentialLibrary, func()) {
|
||||
repo, err := repoFn()
|
||||
require.NoError(t, err)
|
||||
lib, err := vault.NewLdapCredentialLibrary(store.GetPublicId(), "ldap/creds/foo", opt...)
|
||||
require.NoError(t, err)
|
||||
|
||||
vl, err := repo.CreateLdapCredentialLibrary(ctx, prj.GetPublicId(), lib)
|
||||
require.NoError(t, err)
|
||||
clean := func() {
|
||||
_, err := s.DeleteCredentialLibrary(ctx, &pbs.DeleteCredentialLibraryRequest{Id: vl.GetPublicId()})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
return vl, clean
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
req *pbs.UpdateCredentialLibraryRequest
|
||||
res func(*pb.CredentialLibrary) *pb.CredentialLibrary
|
||||
wantErr bool
|
||||
wantErrStatus int
|
||||
wantErrStr string
|
||||
}{
|
||||
{
|
||||
name: "updateNonExistentField",
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
Id: "", // Set at test runtime.
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"doesntExist"}},
|
||||
Item: &pb.CredentialLibrary{
|
||||
Name: wrapperspb.String("updateNonExistentField_test"),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrStatus: runtime.HTTPStatusFromCode(codes.InvalidArgument),
|
||||
wantErrStr: "No valid fields included in the update mask.",
|
||||
},
|
||||
{
|
||||
name: "updateImmutableField",
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
Id: "", // Set at test runtime.
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"storeId"}},
|
||||
Item: &pb.CredentialLibrary{
|
||||
CredentialStoreId: "immutable",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrStatus: runtime.HTTPStatusFromCode(codes.InvalidArgument),
|
||||
wantErrStr: "No valid fields included in the update mask.",
|
||||
},
|
||||
{
|
||||
name: "updateValidFieldWithInvalidData",
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
Id: "", // Set at test runtime.
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"attributes.path"}},
|
||||
Item: &pb.CredentialLibrary{
|
||||
Attrs: &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
|
||||
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String("invalid_path"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrStr: "vault_path_must_have_staticcred_or_creds constraint failed",
|
||||
},
|
||||
{
|
||||
name: "updateImmutableCredentialTypeField",
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
Id: "", // Set at test runtime.
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"credentialType"}},
|
||||
Item: &pb.CredentialLibrary{
|
||||
CredentialType: string(globals.SshPrivateKeyCredentialType),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrStatus: runtime.HTTPStatusFromCode(codes.InvalidArgument),
|
||||
wantErrStr: "Cannot modify credential type.",
|
||||
},
|
||||
{
|
||||
name: "updateImmutableTypeField",
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
Id: "", // Set at test runtime.
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"type"}},
|
||||
Item: &pb.CredentialLibrary{
|
||||
Type: string(globals.SshCertificateCredentialType),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrStatus: runtime.HTTPStatusFromCode(codes.InvalidArgument),
|
||||
wantErrStr: "Cannot modify resource type.",
|
||||
},
|
||||
{
|
||||
name: "unsetVaultPath",
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
Id: "", // Set at test runtime.
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"attributes.path"}},
|
||||
Item: &pb.CredentialLibrary{
|
||||
Attrs: &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
|
||||
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String(""),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrStatus: runtime.HTTPStatusFromCode(codes.InvalidArgument),
|
||||
wantErrStr: "attributes.path\", desc: \"This is a required field and cannot be set to empty.",
|
||||
},
|
||||
{
|
||||
name: "updateName",
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
Id: "", // Set at test runtime
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"name"}},
|
||||
Item: &pb.CredentialLibrary{
|
||||
Name: wrapperspb.String("updated_name"),
|
||||
},
|
||||
},
|
||||
res: func(cl *pb.CredentialLibrary) *pb.CredentialLibrary {
|
||||
cl.Name = wrapperspb.String("updated_name")
|
||||
return cl
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "updateDescription",
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
Id: "", // Set at test runtime
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"description"}},
|
||||
Item: &pb.CredentialLibrary{
|
||||
Description: wrapperspb.String("updated_description"),
|
||||
},
|
||||
},
|
||||
res: func(cl *pb.CredentialLibrary) *pb.CredentialLibrary {
|
||||
cl.Description = wrapperspb.String("updated_description")
|
||||
return cl
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "updateValidVaultPath",
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
Id: "", // Set at test runtime
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"attributes.path"}},
|
||||
Item: &pb.CredentialLibrary{
|
||||
Attrs: &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
|
||||
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String("ldap/static-cred/updated/path"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
res: func(cl *pb.CredentialLibrary) *pb.CredentialLibrary {
|
||||
cl.GetVaultLdapCredentialLibraryAttributes().Path = wrapperspb.String("ldap/static-cred/updated/path")
|
||||
return cl
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "updateMultipleOneNonExistent",
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
Id: "", // Set at test runtime.
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"name", "doesntExist"}},
|
||||
Item: &pb.CredentialLibrary{
|
||||
Name: wrapperspb.String("updated_name"),
|
||||
},
|
||||
},
|
||||
res: func(cl *pb.CredentialLibrary) *pb.CredentialLibrary {
|
||||
cl.Name = wrapperspb.String("updated_name")
|
||||
return cl
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "updateMultipleOneImmutable",
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
Id: "", // Set at test runtime.
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"name", "credentialStoreId"}},
|
||||
Item: &pb.CredentialLibrary{
|
||||
Name: wrapperspb.String("updated_name"),
|
||||
CredentialStoreId: "immutable",
|
||||
},
|
||||
},
|
||||
res: func(cl *pb.CredentialLibrary) *pb.CredentialLibrary {
|
||||
cl.Name = wrapperspb.String("updated_name")
|
||||
return cl
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "updateMultiple",
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
Id: "", // Set at test runtime
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"name", "attributes.path"}},
|
||||
Item: &pb.CredentialLibrary{
|
||||
Name: wrapperspb.String("updated_name"),
|
||||
Attrs: &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
|
||||
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String("ldap/creds/updated/path"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
res: func(cl *pb.CredentialLibrary) *pb.CredentialLibrary {
|
||||
cl.Name = wrapperspb.String("updated_name")
|
||||
cl.GetVaultLdapCredentialLibraryAttributes().Path = wrapperspb.String("ldap/creds/updated/path")
|
||||
return cl
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
st, cleanup := freshLibrary()
|
||||
defer cleanup()
|
||||
|
||||
if tc.req.Item.GetVersion() == 0 {
|
||||
tc.req.Item.Version = 1
|
||||
}
|
||||
if tc.req.GetId() == "" {
|
||||
tc.req.Id = st.GetPublicId()
|
||||
}
|
||||
resToChange, err := s.GetCredentialLibrary(ctx, &pbs.GetCredentialLibraryRequest{Id: st.GetPublicId()})
|
||||
require.NoError(err)
|
||||
|
||||
got, gErr := s.UpdateCredentialLibrary(ctx, tc.req)
|
||||
if tc.wantErr {
|
||||
require.ErrorContains(gErr, tc.wantErrStr)
|
||||
|
||||
gApiErr, ok := gErr.(*handlers.ApiError)
|
||||
if ok {
|
||||
// We don't always return status.
|
||||
require.NotNil(gApiErr)
|
||||
require.EqualValues(tc.wantErrStatus, gApiErr.Status)
|
||||
}
|
||||
require.Nil(got)
|
||||
return
|
||||
}
|
||||
require.NoError(gErr)
|
||||
require.NotNil(got)
|
||||
|
||||
want := &pbs.UpdateCredentialLibraryResponse{Item: tc.res(resToChange.GetItem())}
|
||||
gotUpdateTime := got.GetItem().GetUpdatedTime()
|
||||
created := st.GetCreateTime().GetTimestamp()
|
||||
assert.True(gotUpdateTime.AsTime().After(created.AsTime()), "Should have been updated after it's creation. Was updated %v, which is after %v", gotUpdateTime, created)
|
||||
|
||||
want.Item.UpdatedTime = got.Item.UpdatedTime
|
||||
|
||||
assert.EqualValues(2, got.Item.Version)
|
||||
want.Item.Version = 2
|
||||
|
||||
assert.Empty(cmp.Diff(
|
||||
got,
|
||||
want,
|
||||
protocmp.Transform(),
|
||||
cmpopts.SortSlices(func(a, b string) bool {
|
||||
return a < b
|
||||
}),
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListPagination(t *testing.T) {
|
||||
// Set database read timeout to avoid duplicates in response
|
||||
oldReadTimeout := globals.RefreshReadLookbackDuration
|
||||
@@ -3731,9 +3589,6 @@ func TestListPagination(t *testing.T) {
|
||||
for _, l := range vault.TestSSHCertificateCredentialLibraries(t, conn, wrapper, credStore.GetPublicId(), 5) {
|
||||
allCredentialLibraries = append(allCredentialLibraries, sshCredentialLibraryToProto(l, prj))
|
||||
}
|
||||
for _, l := range vault.TestLdapCredentialLibraries(t, conn, wrapper, credStore.GetPublicId(), 5) {
|
||||
allCredentialLibraries = append(allCredentialLibraries, ldapCredentialLibraryToProto(l, prj))
|
||||
}
|
||||
|
||||
// Reverse as we return items sorted by create_time desceding (newest first)
|
||||
slices.Reverse(allCredentialLibraries)
|
||||
@@ -3787,7 +3642,7 @@ func TestListPagination(t *testing.T) {
|
||||
SortBy: "created_time",
|
||||
SortDir: "desc",
|
||||
RemovedIds: nil,
|
||||
EstItemCount: 15,
|
||||
EstItemCount: 10,
|
||||
},
|
||||
cmpopts.SortSlices(func(a, b string) bool {
|
||||
return a < b
|
||||
@@ -3812,7 +3667,7 @@ func TestListPagination(t *testing.T) {
|
||||
SortBy: "created_time",
|
||||
SortDir: "desc",
|
||||
RemovedIds: nil,
|
||||
EstItemCount: 15,
|
||||
EstItemCount: 10,
|
||||
},
|
||||
cmpopts.SortSlices(func(a, b string) bool {
|
||||
return a < b
|
||||
@@ -3824,10 +3679,10 @@ func TestListPagination(t *testing.T) {
|
||||
|
||||
// Request rest of results
|
||||
req.ListToken = got.ListToken
|
||||
req.PageSize = 15
|
||||
req.PageSize = 10
|
||||
got, err = s.ListCredentialLibraries(ctx, req)
|
||||
require.NoError(err)
|
||||
require.Len(got.GetItems(), 11)
|
||||
require.Len(got.GetItems(), 6)
|
||||
// Compare without comparing the list token
|
||||
assert.Empty(
|
||||
cmp.Diff(
|
||||
@@ -3838,7 +3693,7 @@ func TestListPagination(t *testing.T) {
|
||||
SortBy: "created_time",
|
||||
SortDir: "desc",
|
||||
RemovedIds: nil,
|
||||
EstItemCount: 15,
|
||||
EstItemCount: 10,
|
||||
},
|
||||
cmpopts.SortSlices(func(a, b string) bool {
|
||||
return a < b
|
||||
@@ -3863,14 +3718,14 @@ func TestListPagination(t *testing.T) {
|
||||
// Update one of the other stores
|
||||
allCredentialLibraries[1].Name = wrapperspb.String("new-name")
|
||||
allCredentialLibraries[1].Version = 2
|
||||
updatedLibrary := &vault.LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{
|
||||
updatedLibrary := &vault.SSHCertificateCredentialLibrary{
|
||||
SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{
|
||||
PublicId: allCredentialLibraries[1].GetId(),
|
||||
StoreId: allCredentialLibraries[1].GetCredentialStoreId(),
|
||||
Name: allCredentialLibraries[1].Name.Value,
|
||||
},
|
||||
}
|
||||
cred, _, err := vaultRepo.UpdateLdapCredentialLibrary(ctx, prj.PublicId, updatedLibrary, 1, []string{"name"})
|
||||
cred, _, err := vaultRepo.UpdateSSHCertificateCredentialLibrary(ctx, prj.PublicId, updatedLibrary, 1, []string{"name"})
|
||||
require.NoError(err)
|
||||
allCredentialLibraries[1].UpdatedTime = cred.UpdateTime.GetTimestamp()
|
||||
allCredentialLibraries[1].Version = cred.Version
|
||||
@@ -3904,7 +3759,7 @@ func TestListPagination(t *testing.T) {
|
||||
SortDir: "desc",
|
||||
// Should contain the deleted library
|
||||
RemovedIds: []string{deletedCredLib.Id},
|
||||
EstItemCount: 15,
|
||||
EstItemCount: 10,
|
||||
},
|
||||
cmpopts.SortSlices(func(a, b string) bool {
|
||||
return a < b
|
||||
@@ -3929,7 +3784,7 @@ func TestListPagination(t *testing.T) {
|
||||
SortBy: "updated_time",
|
||||
SortDir: "desc",
|
||||
RemovedIds: nil,
|
||||
EstItemCount: 15,
|
||||
EstItemCount: 10,
|
||||
},
|
||||
cmpopts.SortSlices(func(a, b string) bool {
|
||||
return a < b
|
||||
@@ -3957,7 +3812,7 @@ func TestListPagination(t *testing.T) {
|
||||
SortDir: "desc",
|
||||
// Should be empty again
|
||||
RemovedIds: nil,
|
||||
EstItemCount: 15,
|
||||
EstItemCount: 10,
|
||||
},
|
||||
cmpopts.SortSlices(func(a, b string) bool {
|
||||
return a < b
|
||||
@@ -3980,7 +3835,7 @@ func TestListPagination(t *testing.T) {
|
||||
SortBy: "created_time",
|
||||
SortDir: "desc",
|
||||
RemovedIds: nil,
|
||||
EstItemCount: 15,
|
||||
EstItemCount: 10,
|
||||
},
|
||||
cmpopts.SortSlices(func(a, b string) bool {
|
||||
return a < b
|
||||
|
||||
@@ -49,6 +49,7 @@ var (
|
||||
upMaskManager handlers.MaskManager
|
||||
spkMaskManager handlers.MaskManager
|
||||
jsonMaskManager handlers.MaskManager
|
||||
pMaskmanager handlers.MaskManager
|
||||
|
||||
// IdActions contains the set of actions that can be performed on
|
||||
// individual resources
|
||||
@@ -97,6 +98,13 @@ func init() {
|
||||
); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if pMaskmanager, err = handlers.NewMaskManager(
|
||||
context.Background(),
|
||||
handlers.MaskDestination{&store.PasswordCredential{}},
|
||||
handlers.MaskSource{&pb.Credential{}, &pb.PasswordAttributes{}},
|
||||
); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// TODO: refactor to remove IdActionsMap and CollectionActions package variables
|
||||
action.RegisterResource(resource.Credential, IdActions, CollectionActions)
|
||||
@@ -454,6 +462,23 @@ func (s Service) createInRepo(ctx context.Context, scopeId string, item *pb.Cred
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create credential but no error returned from repository.")
|
||||
}
|
||||
return out, nil
|
||||
case credential.PasswordSubtype.String():
|
||||
cred, err := toPasswordStorageCredential(ctx, item.GetCredentialStoreId(), item)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
out, err := repo.CreatePasswordCredential(ctx, scopeId, cred)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create credential"))
|
||||
}
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create credential but no error returned from repository.")
|
||||
}
|
||||
return out, nil
|
||||
case credential.SshPrivateKeySubtype.String():
|
||||
cred, err := toSshPrivateKeyStorageCredential(ctx, item.GetCredentialStoreId(), item)
|
||||
if err != nil {
|
||||
@@ -551,7 +576,29 @@ func (s Service) updateInRepo(
|
||||
return nil, handlers.NotFoundErrorf("Credential %q doesn't exist or incorrect version provided.", id)
|
||||
}
|
||||
return out, nil
|
||||
case credential.PasswordSubtype:
|
||||
dbMasks = append(dbMasks, pMaskmanager.Translate(masks)...)
|
||||
if len(dbMasks) == 0 {
|
||||
return nil, handlers.InvalidArgumentErrorf("No valid fields included in the update mask.", map[string]string{"update_mask": "No valid fields provided in the update mask."})
|
||||
}
|
||||
|
||||
cred, err := toPasswordStorageCredential(ctx, storeId, in)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to convert to password storage credential"))
|
||||
}
|
||||
cred.PublicId = id
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
out, rowsUpdated, err := repo.UpdatePasswordCredential(ctx, scopeId, cred, item.GetVersion(), dbMasks)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to update credential"))
|
||||
}
|
||||
if rowsUpdated == 0 {
|
||||
return nil, handlers.NotFoundErrorf("Credential %q doesn't exist or incorrect version provided.", id)
|
||||
}
|
||||
return out, nil
|
||||
case credential.SshPrivateKeySubtype:
|
||||
dbMasks = append(dbMasks, spkMaskManager.Translate(masks)...)
|
||||
if len(dbMasks) == 0 {
|
||||
@@ -585,7 +632,6 @@ func (s Service) updateInRepo(
|
||||
return nil, handlers.NotFoundErrorf("Credential %q doesn't exist or incorrect version provided.", id)
|
||||
}
|
||||
return out, nil
|
||||
|
||||
case credential.JsonSubtype:
|
||||
dbMasks = append(dbMasks, jsonMaskManager.Translate(masks, "attributes", "object")...)
|
||||
if len(dbMasks) == 0 {
|
||||
@@ -609,7 +655,6 @@ func (s Service) updateInRepo(
|
||||
return nil, handlers.NotFoundErrorf("Credential %q doesn't exist or incorrect version provided.", id)
|
||||
}
|
||||
return out, nil
|
||||
|
||||
default:
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, fmt.Sprintf("Unsupported credential type %q", item.GetType()))
|
||||
|
||||
@@ -731,6 +776,8 @@ func toProto(in credential.Static, opt ...handlers.Option) (*pb.Credential, erro
|
||||
out.Type = credential.UsernamePasswordSubtype.String()
|
||||
case *static.UsernamePasswordDomainCredential:
|
||||
out.Type = credential.UsernamePasswordDomainSubtype.String()
|
||||
case *static.PasswordCredential:
|
||||
out.Type = credential.PasswordSubtype.String()
|
||||
case *static.SshPrivateKeyCredential:
|
||||
out.Type = credential.SshPrivateKeySubtype.String()
|
||||
case *static.JsonCredential:
|
||||
@@ -779,6 +826,14 @@ func toProto(in credential.Static, opt ...handlers.Option) (*pb.Credential, erro
|
||||
},
|
||||
}
|
||||
}
|
||||
case *static.PasswordCredential:
|
||||
if outputFields.Has(globals.AttributesField) {
|
||||
out.Attrs = &pb.Credential_PasswordAttributes{
|
||||
PasswordAttributes: &pb.PasswordAttributes{
|
||||
PasswordHmac: base64.RawURLEncoding.EncodeToString(cred.GetPasswordHmac()),
|
||||
},
|
||||
}
|
||||
}
|
||||
case *static.SshPrivateKeyCredential:
|
||||
if outputFields.Has(globals.AttributesField) {
|
||||
out.Attrs = &pb.Credential_SshPrivateKeyAttributes{
|
||||
@@ -848,6 +903,28 @@ func toUsernamePasswordDomainStorageCredential(ctx context.Context, storeId stri
|
||||
return cs, err
|
||||
}
|
||||
|
||||
func toPasswordStorageCredential(ctx context.Context, storeId string, in *pb.Credential) (out *static.PasswordCredential, err error) {
|
||||
const op = "credentials.toPasswordStorageCredential"
|
||||
var opts []static.Option
|
||||
if in.GetName() != nil {
|
||||
opts = append(opts, static.WithName(in.GetName().GetValue()))
|
||||
}
|
||||
if in.GetDescription() != nil {
|
||||
opts = append(opts, static.WithDescription(in.GetDescription().GetValue()))
|
||||
}
|
||||
|
||||
attrs := in.GetPasswordAttributes()
|
||||
cs, err := static.NewPasswordCredential(
|
||||
storeId,
|
||||
credential.Password(attrs.GetPassword().GetValue()),
|
||||
opts...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to build credential"))
|
||||
}
|
||||
|
||||
return cs, err
|
||||
}
|
||||
|
||||
func toSshPrivateKeyStorageCredential(ctx context.Context, storeId string, in *pb.Credential) (out *static.SshPrivateKeyCredential, err error) {
|
||||
const op = "credentials.toSshPrivateKeyStorageCredential"
|
||||
var opts []static.Option
|
||||
@@ -919,6 +996,7 @@ func validateGetRequest(req *pbs.GetCredentialRequest) error {
|
||||
globals.UsernamePasswordCredentialPrefix,
|
||||
globals.UsernamePasswordCredentialPreviousPrefix,
|
||||
globals.UsernamePasswordDomainCredentialPrefix,
|
||||
globals.PasswordCredentialPrefix,
|
||||
globals.SshPrivateKeyCredentialPrefix,
|
||||
globals.JsonCredentialPrefix,
|
||||
)
|
||||
@@ -949,6 +1027,10 @@ func validateCreateRequest(req *pbs.CreateCredentialRequest) error {
|
||||
if req.Item.GetUsernamePasswordDomainAttributes().GetDomain().GetValue() == "" {
|
||||
badFields[domainField] = "Field required for creating a username-password-domain credential."
|
||||
}
|
||||
case credential.PasswordSubtype.String():
|
||||
if req.Item.GetPasswordAttributes().GetPassword().GetValue() == "" {
|
||||
badFields[passwordField] = "Field required for creating a password credential."
|
||||
}
|
||||
case credential.SshPrivateKeySubtype.String():
|
||||
if req.Item.GetSshPrivateKeyAttributes().GetUsername().GetValue() == "" {
|
||||
badFields[usernameField] = "Field required for creating an SSH private key credential."
|
||||
@@ -977,7 +1059,6 @@ func validateCreateRequest(req *pbs.CreateCredentialRequest) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case credential.JsonSubtype.String():
|
||||
object := req.GetItem().GetJsonAttributes().GetObject()
|
||||
if object == nil || len(object.AsMap()) <= 0 {
|
||||
@@ -985,7 +1066,6 @@ func validateCreateRequest(req *pbs.CreateCredentialRequest) error {
|
||||
} else if _, err := json.Marshal(object); err != nil {
|
||||
badFields[objectField] = "Unable to parse given json value"
|
||||
}
|
||||
|
||||
default:
|
||||
badFields[globals.TypeField] = fmt.Sprintf("Unsupported credential type %q", req.Item.GetType())
|
||||
}
|
||||
@@ -1018,7 +1098,11 @@ func validateUpdateRequest(req *pbs.UpdateCredentialRequest) error {
|
||||
if handlers.MaskContains(req.GetUpdateMask().GetPaths(), domainField) && attrs.GetDomain().GetValue() == "" {
|
||||
badFields[domainField] = "This is a required field and cannot be set to empty."
|
||||
}
|
||||
|
||||
case credential.PasswordSubtype:
|
||||
attrs := req.GetItem().GetPasswordAttributes()
|
||||
if handlers.MaskContains(req.GetUpdateMask().GetPaths(), passwordField) && attrs.GetPassword().GetValue() == "" {
|
||||
badFields[passwordField] = "This is a required field and cannot be set to empty."
|
||||
}
|
||||
case credential.SshPrivateKeySubtype:
|
||||
attrs := req.GetItem().GetSshPrivateKeyAttributes()
|
||||
if handlers.MaskContains(req.GetUpdateMask().GetPaths(), usernameField) && attrs.GetUsername().GetValue() == "" {
|
||||
@@ -1050,7 +1134,6 @@ func validateUpdateRequest(req *pbs.UpdateCredentialRequest) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case credential.JsonSubtype:
|
||||
if handlers.MaskContainsPrefix(req.GetUpdateMask().GetPaths(), objectField) {
|
||||
object := req.GetItem().GetJsonAttributes().GetObject()
|
||||
@@ -1060,7 +1143,6 @@ func validateUpdateRequest(req *pbs.UpdateCredentialRequest) error {
|
||||
badFields[objectField] = "Unable to parse given json value"
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
badFields[globals.IdField] = "Unknown credential type."
|
||||
}
|
||||
@@ -1070,6 +1152,7 @@ func validateUpdateRequest(req *pbs.UpdateCredentialRequest) error {
|
||||
globals.UsernamePasswordCredentialPrefix,
|
||||
globals.UsernamePasswordCredentialPreviousPrefix,
|
||||
globals.UsernamePasswordDomainCredentialPrefix,
|
||||
globals.PasswordCredentialPrefix,
|
||||
globals.SshPrivateKeyCredentialPrefix,
|
||||
globals.JsonCredentialPrefix,
|
||||
)
|
||||
@@ -1082,6 +1165,7 @@ func validateDeleteRequest(req *pbs.DeleteCredentialRequest) error {
|
||||
globals.UsernamePasswordCredentialPrefix,
|
||||
globals.UsernamePasswordCredentialPreviousPrefix,
|
||||
globals.UsernamePasswordDomainCredentialPrefix,
|
||||
globals.PasswordCredentialPrefix,
|
||||
globals.SshPrivateKeyCredentialPrefix,
|
||||
globals.JsonCredentialPrefix,
|
||||
)
|
||||
|
||||
@@ -68,7 +68,7 @@ func staticJsonCredentialToProto(cred *static.JsonCredential, prj *iam.Scope, hm
|
||||
}
|
||||
}
|
||||
|
||||
func staticPasswordCredentialToProto(cred *static.UsernamePasswordCredential, prj *iam.Scope, hmac string) *pb.Credential {
|
||||
func staticUsernamePasswordCredentialToProto(cred *static.UsernamePasswordCredential, prj *iam.Scope, hmac string) *pb.Credential {
|
||||
return &pb.Credential{
|
||||
Id: cred.GetPublicId(),
|
||||
CredentialStoreId: cred.GetStoreId(),
|
||||
@@ -87,7 +87,25 @@ func staticPasswordCredentialToProto(cred *static.UsernamePasswordCredential, pr
|
||||
}
|
||||
}
|
||||
|
||||
func staticPasswordDomainCredentialToProto(cred *static.UsernamePasswordDomainCredential, prj *iam.Scope, hmac string) *pb.Credential {
|
||||
func staticPasswordCredentialToProto(cred *static.PasswordCredential, prj *iam.Scope, hmac string) *pb.Credential {
|
||||
return &pb.Credential{
|
||||
Id: cred.GetPublicId(),
|
||||
CredentialStoreId: cred.GetStoreId(),
|
||||
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
|
||||
CreatedTime: cred.GetCreateTime().GetTimestamp(),
|
||||
UpdatedTime: cred.GetUpdateTime().GetTimestamp(),
|
||||
Version: cred.GetVersion(),
|
||||
Type: credential.PasswordSubtype.String(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Attrs: &pb.Credential_PasswordAttributes{
|
||||
PasswordAttributes: &pb.PasswordAttributes{
|
||||
PasswordHmac: base64.RawURLEncoding.EncodeToString([]byte(hmac)),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func staticUsernamePasswordDomainCredentialToProto(cred *static.UsernamePasswordDomainCredential, prj *iam.Scope, hmac string) *pb.Credential {
|
||||
return &pb.Credential{
|
||||
Id: cred.GetPublicId(),
|
||||
CredentialStoreId: cred.GetStoreId(),
|
||||
@@ -155,13 +173,18 @@ func TestList(t *testing.T) {
|
||||
c := static.TestUsernamePasswordCredential(t, conn, wrapper, user, pass, store.GetPublicId(), prj.GetPublicId())
|
||||
hm, err := crypto.HmacSha256(ctx, []byte(pass), databaseWrapper, []byte(store.GetPublicId()), nil, crypto.WithEd25519())
|
||||
require.NoError(t, err)
|
||||
wantCreds = append(wantCreds, staticPasswordCredentialToProto(c, prj, hm))
|
||||
wantCreds = append(wantCreds, staticUsernamePasswordCredentialToProto(c, prj, hm))
|
||||
|
||||
domain := fmt.Sprintf("domain-%d", i)
|
||||
upd := static.TestUsernamePasswordDomainCredential(t, conn, wrapper, user, pass, domain, store.GetPublicId(), prj.GetPublicId())
|
||||
hm, err = crypto.HmacSha256(ctx, []byte(pass), databaseWrapper, []byte(store.GetPublicId()), nil, crypto.WithEd25519())
|
||||
require.NoError(t, err)
|
||||
wantCreds = append(wantCreds, staticPasswordDomainCredentialToProto(upd, prj, hm))
|
||||
wantCreds = append(wantCreds, staticUsernamePasswordDomainCredentialToProto(upd, prj, hm))
|
||||
|
||||
p := static.TestPasswordCredential(t, conn, wrapper, pass, store.GetPublicId(), prj.GetPublicId())
|
||||
hm, err = crypto.HmacSha256(ctx, []byte(pass), databaseWrapper, []byte(store.GetPublicId()), nil, crypto.WithEd25519())
|
||||
require.NoError(t, err)
|
||||
wantCreds = append(wantCreds, staticPasswordCredentialToProto(p, prj, hm))
|
||||
|
||||
spk := static.TestSshPrivateKeyCredential(t, conn, wrapper, user, static.TestSshPrivateKeyPem, store.GetPublicId(), prj.GetPublicId())
|
||||
hm, err = crypto.HmacSha256(ctx, []byte(static.TestSshPrivateKeyPem), databaseWrapper, []byte(store.GetPublicId()), nil)
|
||||
@@ -193,7 +216,7 @@ func TestList(t *testing.T) {
|
||||
SortBy: "created_time",
|
||||
SortDir: "desc",
|
||||
RemovedIds: nil,
|
||||
EstItemCount: 40,
|
||||
EstItemCount: 50,
|
||||
},
|
||||
anonRes: &pbs.ListCredentialsResponse{
|
||||
Items: wantCreds,
|
||||
@@ -247,9 +270,9 @@ func TestList(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "Filter on Attribute",
|
||||
req: &pbs.ListCredentialsRequest{CredentialStoreId: store.GetPublicId(), Filter: fmt.Sprintf(`"/item/attributes/username"==%q`, wantCreds[4].GetUsernamePasswordAttributes().GetUsername().Value)},
|
||||
req: &pbs.ListCredentialsRequest{CredentialStoreId: store.GetPublicId(), Filter: fmt.Sprintf(`"/item/attributes/username"==%q`, wantCreds[0].GetUsernamePasswordAttributes().GetUsername().Value)},
|
||||
res: &pbs.ListCredentialsResponse{
|
||||
Items: wantCreds[4:7],
|
||||
Items: []*pb.Credential{wantCreds[0], wantCreds[1], wantCreds[3]},
|
||||
ResponseType: "complete",
|
||||
ListToken: "",
|
||||
SortBy: "created_time",
|
||||
@@ -363,6 +386,10 @@ func TestGet(t *testing.T) {
|
||||
updHm, err := crypto.HmacSha256(context.Background(), []byte("pass"), databaseWrapper, []byte(store.GetPublicId()), nil, crypto.WithEd25519())
|
||||
require.NoError(t, err)
|
||||
|
||||
pCred := static.TestPasswordCredential(t, conn, wrapper, "pass", store.GetPublicId(), prj.GetPublicId())
|
||||
pHm, err := crypto.HmacSha256(context.Background(), []byte("pass"), databaseWrapper, []byte(store.GetPublicId()), nil, crypto.WithEd25519())
|
||||
require.NoError(t, err)
|
||||
|
||||
spkCred := static.TestSshPrivateKeyCredential(t, conn, wrapper, "user", static.TestSshPrivateKeyPem, store.GetPublicId(), prj.GetPublicId())
|
||||
spkHm, err := crypto.HmacSha256(context.Background(), []byte(static.TestSshPrivateKeyPem), databaseWrapper, []byte(store.GetPublicId()), nil)
|
||||
require.NoError(t, err)
|
||||
@@ -455,7 +482,27 @@ func TestGet(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "success-password-only-credential",
|
||||
id: pCred.GetPublicId(),
|
||||
res: &pbs.GetCredentialResponse{
|
||||
Item: &pb.Credential{
|
||||
Id: pCred.GetPublicId(),
|
||||
CredentialStoreId: pCred.GetStoreId(),
|
||||
Scope: &scopepb.ScopeInfo{Id: store.GetProjectId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
|
||||
Type: credential.PasswordSubtype.String(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
CreatedTime: pCred.CreateTime.GetTimestamp(),
|
||||
UpdatedTime: pCred.UpdateTime.GetTimestamp(),
|
||||
Version: 1,
|
||||
Attrs: &pb.Credential_PasswordAttributes{
|
||||
PasswordAttributes: &pb.PasswordAttributes{
|
||||
PasswordHmac: base64.RawURLEncoding.EncodeToString([]byte(pHm)),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "success-spk",
|
||||
id: spkCred.GetPublicId(),
|
||||
@@ -589,6 +636,7 @@ func TestDelete(t *testing.T) {
|
||||
|
||||
upCred := static.TestUsernamePasswordCredential(t, conn, wrapper, "user", "pass", store.GetPublicId(), prj.GetPublicId())
|
||||
updCred := static.TestUsernamePasswordDomainCredential(t, conn, wrapper, "user", "pass", "domain", store.GetPublicId(), prj.GetPublicId())
|
||||
pCred := static.TestPasswordCredential(t, conn, wrapper, "pass", store.GetPublicId(), prj.GetPublicId())
|
||||
spkCred := static.TestSshPrivateKeyCredential(t, conn, wrapper, "user", static.TestSshPrivateKeyPem, store.GetPublicId(), prj.GetPublicId())
|
||||
|
||||
obj, _ := static.TestJsonObject(t)
|
||||
@@ -609,6 +657,10 @@ func TestDelete(t *testing.T) {
|
||||
name: "success-upd",
|
||||
id: updCred.GetPublicId(),
|
||||
},
|
||||
{
|
||||
name: "success-p",
|
||||
id: pCred.GetPublicId(),
|
||||
},
|
||||
{
|
||||
name: "success-spk",
|
||||
id: spkCred.GetPublicId(),
|
||||
@@ -906,6 +958,92 @@ func TestCreate(t *testing.T) {
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "Can't specify Id P",
|
||||
req: &pbs.CreateCredentialRequest{Item: &pb.Credential{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Id: globals.PasswordCredentialPrefix + "_notallowed",
|
||||
Type: credential.PasswordSubtype.String(),
|
||||
Attrs: &pb.Credential_PasswordAttributes{
|
||||
PasswordAttributes: &pb.PasswordAttributes{
|
||||
Password: wrapperspb.String("password"),
|
||||
},
|
||||
},
|
||||
}},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "Invalid Credential Store Id P",
|
||||
req: &pbs.CreateCredentialRequest{Item: &pb.Credential{
|
||||
CredentialStoreId: "p_invalidid",
|
||||
Type: credential.PasswordSubtype.String(),
|
||||
Attrs: &pb.Credential_PasswordAttributes{
|
||||
PasswordAttributes: &pb.PasswordAttributes{
|
||||
Password: wrapperspb.String("password"),
|
||||
},
|
||||
},
|
||||
}},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "Can't specify Created Time",
|
||||
req: &pbs.CreateCredentialRequest{Item: &pb.Credential{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
CreatedTime: timestamppb.Now(),
|
||||
Type: credential.PasswordSubtype.String(),
|
||||
Attrs: &pb.Credential_PasswordAttributes{
|
||||
PasswordAttributes: &pb.PasswordAttributes{
|
||||
Password: wrapperspb.String("password"),
|
||||
},
|
||||
},
|
||||
}},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "Can't specify Updated Time",
|
||||
req: &pbs.CreateCredentialRequest{Item: &pb.Credential{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
UpdatedTime: timestamppb.Now(),
|
||||
Type: credential.PasswordSubtype.String(),
|
||||
Attrs: &pb.Credential_PasswordAttributes{
|
||||
PasswordAttributes: &pb.PasswordAttributes{
|
||||
Password: wrapperspb.String("password"),
|
||||
},
|
||||
},
|
||||
}},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "Must provide type",
|
||||
req: &pbs.CreateCredentialRequest{Item: &pb.Credential{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Attrs: &pb.Credential_PasswordAttributes{
|
||||
PasswordAttributes: &pb.PasswordAttributes{
|
||||
Password: wrapperspb.String("password"),
|
||||
},
|
||||
},
|
||||
}},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "Must provide password",
|
||||
req: &pbs.CreateCredentialRequest{Item: &pb.Credential{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Type: credential.PasswordSubtype.String(),
|
||||
Attrs: &pb.Credential_PasswordAttributes{
|
||||
PasswordAttributes: &pb.PasswordAttributes{
|
||||
// Empty password
|
||||
},
|
||||
},
|
||||
}},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "Must provide private key",
|
||||
req: &pbs.CreateCredentialRequest{Item: &pb.Credential{
|
||||
@@ -959,6 +1097,32 @@ func TestCreate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-p",
|
||||
req: &pbs.CreateCredentialRequest{Item: &pb.Credential{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Type: credential.PasswordSubtype.String(),
|
||||
Attrs: &pb.Credential_PasswordAttributes{
|
||||
PasswordAttributes: &pb.PasswordAttributes{
|
||||
Password: wrapperspb.String("password"),
|
||||
},
|
||||
},
|
||||
}},
|
||||
idPrefix: globals.PasswordCredentialPrefix + "_",
|
||||
res: &pbs.CreateCredentialResponse{
|
||||
Uri: fmt.Sprintf("credentials/%s_", globals.PasswordCredentialPrefix),
|
||||
Item: &pb.Credential{
|
||||
Id: store.GetPublicId(),
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
CreatedTime: store.GetCreateTime().GetTimestamp(),
|
||||
UpdatedTime: store.GetUpdateTime().GetTimestamp(),
|
||||
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: prj.GetType(), ParentScopeId: prj.GetParentId()},
|
||||
Version: 1,
|
||||
Type: credential.PasswordSubtype.String(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-spk",
|
||||
req: &pbs.CreateCredentialRequest{Item: &pb.Credential{
|
||||
@@ -1086,6 +1250,15 @@ func TestCreate(t *testing.T) {
|
||||
assert.Equal(base64.RawURLEncoding.EncodeToString([]byte(hm)), got.GetItem().GetUsernamePasswordAttributes().GetPasswordHmac())
|
||||
assert.Empty(got.GetItem().GetUsernamePasswordAttributes().GetPassword())
|
||||
|
||||
case credential.PasswordSubtype.String():
|
||||
password := tc.req.GetItem().GetPasswordAttributes().GetPassword().GetValue()
|
||||
hm, err := crypto.HmacSha256(ctx, []byte(password), databaseWrapper, []byte(store.GetPublicId()), nil, crypto.WithEd25519())
|
||||
require.NoError(err)
|
||||
|
||||
// Validate attributes equal
|
||||
assert.Equal(base64.RawURLEncoding.EncodeToString([]byte(hm)), got.GetItem().GetPasswordAttributes().GetPasswordHmac())
|
||||
assert.Empty(got.GetItem().GetPasswordAttributes().GetPassword())
|
||||
|
||||
case credential.SshPrivateKeySubtype.String():
|
||||
pk := tc.req.GetItem().GetSshPrivateKeyAttributes().GetPrivateKey().GetValue()
|
||||
hm, err := crypto.HmacSha256(ctx, []byte(pk), databaseWrapper, []byte(store.GetPublicId()), nil)
|
||||
@@ -1193,6 +1366,16 @@ func TestUpdate(t *testing.T) {
|
||||
return cred, clean
|
||||
}
|
||||
|
||||
freshCredP := func(pass string) (*static.PasswordCredential, func()) {
|
||||
t.Helper()
|
||||
cred := static.TestPasswordCredential(t, conn, wrapper, pass, store.GetPublicId(), prj.GetPublicId())
|
||||
clean := func() {
|
||||
_, err := s.DeleteCredential(ctx, &pbs.DeleteCredentialRequest{Id: cred.GetPublicId()})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
return cred, clean
|
||||
}
|
||||
|
||||
freshCredSpk := func(user string) (*static.SshPrivateKeyCredential, func()) {
|
||||
t.Helper()
|
||||
cred := static.TestSshPrivateKeyCredential(t, conn, wrapper, user, static.TestSshPrivateKeyPem, store.GetPublicId(), prj.GetPublicId())
|
||||
@@ -1405,6 +1588,26 @@ func TestUpdate(t *testing.T) {
|
||||
return out
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "update-password-attributes-passwordOnly",
|
||||
req: &pbs.UpdateCredentialRequest{
|
||||
UpdateMask: fieldmask("attributes.password"),
|
||||
Item: &pb.Credential{
|
||||
Attrs: &pb.Credential_PasswordAttributes{
|
||||
PasswordAttributes: &pb.PasswordAttributes{
|
||||
Password: wrapperspb.String("new-password"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
res: func(in *pb.Credential) *pb.Credential {
|
||||
out := proto.Clone(in).(*pb.Credential)
|
||||
hm, err := crypto.HmacSha256(context.Background(), []byte("new-password"), databaseWrapper, []byte(store.GetPublicId()), nil, crypto.WithEd25519())
|
||||
require.NoError(t, err)
|
||||
out.GetPasswordAttributes().PasswordHmac = base64.RawURLEncoding.EncodeToString([]byte(hm))
|
||||
return out
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "update-spk",
|
||||
req: &pbs.UpdateCredentialRequest{
|
||||
@@ -1663,6 +1866,8 @@ func TestUpdate(t *testing.T) {
|
||||
cred, cleanup = freshCredJson()
|
||||
} else if strings.Contains(tc.name, "domainupd") {
|
||||
cred, cleanup = freshCredUpd("user", "pass", "domain")
|
||||
} else if strings.Contains(tc.name, "passwordOnly") {
|
||||
cred, cleanup = freshCredP("pass")
|
||||
} else {
|
||||
cred, cleanup = freshCredUp("user", "pass")
|
||||
}
|
||||
@@ -1716,6 +1921,14 @@ func TestUpdate(t *testing.T) {
|
||||
credUp, cleanUp := freshCredUp("user", "pass")
|
||||
defer cleanUp()
|
||||
|
||||
// cant update read only fields
|
||||
credUpd, cleanUpd := freshCredUpd("user", "pass", "domain")
|
||||
defer cleanUpd()
|
||||
|
||||
// cant update read only fields
|
||||
credP, cleanP := freshCredP("pass")
|
||||
defer cleanP()
|
||||
|
||||
newStore := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
|
||||
|
||||
roCases := []struct {
|
||||
@@ -1781,6 +1994,42 @@ func TestUpdate(t *testing.T) {
|
||||
}
|
||||
matcher(t, gErr)
|
||||
assert.Nil(t, got)
|
||||
|
||||
req = &pbs.UpdateCredentialRequest{
|
||||
Id: credUpd.GetPublicId(),
|
||||
Item: tc.item,
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{tc.path}},
|
||||
}
|
||||
req.Item.Version = credUpd.Version
|
||||
|
||||
got, gErr = s.UpdateCredential(ctx, req)
|
||||
assert.Error(t, gErr)
|
||||
matcher = tc.matcher
|
||||
if matcher == nil {
|
||||
matcher = func(t *testing.T, e error) {
|
||||
assert.Truef(t, errors.Is(gErr, handlers.ApiErrorWithCode(codes.InvalidArgument)), "got error %v, wanted invalid argument", gErr)
|
||||
}
|
||||
}
|
||||
matcher(t, gErr)
|
||||
assert.Nil(t, got)
|
||||
|
||||
req = &pbs.UpdateCredentialRequest{
|
||||
Id: credP.GetPublicId(),
|
||||
Item: tc.item,
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{tc.path}},
|
||||
}
|
||||
req.Item.Version = credP.Version
|
||||
|
||||
got, gErr = s.UpdateCredential(ctx, req)
|
||||
assert.Error(t, gErr)
|
||||
matcher = tc.matcher
|
||||
if matcher == nil {
|
||||
matcher = func(t *testing.T, e error) {
|
||||
assert.Truef(t, errors.Is(gErr, handlers.ApiErrorWithCode(codes.InvalidArgument)), "got error %v, wanted invalid argument", gErr)
|
||||
}
|
||||
}
|
||||
matcher(t, gErr)
|
||||
assert.Nil(t, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1841,12 +2090,12 @@ func TestListPagination(t *testing.T) {
|
||||
for _, l := range static.TestUsernamePasswordDomainCredentials(t, conn, wrapper, "username", "password", "domain", credStore.GetPublicId(), prj.PublicId, 5) {
|
||||
hm, err := crypto.HmacSha256(ctx, []byte("password"), databaseWrapper, []byte(credStore.GetPublicId()), nil, crypto.WithEd25519())
|
||||
require.NoError(err)
|
||||
allCredentials = append(allCredentials, staticPasswordDomainCredentialToProto(l, prj, hm))
|
||||
allCredentials = append(allCredentials, staticUsernamePasswordDomainCredentialToProto(l, prj, hm))
|
||||
}
|
||||
for _, l := range static.TestUsernamePasswordCredentials(t, conn, wrapper, "username", "password", credStore.GetPublicId(), prj.PublicId, 5) {
|
||||
hm, err := crypto.HmacSha256(ctx, []byte("password"), databaseWrapper, []byte(credStore.GetPublicId()), nil, crypto.WithEd25519())
|
||||
require.NoError(err)
|
||||
allCredentials = append(allCredentials, staticPasswordCredentialToProto(l, prj, hm))
|
||||
allCredentials = append(allCredentials, staticUsernamePasswordCredentialToProto(l, prj, hm))
|
||||
}
|
||||
|
||||
// Reverse slices since response is ordered by created_time descending (newest first)
|
||||
|
||||
@@ -137,6 +137,17 @@ func dynamicToSessionCredential(ctx context.Context, cred credential.Dynamic) (*
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for credential"))
|
||||
}
|
||||
|
||||
case credential.PasswordOnly:
|
||||
credData, err = handlers.ProtoToStruct(
|
||||
ctx,
|
||||
&pb.PasswordCredential{
|
||||
Password: string(c.Password()),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for credential"))
|
||||
}
|
||||
|
||||
case credential.SshPrivateKey:
|
||||
credData, err = handlers.ProtoToStruct(
|
||||
ctx,
|
||||
@@ -264,6 +275,21 @@ func staticToSessionCredential(ctx context.Context, cred credential.Static) (*pb
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for username password domain credential"))
|
||||
}
|
||||
case *credstatic.PasswordCredential:
|
||||
var err error
|
||||
credType = string(globals.PasswordCredentialType)
|
||||
credData, err = handlers.ProtoToStruct(
|
||||
ctx,
|
||||
&pb.PasswordCredential{
|
||||
Password: string(c.GetPassword()),
|
||||
},
|
||||
)
|
||||
secret = map[string]any{
|
||||
"password": string(c.GetPassword()),
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for password credential"))
|
||||
}
|
||||
case *credstatic.SshPrivateKeyCredential:
|
||||
var err error
|
||||
credType = string(globals.SshPrivateKeyCredentialType)
|
||||
@@ -285,7 +311,6 @@ func staticToSessionCredential(ctx context.Context, cred credential.Static) (*pb
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for ssh private key credential"))
|
||||
}
|
||||
|
||||
case *credstatic.JsonCredential:
|
||||
var err error
|
||||
credType = string(globals.JsonCredentialType)
|
||||
|
||||
@@ -2060,6 +2060,7 @@ func validateAddCredentialSourcesRequest(req *pbs.AddTargetCredentialSourcesRequ
|
||||
globals.UsernamePasswordCredentialPrefix,
|
||||
globals.UsernamePasswordCredentialPreviousPrefix,
|
||||
globals.UsernamePasswordDomainCredentialPrefix,
|
||||
globals.PasswordCredentialPrefix,
|
||||
globals.SshPrivateKeyCredentialPrefix,
|
||||
globals.JsonCredentialPrefix) {
|
||||
badFields[globals.BrokeredCredentialSourceIdsField] = fmt.Sprintf("Incorrectly formatted credential source identifier %q.", cl)
|
||||
@@ -2100,6 +2101,7 @@ func validateSetCredentialSourcesRequest(req *pbs.SetTargetCredentialSourcesRequ
|
||||
globals.UsernamePasswordCredentialPrefix,
|
||||
globals.UsernamePasswordCredentialPreviousPrefix,
|
||||
globals.UsernamePasswordDomainCredentialPrefix,
|
||||
globals.PasswordCredentialPrefix,
|
||||
globals.SshPrivateKeyCredentialPrefix,
|
||||
globals.JsonCredentialPrefix) {
|
||||
badFields[globals.BrokeredCredentialSourceIdsField] = fmt.Sprintf("Incorrectly formatted credential source identifier %q.", cl)
|
||||
@@ -2144,6 +2146,7 @@ func validateRemoveCredentialSourcesRequest(req *pbs.RemoveTargetCredentialSourc
|
||||
globals.UsernamePasswordCredentialPrefix,
|
||||
globals.UsernamePasswordCredentialPreviousPrefix,
|
||||
globals.UsernamePasswordDomainCredentialPrefix,
|
||||
globals.PasswordCredentialPrefix,
|
||||
globals.SshPrivateKeyCredentialPrefix,
|
||||
globals.JsonCredentialPrefix) {
|
||||
badFields[globals.BrokeredCredentialSourceIdsField] = fmt.Sprintf("Incorrectly formatted credential source identifier %q.", cl)
|
||||
|
||||
@@ -3085,6 +3085,7 @@ func TestAddTargetCredentialSources(t *testing.T) {
|
||||
storeStatic := credstatic.TestCredentialStore(t, conn, wrapper, proj.GetPublicId())
|
||||
creds := credstatic.TestUsernamePasswordCredentials(t, conn, wrapper, "user", "pass", storeStatic.GetPublicId(), proj.GetPublicId(), 2)
|
||||
updCreds := credstatic.TestUsernamePasswordDomainCredentials(t, conn, wrapper, "user", "pass", "domain", storeStatic.GetPublicId(), proj.GetPublicId(), 2)
|
||||
pCreds := credstatic.TestPasswordCredentials(t, conn, wrapper, "pass", storeStatic.GetPublicId(), proj.GetPublicId(), 2)
|
||||
|
||||
addCases := []struct {
|
||||
name string
|
||||
@@ -3110,6 +3111,12 @@ func TestAddTargetCredentialSources(t *testing.T) {
|
||||
addSources: []string{updCreds[1].GetPublicId()},
|
||||
resultSourceIds: []string{updCreds[1].GetPublicId()},
|
||||
},
|
||||
{
|
||||
name: "Add static p cred on empty target",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "empty for static_p sources"),
|
||||
addSources: []string{pCreds[1].GetPublicId()},
|
||||
resultSourceIds: []string{pCreds[1].GetPublicId()},
|
||||
},
|
||||
{
|
||||
name: "Add library on library populated target",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated for lib-lib sources", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.BrokeredPurpose, string(cls[0].CredentialType()))})),
|
||||
@@ -3128,6 +3135,12 @@ func TestAddTargetCredentialSources(t *testing.T) {
|
||||
addSources: []string{cls[1].GetPublicId()},
|
||||
resultSourceIds: []string{updCreds[0].GetPublicId(), cls[1].GetPublicId()},
|
||||
},
|
||||
{
|
||||
name: "Add library on static p cred populated target",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated for lib-static_p sources", target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", pCreds[0].GetPublicId(), credential.BrokeredPurpose)})),
|
||||
addSources: []string{cls[1].GetPublicId()},
|
||||
resultSourceIds: []string{pCreds[0].GetPublicId(), cls[1].GetPublicId()},
|
||||
},
|
||||
{
|
||||
name: "Add static cred on library populated target",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated for static-lib sources", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.BrokeredPurpose, string(cls[0].CredentialType()))})),
|
||||
@@ -3140,6 +3153,12 @@ func TestAddTargetCredentialSources(t *testing.T) {
|
||||
addSources: []string{updCreds[1].GetPublicId()},
|
||||
resultSourceIds: []string{cls[0].GetPublicId(), updCreds[1].GetPublicId()},
|
||||
},
|
||||
{
|
||||
name: "Add p static cred on library populated target",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated for static_p-lib sources", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.BrokeredPurpose, string(cls[0].CredentialType()))})),
|
||||
addSources: []string{pCreds[1].GetPublicId()},
|
||||
resultSourceIds: []string{cls[0].GetPublicId(), pCreds[1].GetPublicId()},
|
||||
},
|
||||
{
|
||||
name: "Add static cred on static cred populated target",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated for static-static sources", target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.BrokeredPurpose)})),
|
||||
@@ -3152,6 +3171,12 @@ func TestAddTargetCredentialSources(t *testing.T) {
|
||||
addSources: []string{updCreds[1].GetPublicId()},
|
||||
resultSourceIds: []string{creds[0].GetPublicId(), updCreds[1].GetPublicId()},
|
||||
},
|
||||
{
|
||||
name: "Add static p cred on static cred populated target",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated for static_p-static sources", target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.BrokeredPurpose)})),
|
||||
addSources: []string{pCreds[1].GetPublicId()},
|
||||
resultSourceIds: []string{creds[0].GetPublicId(), pCreds[1].GetPublicId()},
|
||||
},
|
||||
{
|
||||
name: "Add duplicated sources on library populated target",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "duplicated for lib sources", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.BrokeredPurpose, string(cls[0].CredentialType()))})),
|
||||
@@ -3170,6 +3195,12 @@ func TestAddTargetCredentialSources(t *testing.T) {
|
||||
addSources: []string{cls[1].GetPublicId(), cls[1].GetPublicId(), updCreds[1].GetPublicId(), updCreds[1].GetPublicId()},
|
||||
resultSourceIds: []string{updCreds[0].GetPublicId(), cls[1].GetPublicId(), updCreds[1].GetPublicId()},
|
||||
},
|
||||
{
|
||||
name: "Add duplicated sources on static p cred populated target",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "duplicated for static p sources", target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", pCreds[0].GetPublicId(), credential.BrokeredPurpose)})),
|
||||
addSources: []string{cls[1].GetPublicId(), cls[1].GetPublicId(), pCreds[1].GetPublicId(), pCreds[1].GetPublicId()},
|
||||
resultSourceIds: []string{pCreds[0].GetPublicId(), cls[1].GetPublicId(), pCreds[1].GetPublicId()},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range addCases {
|
||||
@@ -3296,6 +3327,7 @@ func TestSetTargetCredentialSources(t *testing.T) {
|
||||
storeStatic := credstatic.TestCredentialStore(t, conn, wrapper, proj.GetPublicId())
|
||||
creds := credstatic.TestUsernamePasswordCredentials(t, conn, wrapper, "user", "pass", storeStatic.GetPublicId(), proj.GetPublicId(), 2)
|
||||
updCreds := credstatic.TestUsernamePasswordDomainCredentials(t, conn, wrapper, "user", "pass", "domain", storeStatic.GetPublicId(), proj.GetPublicId(), 2)
|
||||
pCreds := credstatic.TestPasswordCredentials(t, conn, wrapper, "pass", storeStatic.GetPublicId(), proj.GetPublicId(), 2)
|
||||
|
||||
setCases := []struct {
|
||||
name string
|
||||
@@ -3321,6 +3353,12 @@ func TestSetTargetCredentialSources(t *testing.T) {
|
||||
setCredentialSources: []string{updCreds[1].GetPublicId()},
|
||||
resultCredentialSourceIds: []string{updCreds[1].GetPublicId()},
|
||||
},
|
||||
{
|
||||
name: "Set static_p on empty target",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "empty static_p"),
|
||||
setCredentialSources: []string{pCreds[1].GetPublicId()},
|
||||
resultCredentialSourceIds: []string{pCreds[1].GetPublicId()},
|
||||
},
|
||||
{
|
||||
name: "Set library on library populated target",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated library-library", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.BrokeredPurpose, string(cls[0].CredentialType()))})),
|
||||
@@ -3339,6 +3377,12 @@ func TestSetTargetCredentialSources(t *testing.T) {
|
||||
setCredentialSources: []string{updCreds[1].GetPublicId()},
|
||||
resultCredentialSourceIds: []string{updCreds[1].GetPublicId()},
|
||||
},
|
||||
{
|
||||
name: "Set static_p on library populated target",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated static_p-library", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.BrokeredPurpose, string(cls[0].CredentialType()))})),
|
||||
setCredentialSources: []string{pCreds[1].GetPublicId()},
|
||||
resultCredentialSourceIds: []string{pCreds[1].GetPublicId()},
|
||||
},
|
||||
{
|
||||
name: "Set library on static populated target",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated library-static", target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.BrokeredPurpose)})),
|
||||
@@ -3351,6 +3395,12 @@ func TestSetTargetCredentialSources(t *testing.T) {
|
||||
setCredentialSources: []string{cls[1].GetPublicId()},
|
||||
resultCredentialSourceIds: []string{cls[1].GetPublicId()},
|
||||
},
|
||||
{
|
||||
name: "Set library on static_p populated target",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated library-static_p", target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", pCreds[0].GetPublicId(), credential.BrokeredPurpose)})),
|
||||
setCredentialSources: []string{cls[1].GetPublicId()},
|
||||
resultCredentialSourceIds: []string{cls[1].GetPublicId()},
|
||||
},
|
||||
{
|
||||
name: "Set static on static populated target",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated static-static", target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.BrokeredPurpose)})),
|
||||
@@ -3363,12 +3413,24 @@ func TestSetTargetCredentialSources(t *testing.T) {
|
||||
setCredentialSources: []string{updCreds[1].GetPublicId()},
|
||||
resultCredentialSourceIds: []string{updCreds[1].GetPublicId()},
|
||||
},
|
||||
{
|
||||
name: "Set static_p on static populated target",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated static_p-static", target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.BrokeredPurpose)})),
|
||||
setCredentialSources: []string{pCreds[1].GetPublicId()},
|
||||
resultCredentialSourceIds: []string{pCreds[1].GetPublicId()},
|
||||
},
|
||||
{
|
||||
name: "Set static on static_upd populated target",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated static-static_upd", target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", updCreds[0].GetPublicId(), credential.BrokeredPurpose)})),
|
||||
setCredentialSources: []string{creds[1].GetPublicId()},
|
||||
resultCredentialSourceIds: []string{creds[1].GetPublicId()},
|
||||
},
|
||||
{
|
||||
name: "Set static on static_p populated target",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated static-static_p", target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", pCreds[0].GetPublicId(), credential.BrokeredPurpose)})),
|
||||
setCredentialSources: []string{creds[1].GetPublicId()},
|
||||
resultCredentialSourceIds: []string{creds[1].GetPublicId()},
|
||||
},
|
||||
{
|
||||
name: "Set duplicate library on populated target",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "duplicate library", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.BrokeredPurpose, string(cls[0].CredentialType()))})),
|
||||
@@ -3387,12 +3449,19 @@ func TestSetTargetCredentialSources(t *testing.T) {
|
||||
setCredentialSources: []string{updCreds[1].GetPublicId(), updCreds[1].GetPublicId()},
|
||||
resultCredentialSourceIds: []string{updCreds[1].GetPublicId()},
|
||||
},
|
||||
{
|
||||
name: "Set duplicate static_p on populated target",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "duplicate static_p", target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", pCreds[0].GetPublicId(), credential.BrokeredPurpose)})),
|
||||
setCredentialSources: []string{pCreds[1].GetPublicId(), pCreds[1].GetPublicId()},
|
||||
resultCredentialSourceIds: []string{pCreds[1].GetPublicId()},
|
||||
},
|
||||
{
|
||||
name: "Set empty on populated target",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "another populated",
|
||||
target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.BrokeredPurpose, string(cls[0].CredentialType()))}),
|
||||
target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.BrokeredPurpose)}),
|
||||
target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", updCreds[0].GetPublicId(), credential.BrokeredPurpose)}),
|
||||
target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", pCreds[0].GetPublicId(), credential.BrokeredPurpose)}),
|
||||
),
|
||||
setCredentialSources: []string{},
|
||||
resultCredentialSourceIds: nil,
|
||||
@@ -3509,6 +3578,7 @@ func TestRemoveTargetCredentialSources(t *testing.T) {
|
||||
csStatic := credstatic.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0]
|
||||
creds := credstatic.TestUsernamePasswordCredentials(t, conn, wrapper, "u", "p", csStatic.GetPublicId(), proj.GetPublicId(), 2)
|
||||
updCreds := credstatic.TestUsernamePasswordDomainCredentials(t, conn, wrapper, "user", "pass", "domain", csStatic.GetPublicId(), proj.GetPublicId(), 2)
|
||||
pCreds := credstatic.TestPasswordCredentials(t, conn, wrapper, "pass", csStatic.GetPublicId(), proj.GetPublicId(), 2)
|
||||
|
||||
removeCases := []struct {
|
||||
name string
|
||||
@@ -3535,6 +3605,12 @@ func TestRemoveTargetCredentialSources(t *testing.T) {
|
||||
removeCredentialSources: []string{updCreds[1].GetPublicId()},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Remove static_p from empty",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "empty static_p"),
|
||||
removeCredentialSources: []string{pCreds[1].GetPublicId()},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Remove 1 of 2 libraries",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "remove partial lib",
|
||||
@@ -3565,6 +3641,16 @@ func TestRemoveTargetCredentialSources(t *testing.T) {
|
||||
removeCredentialSources: []string{updCreds[1].GetPublicId()},
|
||||
resultCredentialSourceIds: []string{updCreds[0].GetPublicId()},
|
||||
},
|
||||
{
|
||||
name: "Remove 1 of 2 static_p credentials",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "remove partial static_p",
|
||||
target.WithStaticCredentials([]*target.StaticCredential{
|
||||
target.TestNewStaticCredential("", pCreds[0].GetPublicId(), credential.BrokeredPurpose),
|
||||
target.TestNewStaticCredential("", pCreds[1].GetPublicId(), credential.BrokeredPurpose),
|
||||
})),
|
||||
removeCredentialSources: []string{pCreds[1].GetPublicId()},
|
||||
resultCredentialSourceIds: []string{pCreds[0].GetPublicId()},
|
||||
},
|
||||
{
|
||||
name: "Remove 1 duplicate set of 2 libraries",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "remove duplicate lib",
|
||||
@@ -3601,6 +3687,19 @@ func TestRemoveTargetCredentialSources(t *testing.T) {
|
||||
},
|
||||
resultCredentialSourceIds: []string{updCreds[0].GetPublicId()},
|
||||
},
|
||||
{
|
||||
name: "Remove 1 duplicate set of 2 static_p credentials",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "remove duplicate static_p",
|
||||
target.WithStaticCredentials([]*target.StaticCredential{
|
||||
target.TestNewStaticCredential("", pCreds[0].GetPublicId(), credential.BrokeredPurpose),
|
||||
target.TestNewStaticCredential("", pCreds[1].GetPublicId(), credential.BrokeredPurpose),
|
||||
})),
|
||||
removeCredentialSources: []string{
|
||||
pCreds[1].GetPublicId(), pCreds[1].GetPublicId(),
|
||||
},
|
||||
resultCredentialSourceIds: []string{pCreds[0].GetPublicId()},
|
||||
},
|
||||
|
||||
{
|
||||
name: "Remove mixed sources from target",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "remove mixed",
|
||||
@@ -3613,12 +3712,14 @@ func TestRemoveTargetCredentialSources(t *testing.T) {
|
||||
target.TestNewStaticCredential("", creds[1].GetPublicId(), credential.BrokeredPurpose),
|
||||
target.TestNewStaticCredential("", updCreds[0].GetPublicId(), credential.BrokeredPurpose),
|
||||
target.TestNewStaticCredential("", updCreds[1].GetPublicId(), credential.BrokeredPurpose),
|
||||
target.TestNewStaticCredential("", pCreds[0].GetPublicId(), credential.BrokeredPurpose),
|
||||
target.TestNewStaticCredential("", pCreds[1].GetPublicId(), credential.BrokeredPurpose),
|
||||
})),
|
||||
removeCredentialSources: []string{
|
||||
cls[1].GetPublicId(), creds[0].GetPublicId(), updCreds[0].GetPublicId(),
|
||||
cls[1].GetPublicId(), creds[0].GetPublicId(), updCreds[0].GetPublicId(), pCreds[0].GetPublicId(),
|
||||
},
|
||||
resultCredentialSourceIds: []string{
|
||||
cls[0].GetPublicId(), creds[1].GetPublicId(), updCreds[1].GetPublicId(),
|
||||
cls[0].GetPublicId(), creds[1].GetPublicId(), updCreds[1].GetPublicId(), pCreds[1].GetPublicId(),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -3633,11 +3734,14 @@ func TestRemoveTargetCredentialSources(t *testing.T) {
|
||||
target.TestNewStaticCredential("", creds[1].GetPublicId(), credential.BrokeredPurpose),
|
||||
target.TestNewStaticCredential("", updCreds[0].GetPublicId(), credential.BrokeredPurpose),
|
||||
target.TestNewStaticCredential("", updCreds[1].GetPublicId(), credential.BrokeredPurpose),
|
||||
target.TestNewStaticCredential("", pCreds[0].GetPublicId(), credential.BrokeredPurpose),
|
||||
target.TestNewStaticCredential("", pCreds[1].GetPublicId(), credential.BrokeredPurpose),
|
||||
})),
|
||||
removeCredentialSources: []string{
|
||||
cls[0].GetPublicId(), cls[1].GetPublicId(),
|
||||
creds[0].GetPublicId(), creds[1].GetPublicId(),
|
||||
updCreds[0].GetPublicId(), updCreds[1].GetPublicId(),
|
||||
pCreds[0].GetPublicId(), pCreds[1].GetPublicId(),
|
||||
},
|
||||
resultCredentialSourceIds: []string{},
|
||||
},
|
||||
@@ -4170,7 +4274,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
HttpMethod: wrapperspb.String("GET"),
|
||||
},
|
||||
},
|
||||
CredentialType: "username_password",
|
||||
CredentialType: string(globals.UsernamePasswordCredentialType),
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -4203,7 +4307,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
HttpMethod: wrapperspb.String("GET"),
|
||||
},
|
||||
},
|
||||
CredentialType: "username_password",
|
||||
CredentialType: string(globals.UsernamePasswordCredentialType),
|
||||
CredentialMappingOverrides: &structpb.Struct{Fields: map[string]*structpb.Value{
|
||||
"username_attribute": structpb.NewStringValue("non-default-user"),
|
||||
"password_attribute": structpb.NewStringValue("non-default-pass"),
|
||||
@@ -4211,6 +4315,44 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
|
||||
defaultPassword := v.CreateKVSecret(t, "default-password", []byte(`{"data": {"password": "my-default-password"}}`))
|
||||
require.NotNil(t, defaultPassword)
|
||||
nonDefaultPassword := v.CreateKVSecret(t, "non-default-password", []byte(`{"data": {"non-default-password": "my-non-default-password"}}`))
|
||||
require.NotNil(t, nonDefaultPassword)
|
||||
|
||||
clsRespPassword, err := credLibService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Name: wrapperspb.String("Password Library"),
|
||||
Description: wrapperspb.String("Password Library Description"),
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String(path.Join("secret", "data", "default-password")),
|
||||
HttpMethod: wrapperspb.String("GET"),
|
||||
},
|
||||
},
|
||||
CredentialType: string(globals.PasswordCredentialType),
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
|
||||
clsRespPasswordWithMapping, err := credLibService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Name: wrapperspb.String("Password Mapping Library"),
|
||||
Description: wrapperspb.String("Password Mapping Library Description"),
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String(path.Join("secret", "data", "non-default-password")),
|
||||
HttpMethod: wrapperspb.String("GET"),
|
||||
},
|
||||
},
|
||||
CredentialType: string(globals.PasswordCredentialType),
|
||||
CredentialMappingOverrides: &structpb.Struct{Fields: map[string]*structpb.Value{
|
||||
"password_attribute": structpb.NewStringValue("non-default-password"),
|
||||
}},
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
|
||||
staticStore := credstatic.TestCredentialStore(t, conn, wrapper, proj.GetPublicId())
|
||||
credService, err := credentials.NewService(ctx, iamRepoFn, staticCredRepoFn, 1000)
|
||||
require.NoError(t, err)
|
||||
@@ -4245,6 +4387,20 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, updCredResp)
|
||||
|
||||
pCredResp, err := credService.CreateCredential(ctx, &pbs.CreateCredentialRequest{Item: &credpb.Credential{
|
||||
CredentialStoreId: staticStore.GetPublicId(),
|
||||
Type: credential.PasswordSubtype.String(),
|
||||
Name: wrapperspb.String("P Cred Name"),
|
||||
Description: wrapperspb.String("P Cred Description"),
|
||||
Attrs: &credpb.Credential_PasswordAttributes{
|
||||
PasswordAttributes: &credpb.PasswordAttributes{
|
||||
Password: wrapperspb.String("static-password"),
|
||||
},
|
||||
},
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, pCredResp)
|
||||
|
||||
sshPkCredResp, err := credService.CreateCredential(ctx, &pbs.CreateCredentialRequest{Item: &credpb.Credential{
|
||||
CredentialStoreId: staticStore.GetPublicId(),
|
||||
Type: credential.SshPrivateKeySubtype.String(),
|
||||
@@ -4291,7 +4447,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
HttpMethod: wrapperspb.String("GET"),
|
||||
},
|
||||
},
|
||||
CredentialType: "ssh_private_key",
|
||||
CredentialType: string(globals.SshPrivateKeyCredentialType),
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -4310,7 +4466,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
HttpMethod: wrapperspb.String("GET"),
|
||||
},
|
||||
},
|
||||
CredentialType: "ssh_private_key",
|
||||
CredentialType: string(globals.SshPrivateKeyCredentialType),
|
||||
CredentialMappingOverrides: &structpb.Struct{Fields: map[string]*structpb.Value{
|
||||
"username_attribute": structpb.NewStringValue("non-default-user"),
|
||||
"private_key_attribute": structpb.NewStringValue("non-default-pk"),
|
||||
@@ -4333,7 +4489,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
HttpMethod: wrapperspb.String("GET"),
|
||||
},
|
||||
},
|
||||
CredentialType: "ssh_private_key",
|
||||
CredentialType: string(globals.SshPrivateKeyCredentialType),
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, clsRespSshPrivateKeyWithPass)
|
||||
@@ -4353,7 +4509,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
HttpMethod: wrapperspb.String("GET"),
|
||||
},
|
||||
},
|
||||
CredentialType: "ssh_private_key",
|
||||
CredentialType: string(globals.SshPrivateKeyCredentialType),
|
||||
CredentialMappingOverrides: &structpb.Struct{Fields: map[string]*structpb.Value{
|
||||
"username_attribute": structpb.NewStringValue("/data/non-default-user"),
|
||||
"private_key_attribute": structpb.NewStringValue("/data/non-default-pk"),
|
||||
@@ -4443,6 +4599,58 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
},
|
||||
wantedConnectionLimit: 100,
|
||||
},
|
||||
{
|
||||
name: "vault-password",
|
||||
hostSourceId: shs.GetPublicId(),
|
||||
credSourceId: clsRespPassword.GetItem().GetId(),
|
||||
wantedHostId: h.GetPublicId(),
|
||||
wantedEndpoint: h.GetAddress(),
|
||||
wantedCred: &pb.SessionCredential{
|
||||
CredentialSource: &pb.CredentialSource{
|
||||
Id: clsRespPassword.GetItem().GetId(),
|
||||
Name: clsRespPassword.GetItem().GetName().GetValue(),
|
||||
Description: clsRespPassword.GetItem().GetDescription().GetValue(),
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
CredentialType: string(globals.PasswordCredentialType),
|
||||
},
|
||||
Credential: func() *structpb.Struct {
|
||||
data := map[string]any{
|
||||
"password": "my-default-password",
|
||||
}
|
||||
st, err := structpb.NewStruct(data)
|
||||
require.NoError(t, err)
|
||||
return st
|
||||
}(),
|
||||
},
|
||||
wantedConnectionLimit: 10,
|
||||
},
|
||||
{
|
||||
name: "vault-password-with-mapping",
|
||||
hostSourceId: shs.GetPublicId(),
|
||||
credSourceId: clsRespPasswordWithMapping.GetItem().GetId(),
|
||||
wantedHostId: h.GetPublicId(),
|
||||
wantedEndpoint: h.GetAddress(),
|
||||
wantedCred: &pb.SessionCredential{
|
||||
CredentialSource: &pb.CredentialSource{
|
||||
Id: clsRespPasswordWithMapping.GetItem().GetId(),
|
||||
Name: clsRespPasswordWithMapping.GetItem().GetName().GetValue(),
|
||||
Description: clsRespPasswordWithMapping.GetItem().GetDescription().GetValue(),
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
CredentialType: string(globals.PasswordCredentialType),
|
||||
},
|
||||
Credential: func() *structpb.Struct {
|
||||
data := map[string]any{
|
||||
"password": "my-non-default-password",
|
||||
}
|
||||
st, err := structpb.NewStruct(data)
|
||||
require.NoError(t, err)
|
||||
return st
|
||||
}(),
|
||||
},
|
||||
wantedConnectionLimit: 100,
|
||||
},
|
||||
{
|
||||
name: "static-UsernamePassword",
|
||||
hostSourceId: shs.GetPublicId(),
|
||||
@@ -4498,6 +4706,32 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
},
|
||||
wantedConnectionLimit: 1000,
|
||||
},
|
||||
{
|
||||
name: "static-Password",
|
||||
hostSourceId: shs.GetPublicId(),
|
||||
credSourceId: pCredResp.GetItem().GetId(),
|
||||
wantedHostId: h.GetPublicId(),
|
||||
wantedEndpoint: h.GetAddress(),
|
||||
wantedCred: &pb.SessionCredential{
|
||||
CredentialSource: &pb.CredentialSource{
|
||||
Id: pCredResp.GetItem().GetId(),
|
||||
Name: pCredResp.GetItem().GetName().GetValue(),
|
||||
Description: pCredResp.GetItem().GetDescription().GetValue(),
|
||||
CredentialStoreId: staticStore.GetPublicId(),
|
||||
Type: credstatic.Subtype.String(),
|
||||
CredentialType: string(globals.PasswordCredentialType),
|
||||
},
|
||||
Credential: func() *structpb.Struct {
|
||||
data := map[string]any{
|
||||
"password": "static-password",
|
||||
}
|
||||
st, err := structpb.NewStruct(data)
|
||||
require.NoError(t, err)
|
||||
return st
|
||||
}(),
|
||||
},
|
||||
wantedConnectionLimit: 1000,
|
||||
},
|
||||
{
|
||||
name: "static-ssh-private-key",
|
||||
hostSourceId: shs.GetPublicId(),
|
||||
|
||||
@@ -79,6 +79,7 @@ begin;
|
||||
for each row execute procedure default_vault_ldap_credential_type();
|
||||
|
||||
-- Replaces view from 99/01_credential_vault_library_refactor.up.sql
|
||||
-- Replaced in 101/02_credential_vault_password_library.up.sql
|
||||
drop view credential_vault_library_issue_credentials;
|
||||
create view credential_vault_library_issue_credentials as
|
||||
with
|
||||
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
-- Copyright (c) HashiCorp, Inc.
|
||||
-- SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
begin;
|
||||
|
||||
create table credential_static_password_credential (
|
||||
public_id wt_public_id primary key,
|
||||
store_id wt_public_id not null
|
||||
constraint credential_static_store_fkey
|
||||
references credential_static_store(public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
name wt_name null,
|
||||
description wt_description,
|
||||
create_time wt_timestamp,
|
||||
update_time wt_timestamp,
|
||||
version wt_version,
|
||||
password_encrypted bytea not null,
|
||||
constraint password_encrypted_must_not_be_empty
|
||||
check(
|
||||
length(password_encrypted) > 0
|
||||
),
|
||||
password_hmac bytea not null,
|
||||
constraint password_hmac_must_not_be_empty
|
||||
check(
|
||||
length(password_hmac) > 0
|
||||
),
|
||||
key_id kms_private_id not null
|
||||
constraint kms_data_key_version_fkey
|
||||
references kms_data_key_version (private_id)
|
||||
on delete restrict
|
||||
on update cascade,
|
||||
project_id wt_public_id not null,
|
||||
constraint credential_static_fkey
|
||||
foreign key (project_id, store_id, public_id)
|
||||
references credential_static (project_id, store_id, public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
constraint credential_static_password_credential_store_id_name_uq
|
||||
unique (store_id, name),
|
||||
constraint credential_static_password_store_id_public_id_uq
|
||||
unique (store_id, public_id)
|
||||
);
|
||||
comment on table credential_static_password_credential is
|
||||
'credential_static_password_credential is a table where each row is a resource that represents a static password credential. '
|
||||
'It is a credential_static subtype and an aggregate root.';
|
||||
|
||||
create trigger default_create_time_column before insert on credential_static_password_credential
|
||||
for each row execute procedure default_create_time();
|
||||
|
||||
create trigger insert_credential_static_subtype before insert on credential_static_password_credential
|
||||
for each row execute procedure insert_credential_static_subtype();
|
||||
|
||||
create trigger immutable_columns before update on credential_static_password_credential
|
||||
for each row execute procedure immutable_columns('public_id', 'store_id', 'project_id', 'create_time');
|
||||
|
||||
create trigger update_credential_static_table_update_time before update on credential_static_password_credential
|
||||
for each row execute procedure update_credential_static_table_update_time();
|
||||
|
||||
create trigger update_time_column before update on credential_static_password_credential
|
||||
for each row execute procedure update_time_column();
|
||||
|
||||
create trigger update_version_column after update on credential_static_password_credential
|
||||
for each row execute procedure update_version_column();
|
||||
|
||||
create trigger delete_credential_static_subtype after delete on credential_static_password_credential
|
||||
for each row execute procedure delete_credential_static_subtype();
|
||||
|
||||
create table credential_static_password_credential_hst (
|
||||
public_id wt_public_id not null,
|
||||
name wt_name,
|
||||
description wt_description,
|
||||
project_id wt_public_id not null,
|
||||
store_id wt_public_id not null,
|
||||
password_hmac bytea not null,
|
||||
history_id wt_url_safe_id default wt_url_safe_id() primary key
|
||||
constraint credential_static_history_base_fkey
|
||||
references credential_static_history_base (history_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
valid_range tstzrange not null default tstzrange (current_timestamp, null),
|
||||
constraint credential_static_password_credential_hst_valid_range_excl
|
||||
exclude using gist (public_id with =, valid_range with &&)
|
||||
);
|
||||
comment on table credential_static_password_credential_hst is
|
||||
'credential_static_password_credential_hst is a history table where each row contains the values from a row '
|
||||
'in the credential_static_password_credential table during the time range in the valid_range column.';
|
||||
|
||||
create trigger insert_credential_static_history_subtype before insert on credential_static_password_credential_hst
|
||||
for each row execute function insert_credential_static_history_subtype();
|
||||
|
||||
create trigger hst_on_insert after insert on credential_static_password_credential
|
||||
for each row execute function hst_on_insert();
|
||||
|
||||
create trigger hst_on_update after update on credential_static_password_credential
|
||||
for each row execute function hst_on_update();
|
||||
|
||||
create trigger hst_on_delete after delete on credential_static_password_credential
|
||||
for each row execute function hst_on_delete();
|
||||
|
||||
create trigger delete_credential_static_history_subtype after delete on credential_static_password_credential_hst
|
||||
for each row execute function delete_credential_static_history_subtype();
|
||||
|
||||
create table credential_static_password_credential_deleted (
|
||||
public_id wt_public_id primary key,
|
||||
delete_time wt_timestamp not null
|
||||
);
|
||||
comment on table credential_static_password_credential_deleted is
|
||||
'credential_static_password_credential_deleted holds the ID and delete_time '
|
||||
'of every deleted static password credential. '
|
||||
'It is automatically trimmed of records older than 30 days by a job.';
|
||||
|
||||
create trigger insert_deleted_id after delete on credential_static_password_credential
|
||||
for each row execute procedure insert_deleted_id('credential_static_password_credential_deleted');
|
||||
|
||||
create index credential_static_password_credential_deleted_delete_time_idx on credential_static_password_credential_deleted (delete_time);
|
||||
|
||||
create view credential_static_password_credential_hst_aggregate as
|
||||
select
|
||||
rsc.recording_id,
|
||||
spc.public_id,
|
||||
spc.name,
|
||||
spc.description,
|
||||
spc.password_hmac,
|
||||
css.public_id as store_public_id,
|
||||
css.project_id as store_project_id,
|
||||
css.name as store_name,
|
||||
css.description as store_description,
|
||||
string_agg(distinct rsc.credential_purpose, '|') as purposes
|
||||
from
|
||||
credential_static_password_credential_hst as spc
|
||||
left join recording_static_credential as rsc on spc.history_id = rsc.credential_static_hst_id
|
||||
join credential_static_store_hst as css on rsc.credential_static_store_hst_id = css.history_id
|
||||
group by spc.history_id, rsc.recording_id, css.history_id;
|
||||
comment on view credential_static_password_credential_hst_aggregate is
|
||||
'credential_static_password_credential_hst_aggregate contains the password credential history data along with its store and purpose data.';
|
||||
|
||||
-- This constraint replaces the previous constraint created in 98/01_credential_static_username_password_domain_credential.up.sql
|
||||
alter table credential_type_enm
|
||||
drop constraint only_predefined_credential_types_allowed;
|
||||
|
||||
alter table credential_type_enm
|
||||
add constraint only_predefined_credential_types_allowed
|
||||
check (
|
||||
name in (
|
||||
'unspecified',
|
||||
'username_password',
|
||||
'ssh_private_key',
|
||||
'ssh_certificate',
|
||||
'username_password_domain',
|
||||
'password'
|
||||
)
|
||||
);
|
||||
|
||||
insert into credential_type_enm (name)
|
||||
values ('password');
|
||||
|
||||
-- This function replaces the previous function created in 98/01_credential_static_username_password_domain_credential.up.sql
|
||||
create or replace function insert_recording_static_credentials() returns trigger
|
||||
as $$
|
||||
begin
|
||||
with
|
||||
session_recording(session_id, recording_id) as (
|
||||
select session_id, public_id
|
||||
from recording_session
|
||||
where session_id = new.session_id
|
||||
),
|
||||
session_static_creds(cred_id, purpose, recording_id) as (
|
||||
select credential_static_id, credential_purpose, recording_id
|
||||
from session_credential_static
|
||||
join session_recording using (session_id)
|
||||
),
|
||||
static_cred_history(public_id, store_id, cred_hst_id, valid_range) as (
|
||||
select public_id, store_id, history_id, valid_range
|
||||
from credential_static_json_credential_hst
|
||||
union
|
||||
select public_id, store_id, history_id, valid_range
|
||||
from credential_static_ssh_private_key_credential_hst
|
||||
union
|
||||
select public_id, store_id, history_id, valid_range
|
||||
from credential_static_username_password_credential_hst
|
||||
union
|
||||
select public_id, store_id, history_id, valid_range
|
||||
from credential_static_username_password_domain_credential_hst
|
||||
union
|
||||
select public_id, store_id, history_id, valid_range
|
||||
from credential_static_password_credential_hst
|
||||
),
|
||||
final(recording_id, cred_id, store_id, cred_hst_id, store_hst_id, cred_purpose) as (
|
||||
select ssc.recording_id, sc.public_id, sc.store_id, sc.cred_hst_id, store_hst.history_id, ssc.purpose
|
||||
from static_cred_history as sc
|
||||
join credential_static_store_hst as store_hst on sc.store_id = store_hst.public_id
|
||||
and store_hst.valid_range @> current_timestamp
|
||||
join session_static_creds as ssc on sc.public_id = ssc.cred_id
|
||||
where sc.public_id in (select cred_id from session_static_creds)
|
||||
and sc.valid_range @> current_timestamp
|
||||
)
|
||||
insert into recording_static_credential
|
||||
(recording_id, credential_static_store_hst_id, credential_static_hst_id, credential_purpose)
|
||||
select recording_id, store_hst_id, cred_hst_id, cred_purpose
|
||||
from final;
|
||||
|
||||
return new;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
comment on function insert_recording_static_credentials is
|
||||
'insert_recording_static_credentials is an after insert trigger for the recording_session table.';
|
||||
|
||||
insert into oplog_ticket (name, version)
|
||||
values ('credential_static_password_credential', 1);
|
||||
commit;
|
||||
+267
@@ -0,0 +1,267 @@
|
||||
-- Copyright (c) HashiCorp, Inc.
|
||||
-- SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
begin;
|
||||
|
||||
create table credential_vault_generic_library_password_mapping_override (
|
||||
library_id wt_public_id primary key
|
||||
constraint credential_vault_generic_library_fkey
|
||||
references credential_vault_generic_library (public_id)
|
||||
on delete cascade
|
||||
on update cascade
|
||||
constraint credential_vault_generic_library_mapping_override_fkey
|
||||
references credential_vault_generic_library_mapping_override (library_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
password_attribute wt_sentinel
|
||||
default wt_to_sentinel('no override')
|
||||
not null
|
||||
);
|
||||
comment on table credential_vault_generic_library_password_mapping_override is
|
||||
'credential_vault_generic_library_password_mapping_override is a table '
|
||||
'where each row represents a mapping that overrides the default mapping '
|
||||
'from a generic vault secret to a password credential type '
|
||||
'for a vault credential library.';
|
||||
|
||||
create trigger insert_credential_vault_generic_library_mapping_override_subtyp before insert on credential_vault_generic_library_password_mapping_override
|
||||
for each row execute procedure insert_credential_vault_generic_library_mapping_override_subtyp();
|
||||
|
||||
create trigger delete_credential_vault_generic_library_mapping_override_subtyp after delete on credential_vault_generic_library_password_mapping_override
|
||||
for each row execute procedure delete_credential_vault_generic_library_mapping_override_subtyp();
|
||||
|
||||
-- Replaces view from 100/01_credential_vault_ldap_library.up.sql
|
||||
drop view credential_vault_library_issue_credentials;
|
||||
create view credential_vault_library_issue_credentials as
|
||||
with
|
||||
username_password_override (library_id, username_attribute, password_attribute) as (
|
||||
select library_id,
|
||||
nullif(username_attribute, wt_to_sentinel('no override')),
|
||||
nullif(password_attribute, wt_to_sentinel('no override'))
|
||||
from credential_vault_generic_library_username_password_mapping_ovrd
|
||||
),
|
||||
ssh_private_key_override (library_id, username_attribute, private_key_attribute, private_key_passphrase_attribute) as (
|
||||
select library_id,
|
||||
nullif(username_attribute, wt_to_sentinel('no override')),
|
||||
nullif(private_key_attribute, wt_to_sentinel('no override')),
|
||||
nullif(private_key_passphrase_attribute, wt_to_sentinel('no override'))
|
||||
from credential_vault_generic_library_ssh_private_key_mapping_ovrd
|
||||
),
|
||||
username_password_domain_override (library_id, username_attribute, password_attribute, domain_attribute) as (
|
||||
select library_id,
|
||||
nullif(username_attribute, wt_to_sentinel('no override')),
|
||||
nullif(password_attribute, wt_to_sentinel('no override')),
|
||||
nullif(domain_attribute, wt_to_sentinel('no override'))
|
||||
from credential_vault_generic_library_usern_pass_domain_mapping_ovrd
|
||||
),
|
||||
password_override (library_id, password_attribute) as (
|
||||
select library_id,
|
||||
nullif(password_attribute, wt_to_sentinel('no override'))
|
||||
from credential_vault_generic_library_password_mapping_override
|
||||
)
|
||||
select library.public_id as public_id,
|
||||
library.store_id as store_id,
|
||||
library.name as name,
|
||||
library.description as description,
|
||||
library.create_time as create_time,
|
||||
library.update_time as update_time,
|
||||
library.version as version,
|
||||
library.vault_path as vault_path,
|
||||
library.http_method as http_method,
|
||||
library.http_request_body as http_request_body,
|
||||
library.credential_type as credential_type,
|
||||
null as key_type,
|
||||
null as key_bits,
|
||||
null as username,
|
||||
null as ttl,
|
||||
null as key_id,
|
||||
null as critical_options,
|
||||
null as extensions,
|
||||
store.project_id as project_id,
|
||||
store.vault_address as vault_address,
|
||||
store.namespace as namespace,
|
||||
store.ca_cert as ca_cert,
|
||||
store.tls_server_name as tls_server_name,
|
||||
store.tls_skip_verify as tls_skip_verify,
|
||||
store.worker_filter as worker_filter,
|
||||
store.ct_token as ct_token, -- encrypted
|
||||
store.token_hmac as token_hmac,
|
||||
store.token_status as token_status,
|
||||
store.token_key_id as token_key_id,
|
||||
store.client_cert as client_cert,
|
||||
store.ct_client_key as ct_client_key, -- encrypted
|
||||
store.client_key_id as client_key_id,
|
||||
coalesce(upasso.username_attribute, sshpk.username_attribute, pd.username_attribute)
|
||||
as username_attribute,
|
||||
coalesce(upasso.password_attribute, pd.password_attribute, po.password_attribute)
|
||||
as password_attribute,
|
||||
pd.domain_attribute as domain_attribute,
|
||||
sshpk.private_key_attribute as private_key_attribute,
|
||||
sshpk.private_key_passphrase_attribute as private_key_passphrase_attribute,
|
||||
'generic' as cred_lib_type, -- used to switch on
|
||||
null as additional_valid_principals
|
||||
from credential_vault_generic_library library
|
||||
join credential_vault_store_client store
|
||||
on library.store_id = store.public_id
|
||||
left join username_password_override upasso
|
||||
on library.public_id = upasso.library_id
|
||||
left join ssh_private_key_override sshpk
|
||||
on library.public_id = sshpk.library_id
|
||||
left join username_password_domain_override pd
|
||||
on library.public_id = pd.library_id
|
||||
left join password_override po
|
||||
on library.public_id = po.library_id
|
||||
union
|
||||
select library.public_id as public_id,
|
||||
library.store_id as store_id,
|
||||
library.name as name,
|
||||
library.description as description,
|
||||
library.create_time as create_time,
|
||||
library.update_time as update_time,
|
||||
library.version as version,
|
||||
library.vault_path as vault_path,
|
||||
null as http_method,
|
||||
null as http_request_body,
|
||||
library.credential_type as credential_type,
|
||||
library.key_type as key_type,
|
||||
library.key_bits as key_bits,
|
||||
library.username as username,
|
||||
library.ttl as ttl,
|
||||
library.key_id as key_id,
|
||||
library.critical_options as critical_options,
|
||||
library.extensions as extensions,
|
||||
store.project_id as project_id,
|
||||
store.vault_address as vault_address,
|
||||
store.namespace as namespace,
|
||||
store.ca_cert as ca_cert,
|
||||
store.tls_server_name as tls_server_name,
|
||||
store.tls_skip_verify as tls_skip_verify,
|
||||
store.worker_filter as worker_filter,
|
||||
store.ct_token as ct_token, -- encrypted
|
||||
store.token_hmac as token_hmac,
|
||||
store.token_status as token_status,
|
||||
store.token_key_id as token_key_id,
|
||||
store.client_cert as client_cert,
|
||||
store.ct_client_key as ct_client_key, -- encrypted
|
||||
store.client_key_id as client_key_id,
|
||||
null as username_attribute,
|
||||
null as password_attribute,
|
||||
null as domain_attribute,
|
||||
null as private_key_attribute,
|
||||
null as private_key_passphrase_attribute,
|
||||
'ssh-signed-cert' as cred_lib_type, -- used to switch on
|
||||
additional_valid_principals as additional_valid_principals
|
||||
from credential_vault_ssh_cert_library library
|
||||
join credential_vault_store_client store
|
||||
on library.store_id = store.public_id
|
||||
union
|
||||
select library.public_id as public_id,
|
||||
library.store_id as store_id,
|
||||
library.name as name,
|
||||
library.description as description,
|
||||
library.create_time as create_time,
|
||||
library.update_time as update_time,
|
||||
library.version as version,
|
||||
library.vault_path as vault_path,
|
||||
null as http_method,
|
||||
null as http_request_body,
|
||||
library.credential_type as credential_type,
|
||||
null as key_type,
|
||||
null as key_bits,
|
||||
null as username,
|
||||
null as ttl,
|
||||
null as key_id,
|
||||
null as critical_options,
|
||||
null as extensions,
|
||||
store.project_id as project_id,
|
||||
store.vault_address as vault_address,
|
||||
store.namespace as namespace,
|
||||
store.ca_cert as ca_cert,
|
||||
store.tls_server_name as tls_server_name,
|
||||
store.tls_skip_verify as tls_skip_verify,
|
||||
store.worker_filter as worker_filter,
|
||||
store.ct_token as ct_token, -- encrypted
|
||||
store.token_hmac as token_hmac,
|
||||
store.token_status as token_status,
|
||||
store.token_key_id as token_key_id,
|
||||
store.client_cert as client_cert,
|
||||
store.ct_client_key as ct_client_key, -- encrypted
|
||||
store.client_key_id as client_key_id,
|
||||
pd.username_attribute as username_attribute,
|
||||
pd.password_attribute as password_attribute,
|
||||
pd.domain_attribute as domain_attribute,
|
||||
null as private_key_attribute,
|
||||
null as private_key_passphrase_attribute,
|
||||
'ldap' as cred_lib_type, -- used to switch on
|
||||
null as additional_valid_principals
|
||||
from credential_vault_ldap_library library
|
||||
join credential_vault_store_client store
|
||||
on library.store_id = store.public_id
|
||||
left join username_password_domain_override pd
|
||||
on library.public_id = pd.library_id;
|
||||
|
||||
comment on view credential_vault_library_issue_credentials is
|
||||
'credential_vault_library_issue_credentials is a view where each row contains a credential library and the credential library''s data needed to connect to Vault. '
|
||||
'This view should only be used when issuing credentials from a Vault credential library. Each row may contain encrypted data. '
|
||||
'This view should not be used to retrieve data which will be returned external to boundary.';
|
||||
|
||||
-- Replaces view created in 99/01_credential_vault_library_refactor.up.sql
|
||||
drop view credential_vault_generic_library_list_lookup;
|
||||
create view credential_vault_generic_library_list_lookup as
|
||||
with
|
||||
username_password_override (library_id, username_attribute, password_attribute) as (
|
||||
select library_id,
|
||||
nullif(username_attribute, wt_to_sentinel('no override')),
|
||||
nullif(password_attribute, wt_to_sentinel('no override'))
|
||||
from credential_vault_generic_library_username_password_mapping_ovrd
|
||||
),
|
||||
ssh_private_key_override (library_id, username_attribute, private_key_attribute, private_key_passphrase_attribute) as (
|
||||
select library_id,
|
||||
nullif(username_attribute, wt_to_sentinel('no override')),
|
||||
nullif(private_key_attribute, wt_to_sentinel('no override')),
|
||||
nullif(private_key_passphrase_attribute, wt_to_sentinel('no override'))
|
||||
from credential_vault_generic_library_ssh_private_key_mapping_ovrd
|
||||
),
|
||||
username_password_domain_override (library_id, username_attribute, password_attribute, domain_attribute) as (
|
||||
select library_id,
|
||||
nullif(username_attribute, wt_to_sentinel('no override')),
|
||||
nullif(password_attribute, wt_to_sentinel('no override')),
|
||||
nullif(domain_attribute, wt_to_sentinel('no override'))
|
||||
from credential_vault_generic_library_usern_pass_domain_mapping_ovrd
|
||||
),
|
||||
password_override (library_id, password_attribute) as (
|
||||
select library_id,
|
||||
nullif(password_attribute, wt_to_sentinel('no override'))
|
||||
from credential_vault_generic_library_password_mapping_override
|
||||
)
|
||||
select library.public_id as public_id,
|
||||
library.store_id as store_id,
|
||||
library.name as name,
|
||||
library.description as description,
|
||||
library.create_time as create_time,
|
||||
library.update_time as update_time,
|
||||
library.version as version,
|
||||
library.vault_path as vault_path,
|
||||
library.http_method as http_method,
|
||||
library.http_request_body as http_request_body,
|
||||
library.credential_type as credential_type,
|
||||
coalesce(upasso.username_attribute, sshpk.username_attribute, pd.username_attribute)
|
||||
as username_attribute,
|
||||
coalesce(upasso.password_attribute, pd.password_attribute, po.password_attribute)
|
||||
as password_attribute,
|
||||
sshpk.private_key_attribute as private_key_attribute,
|
||||
sshpk.private_key_passphrase_attribute as private_key_passphrase_attribute,
|
||||
pd.domain_attribute as domain_attribute
|
||||
from credential_vault_generic_library library
|
||||
left join username_password_override upasso
|
||||
on library.public_id = upasso.library_id
|
||||
left join ssh_private_key_override sshpk
|
||||
on library.public_id = sshpk.library_id
|
||||
left join username_password_domain_override pd
|
||||
on library.public_id = pd.library_id
|
||||
left join password_override po
|
||||
on library.public_id = po.library_id;
|
||||
comment on view credential_vault_generic_library_list_lookup is
|
||||
'credential_vault_generic_library_list_lookup is a view where each row contains a credential library and any of library''s credential mapping overrides. '
|
||||
'No encrypted data is returned. This view can be used to retrieve data which will be returned external to boundary.';
|
||||
|
||||
commit;
|
||||
+2
-2
@@ -153,8 +153,8 @@ begin;
|
||||
comment on view credential_static_username_password_domain_credential_hst_agg is
|
||||
'credential_static_username_password_domain_credential_hst_aggregate contains the username password credential history data along with its store and purpose data.';
|
||||
|
||||
|
||||
-- This constraint replaces the previous constraint created in 63/01_credential_vault_ssh_cert_library.up.sql
|
||||
-- This constraint is replaced in 101/01_credential_static_password_credential.up.sql
|
||||
alter table credential_type_enm
|
||||
drop constraint only_predefined_credential_types_allowed;
|
||||
|
||||
@@ -173,8 +173,8 @@ begin;
|
||||
insert into credential_type_enm (name)
|
||||
values ('username_password_domain');
|
||||
|
||||
|
||||
-- This function replaces the previous function created in 71/14_recording_static_credential.up.sql
|
||||
-- This function is replaced in 101/01_credential_static_password_credential.up.sql
|
||||
create or replace function insert_recording_static_credentials() returns trigger
|
||||
as $$
|
||||
begin
|
||||
|
||||
+5
-7
@@ -37,9 +37,8 @@ begin;
|
||||
create trigger delete_credential_vault_library_mapping_override_subtype after delete on credential_vault_library_username_password_domain_mapping_ovrd
|
||||
for each row execute procedure delete_credential_vault_library_mapping_override_subtype();
|
||||
|
||||
|
||||
-- Replaces view from 78/01_ssh_signed_certs_additional_valid_principals.up.sql
|
||||
-- Replaced in 99/01_credential_vault_library_refactor.up.sql
|
||||
-- Replaces view from 78/01_ssh_signed_certs_additional_valid_principals.up.sql
|
||||
-- Replaced in 99/01_credential_vault_library_refactor.up.sql
|
||||
drop view credential_vault_library_issue_credentials;
|
||||
create view credential_vault_library_issue_credentials as
|
||||
with
|
||||
@@ -161,10 +160,9 @@ begin;
|
||||
'This view should only be used when issuing credentials from a Vault credential library. Each row may contain encrypted data. '
|
||||
'This view should not be used to retrieve data which will be returned external to boundary.';
|
||||
|
||||
|
||||
-- Replaces view created in 49/01_vault_credentials.up.sql
|
||||
-- Replaced in 99/01_credential_vault_library_refactor.up.sql where this
|
||||
-- view's name changed to credential_vault_generic_library_list_lookup.
|
||||
-- Replaces view created in 49/01_vault_credentials.up.sql
|
||||
-- Replaced in 99/01_credential_vault_library_refactor.up.sql where this
|
||||
-- view's name changed to credential_vault_generic_library_list_lookup.
|
||||
drop view credential_vault_library_list_lookup;
|
||||
create view credential_vault_library_list_lookup as
|
||||
with
|
||||
|
||||
+1
@@ -503,6 +503,7 @@ begin;
|
||||
'This view should not be used to retrieve data which will be returned external to boundary.';
|
||||
|
||||
-- Replaces and renames view defined in 98/02_username_password_domain_vault.up.sql.
|
||||
-- Replaced in 101/02_credential_vault_password_library.up.sql
|
||||
alter view credential_vault_library_list_lookup
|
||||
rename to credential_vault_generic_library_list_lookup;
|
||||
drop view credential_vault_generic_library_list_lookup;
|
||||
|
||||
@@ -82,6 +82,11 @@ message Credential {
|
||||
(custom_options.v1.generate_sdk_option) = true,
|
||||
(custom_options.v1.subtype) = "username_password_domain"
|
||||
];
|
||||
PasswordAttributes password_attributes = 105 [
|
||||
(google.api.field_visibility).restriction = "INTERNAL",
|
||||
(custom_options.v1.generate_sdk_option) = true,
|
||||
(custom_options.v1.subtype) = "password"
|
||||
];
|
||||
}
|
||||
|
||||
// Output only. The available actions on this resource for this user.
|
||||
@@ -157,6 +162,28 @@ message UsernamePasswordDomainAttributes {
|
||||
]; // @gotags: `class:"public"`
|
||||
}
|
||||
|
||||
// The attributes of a Password Credential.
|
||||
message PasswordAttributes {
|
||||
// Input only. The password associated with the credential.
|
||||
google.protobuf.StringValue password = 10 [
|
||||
json_name = "password",
|
||||
(custom_options.v1.generate_sdk_option) = true,
|
||||
(custom_options.v1.mask_mapping) = {
|
||||
this: "attributes.password"
|
||||
that: "Password"
|
||||
}
|
||||
]; // @gotags: `class:"secret"`
|
||||
|
||||
// Output only. The hmac value of the password.
|
||||
string password_hmac = 20 [
|
||||
json_name = "password_hmac",
|
||||
(custom_options.v1.mask_mapping) = {
|
||||
this: "attributes.password_hmac"
|
||||
that: "PasswordHmac"
|
||||
}
|
||||
]; // @gotags: `class:"public"`
|
||||
}
|
||||
|
||||
// The attributes of a SshPrivateKey Credential.
|
||||
message SshPrivateKeyAttributes {
|
||||
// The username associated with the credential.
|
||||
|
||||
@@ -308,6 +308,10 @@ message Credential {
|
||||
(google.api.field_visibility).restriction = "INTERNAL",
|
||||
(custom_options.v1.subtype) = "json"
|
||||
];
|
||||
PasswordCredentialAttributes password_attributes = 11 [
|
||||
(google.api.field_visibility).restriction = "INTERNAL",
|
||||
(custom_options.v1.subtype) = "password"
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -338,6 +342,12 @@ message JsonCredentialAttributes {
|
||||
string object_hmac = 1; // @gotags: class:"public"
|
||||
}
|
||||
|
||||
// The attributes of a Password Credential.
|
||||
message PasswordCredentialAttributes {
|
||||
// The hmac value of the password.
|
||||
string password_hmac = 1; // @gotags: class:"public"
|
||||
}
|
||||
|
||||
// CredentialLibrary contains all fields related to an Credential Library resource
|
||||
message CredentialLibrary {
|
||||
// The ID of the Credential Library.
|
||||
|
||||
@@ -475,6 +475,12 @@ message UsernamePasswordDomainCredential {
|
||||
string domain = 3; // @gotags: `class:"public"`
|
||||
}
|
||||
|
||||
// The layout of the struct for "credential" field in SessionCredential for a password credential type.
|
||||
message PasswordCredential {
|
||||
// Password of the credential
|
||||
string password = 1; // @gotags: `class:"secret"`
|
||||
}
|
||||
|
||||
// The layout of the struct for "credential" field in SessionCredential for a ssh_private_key credential type.
|
||||
message SshPrivateKeyCredential {
|
||||
// Username of the credential
|
||||
|
||||
@@ -49,6 +49,69 @@ message CredentialStore {
|
||||
uint32 version = 7;
|
||||
}
|
||||
|
||||
message PasswordCredential {
|
||||
// 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 static 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;
|
||||
|
||||
// password is the plain-text of the password associated with the credential. We are
|
||||
// not storing this plain-text password in the database.
|
||||
// @inject_tag: `gorm:"-" wrapping:"pt,password_data"`
|
||||
bytes password = 8 [(custom_options.v1.mask_mapping) = {
|
||||
this: "Password"
|
||||
that: "attributes.password"
|
||||
}];
|
||||
|
||||
// ct_password is the ciphertext of the password. It
|
||||
// is stored in the database.
|
||||
// @inject_tag: `gorm:"column:password_encrypted;not_null" wrapping:"ct,password_data"`
|
||||
bytes ct_password = 9;
|
||||
|
||||
// password_hmac is a sha256-hmac of the unencrypted password. It is recalculated
|
||||
// everytime the password is updated.
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
bytes password_hmac = 10 [(custom_options.v1.mask_mapping) = {
|
||||
this: "PasswordHmac"
|
||||
that: "attributes.password_hmac"
|
||||
}];
|
||||
|
||||
// The key_id of the kms database key used for encrypting this entry.
|
||||
// It must be set.
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
string key_id = 11;
|
||||
}
|
||||
|
||||
message UsernamePasswordCredential {
|
||||
// public_id is a surrogate key suitable for use in a public API.
|
||||
// @inject_tag: `gorm:"primary_key"`
|
||||
|
||||
@@ -499,6 +499,22 @@ message UsernamePasswordDomainOverride {
|
||||
string domain_attribute = 4;
|
||||
}
|
||||
|
||||
message PasswordOverride {
|
||||
// library_id of the owning vault credential library.
|
||||
// @inject_tag: `gorm:"primary_key"`
|
||||
string library_id = 1;
|
||||
|
||||
// password_attribute is the name of the attribute in the Data field of a
|
||||
// Vault api.Secret that maps to a password.
|
||||
// If set, it overrides any default attribute names the system uses to
|
||||
// find a password attribute.
|
||||
//
|
||||
// See https://github.com/hashicorp/vault/blob/5e505ec039177e8212cbbab74ccb644c46e62e63/api/secret.go#L25
|
||||
//
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string password_attribute = 2;
|
||||
}
|
||||
|
||||
message SshPrivateKeyOverride {
|
||||
// library_id of the owning vault credential library.
|
||||
// @inject_tag: `gorm:"primary_key"`
|
||||
|
||||
@@ -63,6 +63,19 @@ function create_json_credential() {
|
||||
$args
|
||||
}
|
||||
|
||||
function create_password_credential() {
|
||||
local name=$1
|
||||
local sid=$2
|
||||
local pass=$3
|
||||
|
||||
export BP="${pass}"
|
||||
boundary credentials create password \
|
||||
-name $name \
|
||||
-description 'test password credential' \
|
||||
-credential-store-id $sid \
|
||||
-password env://BP
|
||||
}
|
||||
|
||||
function read_credential() {
|
||||
boundary credentials read -id $1 -format json
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ export NEW_UPD_AT_CREDENTIAL='test-at-user-domain-pass'
|
||||
export NEW_UPD_SLASH_CREDENTIAL='test-slash-domain-user-pass'
|
||||
export NEW_UPD_CREDENTIAL_DOMAIN='test-domain-user-plus-domain'
|
||||
export NEW_JSON_CREDENTIAL='test-json'
|
||||
export NEW_PASSWORD_CREDENTIAL='test-pass'
|
||||
|
||||
@test "boundary/login: can login as default user" {
|
||||
run login $DEFAULT_LOGIN
|
||||
@@ -68,6 +69,20 @@ export NEW_JSON_CREDENTIAL='test-json'
|
||||
|
||||
}
|
||||
|
||||
@test "boundary/credentials: can create $NEW_PASSWORD_CREDENTIAL credential in $NEW_STORE store" {
|
||||
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
|
||||
run create_password_credential $NEW_PASSWORD_CREDENTIAL $csid 'password'
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "boundary/credentials: can not create already created $NEW_PASSWORD_CREDENTIAL credential" {
|
||||
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
|
||||
run create_password_credential $NEW_PASSWORD_CREDENTIAL $csid 'password'
|
||||
echo "$output"
|
||||
[ "$status" -eq 1 ]
|
||||
}
|
||||
|
||||
@test "boundary/credentials: can create $NEW_JSON_CREDENTIAL credential in $NEW_STORE store" {
|
||||
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
|
||||
run create_json_credential $NEW_JSON_CREDENTIAL $csid '-string-kv username=admin -string-kv password=pass'
|
||||
@@ -142,6 +157,14 @@ export NEW_JSON_CREDENTIAL='test-json'
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "boundary/credentials: can read $NEW_PASSWORD_CREDENTIAL credential" {
|
||||
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
|
||||
local cid=$(credential_id $NEW_PASSWORD_CREDENTIAL $csid)
|
||||
run read_credential $cid
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "boundary/credentials: can read $NEW_JSON_CREDENTIAL credential" {
|
||||
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
|
||||
local cid=$(credential_id $NEW_JSON_CREDENTIAL $csid)
|
||||
@@ -186,6 +209,15 @@ export NEW_JSON_CREDENTIAL='test-json'
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "boundary/credentials: can delete $NEW_PASSWORD_CREDENTIAL credential" {
|
||||
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
|
||||
local cid=$(credential_id $NEW_PASSWORD_CREDENTIAL $csid)
|
||||
run delete_credential $cid
|
||||
echo "$output"
|
||||
run has_status_code "$output" "204"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "boundary/credentials: can delete $NEW_JSON_CREDENTIAL credential" {
|
||||
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
|
||||
local cid=$(credential_id $NEW_JSON_CREDENTIAL $csid)
|
||||
@@ -255,6 +287,14 @@ export NEW_JSON_CREDENTIAL='test-json'
|
||||
[ "$status" -eq 1 ]
|
||||
}
|
||||
|
||||
@test "boundary/credential-stores: can not read deleted $NEW_PASSWORD_CREDENTIAL credential" {
|
||||
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
|
||||
local cid=$(credential_id $NEW_PASSWORD_CREDENTIAL $csid)
|
||||
run read_credential $cid
|
||||
echo "$output"
|
||||
[ "$status" -eq 1 ]
|
||||
}
|
||||
|
||||
@test "boundary/credential-stores: can delete $NEW_STORE static store" {
|
||||
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
|
||||
run delete_credential_store $csid
|
||||
|
||||
@@ -59,6 +59,7 @@ type Credential struct {
|
||||
// *Credential_SshPrivateKeyAttributes
|
||||
// *Credential_JsonAttributes
|
||||
// *Credential_UsernamePasswordDomainAttributes
|
||||
// *Credential_PasswordAttributes
|
||||
Attrs isCredential_Attrs `protobuf_oneof:"attrs"`
|
||||
// Output only. The available actions on this resource for this user.
|
||||
AuthorizedActions []string `protobuf:"bytes,300,rep,name=authorized_actions,proto3" json:"authorized_actions,omitempty" class:"public"` // @gotags: `class:"public"`
|
||||
@@ -211,6 +212,15 @@ func (x *Credential) GetUsernamePasswordDomainAttributes() *UsernamePasswordDoma
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Credential) GetPasswordAttributes() *PasswordAttributes {
|
||||
if x != nil {
|
||||
if x, ok := x.Attrs.(*Credential_PasswordAttributes); ok {
|
||||
return x.PasswordAttributes
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Credential) GetAuthorizedActions() []string {
|
||||
if x != nil {
|
||||
return x.AuthorizedActions
|
||||
@@ -243,6 +253,10 @@ type Credential_UsernamePasswordDomainAttributes struct {
|
||||
UsernamePasswordDomainAttributes *UsernamePasswordDomainAttributes `protobuf:"bytes,104,opt,name=username_password_domain_attributes,json=usernamePasswordDomainAttributes,proto3,oneof"`
|
||||
}
|
||||
|
||||
type Credential_PasswordAttributes struct {
|
||||
PasswordAttributes *PasswordAttributes `protobuf:"bytes,105,opt,name=password_attributes,json=passwordAttributes,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*Credential_Attributes) isCredential_Attrs() {}
|
||||
|
||||
func (*Credential_UsernamePasswordAttributes) isCredential_Attrs() {}
|
||||
@@ -253,6 +267,8 @@ func (*Credential_JsonAttributes) isCredential_Attrs() {}
|
||||
|
||||
func (*Credential_UsernamePasswordDomainAttributes) isCredential_Attrs() {}
|
||||
|
||||
func (*Credential_PasswordAttributes) isCredential_Attrs() {}
|
||||
|
||||
// The attributes of a UsernamePassword Credential.
|
||||
type UsernamePasswordAttributes struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
@@ -388,6 +404,61 @@ func (x *UsernamePasswordDomainAttributes) GetDomain() *wrapperspb.StringValue {
|
||||
return nil
|
||||
}
|
||||
|
||||
// The attributes of a Password Credential.
|
||||
type PasswordAttributes struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// Input only. The password associated with the credential.
|
||||
Password *wrapperspb.StringValue `protobuf:"bytes,10,opt,name=password,proto3" json:"password,omitempty" class:"secret"` // @gotags: `class:"secret"`
|
||||
// Output only. The hmac value of the password.
|
||||
PasswordHmac string `protobuf:"bytes,20,opt,name=password_hmac,proto3" json:"password_hmac,omitempty" class:"public"` // @gotags: `class:"public"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *PasswordAttributes) Reset() {
|
||||
*x = PasswordAttributes{}
|
||||
mi := &file_controller_api_resources_credentials_v1_credential_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *PasswordAttributes) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*PasswordAttributes) ProtoMessage() {}
|
||||
|
||||
func (x *PasswordAttributes) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_api_resources_credentials_v1_credential_proto_msgTypes[3]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use PasswordAttributes.ProtoReflect.Descriptor instead.
|
||||
func (*PasswordAttributes) Descriptor() ([]byte, []int) {
|
||||
return file_controller_api_resources_credentials_v1_credential_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *PasswordAttributes) GetPassword() *wrapperspb.StringValue {
|
||||
if x != nil {
|
||||
return x.Password
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *PasswordAttributes) GetPasswordHmac() string {
|
||||
if x != nil {
|
||||
return x.PasswordHmac
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// The attributes of a SshPrivateKey Credential.
|
||||
type SshPrivateKeyAttributes struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
@@ -407,7 +478,7 @@ type SshPrivateKeyAttributes struct {
|
||||
|
||||
func (x *SshPrivateKeyAttributes) Reset() {
|
||||
*x = SshPrivateKeyAttributes{}
|
||||
mi := &file_controller_api_resources_credentials_v1_credential_proto_msgTypes[3]
|
||||
mi := &file_controller_api_resources_credentials_v1_credential_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -419,7 +490,7 @@ func (x *SshPrivateKeyAttributes) String() string {
|
||||
func (*SshPrivateKeyAttributes) ProtoMessage() {}
|
||||
|
||||
func (x *SshPrivateKeyAttributes) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_api_resources_credentials_v1_credential_proto_msgTypes[3]
|
||||
mi := &file_controller_api_resources_credentials_v1_credential_proto_msgTypes[4]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -432,7 +503,7 @@ func (x *SshPrivateKeyAttributes) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use SshPrivateKeyAttributes.ProtoReflect.Descriptor instead.
|
||||
func (*SshPrivateKeyAttributes) Descriptor() ([]byte, []int) {
|
||||
return file_controller_api_resources_credentials_v1_credential_proto_rawDescGZIP(), []int{3}
|
||||
return file_controller_api_resources_credentials_v1_credential_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *SshPrivateKeyAttributes) GetUsername() *wrapperspb.StringValue {
|
||||
@@ -483,7 +554,7 @@ type JsonAttributes struct {
|
||||
|
||||
func (x *JsonAttributes) Reset() {
|
||||
*x = JsonAttributes{}
|
||||
mi := &file_controller_api_resources_credentials_v1_credential_proto_msgTypes[4]
|
||||
mi := &file_controller_api_resources_credentials_v1_credential_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -495,7 +566,7 @@ func (x *JsonAttributes) String() string {
|
||||
func (*JsonAttributes) ProtoMessage() {}
|
||||
|
||||
func (x *JsonAttributes) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_api_resources_credentials_v1_credential_proto_msgTypes[4]
|
||||
mi := &file_controller_api_resources_credentials_v1_credential_proto_msgTypes[5]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -508,7 +579,7 @@ func (x *JsonAttributes) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use JsonAttributes.ProtoReflect.Descriptor instead.
|
||||
func (*JsonAttributes) Descriptor() ([]byte, []int) {
|
||||
return file_controller_api_resources_credentials_v1_credential_proto_rawDescGZIP(), []int{4}
|
||||
return file_controller_api_resources_credentials_v1_credential_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *JsonAttributes) GetObject() *structpb.Struct {
|
||||
@@ -529,8 +600,7 @@ var File_controller_api_resources_credentials_v1_credential_proto protoreflect.F
|
||||
|
||||
const file_controller_api_resources_credentials_v1_credential_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"8controller/api/resources/credentials/v1/credential.proto\x12'controller.api.resources.credentials.v1\x1a.controller/api/resources/scopes/v1/scope.proto\x1a*controller/custom_options/v1/options.proto\x1a\x1bgoogle/api/visibility.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1egoogle/protobuf/wrappers.proto\"\xa5\n" +
|
||||
"\n" +
|
||||
"8controller/api/resources/credentials/v1/credential.proto\x12'controller.api.resources.credentials.v1\x1a.controller/api/resources/scopes/v1/scope.proto\x1a*controller/custom_options/v1/options.proto\x1a\x1bgoogle/api/visibility.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1egoogle/protobuf/wrappers.proto\"\xb8\v\n" +
|
||||
"\n" +
|
||||
"Credential\x12\x0e\n" +
|
||||
"\x02id\x18\n" +
|
||||
@@ -555,7 +625,9 @@ const file_controller_api_resources_credentials_v1_credential_proto_rawDesc = ""
|
||||
"\x0fjson_attributes\x18g \x01(\v27.controller.api.resources.credentials.v1.JsonAttributesB\x1c\xa0\xda)\x01\x9a\xe3)\x04json\xfa\xd2\xe4\x93\x02\n" +
|
||||
"\x12\bINTERNALH\x00R\x0ejsonAttributes\x12\xcc\x01\n" +
|
||||
"#username_password_domain_attributes\x18h \x01(\v2I.controller.api.resources.credentials.v1.UsernamePasswordDomainAttributesB0\xa0\xda)\x01\x9a\xe3)\x18username_password_domain\xfa\xd2\xe4\x93\x02\n" +
|
||||
"\x12\bINTERNALH\x00R usernamePasswordDomainAttributes\x12/\n" +
|
||||
"\x12\bINTERNALH\x00R usernamePasswordDomainAttributes\x12\x90\x01\n" +
|
||||
"\x13password_attributes\x18i \x01(\v2;.controller.api.resources.credentials.v1.PasswordAttributesB \xa0\xda)\x01\x9a\xe3)\bpassword\xfa\xd2\xe4\x93\x02\n" +
|
||||
"\x12\bINTERNALH\x00R\x12passwordAttributes\x12/\n" +
|
||||
"\x12authorized_actions\x18\xac\x02 \x03(\tR\x12authorized_actionsB\a\n" +
|
||||
"\x05attrs\"\xb6\x02\n" +
|
||||
"\x1aUsernamePasswordAttributes\x12a\n" +
|
||||
@@ -575,7 +647,13 @@ const file_controller_api_resources_credentials_v1_credential_proto_rawDesc = ""
|
||||
"\rpassword_hmac\x18\x1e \x01(\tB,\xc2\xdd)(\n" +
|
||||
"\x18attributes.password_hmac\x12\fPasswordHmacR\rpassword_hmac\x12Y\n" +
|
||||
"\x06domain\x18( \x01(\v2\x1c.google.protobuf.StringValueB#\xa0\xda)\x01\xc2\xdd)\x1b\n" +
|
||||
"\x11attributes.domain\x12\x06DomainR\x06domain\"\xee\x04\n" +
|
||||
"\x11attributes.domain\x12\x06DomainR\x06domain\"\xcb\x01\n" +
|
||||
"\x12PasswordAttributes\x12a\n" +
|
||||
"\bpassword\x18\n" +
|
||||
" \x01(\v2\x1c.google.protobuf.StringValueB'\xa0\xda)\x01\xc2\xdd)\x1f\n" +
|
||||
"\x13attributes.password\x12\bPasswordR\bpassword\x12R\n" +
|
||||
"\rpassword_hmac\x18\x14 \x01(\tB,\xc2\xdd)(\n" +
|
||||
"\x18attributes.password_hmac\x12\fPasswordHmacR\rpassword_hmac\"\xee\x04\n" +
|
||||
"\x17SshPrivateKeyAttributes\x12a\n" +
|
||||
"\busername\x18\n" +
|
||||
" \x01(\v2\x1c.google.protobuf.StringValueB'\xa0\xda)\x01\xc2\xdd)\x1f\n" +
|
||||
@@ -609,43 +687,46 @@ func file_controller_api_resources_credentials_v1_credential_proto_rawDescGZIP()
|
||||
return file_controller_api_resources_credentials_v1_credential_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_controller_api_resources_credentials_v1_credential_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
|
||||
var file_controller_api_resources_credentials_v1_credential_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
|
||||
var file_controller_api_resources_credentials_v1_credential_proto_goTypes = []any{
|
||||
(*Credential)(nil), // 0: controller.api.resources.credentials.v1.Credential
|
||||
(*UsernamePasswordAttributes)(nil), // 1: controller.api.resources.credentials.v1.UsernamePasswordAttributes
|
||||
(*UsernamePasswordDomainAttributes)(nil), // 2: controller.api.resources.credentials.v1.UsernamePasswordDomainAttributes
|
||||
(*SshPrivateKeyAttributes)(nil), // 3: controller.api.resources.credentials.v1.SshPrivateKeyAttributes
|
||||
(*JsonAttributes)(nil), // 4: controller.api.resources.credentials.v1.JsonAttributes
|
||||
(*scopes.ScopeInfo)(nil), // 5: controller.api.resources.scopes.v1.ScopeInfo
|
||||
(*wrapperspb.StringValue)(nil), // 6: google.protobuf.StringValue
|
||||
(*timestamppb.Timestamp)(nil), // 7: google.protobuf.Timestamp
|
||||
(*structpb.Struct)(nil), // 8: google.protobuf.Struct
|
||||
(*PasswordAttributes)(nil), // 3: controller.api.resources.credentials.v1.PasswordAttributes
|
||||
(*SshPrivateKeyAttributes)(nil), // 4: controller.api.resources.credentials.v1.SshPrivateKeyAttributes
|
||||
(*JsonAttributes)(nil), // 5: controller.api.resources.credentials.v1.JsonAttributes
|
||||
(*scopes.ScopeInfo)(nil), // 6: controller.api.resources.scopes.v1.ScopeInfo
|
||||
(*wrapperspb.StringValue)(nil), // 7: google.protobuf.StringValue
|
||||
(*timestamppb.Timestamp)(nil), // 8: google.protobuf.Timestamp
|
||||
(*structpb.Struct)(nil), // 9: google.protobuf.Struct
|
||||
}
|
||||
var file_controller_api_resources_credentials_v1_credential_proto_depIdxs = []int32{
|
||||
5, // 0: controller.api.resources.credentials.v1.Credential.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
|
||||
6, // 1: controller.api.resources.credentials.v1.Credential.name:type_name -> google.protobuf.StringValue
|
||||
6, // 2: controller.api.resources.credentials.v1.Credential.description:type_name -> google.protobuf.StringValue
|
||||
7, // 3: controller.api.resources.credentials.v1.Credential.created_time:type_name -> google.protobuf.Timestamp
|
||||
7, // 4: controller.api.resources.credentials.v1.Credential.updated_time:type_name -> google.protobuf.Timestamp
|
||||
8, // 5: controller.api.resources.credentials.v1.Credential.attributes:type_name -> google.protobuf.Struct
|
||||
6, // 0: controller.api.resources.credentials.v1.Credential.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
|
||||
7, // 1: controller.api.resources.credentials.v1.Credential.name:type_name -> google.protobuf.StringValue
|
||||
7, // 2: controller.api.resources.credentials.v1.Credential.description:type_name -> google.protobuf.StringValue
|
||||
8, // 3: controller.api.resources.credentials.v1.Credential.created_time:type_name -> google.protobuf.Timestamp
|
||||
8, // 4: controller.api.resources.credentials.v1.Credential.updated_time:type_name -> google.protobuf.Timestamp
|
||||
9, // 5: controller.api.resources.credentials.v1.Credential.attributes:type_name -> google.protobuf.Struct
|
||||
1, // 6: controller.api.resources.credentials.v1.Credential.username_password_attributes:type_name -> controller.api.resources.credentials.v1.UsernamePasswordAttributes
|
||||
3, // 7: controller.api.resources.credentials.v1.Credential.ssh_private_key_attributes:type_name -> controller.api.resources.credentials.v1.SshPrivateKeyAttributes
|
||||
4, // 8: controller.api.resources.credentials.v1.Credential.json_attributes:type_name -> controller.api.resources.credentials.v1.JsonAttributes
|
||||
4, // 7: controller.api.resources.credentials.v1.Credential.ssh_private_key_attributes:type_name -> controller.api.resources.credentials.v1.SshPrivateKeyAttributes
|
||||
5, // 8: controller.api.resources.credentials.v1.Credential.json_attributes:type_name -> controller.api.resources.credentials.v1.JsonAttributes
|
||||
2, // 9: controller.api.resources.credentials.v1.Credential.username_password_domain_attributes:type_name -> controller.api.resources.credentials.v1.UsernamePasswordDomainAttributes
|
||||
6, // 10: controller.api.resources.credentials.v1.UsernamePasswordAttributes.username:type_name -> google.protobuf.StringValue
|
||||
6, // 11: controller.api.resources.credentials.v1.UsernamePasswordAttributes.password:type_name -> google.protobuf.StringValue
|
||||
6, // 12: controller.api.resources.credentials.v1.UsernamePasswordDomainAttributes.username:type_name -> google.protobuf.StringValue
|
||||
6, // 13: controller.api.resources.credentials.v1.UsernamePasswordDomainAttributes.password:type_name -> google.protobuf.StringValue
|
||||
6, // 14: controller.api.resources.credentials.v1.UsernamePasswordDomainAttributes.domain:type_name -> google.protobuf.StringValue
|
||||
6, // 15: controller.api.resources.credentials.v1.SshPrivateKeyAttributes.username:type_name -> google.protobuf.StringValue
|
||||
6, // 16: controller.api.resources.credentials.v1.SshPrivateKeyAttributes.private_key:type_name -> google.protobuf.StringValue
|
||||
6, // 17: controller.api.resources.credentials.v1.SshPrivateKeyAttributes.private_key_passphrase:type_name -> google.protobuf.StringValue
|
||||
8, // 18: controller.api.resources.credentials.v1.JsonAttributes.object:type_name -> google.protobuf.Struct
|
||||
19, // [19:19] is the sub-list for method output_type
|
||||
19, // [19:19] is the sub-list for method input_type
|
||||
19, // [19:19] is the sub-list for extension type_name
|
||||
19, // [19:19] is the sub-list for extension extendee
|
||||
0, // [0:19] is the sub-list for field type_name
|
||||
3, // 10: controller.api.resources.credentials.v1.Credential.password_attributes:type_name -> controller.api.resources.credentials.v1.PasswordAttributes
|
||||
7, // 11: controller.api.resources.credentials.v1.UsernamePasswordAttributes.username:type_name -> google.protobuf.StringValue
|
||||
7, // 12: controller.api.resources.credentials.v1.UsernamePasswordAttributes.password:type_name -> google.protobuf.StringValue
|
||||
7, // 13: controller.api.resources.credentials.v1.UsernamePasswordDomainAttributes.username:type_name -> google.protobuf.StringValue
|
||||
7, // 14: controller.api.resources.credentials.v1.UsernamePasswordDomainAttributes.password:type_name -> google.protobuf.StringValue
|
||||
7, // 15: controller.api.resources.credentials.v1.UsernamePasswordDomainAttributes.domain:type_name -> google.protobuf.StringValue
|
||||
7, // 16: controller.api.resources.credentials.v1.PasswordAttributes.password:type_name -> google.protobuf.StringValue
|
||||
7, // 17: controller.api.resources.credentials.v1.SshPrivateKeyAttributes.username:type_name -> google.protobuf.StringValue
|
||||
7, // 18: controller.api.resources.credentials.v1.SshPrivateKeyAttributes.private_key:type_name -> google.protobuf.StringValue
|
||||
7, // 19: controller.api.resources.credentials.v1.SshPrivateKeyAttributes.private_key_passphrase:type_name -> google.protobuf.StringValue
|
||||
9, // 20: controller.api.resources.credentials.v1.JsonAttributes.object:type_name -> google.protobuf.Struct
|
||||
21, // [21:21] is the sub-list for method output_type
|
||||
21, // [21:21] is the sub-list for method input_type
|
||||
21, // [21:21] is the sub-list for extension type_name
|
||||
21, // [21:21] is the sub-list for extension extendee
|
||||
0, // [0:21] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_controller_api_resources_credentials_v1_credential_proto_init() }
|
||||
@@ -659,6 +740,7 @@ func file_controller_api_resources_credentials_v1_credential_proto_init() {
|
||||
(*Credential_SshPrivateKeyAttributes)(nil),
|
||||
(*Credential_JsonAttributes)(nil),
|
||||
(*Credential_UsernamePasswordDomainAttributes)(nil),
|
||||
(*Credential_PasswordAttributes)(nil),
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
@@ -666,7 +748,7 @@ func file_controller_api_resources_credentials_v1_credential_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_controller_api_resources_credentials_v1_credential_proto_rawDesc), len(file_controller_api_resources_credentials_v1_credential_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 5,
|
||||
NumMessages: 6,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
||||
@@ -1132,6 +1132,7 @@ type Credential struct {
|
||||
// *Credential_UsernamePasswordAttributes
|
||||
// *Credential_SshPrivateKeyAttributes
|
||||
// *Credential_JsonAttributes
|
||||
// *Credential_PasswordAttributes
|
||||
Attrs isCredential_Attrs `protobuf_oneof:"attrs"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -1252,6 +1253,15 @@ func (x *Credential) GetJsonAttributes() *JsonCredentialAttributes {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Credential) GetPasswordAttributes() *PasswordCredentialAttributes {
|
||||
if x != nil {
|
||||
if x, ok := x.Attrs.(*Credential_PasswordAttributes); ok {
|
||||
return x.PasswordAttributes
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type isCredential_Attrs interface {
|
||||
isCredential_Attrs()
|
||||
}
|
||||
@@ -1273,6 +1283,10 @@ type Credential_JsonAttributes struct {
|
||||
JsonAttributes *JsonCredentialAttributes `protobuf:"bytes,10,opt,name=json_attributes,json=jsonAttributes,proto3,oneof"`
|
||||
}
|
||||
|
||||
type Credential_PasswordAttributes struct {
|
||||
PasswordAttributes *PasswordCredentialAttributes `protobuf:"bytes,11,opt,name=password_attributes,json=passwordAttributes,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*Credential_Attributes) isCredential_Attrs() {}
|
||||
|
||||
func (*Credential_UsernamePasswordAttributes) isCredential_Attrs() {}
|
||||
@@ -1281,6 +1295,8 @@ func (*Credential_SshPrivateKeyAttributes) isCredential_Attrs() {}
|
||||
|
||||
func (*Credential_JsonAttributes) isCredential_Attrs() {}
|
||||
|
||||
func (*Credential_PasswordAttributes) isCredential_Attrs() {}
|
||||
|
||||
// The attributes of a UsernamePassword Credential.
|
||||
type UsernamePasswordCredentialAttributes struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
@@ -1446,6 +1462,52 @@ func (x *JsonCredentialAttributes) GetObjectHmac() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// The attributes of a Password Credential.
|
||||
type PasswordCredentialAttributes struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// The hmac value of the password.
|
||||
PasswordHmac string `protobuf:"bytes,1,opt,name=password_hmac,json=passwordHmac,proto3" json:"password_hmac,omitempty" class:"public"` // @gotags: class:"public"
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *PasswordCredentialAttributes) Reset() {
|
||||
*x = PasswordCredentialAttributes{}
|
||||
mi := &file_controller_api_resources_sessionrecordings_v1_session_recording_proto_msgTypes[14]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *PasswordCredentialAttributes) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*PasswordCredentialAttributes) ProtoMessage() {}
|
||||
|
||||
func (x *PasswordCredentialAttributes) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_api_resources_sessionrecordings_v1_session_recording_proto_msgTypes[14]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use PasswordCredentialAttributes.ProtoReflect.Descriptor instead.
|
||||
func (*PasswordCredentialAttributes) Descriptor() ([]byte, []int) {
|
||||
return file_controller_api_resources_sessionrecordings_v1_session_recording_proto_rawDescGZIP(), []int{14}
|
||||
}
|
||||
|
||||
func (x *PasswordCredentialAttributes) GetPasswordHmac() string {
|
||||
if x != nil {
|
||||
return x.PasswordHmac
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// CredentialLibrary contains all fields related to an Credential Library resource
|
||||
type CredentialLibrary struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
@@ -1474,7 +1536,7 @@ type CredentialLibrary struct {
|
||||
|
||||
func (x *CredentialLibrary) Reset() {
|
||||
*x = CredentialLibrary{}
|
||||
mi := &file_controller_api_resources_sessionrecordings_v1_session_recording_proto_msgTypes[14]
|
||||
mi := &file_controller_api_resources_sessionrecordings_v1_session_recording_proto_msgTypes[15]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1486,7 +1548,7 @@ func (x *CredentialLibrary) String() string {
|
||||
func (*CredentialLibrary) ProtoMessage() {}
|
||||
|
||||
func (x *CredentialLibrary) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_api_resources_sessionrecordings_v1_session_recording_proto_msgTypes[14]
|
||||
mi := &file_controller_api_resources_sessionrecordings_v1_session_recording_proto_msgTypes[15]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1499,7 +1561,7 @@ func (x *CredentialLibrary) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use CredentialLibrary.ProtoReflect.Descriptor instead.
|
||||
func (*CredentialLibrary) Descriptor() ([]byte, []int) {
|
||||
return file_controller_api_resources_sessionrecordings_v1_session_recording_proto_rawDescGZIP(), []int{14}
|
||||
return file_controller_api_resources_sessionrecordings_v1_session_recording_proto_rawDescGZIP(), []int{15}
|
||||
}
|
||||
|
||||
func (x *CredentialLibrary) GetId() string {
|
||||
@@ -1632,7 +1694,7 @@ type VaultCredentialLibraryAttributes struct {
|
||||
|
||||
func (x *VaultCredentialLibraryAttributes) Reset() {
|
||||
*x = VaultCredentialLibraryAttributes{}
|
||||
mi := &file_controller_api_resources_sessionrecordings_v1_session_recording_proto_msgTypes[15]
|
||||
mi := &file_controller_api_resources_sessionrecordings_v1_session_recording_proto_msgTypes[16]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1644,7 +1706,7 @@ func (x *VaultCredentialLibraryAttributes) String() string {
|
||||
func (*VaultCredentialLibraryAttributes) ProtoMessage() {}
|
||||
|
||||
func (x *VaultCredentialLibraryAttributes) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_api_resources_sessionrecordings_v1_session_recording_proto_msgTypes[15]
|
||||
mi := &file_controller_api_resources_sessionrecordings_v1_session_recording_proto_msgTypes[16]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1657,7 +1719,7 @@ func (x *VaultCredentialLibraryAttributes) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use VaultCredentialLibraryAttributes.ProtoReflect.Descriptor instead.
|
||||
func (*VaultCredentialLibraryAttributes) Descriptor() ([]byte, []int) {
|
||||
return file_controller_api_resources_sessionrecordings_v1_session_recording_proto_rawDescGZIP(), []int{15}
|
||||
return file_controller_api_resources_sessionrecordings_v1_session_recording_proto_rawDescGZIP(), []int{16}
|
||||
}
|
||||
|
||||
func (x *VaultCredentialLibraryAttributes) GetPath() string {
|
||||
@@ -1706,7 +1768,7 @@ type VaultSSHCertificateCredentialLibraryAttributes struct {
|
||||
|
||||
func (x *VaultSSHCertificateCredentialLibraryAttributes) Reset() {
|
||||
*x = VaultSSHCertificateCredentialLibraryAttributes{}
|
||||
mi := &file_controller_api_resources_sessionrecordings_v1_session_recording_proto_msgTypes[16]
|
||||
mi := &file_controller_api_resources_sessionrecordings_v1_session_recording_proto_msgTypes[17]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1718,7 +1780,7 @@ func (x *VaultSSHCertificateCredentialLibraryAttributes) String() string {
|
||||
func (*VaultSSHCertificateCredentialLibraryAttributes) ProtoMessage() {}
|
||||
|
||||
func (x *VaultSSHCertificateCredentialLibraryAttributes) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_api_resources_sessionrecordings_v1_session_recording_proto_msgTypes[16]
|
||||
mi := &file_controller_api_resources_sessionrecordings_v1_session_recording_proto_msgTypes[17]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1731,7 +1793,7 @@ func (x *VaultSSHCertificateCredentialLibraryAttributes) ProtoReflect() protoref
|
||||
|
||||
// Deprecated: Use VaultSSHCertificateCredentialLibraryAttributes.ProtoReflect.Descriptor instead.
|
||||
func (*VaultSSHCertificateCredentialLibraryAttributes) Descriptor() ([]byte, []int) {
|
||||
return file_controller_api_resources_sessionrecordings_v1_session_recording_proto_rawDescGZIP(), []int{16}
|
||||
return file_controller_api_resources_sessionrecordings_v1_session_recording_proto_rawDescGZIP(), []int{17}
|
||||
}
|
||||
|
||||
func (x *VaultSSHCertificateCredentialLibraryAttributes) GetPath() string {
|
||||
@@ -1810,7 +1872,7 @@ type ValuesAtTime struct {
|
||||
|
||||
func (x *ValuesAtTime) Reset() {
|
||||
*x = ValuesAtTime{}
|
||||
mi := &file_controller_api_resources_sessionrecordings_v1_session_recording_proto_msgTypes[17]
|
||||
mi := &file_controller_api_resources_sessionrecordings_v1_session_recording_proto_msgTypes[18]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1822,7 +1884,7 @@ func (x *ValuesAtTime) String() string {
|
||||
func (*ValuesAtTime) ProtoMessage() {}
|
||||
|
||||
func (x *ValuesAtTime) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_api_resources_sessionrecordings_v1_session_recording_proto_msgTypes[17]
|
||||
mi := &file_controller_api_resources_sessionrecordings_v1_session_recording_proto_msgTypes[18]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1835,7 +1897,7 @@ func (x *ValuesAtTime) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ValuesAtTime.ProtoReflect.Descriptor instead.
|
||||
func (*ValuesAtTime) Descriptor() ([]byte, []int) {
|
||||
return file_controller_api_resources_sessionrecordings_v1_session_recording_proto_rawDescGZIP(), []int{17}
|
||||
return file_controller_api_resources_sessionrecordings_v1_session_recording_proto_rawDescGZIP(), []int{18}
|
||||
}
|
||||
|
||||
func (x *ValuesAtTime) GetUser() *User {
|
||||
@@ -1936,7 +1998,7 @@ type SessionRecording struct {
|
||||
|
||||
func (x *SessionRecording) Reset() {
|
||||
*x = SessionRecording{}
|
||||
mi := &file_controller_api_resources_sessionrecordings_v1_session_recording_proto_msgTypes[18]
|
||||
mi := &file_controller_api_resources_sessionrecordings_v1_session_recording_proto_msgTypes[19]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1948,7 +2010,7 @@ func (x *SessionRecording) String() string {
|
||||
func (*SessionRecording) ProtoMessage() {}
|
||||
|
||||
func (x *SessionRecording) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_api_resources_sessionrecordings_v1_session_recording_proto_msgTypes[18]
|
||||
mi := &file_controller_api_resources_sessionrecordings_v1_session_recording_proto_msgTypes[19]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1961,7 +2023,7 @@ func (x *SessionRecording) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use SessionRecording.ProtoReflect.Descriptor instead.
|
||||
func (*SessionRecording) Descriptor() ([]byte, []int) {
|
||||
return file_controller_api_resources_sessionrecordings_v1_session_recording_proto_rawDescGZIP(), []int{18}
|
||||
return file_controller_api_resources_sessionrecordings_v1_session_recording_proto_rawDescGZIP(), []int{19}
|
||||
}
|
||||
|
||||
func (x *SessionRecording) GetId() string {
|
||||
@@ -2220,7 +2282,7 @@ const file_controller_api_resources_sessionrecordings_v1_session_recording_proto
|
||||
"\tnamespace\x18\x02 \x01(\tR\tnamespace\x12(\n" +
|
||||
"\x0ftls_server_name\x18\x03 \x01(\tR\x0ftls_server_name\x12(\n" +
|
||||
"\x0ftls_skip_verify\x18\x04 \x01(\bR\x0ftls_skip_verify\x12$\n" +
|
||||
"\rworker_filter\x18\x05 \x01(\tR\rworker_filter\"\xc6\x06\n" +
|
||||
"\rworker_filter\x18\x05 \x01(\tR\rworker_filter\"\xe5\a\n" +
|
||||
"\n" +
|
||||
"Credential\x12\x0e\n" +
|
||||
"\x02id\x18\x01 \x01(\tR\x02id\x12j\n" +
|
||||
@@ -2238,7 +2300,9 @@ const file_controller_api_resources_sessionrecordings_v1_session_recording_proto
|
||||
"\x12\bINTERNALH\x00R\x17sshPrivateKeyAttributes\x12\x8c\x01\n" +
|
||||
"\x0fjson_attributes\x18\n" +
|
||||
" \x01(\v2G.controller.api.resources.sessionrecordings.v1.JsonCredentialAttributesB\x18\x9a\xe3)\x04json\xfa\xd2\xe4\x93\x02\n" +
|
||||
"\x12\bINTERNALH\x00R\x0ejsonAttributesB\a\n" +
|
||||
"\x12\bINTERNALH\x00R\x0ejsonAttributes\x12\x9c\x01\n" +
|
||||
"\x13password_attributes\x18\v \x01(\v2K.controller.api.resources.sessionrecordings.v1.PasswordCredentialAttributesB\x1c\x9a\xe3)\bpassword\xfa\xd2\xe4\x93\x02\n" +
|
||||
"\x12\bINTERNALH\x00R\x12passwordAttributesB\a\n" +
|
||||
"\x05attrs\"g\n" +
|
||||
"$UsernamePasswordCredentialAttributes\x12\x1a\n" +
|
||||
"\busername\x18\x01 \x01(\tR\busername\x12#\n" +
|
||||
@@ -2249,7 +2313,9 @@ const file_controller_api_resources_sessionrecordings_v1_session_recording_proto
|
||||
"\x1bprivate_key_passphrase_hmac\x18\x03 \x01(\tR\x18privateKeyPassphraseHmac\";\n" +
|
||||
"\x18JsonCredentialAttributes\x12\x1f\n" +
|
||||
"\vobject_hmac\x18\x01 \x01(\tR\n" +
|
||||
"objectHmac\"\xd3\a\n" +
|
||||
"objectHmac\"C\n" +
|
||||
"\x1cPasswordCredentialAttributes\x12#\n" +
|
||||
"\rpassword_hmac\x18\x01 \x01(\tR\fpasswordHmac\"\xd3\a\n" +
|
||||
"\x11CredentialLibrary\x12\x0e\n" +
|
||||
"\x02id\x18\x01 \x01(\tR\x02id\x12j\n" +
|
||||
"\x10credential_store\x18\x02 \x01(\v2>.controller.api.resources.sessionrecordings.v1.CredentialStoreR\x10credential_store\x12\x12\n" +
|
||||
@@ -2340,7 +2406,7 @@ func file_controller_api_resources_sessionrecordings_v1_session_recording_proto_
|
||||
return file_controller_api_resources_sessionrecordings_v1_session_recording_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_controller_api_resources_sessionrecordings_v1_session_recording_proto_msgTypes = make([]protoimpl.MessageInfo, 21)
|
||||
var file_controller_api_resources_sessionrecordings_v1_session_recording_proto_msgTypes = make([]protoimpl.MessageInfo, 22)
|
||||
var file_controller_api_resources_sessionrecordings_v1_session_recording_proto_goTypes = []any{
|
||||
(*ChannelRecording)(nil), // 0: controller.api.resources.sessionrecordings.v1.ChannelRecording
|
||||
(*ConnectionRecording)(nil), // 1: controller.api.resources.sessionrecordings.v1.ConnectionRecording
|
||||
@@ -2356,73 +2422,75 @@ var file_controller_api_resources_sessionrecordings_v1_session_recording_proto_g
|
||||
(*UsernamePasswordCredentialAttributes)(nil), // 11: controller.api.resources.sessionrecordings.v1.UsernamePasswordCredentialAttributes
|
||||
(*SshPrivateKeyCredentialAttributes)(nil), // 12: controller.api.resources.sessionrecordings.v1.SshPrivateKeyCredentialAttributes
|
||||
(*JsonCredentialAttributes)(nil), // 13: controller.api.resources.sessionrecordings.v1.JsonCredentialAttributes
|
||||
(*CredentialLibrary)(nil), // 14: controller.api.resources.sessionrecordings.v1.CredentialLibrary
|
||||
(*VaultCredentialLibraryAttributes)(nil), // 15: controller.api.resources.sessionrecordings.v1.VaultCredentialLibraryAttributes
|
||||
(*VaultSSHCertificateCredentialLibraryAttributes)(nil), // 16: controller.api.resources.sessionrecordings.v1.VaultSSHCertificateCredentialLibraryAttributes
|
||||
(*ValuesAtTime)(nil), // 17: controller.api.resources.sessionrecordings.v1.ValuesAtTime
|
||||
(*SessionRecording)(nil), // 18: controller.api.resources.sessionrecordings.v1.SessionRecording
|
||||
nil, // 19: controller.api.resources.sessionrecordings.v1.VaultSSHCertificateCredentialLibraryAttributes.CriticalOptionsEntry
|
||||
nil, // 20: controller.api.resources.sessionrecordings.v1.VaultSSHCertificateCredentialLibraryAttributes.ExtensionsEntry
|
||||
(*timestamppb.Timestamp)(nil), // 21: google.protobuf.Timestamp
|
||||
(*durationpb.Duration)(nil), // 22: google.protobuf.Duration
|
||||
(*scopes.ScopeInfo)(nil), // 23: controller.api.resources.scopes.v1.ScopeInfo
|
||||
(*structpb.Struct)(nil), // 24: google.protobuf.Struct
|
||||
(*PasswordCredentialAttributes)(nil), // 14: controller.api.resources.sessionrecordings.v1.PasswordCredentialAttributes
|
||||
(*CredentialLibrary)(nil), // 15: controller.api.resources.sessionrecordings.v1.CredentialLibrary
|
||||
(*VaultCredentialLibraryAttributes)(nil), // 16: controller.api.resources.sessionrecordings.v1.VaultCredentialLibraryAttributes
|
||||
(*VaultSSHCertificateCredentialLibraryAttributes)(nil), // 17: controller.api.resources.sessionrecordings.v1.VaultSSHCertificateCredentialLibraryAttributes
|
||||
(*ValuesAtTime)(nil), // 18: controller.api.resources.sessionrecordings.v1.ValuesAtTime
|
||||
(*SessionRecording)(nil), // 19: controller.api.resources.sessionrecordings.v1.SessionRecording
|
||||
nil, // 20: controller.api.resources.sessionrecordings.v1.VaultSSHCertificateCredentialLibraryAttributes.CriticalOptionsEntry
|
||||
nil, // 21: controller.api.resources.sessionrecordings.v1.VaultSSHCertificateCredentialLibraryAttributes.ExtensionsEntry
|
||||
(*timestamppb.Timestamp)(nil), // 22: google.protobuf.Timestamp
|
||||
(*durationpb.Duration)(nil), // 23: google.protobuf.Duration
|
||||
(*scopes.ScopeInfo)(nil), // 24: controller.api.resources.scopes.v1.ScopeInfo
|
||||
(*structpb.Struct)(nil), // 25: google.protobuf.Struct
|
||||
}
|
||||
var file_controller_api_resources_sessionrecordings_v1_session_recording_proto_depIdxs = []int32{
|
||||
21, // 0: controller.api.resources.sessionrecordings.v1.ChannelRecording.created_time:type_name -> google.protobuf.Timestamp
|
||||
21, // 1: controller.api.resources.sessionrecordings.v1.ChannelRecording.updated_time:type_name -> google.protobuf.Timestamp
|
||||
21, // 2: controller.api.resources.sessionrecordings.v1.ChannelRecording.start_time:type_name -> google.protobuf.Timestamp
|
||||
21, // 3: controller.api.resources.sessionrecordings.v1.ChannelRecording.end_time:type_name -> google.protobuf.Timestamp
|
||||
22, // 4: controller.api.resources.sessionrecordings.v1.ChannelRecording.duration:type_name -> google.protobuf.Duration
|
||||
21, // 5: controller.api.resources.sessionrecordings.v1.ConnectionRecording.created_time:type_name -> google.protobuf.Timestamp
|
||||
21, // 6: controller.api.resources.sessionrecordings.v1.ConnectionRecording.updated_time:type_name -> google.protobuf.Timestamp
|
||||
21, // 7: controller.api.resources.sessionrecordings.v1.ConnectionRecording.start_time:type_name -> google.protobuf.Timestamp
|
||||
21, // 8: controller.api.resources.sessionrecordings.v1.ConnectionRecording.end_time:type_name -> google.protobuf.Timestamp
|
||||
22, // 9: controller.api.resources.sessionrecordings.v1.ConnectionRecording.duration:type_name -> google.protobuf.Duration
|
||||
22, // 0: controller.api.resources.sessionrecordings.v1.ChannelRecording.created_time:type_name -> google.protobuf.Timestamp
|
||||
22, // 1: controller.api.resources.sessionrecordings.v1.ChannelRecording.updated_time:type_name -> google.protobuf.Timestamp
|
||||
22, // 2: controller.api.resources.sessionrecordings.v1.ChannelRecording.start_time:type_name -> google.protobuf.Timestamp
|
||||
22, // 3: controller.api.resources.sessionrecordings.v1.ChannelRecording.end_time:type_name -> google.protobuf.Timestamp
|
||||
23, // 4: controller.api.resources.sessionrecordings.v1.ChannelRecording.duration:type_name -> google.protobuf.Duration
|
||||
22, // 5: controller.api.resources.sessionrecordings.v1.ConnectionRecording.created_time:type_name -> google.protobuf.Timestamp
|
||||
22, // 6: controller.api.resources.sessionrecordings.v1.ConnectionRecording.updated_time:type_name -> google.protobuf.Timestamp
|
||||
22, // 7: controller.api.resources.sessionrecordings.v1.ConnectionRecording.start_time:type_name -> google.protobuf.Timestamp
|
||||
22, // 8: controller.api.resources.sessionrecordings.v1.ConnectionRecording.end_time:type_name -> google.protobuf.Timestamp
|
||||
23, // 9: controller.api.resources.sessionrecordings.v1.ConnectionRecording.duration:type_name -> google.protobuf.Duration
|
||||
0, // 10: controller.api.resources.sessionrecordings.v1.ConnectionRecording.channel_recordings:type_name -> controller.api.resources.sessionrecordings.v1.ChannelRecording
|
||||
23, // 11: controller.api.resources.sessionrecordings.v1.User.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
|
||||
23, // 12: controller.api.resources.sessionrecordings.v1.HostCatalog.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
|
||||
24, // 13: controller.api.resources.sessionrecordings.v1.HostCatalog.attributes:type_name -> google.protobuf.Struct
|
||||
24, // 11: controller.api.resources.sessionrecordings.v1.User.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
|
||||
24, // 12: controller.api.resources.sessionrecordings.v1.HostCatalog.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
|
||||
25, // 13: controller.api.resources.sessionrecordings.v1.HostCatalog.attributes:type_name -> google.protobuf.Struct
|
||||
3, // 14: controller.api.resources.sessionrecordings.v1.Host.host_catalog:type_name -> controller.api.resources.sessionrecordings.v1.HostCatalog
|
||||
24, // 15: controller.api.resources.sessionrecordings.v1.Host.attributes:type_name -> google.protobuf.Struct
|
||||
25, // 15: controller.api.resources.sessionrecordings.v1.Host.attributes:type_name -> google.protobuf.Struct
|
||||
5, // 16: controller.api.resources.sessionrecordings.v1.Host.static_host_attributes:type_name -> controller.api.resources.sessionrecordings.v1.StaticHostAttributes
|
||||
23, // 17: controller.api.resources.sessionrecordings.v1.Target.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
|
||||
24, // 18: controller.api.resources.sessionrecordings.v1.Target.attributes:type_name -> google.protobuf.Struct
|
||||
24, // 17: controller.api.resources.sessionrecordings.v1.Target.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
|
||||
25, // 18: controller.api.resources.sessionrecordings.v1.Target.attributes:type_name -> google.protobuf.Struct
|
||||
7, // 19: controller.api.resources.sessionrecordings.v1.Target.ssh_target_attributes:type_name -> controller.api.resources.sessionrecordings.v1.SshTargetAttributes
|
||||
24, // 20: controller.api.resources.sessionrecordings.v1.CredentialStore.attributes:type_name -> google.protobuf.Struct
|
||||
25, // 20: controller.api.resources.sessionrecordings.v1.CredentialStore.attributes:type_name -> google.protobuf.Struct
|
||||
9, // 21: controller.api.resources.sessionrecordings.v1.CredentialStore.vault_credential_store_attributes:type_name -> controller.api.resources.sessionrecordings.v1.VaultCredentialStoreAttributes
|
||||
8, // 22: controller.api.resources.sessionrecordings.v1.Credential.credential_store:type_name -> controller.api.resources.sessionrecordings.v1.CredentialStore
|
||||
24, // 23: controller.api.resources.sessionrecordings.v1.Credential.attributes:type_name -> google.protobuf.Struct
|
||||
25, // 23: controller.api.resources.sessionrecordings.v1.Credential.attributes:type_name -> google.protobuf.Struct
|
||||
11, // 24: controller.api.resources.sessionrecordings.v1.Credential.username_password_attributes:type_name -> controller.api.resources.sessionrecordings.v1.UsernamePasswordCredentialAttributes
|
||||
12, // 25: controller.api.resources.sessionrecordings.v1.Credential.ssh_private_key_attributes:type_name -> controller.api.resources.sessionrecordings.v1.SshPrivateKeyCredentialAttributes
|
||||
13, // 26: controller.api.resources.sessionrecordings.v1.Credential.json_attributes:type_name -> controller.api.resources.sessionrecordings.v1.JsonCredentialAttributes
|
||||
8, // 27: controller.api.resources.sessionrecordings.v1.CredentialLibrary.credential_store:type_name -> controller.api.resources.sessionrecordings.v1.CredentialStore
|
||||
24, // 28: controller.api.resources.sessionrecordings.v1.CredentialLibrary.attributes:type_name -> google.protobuf.Struct
|
||||
15, // 29: controller.api.resources.sessionrecordings.v1.CredentialLibrary.vault_credential_library_attributes:type_name -> controller.api.resources.sessionrecordings.v1.VaultCredentialLibraryAttributes
|
||||
15, // 30: controller.api.resources.sessionrecordings.v1.CredentialLibrary.vault_generic_credential_library_attributes:type_name -> controller.api.resources.sessionrecordings.v1.VaultCredentialLibraryAttributes
|
||||
16, // 31: controller.api.resources.sessionrecordings.v1.CredentialLibrary.vault_ssh_certificate_credential_library_attributes:type_name -> controller.api.resources.sessionrecordings.v1.VaultSSHCertificateCredentialLibraryAttributes
|
||||
19, // 32: controller.api.resources.sessionrecordings.v1.VaultSSHCertificateCredentialLibraryAttributes.critical_options:type_name -> controller.api.resources.sessionrecordings.v1.VaultSSHCertificateCredentialLibraryAttributes.CriticalOptionsEntry
|
||||
20, // 33: controller.api.resources.sessionrecordings.v1.VaultSSHCertificateCredentialLibraryAttributes.extensions:type_name -> controller.api.resources.sessionrecordings.v1.VaultSSHCertificateCredentialLibraryAttributes.ExtensionsEntry
|
||||
2, // 34: controller.api.resources.sessionrecordings.v1.ValuesAtTime.user:type_name -> controller.api.resources.sessionrecordings.v1.User
|
||||
6, // 35: controller.api.resources.sessionrecordings.v1.ValuesAtTime.target:type_name -> controller.api.resources.sessionrecordings.v1.Target
|
||||
4, // 36: controller.api.resources.sessionrecordings.v1.ValuesAtTime.host:type_name -> controller.api.resources.sessionrecordings.v1.Host
|
||||
10, // 37: controller.api.resources.sessionrecordings.v1.ValuesAtTime.credentials:type_name -> controller.api.resources.sessionrecordings.v1.Credential
|
||||
14, // 38: controller.api.resources.sessionrecordings.v1.ValuesAtTime.credential_libraries:type_name -> controller.api.resources.sessionrecordings.v1.CredentialLibrary
|
||||
23, // 39: controller.api.resources.sessionrecordings.v1.SessionRecording.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
|
||||
21, // 40: controller.api.resources.sessionrecordings.v1.SessionRecording.created_time:type_name -> google.protobuf.Timestamp
|
||||
21, // 41: controller.api.resources.sessionrecordings.v1.SessionRecording.updated_time:type_name -> google.protobuf.Timestamp
|
||||
21, // 42: controller.api.resources.sessionrecordings.v1.SessionRecording.start_time:type_name -> google.protobuf.Timestamp
|
||||
21, // 43: controller.api.resources.sessionrecordings.v1.SessionRecording.end_time:type_name -> google.protobuf.Timestamp
|
||||
22, // 44: controller.api.resources.sessionrecordings.v1.SessionRecording.duration:type_name -> google.protobuf.Duration
|
||||
1, // 45: controller.api.resources.sessionrecordings.v1.SessionRecording.connection_recordings:type_name -> controller.api.resources.sessionrecordings.v1.ConnectionRecording
|
||||
17, // 46: controller.api.resources.sessionrecordings.v1.SessionRecording.create_time_values:type_name -> controller.api.resources.sessionrecordings.v1.ValuesAtTime
|
||||
21, // 47: controller.api.resources.sessionrecordings.v1.SessionRecording.retain_until:type_name -> google.protobuf.Timestamp
|
||||
21, // 48: controller.api.resources.sessionrecordings.v1.SessionRecording.delete_after:type_name -> google.protobuf.Timestamp
|
||||
49, // [49:49] is the sub-list for method output_type
|
||||
49, // [49:49] is the sub-list for method input_type
|
||||
49, // [49:49] is the sub-list for extension type_name
|
||||
49, // [49:49] is the sub-list for extension extendee
|
||||
0, // [0:49] is the sub-list for field type_name
|
||||
14, // 27: controller.api.resources.sessionrecordings.v1.Credential.password_attributes:type_name -> controller.api.resources.sessionrecordings.v1.PasswordCredentialAttributes
|
||||
8, // 28: controller.api.resources.sessionrecordings.v1.CredentialLibrary.credential_store:type_name -> controller.api.resources.sessionrecordings.v1.CredentialStore
|
||||
25, // 29: controller.api.resources.sessionrecordings.v1.CredentialLibrary.attributes:type_name -> google.protobuf.Struct
|
||||
16, // 30: controller.api.resources.sessionrecordings.v1.CredentialLibrary.vault_credential_library_attributes:type_name -> controller.api.resources.sessionrecordings.v1.VaultCredentialLibraryAttributes
|
||||
16, // 31: controller.api.resources.sessionrecordings.v1.CredentialLibrary.vault_generic_credential_library_attributes:type_name -> controller.api.resources.sessionrecordings.v1.VaultCredentialLibraryAttributes
|
||||
17, // 32: controller.api.resources.sessionrecordings.v1.CredentialLibrary.vault_ssh_certificate_credential_library_attributes:type_name -> controller.api.resources.sessionrecordings.v1.VaultSSHCertificateCredentialLibraryAttributes
|
||||
20, // 33: controller.api.resources.sessionrecordings.v1.VaultSSHCertificateCredentialLibraryAttributes.critical_options:type_name -> controller.api.resources.sessionrecordings.v1.VaultSSHCertificateCredentialLibraryAttributes.CriticalOptionsEntry
|
||||
21, // 34: controller.api.resources.sessionrecordings.v1.VaultSSHCertificateCredentialLibraryAttributes.extensions:type_name -> controller.api.resources.sessionrecordings.v1.VaultSSHCertificateCredentialLibraryAttributes.ExtensionsEntry
|
||||
2, // 35: controller.api.resources.sessionrecordings.v1.ValuesAtTime.user:type_name -> controller.api.resources.sessionrecordings.v1.User
|
||||
6, // 36: controller.api.resources.sessionrecordings.v1.ValuesAtTime.target:type_name -> controller.api.resources.sessionrecordings.v1.Target
|
||||
4, // 37: controller.api.resources.sessionrecordings.v1.ValuesAtTime.host:type_name -> controller.api.resources.sessionrecordings.v1.Host
|
||||
10, // 38: controller.api.resources.sessionrecordings.v1.ValuesAtTime.credentials:type_name -> controller.api.resources.sessionrecordings.v1.Credential
|
||||
15, // 39: controller.api.resources.sessionrecordings.v1.ValuesAtTime.credential_libraries:type_name -> controller.api.resources.sessionrecordings.v1.CredentialLibrary
|
||||
24, // 40: controller.api.resources.sessionrecordings.v1.SessionRecording.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
|
||||
22, // 41: controller.api.resources.sessionrecordings.v1.SessionRecording.created_time:type_name -> google.protobuf.Timestamp
|
||||
22, // 42: controller.api.resources.sessionrecordings.v1.SessionRecording.updated_time:type_name -> google.protobuf.Timestamp
|
||||
22, // 43: controller.api.resources.sessionrecordings.v1.SessionRecording.start_time:type_name -> google.protobuf.Timestamp
|
||||
22, // 44: controller.api.resources.sessionrecordings.v1.SessionRecording.end_time:type_name -> google.protobuf.Timestamp
|
||||
23, // 45: controller.api.resources.sessionrecordings.v1.SessionRecording.duration:type_name -> google.protobuf.Duration
|
||||
1, // 46: controller.api.resources.sessionrecordings.v1.SessionRecording.connection_recordings:type_name -> controller.api.resources.sessionrecordings.v1.ConnectionRecording
|
||||
18, // 47: controller.api.resources.sessionrecordings.v1.SessionRecording.create_time_values:type_name -> controller.api.resources.sessionrecordings.v1.ValuesAtTime
|
||||
22, // 48: controller.api.resources.sessionrecordings.v1.SessionRecording.retain_until:type_name -> google.protobuf.Timestamp
|
||||
22, // 49: controller.api.resources.sessionrecordings.v1.SessionRecording.delete_after:type_name -> google.protobuf.Timestamp
|
||||
50, // [50:50] is the sub-list for method output_type
|
||||
50, // [50:50] is the sub-list for method input_type
|
||||
50, // [50:50] is the sub-list for extension type_name
|
||||
50, // [50:50] is the sub-list for extension extendee
|
||||
0, // [0:50] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_controller_api_resources_sessionrecordings_v1_session_recording_proto_init() }
|
||||
@@ -2450,8 +2518,9 @@ func file_controller_api_resources_sessionrecordings_v1_session_recording_proto_
|
||||
(*Credential_UsernamePasswordAttributes)(nil),
|
||||
(*Credential_SshPrivateKeyAttributes)(nil),
|
||||
(*Credential_JsonAttributes)(nil),
|
||||
(*Credential_PasswordAttributes)(nil),
|
||||
}
|
||||
file_controller_api_resources_sessionrecordings_v1_session_recording_proto_msgTypes[14].OneofWrappers = []any{
|
||||
file_controller_api_resources_sessionrecordings_v1_session_recording_proto_msgTypes[15].OneofWrappers = []any{
|
||||
(*CredentialLibrary_Attributes)(nil),
|
||||
(*CredentialLibrary_VaultCredentialLibraryAttributes)(nil),
|
||||
(*CredentialLibrary_VaultGenericCredentialLibraryAttributes)(nil),
|
||||
@@ -2463,7 +2532,7 @@ func file_controller_api_resources_sessionrecordings_v1_session_recording_proto_
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_controller_api_resources_sessionrecordings_v1_session_recording_proto_rawDesc), len(file_controller_api_resources_sessionrecordings_v1_session_recording_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 21,
|
||||
NumMessages: 22,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
||||
@@ -1487,6 +1487,52 @@ func (x *UsernamePasswordDomainCredential) GetDomain() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// The layout of the struct for "credential" field in SessionCredential for a password credential type.
|
||||
type PasswordCredential struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// Password of the credential
|
||||
Password string `protobuf:"bytes,1,opt,name=password,proto3" json:"password,omitempty" class:"secret"` // @gotags: `class:"secret"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *PasswordCredential) Reset() {
|
||||
*x = PasswordCredential{}
|
||||
mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[16]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *PasswordCredential) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*PasswordCredential) ProtoMessage() {}
|
||||
|
||||
func (x *PasswordCredential) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[16]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use PasswordCredential.ProtoReflect.Descriptor instead.
|
||||
func (*PasswordCredential) Descriptor() ([]byte, []int) {
|
||||
return file_controller_api_resources_targets_v1_target_proto_rawDescGZIP(), []int{16}
|
||||
}
|
||||
|
||||
func (x *PasswordCredential) GetPassword() string {
|
||||
if x != nil {
|
||||
return x.Password
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// The layout of the struct for "credential" field in SessionCredential for a ssh_private_key credential type.
|
||||
type SshPrivateKeyCredential struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
@@ -1502,7 +1548,7 @@ type SshPrivateKeyCredential struct {
|
||||
|
||||
func (x *SshPrivateKeyCredential) Reset() {
|
||||
*x = SshPrivateKeyCredential{}
|
||||
mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[16]
|
||||
mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[17]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1514,7 +1560,7 @@ func (x *SshPrivateKeyCredential) String() string {
|
||||
func (*SshPrivateKeyCredential) ProtoMessage() {}
|
||||
|
||||
func (x *SshPrivateKeyCredential) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[16]
|
||||
mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[17]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1527,7 +1573,7 @@ func (x *SshPrivateKeyCredential) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use SshPrivateKeyCredential.ProtoReflect.Descriptor instead.
|
||||
func (*SshPrivateKeyCredential) Descriptor() ([]byte, []int) {
|
||||
return file_controller_api_resources_targets_v1_target_proto_rawDescGZIP(), []int{16}
|
||||
return file_controller_api_resources_targets_v1_target_proto_rawDescGZIP(), []int{17}
|
||||
}
|
||||
|
||||
func (x *SshPrivateKeyCredential) GetUsername() string {
|
||||
@@ -1709,7 +1755,9 @@ const file_controller_api_resources_targets_v1_target_proto_rawDesc = "" +
|
||||
" UsernamePasswordDomainCredential\x12\x1a\n" +
|
||||
"\busername\x18\x01 \x01(\tR\busername\x12\x1a\n" +
|
||||
"\bpassword\x18\x02 \x01(\tR\bpassword\x12\x16\n" +
|
||||
"\x06domain\x18\x03 \x01(\tR\x06domain\"\x8c\x01\n" +
|
||||
"\x06domain\x18\x03 \x01(\tR\x06domain\"0\n" +
|
||||
"\x12PasswordCredential\x12\x1a\n" +
|
||||
"\bpassword\x18\x01 \x01(\tR\bpassword\"\x8c\x01\n" +
|
||||
"\x17SshPrivateKeyCredential\x12\x1a\n" +
|
||||
"\busername\x18\x01 \x01(\tR\busername\x12\x1f\n" +
|
||||
"\vprivate_key\x18\x02 \x01(\tR\n" +
|
||||
@@ -1728,7 +1776,7 @@ func file_controller_api_resources_targets_v1_target_proto_rawDescGZIP() []byte
|
||||
return file_controller_api_resources_targets_v1_target_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_controller_api_resources_targets_v1_target_proto_msgTypes = make([]protoimpl.MessageInfo, 17)
|
||||
var file_controller_api_resources_targets_v1_target_proto_msgTypes = make([]protoimpl.MessageInfo, 18)
|
||||
var file_controller_api_resources_targets_v1_target_proto_goTypes = []any{
|
||||
(*Alias)(nil), // 0: controller.api.resources.targets.v1.Alias
|
||||
(*TargetAliasAttributes)(nil), // 1: controller.api.resources.targets.v1.TargetAliasAttributes
|
||||
@@ -1746,57 +1794,58 @@ var file_controller_api_resources_targets_v1_target_proto_goTypes = []any{
|
||||
(*SessionAuthorization)(nil), // 13: controller.api.resources.targets.v1.SessionAuthorization
|
||||
(*UsernamePasswordCredential)(nil), // 14: controller.api.resources.targets.v1.UsernamePasswordCredential
|
||||
(*UsernamePasswordDomainCredential)(nil), // 15: controller.api.resources.targets.v1.UsernamePasswordDomainCredential
|
||||
(*SshPrivateKeyCredential)(nil), // 16: controller.api.resources.targets.v1.SshPrivateKeyCredential
|
||||
(*structpb.Struct)(nil), // 17: google.protobuf.Struct
|
||||
(*scopes.ScopeInfo)(nil), // 18: controller.api.resources.scopes.v1.ScopeInfo
|
||||
(*wrapperspb.StringValue)(nil), // 19: google.protobuf.StringValue
|
||||
(*timestamppb.Timestamp)(nil), // 20: google.protobuf.Timestamp
|
||||
(*wrapperspb.UInt32Value)(nil), // 21: google.protobuf.UInt32Value
|
||||
(*wrapperspb.Int32Value)(nil), // 22: google.protobuf.Int32Value
|
||||
(*wrapperspb.BoolValue)(nil), // 23: google.protobuf.BoolValue
|
||||
(*PasswordCredential)(nil), // 16: controller.api.resources.targets.v1.PasswordCredential
|
||||
(*SshPrivateKeyCredential)(nil), // 17: controller.api.resources.targets.v1.SshPrivateKeyCredential
|
||||
(*structpb.Struct)(nil), // 18: google.protobuf.Struct
|
||||
(*scopes.ScopeInfo)(nil), // 19: controller.api.resources.scopes.v1.ScopeInfo
|
||||
(*wrapperspb.StringValue)(nil), // 20: google.protobuf.StringValue
|
||||
(*timestamppb.Timestamp)(nil), // 21: google.protobuf.Timestamp
|
||||
(*wrapperspb.UInt32Value)(nil), // 22: google.protobuf.UInt32Value
|
||||
(*wrapperspb.Int32Value)(nil), // 23: google.protobuf.Int32Value
|
||||
(*wrapperspb.BoolValue)(nil), // 24: google.protobuf.BoolValue
|
||||
}
|
||||
var file_controller_api_resources_targets_v1_target_proto_depIdxs = []int32{
|
||||
1, // 0: controller.api.resources.targets.v1.Alias.attributes:type_name -> controller.api.resources.targets.v1.TargetAliasAttributes
|
||||
2, // 1: controller.api.resources.targets.v1.TargetAliasAttributes.authorize_session_arguments:type_name -> controller.api.resources.targets.v1.AuthorizeSessionArguments
|
||||
17, // 2: controller.api.resources.targets.v1.SessionSecret.decoded:type_name -> google.protobuf.Struct
|
||||
18, // 2: controller.api.resources.targets.v1.SessionSecret.decoded:type_name -> google.protobuf.Struct
|
||||
4, // 3: controller.api.resources.targets.v1.SessionCredential.credential_source:type_name -> controller.api.resources.targets.v1.CredentialSource
|
||||
5, // 4: controller.api.resources.targets.v1.SessionCredential.secret:type_name -> controller.api.resources.targets.v1.SessionSecret
|
||||
17, // 5: controller.api.resources.targets.v1.SessionCredential.credential:type_name -> google.protobuf.Struct
|
||||
18, // 6: controller.api.resources.targets.v1.Target.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
|
||||
19, // 7: controller.api.resources.targets.v1.Target.name:type_name -> google.protobuf.StringValue
|
||||
19, // 8: controller.api.resources.targets.v1.Target.description:type_name -> google.protobuf.StringValue
|
||||
20, // 9: controller.api.resources.targets.v1.Target.created_time:type_name -> google.protobuf.Timestamp
|
||||
20, // 10: controller.api.resources.targets.v1.Target.updated_time:type_name -> google.protobuf.Timestamp
|
||||
18, // 5: controller.api.resources.targets.v1.SessionCredential.credential:type_name -> google.protobuf.Struct
|
||||
19, // 6: controller.api.resources.targets.v1.Target.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
|
||||
20, // 7: controller.api.resources.targets.v1.Target.name:type_name -> google.protobuf.StringValue
|
||||
20, // 8: controller.api.resources.targets.v1.Target.description:type_name -> google.protobuf.StringValue
|
||||
21, // 9: controller.api.resources.targets.v1.Target.created_time:type_name -> google.protobuf.Timestamp
|
||||
21, // 10: controller.api.resources.targets.v1.Target.updated_time:type_name -> google.protobuf.Timestamp
|
||||
3, // 11: controller.api.resources.targets.v1.Target.host_sources:type_name -> controller.api.resources.targets.v1.HostSource
|
||||
21, // 12: controller.api.resources.targets.v1.Target.session_max_seconds:type_name -> google.protobuf.UInt32Value
|
||||
22, // 13: controller.api.resources.targets.v1.Target.session_connection_limit:type_name -> google.protobuf.Int32Value
|
||||
19, // 14: controller.api.resources.targets.v1.Target.worker_filter:type_name -> google.protobuf.StringValue
|
||||
19, // 15: controller.api.resources.targets.v1.Target.egress_worker_filter:type_name -> google.protobuf.StringValue
|
||||
19, // 16: controller.api.resources.targets.v1.Target.ingress_worker_filter:type_name -> google.protobuf.StringValue
|
||||
22, // 12: controller.api.resources.targets.v1.Target.session_max_seconds:type_name -> google.protobuf.UInt32Value
|
||||
23, // 13: controller.api.resources.targets.v1.Target.session_connection_limit:type_name -> google.protobuf.Int32Value
|
||||
20, // 14: controller.api.resources.targets.v1.Target.worker_filter:type_name -> google.protobuf.StringValue
|
||||
20, // 15: controller.api.resources.targets.v1.Target.egress_worker_filter:type_name -> google.protobuf.StringValue
|
||||
20, // 16: controller.api.resources.targets.v1.Target.ingress_worker_filter:type_name -> google.protobuf.StringValue
|
||||
4, // 17: controller.api.resources.targets.v1.Target.brokered_credential_sources:type_name -> controller.api.resources.targets.v1.CredentialSource
|
||||
4, // 18: controller.api.resources.targets.v1.Target.injected_application_credential_sources:type_name -> controller.api.resources.targets.v1.CredentialSource
|
||||
17, // 19: controller.api.resources.targets.v1.Target.attributes:type_name -> google.protobuf.Struct
|
||||
18, // 19: controller.api.resources.targets.v1.Target.attributes:type_name -> google.protobuf.Struct
|
||||
8, // 20: controller.api.resources.targets.v1.Target.tcp_target_attributes:type_name -> controller.api.resources.targets.v1.TcpTargetAttributes
|
||||
9, // 21: controller.api.resources.targets.v1.Target.ssh_target_attributes:type_name -> controller.api.resources.targets.v1.SshTargetAttributes
|
||||
10, // 22: controller.api.resources.targets.v1.Target.rdp_target_attributes:type_name -> controller.api.resources.targets.v1.RdpTargetAttributes
|
||||
19, // 23: controller.api.resources.targets.v1.Target.address:type_name -> google.protobuf.StringValue
|
||||
20, // 23: controller.api.resources.targets.v1.Target.address:type_name -> google.protobuf.StringValue
|
||||
0, // 24: controller.api.resources.targets.v1.Target.aliases:type_name -> controller.api.resources.targets.v1.Alias
|
||||
0, // 25: controller.api.resources.targets.v1.Target.with_aliases:type_name -> controller.api.resources.targets.v1.Alias
|
||||
21, // 26: controller.api.resources.targets.v1.TcpTargetAttributes.default_port:type_name -> google.protobuf.UInt32Value
|
||||
21, // 27: controller.api.resources.targets.v1.TcpTargetAttributes.default_client_port:type_name -> google.protobuf.UInt32Value
|
||||
21, // 28: controller.api.resources.targets.v1.SshTargetAttributes.default_port:type_name -> google.protobuf.UInt32Value
|
||||
21, // 29: controller.api.resources.targets.v1.SshTargetAttributes.default_client_port:type_name -> google.protobuf.UInt32Value
|
||||
19, // 30: controller.api.resources.targets.v1.SshTargetAttributes.storage_bucket_id:type_name -> google.protobuf.StringValue
|
||||
23, // 31: controller.api.resources.targets.v1.SshTargetAttributes.enable_session_recording:type_name -> google.protobuf.BoolValue
|
||||
21, // 32: controller.api.resources.targets.v1.RdpTargetAttributes.default_port:type_name -> google.protobuf.UInt32Value
|
||||
21, // 33: controller.api.resources.targets.v1.RdpTargetAttributes.default_client_port:type_name -> google.protobuf.UInt32Value
|
||||
18, // 34: controller.api.resources.targets.v1.SessionAuthorizationData.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
|
||||
20, // 35: controller.api.resources.targets.v1.SessionAuthorizationData.created_time:type_name -> google.protobuf.Timestamp
|
||||
20, // 36: controller.api.resources.targets.v1.SessionAuthorizationData.expiration:type_name -> google.protobuf.Timestamp
|
||||
22, // 26: controller.api.resources.targets.v1.TcpTargetAttributes.default_port:type_name -> google.protobuf.UInt32Value
|
||||
22, // 27: controller.api.resources.targets.v1.TcpTargetAttributes.default_client_port:type_name -> google.protobuf.UInt32Value
|
||||
22, // 28: controller.api.resources.targets.v1.SshTargetAttributes.default_port:type_name -> google.protobuf.UInt32Value
|
||||
22, // 29: controller.api.resources.targets.v1.SshTargetAttributes.default_client_port:type_name -> google.protobuf.UInt32Value
|
||||
20, // 30: controller.api.resources.targets.v1.SshTargetAttributes.storage_bucket_id:type_name -> google.protobuf.StringValue
|
||||
24, // 31: controller.api.resources.targets.v1.SshTargetAttributes.enable_session_recording:type_name -> google.protobuf.BoolValue
|
||||
22, // 32: controller.api.resources.targets.v1.RdpTargetAttributes.default_port:type_name -> google.protobuf.UInt32Value
|
||||
22, // 33: controller.api.resources.targets.v1.RdpTargetAttributes.default_client_port:type_name -> google.protobuf.UInt32Value
|
||||
19, // 34: controller.api.resources.targets.v1.SessionAuthorizationData.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
|
||||
21, // 35: controller.api.resources.targets.v1.SessionAuthorizationData.created_time:type_name -> google.protobuf.Timestamp
|
||||
21, // 36: controller.api.resources.targets.v1.SessionAuthorizationData.expiration:type_name -> google.protobuf.Timestamp
|
||||
11, // 37: controller.api.resources.targets.v1.SessionAuthorizationData.worker_info:type_name -> controller.api.resources.targets.v1.WorkerInfo
|
||||
18, // 38: controller.api.resources.targets.v1.SessionAuthorization.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
|
||||
20, // 39: controller.api.resources.targets.v1.SessionAuthorization.created_time:type_name -> google.protobuf.Timestamp
|
||||
20, // 40: controller.api.resources.targets.v1.SessionAuthorization.expiration:type_name -> google.protobuf.Timestamp
|
||||
19, // 38: controller.api.resources.targets.v1.SessionAuthorization.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
|
||||
21, // 39: controller.api.resources.targets.v1.SessionAuthorization.created_time:type_name -> google.protobuf.Timestamp
|
||||
21, // 40: controller.api.resources.targets.v1.SessionAuthorization.expiration:type_name -> google.protobuf.Timestamp
|
||||
6, // 41: controller.api.resources.targets.v1.SessionAuthorization.credentials:type_name -> controller.api.resources.targets.v1.SessionCredential
|
||||
42, // [42:42] is the sub-list for method output_type
|
||||
42, // [42:42] is the sub-list for method input_type
|
||||
@@ -1822,7 +1871,7 @@ func file_controller_api_resources_targets_v1_target_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_controller_api_resources_targets_v1_target_proto_rawDesc), len(file_controller_api_resources_targets_v1_target_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 17,
|
||||
NumMessages: 18,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
||||
@@ -262,6 +262,41 @@ func CreateStaticCredentialUsernamePasswordCli(t testing.TB, ctx context.Context
|
||||
return credentialId, nil
|
||||
}
|
||||
|
||||
// CreateStaticCredentialPasswordCli uses the cli to create a new password credential in the
|
||||
// provided static credential store.
|
||||
// Returns the id of the new credential
|
||||
func CreateStaticCredentialPasswordCli(t testing.TB, ctx context.Context, credentialStoreId string, password string) (string, error) {
|
||||
name, err := base62.Random(16)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
output := e2e.RunCommand(ctx, "boundary",
|
||||
e2e.WithArgs(
|
||||
"credentials", "create", "password",
|
||||
"-credential-store-id", credentialStoreId,
|
||||
"-password", "env://E2E_CREDENTIALS_PASSWORD",
|
||||
"-name", fmt.Sprintf("e2e Credential %s", name),
|
||||
"-description", "e2e",
|
||||
"-format", "json",
|
||||
),
|
||||
e2e.WithEnv("E2E_CREDENTIALS_PASSWORD", password),
|
||||
)
|
||||
if output.Err != nil {
|
||||
return "", fmt.Errorf("%w: %s", output.Err, string(output.Stderr))
|
||||
}
|
||||
|
||||
var createCredentialsResult credentials.CredentialCreateResult
|
||||
err = json.Unmarshal(output.Stdout, &createCredentialsResult)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
credentialId := createCredentialsResult.Item.Id
|
||||
t.Logf("Created Password Credential: %s", credentialId)
|
||||
return credentialId, nil
|
||||
}
|
||||
|
||||
// CreateStaticCredentialUsernamePasswordDomainCli uses the cli to create a new username password domain credential in the
|
||||
// provided static credential store.
|
||||
// Returns the id of the new credential
|
||||
|
||||
@@ -35,12 +35,6 @@ type cassandraConfig struct {
|
||||
NetworkAlias string
|
||||
}
|
||||
|
||||
type redisConfig struct {
|
||||
User string
|
||||
Password string
|
||||
NetworkAlias string
|
||||
}
|
||||
|
||||
// StartBoundaryDatabase spins up a postgres database in a docker container.
|
||||
// Returns information about the container
|
||||
func StartBoundaryDatabase(t testing.TB, pool *dockertest.Pool, network *dockertest.Network, repository, tag string) *Container {
|
||||
@@ -478,63 +472,6 @@ func StartCassandra(t testing.TB, pool *dockertest.Pool, network *dockertest.Net
|
||||
}
|
||||
}
|
||||
|
||||
// StartRedis starts a Redis database in a docker container.
|
||||
// Returns information about the container
|
||||
func StartRedis(t testing.TB, pool *dockertest.Pool, network *dockertest.Network, repository, tag string) *Container {
|
||||
t.Log("Starting Redis database...")
|
||||
c, err := LoadConfig()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = pool.Client.PullImage(docker.PullImageOptions{
|
||||
Repository: fmt.Sprintf("%s/%s", c.DockerMirror, repository),
|
||||
Tag: tag,
|
||||
}, docker.AuthConfiguration{})
|
||||
require.NoError(t, err)
|
||||
|
||||
config := redisConfig{
|
||||
User: "e2eboundary",
|
||||
Password: "e2eboundary",
|
||||
NetworkAlias: "e2eredis",
|
||||
}
|
||||
|
||||
resource, err := pool.RunWithOptions(&dockertest.RunOptions{
|
||||
Repository: fmt.Sprintf("%s/%s", c.DockerMirror, repository),
|
||||
Tag: tag,
|
||||
ExposedPorts: []string{"6379/tcp"},
|
||||
Name: config.NetworkAlias,
|
||||
Networks: []*dockertest.Network{network},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = pool.Retry(func() error {
|
||||
cmd := exec.Command("docker", "exec", config.NetworkAlias, "redis-cli", "PING")
|
||||
output, cmdErr := cmd.CombinedOutput()
|
||||
if cmdErr != nil {
|
||||
return fmt.Errorf("failed to connect to Redis container '%s': %v\nOutput: %s", config.NetworkAlias, cmdErr, string(output))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err, "Redis container did not start in time or is not healthy")
|
||||
|
||||
err = setupRedisAuthAndUser(t, resource, pool, &config)
|
||||
require.NoError(t, err)
|
||||
|
||||
return &Container{
|
||||
Resource: resource,
|
||||
UriLocalhost: fmt.Sprintf(
|
||||
"redis://%s:%s@localhost:6379",
|
||||
config.User,
|
||||
config.Password,
|
||||
),
|
||||
UriNetwork: fmt.Sprintf(
|
||||
"redis://%s:%s@%s:6379",
|
||||
config.User,
|
||||
config.Password,
|
||||
config.NetworkAlias,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
// setupCassandraAuthAndUser enables authentication on a Cassandra container and creates a user with permissions.
|
||||
func setupCassandraAuthAndUser(t testing.TB, resource *dockertest.Resource, pool *dockertest.Pool, config *cassandraConfig) error {
|
||||
t.Helper()
|
||||
@@ -593,19 +530,3 @@ func setupCassandraAuthAndUser(t testing.TB, resource *dockertest.Resource, pool
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// setupRedisAuthAndUser configures a Redis container by creating a user with permissions.
|
||||
func setupRedisAuthAndUser(t testing.TB, resource *dockertest.Resource, pool *dockertest.Pool, config *redisConfig) error {
|
||||
t.Helper()
|
||||
t.Log("Configuring Redis authentication and user permissions...")
|
||||
|
||||
err := exec.Command(
|
||||
"docker", "exec", config.NetworkAlias, "redis-cli",
|
||||
"ACL", "SETUSER", config.User, "on", fmt.Sprintf(">%s", config.Password), "+@read", "+@write", "allkeys",
|
||||
).Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package infra
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ory/dockertest/v3"
|
||||
"github.com/ory/dockertest/v3/docker"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type RedisContainerInfo struct {
|
||||
Hostname string
|
||||
Port string
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
type redisConfig struct {
|
||||
User string
|
||||
Password string
|
||||
NetworkAlias string
|
||||
}
|
||||
|
||||
// SetupRedisContainer starts a Redis container and returns its connection info
|
||||
func SetupRedisContainer(t *testing.T) *RedisContainerInfo {
|
||||
pool, err := dockertest.NewPool("")
|
||||
require.NoError(t, err)
|
||||
|
||||
network, err := pool.NetworksByName("e2e_cluster")
|
||||
require.NoError(t, err, "Failed to get e2e_cluster network")
|
||||
|
||||
c := startRedis(t, pool, &network[0], "redis", "latest")
|
||||
require.NotNil(t, c, "Redis container should not be nil")
|
||||
t.Cleanup(func() {
|
||||
if err := pool.Purge(c.Resource); err != nil {
|
||||
t.Logf("Failed to purge Redis container: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
u, err := url.Parse(c.UriNetwork)
|
||||
t.Log(u)
|
||||
require.NoError(t, err, "Failed to parse Redis URL")
|
||||
|
||||
user, hostname, port := u.User.Username(), u.Hostname(), u.Port()
|
||||
pw, pwSet := u.User.Password()
|
||||
|
||||
t.Logf("Redis info: user=%s, host=%s, port=%s, password-set:%t",
|
||||
user, hostname, port, pwSet)
|
||||
|
||||
return &RedisContainerInfo{
|
||||
Hostname: hostname,
|
||||
Port: port,
|
||||
Username: user,
|
||||
Password: pw,
|
||||
}
|
||||
}
|
||||
|
||||
// startRedis starts a Redis database in a docker container.
|
||||
// Returns information about the container
|
||||
func startRedis(t testing.TB, pool *dockertest.Pool, network *dockertest.Network, repository, tag string) *Container {
|
||||
t.Log("Starting Redis database...")
|
||||
c, err := LoadConfig()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = pool.Client.PullImage(docker.PullImageOptions{
|
||||
Repository: fmt.Sprintf("%s/%s", c.DockerMirror, repository),
|
||||
Tag: tag,
|
||||
}, docker.AuthConfiguration{})
|
||||
require.NoError(t, err)
|
||||
|
||||
config := redisConfig{
|
||||
User: "e2eboundary",
|
||||
Password: "e2eboundary",
|
||||
NetworkAlias: "e2eredis",
|
||||
}
|
||||
|
||||
resource, err := pool.RunWithOptions(&dockertest.RunOptions{
|
||||
Repository: fmt.Sprintf("%s/%s", c.DockerMirror, repository),
|
||||
Tag: tag,
|
||||
ExposedPorts: []string{"6379/tcp"},
|
||||
Name: config.NetworkAlias,
|
||||
Networks: []*dockertest.Network{network},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = pool.Retry(func() error {
|
||||
cmd := exec.Command("docker", "exec", config.NetworkAlias, "redis-cli", "PING")
|
||||
output, cmdErr := cmd.CombinedOutput()
|
||||
if cmdErr != nil {
|
||||
return fmt.Errorf("failed to connect to Redis container '%s': %v\nOutput: %s", config.NetworkAlias, cmdErr, string(output))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err, "Redis container did not start in time or is not healthy")
|
||||
|
||||
// configure redis users
|
||||
// e2e user
|
||||
err = exec.Command(
|
||||
"docker", "exec", config.NetworkAlias, "redis-cli",
|
||||
"ACL", "SETUSER", config.User, "on", fmt.Sprintf(">%s", config.Password), "+@all", "allkeys",
|
||||
).Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
// default user
|
||||
err = exec.Command(
|
||||
"docker", "exec", config.NetworkAlias, "redis-cli",
|
||||
"ACL", "SETUSER", "default", fmt.Sprintf(">%s", config.Password),
|
||||
).Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
return &Container{
|
||||
Resource: resource,
|
||||
UriLocalhost: fmt.Sprintf(
|
||||
"redis://%s:%s@localhost:6379",
|
||||
config.User,
|
||||
config.Password,
|
||||
),
|
||||
UriNetwork: fmt.Sprintf(
|
||||
"redis://%s:%s@%s:6379",
|
||||
config.User,
|
||||
config.Password,
|
||||
config.NetworkAlias,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func SendRedisCommand(stdin io.WriteCloser, stdout io.ReadCloser, cmdStr string) (string, error) {
|
||||
_, err := stdin.Write([]byte(cmdStr))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf := make([]byte, 1024)
|
||||
n, err := stdout.Read(buf)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSpace(string(buf[:n])), nil
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package base_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os/exec"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/target"
|
||||
"github.com/hashicorp/boundary/testing/internal/e2e"
|
||||
"github.com/hashicorp/boundary/testing/internal/e2e/boundary"
|
||||
"github.com/hashicorp/boundary/testing/internal/e2e/infra"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestCliTcpTargetConnectRedisPassword uses the boundary cli to connect to a target
|
||||
// using `connect redis` and a password credential.
|
||||
func TestCliTcpTargetConnectRedisPassword(t *testing.T) {
|
||||
e2e.MaybeSkipTest(t)
|
||||
|
||||
// Setup
|
||||
ctx := context.Background()
|
||||
redisInfo := infra.SetupRedisContainer(t)
|
||||
boundary.AuthenticateAdminCli(t, ctx)
|
||||
|
||||
// Create Boundary resources
|
||||
orgId, err := boundary.CreateOrgCli(t, ctx)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
ctx := context.Background()
|
||||
boundary.AuthenticateAdminCli(t, ctx)
|
||||
output := e2e.RunCommand(ctx, "boundary", e2e.WithArgs("scopes", "delete", "-id", orgId))
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
})
|
||||
|
||||
projectId, err := boundary.CreateProjectCli(t, ctx, orgId)
|
||||
require.NoError(t, err)
|
||||
|
||||
targetId, err := boundary.CreateTargetCli(
|
||||
t,
|
||||
ctx,
|
||||
projectId,
|
||||
redisInfo.Port,
|
||||
[]target.Option{target.WithAddress(redisInfo.Hostname)},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
storeId, err := boundary.CreateCredentialStoreStaticCli(t, ctx, projectId)
|
||||
require.NoError(t, err)
|
||||
credentialId, err := boundary.CreateStaticCredentialPasswordCli(
|
||||
t,
|
||||
ctx,
|
||||
storeId,
|
||||
redisInfo.Password,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = boundary.AddBrokeredCredentialSourceToTargetCli(t, ctx, targetId, credentialId)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Validate redis connect
|
||||
cmd := exec.CommandContext(ctx,
|
||||
"boundary",
|
||||
"connect", "redis",
|
||||
"-target-id", targetId,
|
||||
)
|
||||
|
||||
stdin, err := cmd.StdinPipe()
|
||||
require.NoError(t, err)
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, cmd.Start())
|
||||
|
||||
output, err := infra.SendRedisCommand(stdin, stdout, "ACL WHOAMI\r\n")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "default", output) // redis default user
|
||||
|
||||
output, err = infra.SendRedisCommand(stdin, stdout, "SET e2etestkey e2etestvalue\r\n")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "OK", output)
|
||||
|
||||
output, err = infra.SendRedisCommand(stdin, stdout, "GET e2etestkey\r\n")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "e2etestvalue", output)
|
||||
|
||||
output, err = infra.SendRedisCommand(stdin, stdout, "QUIT\r\n")
|
||||
require.Equal(t, io.EOF, err)
|
||||
require.Empty(t, output)
|
||||
|
||||
// Confirm that boundary connect has closed
|
||||
err = cmd.Wait()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package base_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/url"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/target"
|
||||
"github.com/hashicorp/boundary/testing/internal/e2e"
|
||||
"github.com/hashicorp/boundary/testing/internal/e2e/boundary"
|
||||
"github.com/hashicorp/boundary/testing/internal/e2e/infra"
|
||||
"github.com/ory/dockertest/v3"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestCliTcpTargetConnectRedis uses the boundary cli to connect to a target using `connect redis`
|
||||
func TestCliTcpTargetConnectRedis(t *testing.T) {
|
||||
e2e.MaybeSkipTest(t)
|
||||
|
||||
pool, err := dockertest.NewPool("")
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
network, err := pool.NetworksByName("e2e_cluster")
|
||||
require.NoError(t, err, "Failed to get e2e_cluster network")
|
||||
|
||||
c := infra.StartRedis(t, pool, &network[0], "redis", "latest")
|
||||
require.NotNil(t, c, "Redis container should not be nil")
|
||||
t.Cleanup(func() {
|
||||
if err := pool.Purge(c.Resource); err != nil {
|
||||
t.Logf("Failed to purge Redis container: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
u, err := url.Parse(c.UriNetwork)
|
||||
t.Log(u)
|
||||
require.NoError(t, err, "Failed to parse Redis URL")
|
||||
|
||||
user, hostname, port := u.User.Username(), u.Hostname(), u.Port()
|
||||
pw, pwSet := u.User.Password()
|
||||
|
||||
t.Logf("Redis info: user=%s, host=%s, port=%s, password-set:%t",
|
||||
user, hostname, port, pwSet)
|
||||
|
||||
// Wait for Redis to be ready
|
||||
err = pool.Retry(func() error {
|
||||
out, e := exec.CommandContext(ctx, "docker", "exec", hostname,
|
||||
"redis-cli", "-h", hostname, "-p", port, "PING").CombinedOutput()
|
||||
t.Logf("Redis PING output: %s", out)
|
||||
return e
|
||||
})
|
||||
require.NoError(t, err, "Redis container failed to start")
|
||||
|
||||
boundary.AuthenticateAdminCli(t, ctx)
|
||||
|
||||
orgId, err := boundary.CreateOrgCli(t, ctx)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
ctx := context.Background()
|
||||
boundary.AuthenticateAdminCli(t, ctx)
|
||||
output := e2e.RunCommand(ctx, "boundary", e2e.WithArgs("scopes", "delete", "-id", orgId))
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
})
|
||||
|
||||
projectId, err := boundary.CreateProjectCli(t, ctx, orgId)
|
||||
require.NoError(t, err)
|
||||
|
||||
targetId, err := boundary.CreateTargetCli(
|
||||
t,
|
||||
ctx,
|
||||
projectId,
|
||||
port,
|
||||
[]target.Option{target.WithAddress(hostname)},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
storeId, err := boundary.CreateCredentialStoreStaticCli(t, ctx, projectId)
|
||||
require.NoError(t, err)
|
||||
|
||||
credentialId, err := boundary.CreateStaticCredentialUsernamePasswordCli(
|
||||
t,
|
||||
ctx,
|
||||
storeId,
|
||||
user,
|
||||
pw,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = boundary.AddBrokeredCredentialSourceToTargetCli(t, ctx, targetId, credentialId)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Logf("Attempting to connect to Redis target %s", targetId)
|
||||
|
||||
cmd := exec.CommandContext(ctx,
|
||||
"boundary",
|
||||
"connect", "redis",
|
||||
"-target-id", targetId,
|
||||
)
|
||||
|
||||
stdin, err := cmd.StdinPipe()
|
||||
require.NoError(t, err)
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, cmd.Start())
|
||||
|
||||
output, err := sendRedisCommand(stdin, stdout, "SET e2etestkey e2etestvalue\r\n")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "OK", output)
|
||||
|
||||
output, err = sendRedisCommand(stdin, stdout, "GET e2etestkey\r\n")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "e2etestvalue", output)
|
||||
|
||||
output, err = sendRedisCommand(stdin, stdout, "QUIT\r\n")
|
||||
require.Equal(t, io.EOF, err)
|
||||
require.Empty(t, output)
|
||||
|
||||
// Confirm that boundary connect has closed
|
||||
err = cmd.Wait()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func sendRedisCommand(stdin io.WriteCloser, stdout io.ReadCloser, cmdStr string) (string, error) {
|
||||
_, err := stdin.Write([]byte(cmdStr))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf := make([]byte, 1024)
|
||||
n, err := stdout.Read(buf)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSpace(string(buf[:n])), nil
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package base_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os/exec"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/target"
|
||||
"github.com/hashicorp/boundary/testing/internal/e2e"
|
||||
"github.com/hashicorp/boundary/testing/internal/e2e/boundary"
|
||||
"github.com/hashicorp/boundary/testing/internal/e2e/infra"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestCliTcpTargetConnectRedisUsernamePassword uses the boundary cli to connect to a target
|
||||
// using `connect redis` and a username-password credential.
|
||||
func TestCliTcpTargetConnectRedisUsernamePassword(t *testing.T) {
|
||||
e2e.MaybeSkipTest(t)
|
||||
|
||||
// Setup
|
||||
ctx := context.Background()
|
||||
redisInfo := infra.SetupRedisContainer(t)
|
||||
boundary.AuthenticateAdminCli(t, ctx)
|
||||
|
||||
// Create Boundary resources
|
||||
orgId, err := boundary.CreateOrgCli(t, ctx)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
ctx := context.Background()
|
||||
boundary.AuthenticateAdminCli(t, ctx)
|
||||
output := e2e.RunCommand(ctx, "boundary", e2e.WithArgs("scopes", "delete", "-id", orgId))
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
})
|
||||
|
||||
projectId, err := boundary.CreateProjectCli(t, ctx, orgId)
|
||||
require.NoError(t, err)
|
||||
|
||||
targetId, err := boundary.CreateTargetCli(
|
||||
t,
|
||||
ctx,
|
||||
projectId,
|
||||
redisInfo.Port,
|
||||
[]target.Option{target.WithAddress(redisInfo.Hostname)},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
storeId, err := boundary.CreateCredentialStoreStaticCli(t, ctx, projectId)
|
||||
require.NoError(t, err)
|
||||
credentialId, err := boundary.CreateStaticCredentialUsernamePasswordCli(
|
||||
t,
|
||||
ctx,
|
||||
storeId,
|
||||
redisInfo.Username,
|
||||
redisInfo.Password,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = boundary.AddBrokeredCredentialSourceToTargetCli(t, ctx, targetId, credentialId)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Validate redis connect
|
||||
cmd := exec.CommandContext(ctx,
|
||||
"boundary",
|
||||
"connect", "redis",
|
||||
"-target-id", targetId,
|
||||
)
|
||||
|
||||
stdin, err := cmd.StdinPipe()
|
||||
require.NoError(t, err)
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, cmd.Start())
|
||||
|
||||
output, err := infra.SendRedisCommand(stdin, stdout, "ACL WHOAMI\r\n")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, redisInfo.Username, output)
|
||||
|
||||
output, err = infra.SendRedisCommand(stdin, stdout, "SET e2etestkey e2etestvalue\r\n")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "OK", output)
|
||||
|
||||
output, err = infra.SendRedisCommand(stdin, stdout, "GET e2etestkey\r\n")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "e2etestvalue", output)
|
||||
|
||||
output, err = infra.SendRedisCommand(stdin, stdout, "QUIT\r\n")
|
||||
require.Equal(t, io.EOF, err)
|
||||
require.Empty(t, output)
|
||||
|
||||
// Confirm that boundary connect has closed
|
||||
err = cmd.Wait()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
Reference in new issue
Block a user