Merge pull request #2860 from hashicorp/llb-vault-ssh-cert-injection
Add vault ssh certificate credential library
This adds a new credential library type, `vault-ssh-certificate`, which
can be used with vault's ssh secret engine to generate certificates to
use as credentials for ssh. This new credential library can be created
using:
boundary credential-libraries create vault-ssh-certificate
See the help output for a full list of the options supported.
This also renames the `vault` credential library subtype to
`vault-generic`. The previous subtype of `vault` will continue to work,
thus the following commands are equivalent:
boundary credential-libraries create vault-generic
boundary credential-libraries create vault
See: https://developer.hashicorp.com/vault/docs/secrets/ssh
This commit is contained in:
69 files changed
+8784
-705
No files matched your search
@@ -69,7 +69,7 @@ jobs:
|
||||
echo "::set-output name=product-minor-version::$MINOR_VERSION"
|
||||
|
||||
verify-product-metadata:
|
||||
needs:
|
||||
needs:
|
||||
- set-product-version
|
||||
- product-metadata
|
||||
runs-on: ubuntu-latest
|
||||
@@ -422,7 +422,7 @@ jobs:
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: /tmp/bats-cli-ui-deps
|
||||
key: enos-bats-cli-ui-deps-jq-1.6-password-store-1.7.4
|
||||
key: enos-bats-cli-ui-deps-jq-1.6-password-store-1.7.4-vault-1.12.2
|
||||
- name: Set up Node for Bats install
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
@@ -453,6 +453,13 @@ jobs:
|
||||
cd /tmp/bats-cli-ui-deps/pass/password-store-1.7.4
|
||||
sudo make install
|
||||
pass init ${{ secrets.ENOS_GPG_UID }}
|
||||
- name: Download Vault AMD64 binary for integration testing
|
||||
if: steps.dep-cache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
wget https://releases.hashicorp.com/vault/1.12.2/vault_1.12.2_linux_amd64.zip -O /tmp/bats-cli-ui-deps/vault.zip
|
||||
- name: Install Vault
|
||||
run: |
|
||||
unzip /tmp/bats-cli-ui-deps/vault.zip -d /usr/local/bin
|
||||
- name: Download Linux AMD64 Boundary bundle
|
||||
id: download
|
||||
uses: actions/download-artifact@v3
|
||||
@@ -479,6 +486,16 @@ jobs:
|
||||
bash --version
|
||||
echo "boundary version:"
|
||||
boundary version
|
||||
echo "vault version:"
|
||||
vault version
|
||||
- name: Run cli bats tests
|
||||
run: |
|
||||
export VAULT_ADDR=http://127.0.0.1:8200
|
||||
export VAULT_TOKEN=boundarytok
|
||||
make -C internal/tests/cli test-vault-up
|
||||
until vault status; do docker container inspect boundary-cli-tests-vault &> /dev/null || exit -1; sleep 1; done
|
||||
make test-cli
|
||||
- name: Cleanup
|
||||
if: success() || failure()
|
||||
run: |
|
||||
make -C internal/tests/cli test-vault-down
|
||||
@@ -18,6 +18,22 @@ Canonical reference for changes, improvements, and bugfixes for Boundary.
|
||||
0.14.0, this will become an error instead. As a consequence, it means that the
|
||||
fallback logic for targets that did not have a default port defined is no
|
||||
longer in service; all targets must now have a default port defined.
|
||||
* With the introduction of `vault-ssh-certificate` credential libraries, the
|
||||
`vault` credential library subtype is being renamed to `vault-generic` to
|
||||
denote it as a credential library that can be used in a generalized way to
|
||||
issue credentials from vault. Existing credential libraries with the
|
||||
subtype of `vault` will be updated to `vault-generic`. The subtype of
|
||||
`vault` will still be accepted as a valid subtype in API requests to the
|
||||
credential libraries endpoints, but is deprecated. Instead `vault-generic`
|
||||
should be used. In addition the `boundary credential-libraries create
|
||||
vault` and `boundary credential-libraries update vault` subcommands will
|
||||
still function, but are deprecated. Instead `boundary credential-libraries
|
||||
create vault-generic` and `boundary credential-libraries update
|
||||
vault-generic` should be used. Also note that any credential library created
|
||||
using the subtype of `vault`, either via the API or via the deprecated
|
||||
subcommand, will have the subtype set to `vault-generic`. The deprecated
|
||||
subtype and subcommands will be removed in boundary 0.14.0, at which point
|
||||
`vault-generic` must be used.
|
||||
|
||||
### New and Improved
|
||||
|
||||
@@ -39,6 +55,10 @@ Canonical reference for changes, improvements, and bugfixes for Boundary.
|
||||
multiple workers together to access services hidden under layers of network security. Multi-hop
|
||||
workers can also establish a TCP session through multiple workers, with the ability to reverse
|
||||
proxy and establish a connection.
|
||||
* Vault SSH certificate credential library: A new credential library that uses
|
||||
the vault ssh secret engine to generate ssh private key and certificates. The
|
||||
library can be used as an injected application credential source for targets
|
||||
that support credential injection. ([PR](https://github.com/hashicorp/boundary/pull/2860))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ func (c *Client) ApiClient() *api.Client {
|
||||
return c.client
|
||||
}
|
||||
|
||||
func (c *Client) Create(ctx context.Context, credentialStoreId string, opt ...Option) (*CredentialLibraryCreateResult, error) {
|
||||
func (c *Client) Create(ctx context.Context, resourceType string, credentialStoreId string, opt ...Option) (*CredentialLibraryCreateResult, error) {
|
||||
if credentialStoreId == "" {
|
||||
return nil, fmt.Errorf("empty credentialStoreId value passed into Create request")
|
||||
}
|
||||
@@ -100,6 +100,11 @@ func (c *Client) Create(ctx context.Context, credentialStoreId string, opt ...Op
|
||||
if c.client == nil {
|
||||
return nil, fmt.Errorf("nil client")
|
||||
}
|
||||
if resourceType == "" {
|
||||
return nil, fmt.Errorf("empty resourceType value passed into Create request")
|
||||
} else {
|
||||
opts.postMap["type"] = resourceType
|
||||
}
|
||||
|
||||
opts.postMap["credential_store_id"] = credentialStoreId
|
||||
|
||||
|
||||
@@ -110,6 +110,30 @@ func DefaultCredentialType() Option {
|
||||
}
|
||||
}
|
||||
|
||||
func WithVaultSSHCertificateCredentialLibraryCriticalOptions(inCriticalOptions map[string]string) Option {
|
||||
return func(o *options) {
|
||||
raw, ok := o.postMap["attributes"]
|
||||
if !ok {
|
||||
raw = interface{}(map[string]interface{}{})
|
||||
}
|
||||
val := raw.(map[string]interface{})
|
||||
val["critical_options"] = inCriticalOptions
|
||||
o.postMap["attributes"] = val
|
||||
}
|
||||
}
|
||||
|
||||
func DefaultVaultSSHCertificateCredentialLibraryCriticalOptions() Option {
|
||||
return func(o *options) {
|
||||
raw, ok := o.postMap["attributes"]
|
||||
if !ok {
|
||||
raw = interface{}(map[string]interface{}{})
|
||||
}
|
||||
val := raw.(map[string]interface{})
|
||||
val["critical_options"] = nil
|
||||
o.postMap["attributes"] = val
|
||||
}
|
||||
}
|
||||
|
||||
func WithDescription(inDescription string) Option {
|
||||
return func(o *options) {
|
||||
o.postMap["description"] = inDescription
|
||||
@@ -122,6 +146,30 @@ func DefaultDescription() Option {
|
||||
}
|
||||
}
|
||||
|
||||
func WithVaultSSHCertificateCredentialLibraryExtensions(inExtensions map[string]string) Option {
|
||||
return func(o *options) {
|
||||
raw, ok := o.postMap["attributes"]
|
||||
if !ok {
|
||||
raw = interface{}(map[string]interface{}{})
|
||||
}
|
||||
val := raw.(map[string]interface{})
|
||||
val["extensions"] = inExtensions
|
||||
o.postMap["attributes"] = val
|
||||
}
|
||||
}
|
||||
|
||||
func DefaultVaultSSHCertificateCredentialLibraryExtensions() Option {
|
||||
return func(o *options) {
|
||||
raw, ok := o.postMap["attributes"]
|
||||
if !ok {
|
||||
raw = interface{}(map[string]interface{}{})
|
||||
}
|
||||
val := raw.(map[string]interface{})
|
||||
val["extensions"] = nil
|
||||
o.postMap["attributes"] = val
|
||||
}
|
||||
}
|
||||
|
||||
func WithVaultCredentialLibraryHttpMethod(inHttpMethod string) Option {
|
||||
return func(o *options) {
|
||||
raw, ok := o.postMap["attributes"]
|
||||
@@ -170,6 +218,78 @@ func DefaultVaultCredentialLibraryHttpRequestBody() Option {
|
||||
}
|
||||
}
|
||||
|
||||
func WithVaultSSHCertificateCredentialLibraryKeyBits(inKeyBits uint32) Option {
|
||||
return func(o *options) {
|
||||
raw, ok := o.postMap["attributes"]
|
||||
if !ok {
|
||||
raw = interface{}(map[string]interface{}{})
|
||||
}
|
||||
val := raw.(map[string]interface{})
|
||||
val["key_bits"] = inKeyBits
|
||||
o.postMap["attributes"] = val
|
||||
}
|
||||
}
|
||||
|
||||
func DefaultVaultSSHCertificateCredentialLibraryKeyBits() Option {
|
||||
return func(o *options) {
|
||||
raw, ok := o.postMap["attributes"]
|
||||
if !ok {
|
||||
raw = interface{}(map[string]interface{}{})
|
||||
}
|
||||
val := raw.(map[string]interface{})
|
||||
val["key_bits"] = nil
|
||||
o.postMap["attributes"] = val
|
||||
}
|
||||
}
|
||||
|
||||
func WithVaultSSHCertificateCredentialLibraryKeyId(inKeyId string) Option {
|
||||
return func(o *options) {
|
||||
raw, ok := o.postMap["attributes"]
|
||||
if !ok {
|
||||
raw = interface{}(map[string]interface{}{})
|
||||
}
|
||||
val := raw.(map[string]interface{})
|
||||
val["key_id"] = inKeyId
|
||||
o.postMap["attributes"] = val
|
||||
}
|
||||
}
|
||||
|
||||
func DefaultVaultSSHCertificateCredentialLibraryKeyId() Option {
|
||||
return func(o *options) {
|
||||
raw, ok := o.postMap["attributes"]
|
||||
if !ok {
|
||||
raw = interface{}(map[string]interface{}{})
|
||||
}
|
||||
val := raw.(map[string]interface{})
|
||||
val["key_id"] = nil
|
||||
o.postMap["attributes"] = val
|
||||
}
|
||||
}
|
||||
|
||||
func WithVaultSSHCertificateCredentialLibraryKeyType(inKeyType string) Option {
|
||||
return func(o *options) {
|
||||
raw, ok := o.postMap["attributes"]
|
||||
if !ok {
|
||||
raw = interface{}(map[string]interface{}{})
|
||||
}
|
||||
val := raw.(map[string]interface{})
|
||||
val["key_type"] = inKeyType
|
||||
o.postMap["attributes"] = val
|
||||
}
|
||||
}
|
||||
|
||||
func DefaultVaultSSHCertificateCredentialLibraryKeyType() Option {
|
||||
return func(o *options) {
|
||||
raw, ok := o.postMap["attributes"]
|
||||
if !ok {
|
||||
raw = interface{}(map[string]interface{}{})
|
||||
}
|
||||
val := raw.(map[string]interface{})
|
||||
val["key_type"] = nil
|
||||
o.postMap["attributes"] = val
|
||||
}
|
||||
}
|
||||
|
||||
func WithName(inName string) Option {
|
||||
return func(o *options) {
|
||||
o.postMap["name"] = inName
|
||||
@@ -193,3 +313,51 @@ func WithVaultCredentialLibraryPath(inPath string) Option {
|
||||
o.postMap["attributes"] = val
|
||||
}
|
||||
}
|
||||
|
||||
func WithVaultSSHCertificateCredentialLibraryPath(inPath string) Option {
|
||||
return func(o *options) {
|
||||
raw, ok := o.postMap["attributes"]
|
||||
if !ok {
|
||||
raw = interface{}(map[string]interface{}{})
|
||||
}
|
||||
val := raw.(map[string]interface{})
|
||||
val["path"] = inPath
|
||||
o.postMap["attributes"] = val
|
||||
}
|
||||
}
|
||||
|
||||
func WithVaultSSHCertificateCredentialLibraryTtl(inTtl string) Option {
|
||||
return func(o *options) {
|
||||
raw, ok := o.postMap["attributes"]
|
||||
if !ok {
|
||||
raw = interface{}(map[string]interface{}{})
|
||||
}
|
||||
val := raw.(map[string]interface{})
|
||||
val["ttl"] = inTtl
|
||||
o.postMap["attributes"] = val
|
||||
}
|
||||
}
|
||||
|
||||
func DefaultVaultSSHCertificateCredentialLibraryTtl() Option {
|
||||
return func(o *options) {
|
||||
raw, ok := o.postMap["attributes"]
|
||||
if !ok {
|
||||
raw = interface{}(map[string]interface{}{})
|
||||
}
|
||||
val := raw.(map[string]interface{})
|
||||
val["ttl"] = nil
|
||||
o.postMap["attributes"] = val
|
||||
}
|
||||
}
|
||||
|
||||
func WithVaultSSHCertificateCredentialLibraryUsername(inUsername string) Option {
|
||||
return func(o *options) {
|
||||
raw, ok := o.postMap["attributes"]
|
||||
if !ok {
|
||||
raw = interface{}(map[string]interface{}{})
|
||||
}
|
||||
val := raw.(map[string]interface{})
|
||||
val["username"] = inUsername
|
||||
o.postMap["attributes"] = val
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Code generated by "make api"; DO NOT EDIT.
|
||||
package credentiallibraries
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
type VaultSSHCertificateCredentialLibraryAttributes struct {
|
||||
Path string `json:"path,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
KeyType string `json:"key_type,omitempty"`
|
||||
KeyBits uint32 `json:"key_bits,omitempty"`
|
||||
Ttl string `json:"ttl,omitempty"`
|
||||
KeyId string `json:"key_id,omitempty"`
|
||||
CriticalOptions map[string]string `json:"critical_options,omitempty"`
|
||||
Extensions map[string]string `json:"extensions,omitempty"`
|
||||
}
|
||||
|
||||
func AttributesMapToVaultSSHCertificateCredentialLibraryAttributes(in map[string]interface{}) (*VaultSSHCertificateCredentialLibraryAttributes, error) {
|
||||
if in == nil {
|
||||
return nil, fmt.Errorf("nil input map")
|
||||
}
|
||||
var out VaultSSHCertificateCredentialLibraryAttributes
|
||||
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 *CredentialLibrary) GetVaultSSHCertificateCredentialLibraryAttributes() (*VaultSSHCertificateCredentialLibraryAttributes, error) {
|
||||
if pt.Type != "vaultsshcertificate" {
|
||||
return nil, fmt.Errorf("asked to fetch %s-type attributes but credential-library is of type %s", "vaultsshcertificate", pt.Type)
|
||||
}
|
||||
return AttributesMapToVaultSSHCertificateCredentialLibraryAttributes(pt.Attributes)
|
||||
}
|
||||
@@ -161,6 +161,7 @@ require (
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
||||
github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.0.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
|
||||
@@ -980,6 +980,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182aff
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a h1:eU8j/ClY2Ty3qdHnn0TyW3ivFoPC/0F1gQZz8yTxbbE=
|
||||
github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a/go.mod h1:v8eSC2SMp9/7FTKUncp7fH9IwPfw+ysMObcEz5FWheQ=
|
||||
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/cli v1.1.2 h1:PvH+lL2B7IQ101xQL63Of8yFS2y+aDlsFcsqNc+u/Kw=
|
||||
|
||||
@@ -518,12 +518,58 @@ var inputStructs = []*structInfo{
|
||||
mapstructureConversionTemplate,
|
||||
},
|
||||
},
|
||||
{
|
||||
inProto: &credentiallibraries.VaultSSHCertificateCredentialLibraryAttributes{},
|
||||
outFile: "credentiallibraries/vault_ssh_certificate_credential_library_attributes.gen.go",
|
||||
subtypeName: "VaultSSHCertificateCredentialLibrary",
|
||||
fieldOverrides: []fieldInfo{
|
||||
{
|
||||
Name: "Path",
|
||||
SkipDefault: true,
|
||||
},
|
||||
{
|
||||
Name: "Username",
|
||||
SkipDefault: true,
|
||||
},
|
||||
{
|
||||
Name: "CriticalOptions",
|
||||
FieldType: "map[string]string",
|
||||
},
|
||||
{
|
||||
Name: "Extensions",
|
||||
FieldType: "map[string]string",
|
||||
},
|
||||
},
|
||||
parentTypeName: "CredentialLibrary",
|
||||
templates: []*template.Template{
|
||||
mapstructureConversionTemplate,
|
||||
},
|
||||
},
|
||||
{
|
||||
inProto: &credentiallibraries.CredentialLibrary{},
|
||||
outFile: "credentiallibraries/credential_library.gen.go",
|
||||
templates: []*template.Template{
|
||||
clientTemplate,
|
||||
commonCreateTemplate,
|
||||
template.Must(template.New("").Funcs(
|
||||
template.FuncMap{
|
||||
"snakeCase": snakeCase,
|
||||
"funcName": func() string {
|
||||
return "Create"
|
||||
},
|
||||
"apiAction": func() string {
|
||||
return ""
|
||||
},
|
||||
"extraRequiredParams": func() []requiredParam {
|
||||
return []requiredParam{
|
||||
{
|
||||
Name: "resourceType",
|
||||
Typ: "string",
|
||||
PostType: "type",
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
).Parse(createTemplateStr)),
|
||||
readTemplate,
|
||||
updateTemplate,
|
||||
deleteTemplate,
|
||||
|
||||
@@ -364,6 +364,18 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
||||
Func: "create",
|
||||
}, nil
|
||||
},
|
||||
"credential-libraries create vault-generic": func() (cli.Command, error) {
|
||||
return &credentiallibrariescmd.VaultGenericCommand{
|
||||
Command: base.NewCommand(ui),
|
||||
Func: "create",
|
||||
}, nil
|
||||
},
|
||||
"credential-libraries create vault-ssh-certificate": func() (cli.Command, error) {
|
||||
return &credentiallibrariescmd.VaultSshCertificateCommand{
|
||||
Command: base.NewCommand(ui),
|
||||
Func: "create",
|
||||
}, nil
|
||||
},
|
||||
"credential-libraries update": func() (cli.Command, error) {
|
||||
return &credentiallibrariescmd.Command{
|
||||
Command: base.NewCommand(ui),
|
||||
@@ -371,7 +383,19 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
||||
}, nil
|
||||
},
|
||||
"credential-libraries update vault": func() (cli.Command, error) {
|
||||
return &credentiallibrariescmd.VaultCommand{
|
||||
return &credentiallibrariescmd.VaultGenericCommand{
|
||||
Command: base.NewCommand(ui),
|
||||
Func: "update",
|
||||
}, nil
|
||||
},
|
||||
"credential-libraries update vault-generic": func() (cli.Command, error) {
|
||||
return &credentiallibrariescmd.VaultGenericCommand{
|
||||
Command: base.NewCommand(ui),
|
||||
Func: "update",
|
||||
}, nil
|
||||
},
|
||||
"credential-libraries update vault-ssh-certificate": func() (cli.Command, error) {
|
||||
return &credentiallibrariescmd.VaultSshCertificateCommand{
|
||||
Command: base.NewCommand(ui),
|
||||
Func: "update",
|
||||
}, nil
|
||||
|
||||
@@ -140,6 +140,16 @@ func printItemTable(item *credentiallibraries.CredentialLibrary, resp *api.Respo
|
||||
nonAttributeMap["Type"] = item.Type
|
||||
}
|
||||
|
||||
var keySubstMap map[string]string
|
||||
switch item.Type {
|
||||
case "vault":
|
||||
fallthrough
|
||||
case "vault-generic":
|
||||
keySubstMap = genericKeySubstMap
|
||||
case "vault-ssh-certificate":
|
||||
keySubstMap = sshCertKeySubstMap
|
||||
}
|
||||
|
||||
maxLength := base.MaxAttributesLength(nonAttributeMap, item.Attributes, keySubstMap)
|
||||
|
||||
ret := []string{
|
||||
@@ -189,8 +199,19 @@ func printItemTable(item *credentiallibraries.CredentialLibrary, resp *api.Respo
|
||||
return base.WrapForHelpText(ret)
|
||||
}
|
||||
|
||||
var keySubstMap = map[string]string{
|
||||
var genericKeySubstMap = map[string]string{
|
||||
"path": "Path",
|
||||
"http_method": "HTTP Method",
|
||||
"http_request_body": "HTTP Request Body",
|
||||
}
|
||||
|
||||
var sshCertKeySubstMap = map[string]string{
|
||||
"path": "Path",
|
||||
"username": "Username",
|
||||
"key_type": "Key Type",
|
||||
"key_bits": "Key Bits",
|
||||
"ttl": "TTL",
|
||||
"key_id": "Key ID",
|
||||
"critical_options": "Critical Options",
|
||||
"extensions": "Extensions",
|
||||
}
|
||||
+275
@@ -0,0 +1,275 @@
|
||||
// Code generated by "make cli"; DO NOT EDIT.
|
||||
package credentiallibrariescmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/api"
|
||||
"github.com/hashicorp/boundary/api/credentiallibraries"
|
||||
"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 initVaultGenericFlags() {
|
||||
flagsOnce.Do(func() {
|
||||
extraFlags := extraVaultGenericActionsFlagsMapFunc()
|
||||
for k, v := range extraFlags {
|
||||
flagsVaultGenericMap[k] = append(flagsVaultGenericMap[k], v...)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
var (
|
||||
_ cli.Command = (*VaultGenericCommand)(nil)
|
||||
_ cli.CommandAutocomplete = (*VaultGenericCommand)(nil)
|
||||
)
|
||||
|
||||
type VaultGenericCommand struct {
|
||||
*base.Command
|
||||
|
||||
Func string
|
||||
|
||||
plural string
|
||||
|
||||
extraVaultGenericCmdVars
|
||||
}
|
||||
|
||||
func (c *VaultGenericCommand) AutocompleteArgs() complete.Predictor {
|
||||
initVaultGenericFlags()
|
||||
return complete.PredictAnything
|
||||
}
|
||||
|
||||
func (c *VaultGenericCommand) AutocompleteFlags() complete.Flags {
|
||||
initVaultGenericFlags()
|
||||
return c.Flags().Completions()
|
||||
}
|
||||
|
||||
func (c *VaultGenericCommand) Synopsis() string {
|
||||
if extra := extraVaultGenericSynopsisFunc(c); extra != "" {
|
||||
return extra
|
||||
}
|
||||
|
||||
synopsisStr := "credential library"
|
||||
|
||||
synopsisStr = fmt.Sprintf("%s %s", "vault-generic-type", synopsisStr)
|
||||
|
||||
return common.SynopsisFunc(c.Func, synopsisStr)
|
||||
}
|
||||
|
||||
func (c *VaultGenericCommand) Help() string {
|
||||
initVaultGenericFlags()
|
||||
|
||||
var helpStr string
|
||||
helpMap := common.HelpMap("credential library")
|
||||
|
||||
switch c.Func {
|
||||
|
||||
default:
|
||||
|
||||
helpStr = c.extraVaultGenericHelpFunc(helpMap)
|
||||
|
||||
}
|
||||
|
||||
// Keep linter from complaining if we don't actually generate code using it
|
||||
_ = helpMap
|
||||
return helpStr
|
||||
}
|
||||
|
||||
var flagsVaultGenericMap = map[string][]string{
|
||||
|
||||
"create": {"credential-store-id", "name", "description"},
|
||||
|
||||
"update": {"id", "name", "description", "version"},
|
||||
}
|
||||
|
||||
func (c *VaultGenericCommand) Flags() *base.FlagSets {
|
||||
if len(flagsVaultGenericMap[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, "vault-generic-type credential library", flagsVaultGenericMap, c.Func)
|
||||
|
||||
extraVaultGenericFlagsFunc(c, set, f)
|
||||
|
||||
return set
|
||||
}
|
||||
|
||||
func (c *VaultGenericCommand) Run(args []string) int {
|
||||
initVaultGenericFlags()
|
||||
|
||||
switch c.Func {
|
||||
case "":
|
||||
return cli.RunResultHelp
|
||||
|
||||
}
|
||||
|
||||
c.plural = "vault-generic-type credential library"
|
||||
switch c.Func {
|
||||
case "list":
|
||||
c.plural = "vault-generic-type credential librarys"
|
||||
}
|
||||
|
||||
f := c.Flags()
|
||||
|
||||
if err := f.Parse(args); err != nil {
|
||||
c.PrintCliError(err)
|
||||
return base.CommandUserError
|
||||
}
|
||||
|
||||
if strutil.StrListContains(flagsVaultGenericMap[c.Func], "id") && c.FlagId == "" {
|
||||
c.PrintCliError(errors.New("ID is required but not passed in via -id"))
|
||||
return base.CommandUserError
|
||||
}
|
||||
|
||||
var opts []credentiallibraries.Option
|
||||
|
||||
if strutil.StrListContains(flagsVaultGenericMap[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
|
||||
}
|
||||
credentiallibrariesClient := credentiallibraries.NewClient(client)
|
||||
|
||||
switch c.FlagName {
|
||||
case "":
|
||||
case "null":
|
||||
opts = append(opts, credentiallibraries.DefaultName())
|
||||
default:
|
||||
opts = append(opts, credentiallibraries.WithName(c.FlagName))
|
||||
}
|
||||
|
||||
switch c.FlagDescription {
|
||||
case "":
|
||||
case "null":
|
||||
opts = append(opts, credentiallibraries.DefaultDescription())
|
||||
default:
|
||||
opts = append(opts, credentiallibraries.WithDescription(c.FlagDescription))
|
||||
}
|
||||
|
||||
if c.FlagFilter != "" {
|
||||
opts = append(opts, credentiallibraries.WithFilter(c.FlagFilter))
|
||||
}
|
||||
|
||||
var version uint32
|
||||
|
||||
switch c.Func {
|
||||
|
||||
case "update":
|
||||
switch c.FlagVersion {
|
||||
case 0:
|
||||
opts = append(opts, credentiallibraries.WithAutomaticVersioning(true))
|
||||
default:
|
||||
version = uint32(c.FlagVersion)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ok := extraVaultGenericFlagsHandlingFunc(c, f, &opts); !ok {
|
||||
return base.CommandUserError
|
||||
}
|
||||
|
||||
var resp *api.Response
|
||||
var item *credentiallibraries.CredentialLibrary
|
||||
|
||||
var createResult *credentiallibraries.CredentialLibraryCreateResult
|
||||
|
||||
var updateResult *credentiallibraries.CredentialLibraryUpdateResult
|
||||
|
||||
switch c.Func {
|
||||
|
||||
case "create":
|
||||
createResult, err = credentiallibrariesClient.Create(c.Context, "vault-generic", c.FlagCredentialStoreId, opts...)
|
||||
if exitCode := c.checkFuncError(err); exitCode > 0 {
|
||||
return exitCode
|
||||
}
|
||||
resp = createResult.GetResponse()
|
||||
item = createResult.GetItem()
|
||||
|
||||
case "update":
|
||||
updateResult, err = credentiallibrariesClient.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 = executeExtraVaultGenericActions(c, resp, item, err, credentiallibrariesClient, version, opts)
|
||||
if exitCode := c.checkFuncError(err); exitCode > 0 {
|
||||
return exitCode
|
||||
}
|
||||
|
||||
output, err := printCustomVaultGenericActionOutput(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 *VaultGenericCommand) 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 (
|
||||
extraVaultGenericActionsFlagsMapFunc = func() map[string][]string { return nil }
|
||||
extraVaultGenericSynopsisFunc = func(*VaultGenericCommand) string { return "" }
|
||||
extraVaultGenericFlagsFunc = func(*VaultGenericCommand, *base.FlagSets, *base.FlagSet) {}
|
||||
extraVaultGenericFlagsHandlingFunc = func(*VaultGenericCommand, *base.FlagSets, *[]credentiallibraries.Option) bool { return true }
|
||||
executeExtraVaultGenericActions = func(_ *VaultGenericCommand, inResp *api.Response, inItem *credentiallibraries.CredentialLibrary, inErr error, _ *credentiallibraries.Client, _ uint32, _ []credentiallibraries.Option) (*api.Response, *credentiallibraries.CredentialLibrary, error) {
|
||||
return inResp, inItem, inErr
|
||||
}
|
||||
printCustomVaultGenericActionOutput = func(*VaultGenericCommand) (bool, error) { return false, nil }
|
||||
)
|
||||
@@ -0,0 +1,174 @@
|
||||
package credentiallibrariescmd
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/boundary/api/credentiallibraries"
|
||||
"github.com/hashicorp/boundary/internal/cmd/base"
|
||||
"github.com/hashicorp/go-secure-stdlib/parseutil"
|
||||
)
|
||||
|
||||
func init() {
|
||||
extraVaultGenericFlagsFunc = extraVaultGenericFlagsFuncImpl
|
||||
extraVaultGenericActionsFlagsMapFunc = extraVaultGenericActionsFlagsMapFuncImpl
|
||||
extraVaultGenericFlagsHandlingFunc = extraVaultGenericFlagHandlingFuncImpl
|
||||
}
|
||||
|
||||
const (
|
||||
pathFlagName = "vault-path"
|
||||
httpMethodFlagName = "vault-http-method"
|
||||
httpRequestBodyFlagName = "vault-http-request-body"
|
||||
credentialTypeFlagName = "credential-type"
|
||||
credentialMappingFlagName = "credential-mapping-override"
|
||||
)
|
||||
|
||||
type extraVaultGenericCmdVars struct {
|
||||
flagPath string
|
||||
flagHttpMethod string
|
||||
flagHttpRequestBody string
|
||||
flagCredentialType string
|
||||
flagCredentialMapping []base.CombinedSliceFlagValue
|
||||
}
|
||||
|
||||
func extraVaultGenericActionsFlagsMapFuncImpl() map[string][]string {
|
||||
flags := map[string][]string{
|
||||
"create": {
|
||||
pathFlagName,
|
||||
httpMethodFlagName,
|
||||
httpRequestBodyFlagName,
|
||||
credentialTypeFlagName,
|
||||
credentialMappingFlagName,
|
||||
},
|
||||
"update": {
|
||||
pathFlagName,
|
||||
httpMethodFlagName,
|
||||
httpRequestBodyFlagName,
|
||||
credentialMappingFlagName,
|
||||
},
|
||||
}
|
||||
return flags
|
||||
}
|
||||
|
||||
func extraVaultGenericFlagsFuncImpl(c *VaultGenericCommand, set *base.FlagSets, _ *base.FlagSet) {
|
||||
f := set.NewFlagSet("Vault Credential Library Options")
|
||||
|
||||
for _, name := range flagsVaultGenericMap[c.Func] {
|
||||
switch name {
|
||||
case pathFlagName:
|
||||
f.StringVar(&base.StringVar{
|
||||
Name: pathFlagName,
|
||||
Target: &c.flagPath,
|
||||
Usage: "The path in vault to request credentials from.",
|
||||
})
|
||||
case httpMethodFlagName:
|
||||
f.StringVar(&base.StringVar{
|
||||
Name: httpMethodFlagName,
|
||||
Target: &c.flagHttpMethod,
|
||||
Usage: "The http method the library should use when communicating with vault.",
|
||||
})
|
||||
case httpRequestBodyFlagName:
|
||||
f.StringVar(&base.StringVar{
|
||||
Name: httpRequestBodyFlagName,
|
||||
Target: &c.flagHttpRequestBody,
|
||||
Usage: "The http request body the library uses to communicate with vault. This can be the value itself, refer to a file on disk (file://) from which the value will be read, or an env var (env://) from which the value will be read.",
|
||||
})
|
||||
case credentialTypeFlagName:
|
||||
f.StringVar(&base.StringVar{
|
||||
Name: credentialTypeFlagName,
|
||||
Target: &c.flagCredentialType,
|
||||
Usage: "The type of credential this library will issue, defaults to Unspecified.",
|
||||
})
|
||||
case credentialMappingFlagName:
|
||||
f.CombinationSliceVar(&base.CombinationSliceVar{
|
||||
Name: credentialMappingFlagName,
|
||||
Target: &c.flagCredentialMapping,
|
||||
KvSplit: true,
|
||||
Usage: "The credential mapping override.",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func extraVaultGenericFlagHandlingFuncImpl(c *VaultGenericCommand, _ *base.FlagSets, opts *[]credentiallibraries.Option) bool {
|
||||
switch c.flagPath {
|
||||
case "":
|
||||
default:
|
||||
*opts = append(*opts, credentiallibraries.WithVaultCredentialLibraryPath(c.flagPath))
|
||||
}
|
||||
switch c.flagHttpMethod {
|
||||
case "":
|
||||
case "null":
|
||||
*opts = append(*opts, credentiallibraries.DefaultVaultCredentialLibraryHttpMethod())
|
||||
default:
|
||||
*opts = append(*opts, credentiallibraries.WithVaultCredentialLibraryHttpMethod(c.flagHttpMethod))
|
||||
}
|
||||
switch c.flagHttpRequestBody {
|
||||
case "":
|
||||
case "null":
|
||||
*opts = append(*opts, credentiallibraries.DefaultVaultCredentialLibraryHttpRequestBody())
|
||||
default:
|
||||
rb, _ := parseutil.ParsePath(c.flagHttpRequestBody)
|
||||
*opts = append(*opts, credentiallibraries.WithVaultCredentialLibraryHttpRequestBody(rb))
|
||||
}
|
||||
switch c.flagCredentialType {
|
||||
case "":
|
||||
case "null":
|
||||
*opts = append(*opts, credentiallibraries.DefaultCredentialType())
|
||||
default:
|
||||
*opts = append(*opts, credentiallibraries.WithCredentialType(c.flagCredentialType))
|
||||
}
|
||||
switch len(c.flagCredentialMapping) {
|
||||
case 0:
|
||||
case 1:
|
||||
if len(c.flagCredentialMapping[0].Keys) == 1 && c.flagCredentialMapping[0].Keys[0] == "null" && c.flagCredentialMapping[0].Value == nil {
|
||||
*opts = append(*opts, credentiallibraries.DefaultCredentialMappingOverrides())
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
mappings := make(map[string]any, len(c.flagCredentialMapping))
|
||||
for _, mapping := range c.flagCredentialMapping {
|
||||
switch {
|
||||
case len(mapping.Keys) != 1 || mapping.Keys[0] == "" || mapping.Value == nil || mapping.Value.GetValue() == "":
|
||||
// mapping override does not support key segments (e.g. 'x.y=z')
|
||||
c.UI.Error("Credential mapping override must be in the format 'key=value', 'key=null' to clear field or 'null' to clear all.")
|
||||
return false
|
||||
case mapping.Value.GetValue() == "null":
|
||||
// user provided 'key=null' indicating the field specific override should
|
||||
// be cleared, set map value to nil
|
||||
mappings[mapping.Keys[0]] = nil
|
||||
default:
|
||||
mappings[mapping.Keys[0]] = mapping.Value.GetValue()
|
||||
}
|
||||
}
|
||||
*opts = append(*opts, credentiallibraries.WithCredentialMappingOverrides(mappings))
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *VaultGenericCommand) extraVaultGenericHelpFunc(_ map[string]func() string) string {
|
||||
var helpStr string
|
||||
switch c.Func {
|
||||
case "create":
|
||||
helpStr = base.WrapForHelpText([]string{
|
||||
"Usage: boundary credential-libraries create vault -credential-store-id [options] [args]",
|
||||
"",
|
||||
" Create a vault-generic-type credential library. Example:",
|
||||
"",
|
||||
` $ boundary credential-libraries create vault-generic -credential-store-id csvlt_1234567890 -vault-path "/some/path"`,
|
||||
"",
|
||||
"",
|
||||
})
|
||||
|
||||
case "update":
|
||||
helpStr = base.WrapForHelpText([]string{
|
||||
"Usage: boundary credential-libraries update vault [options] [args]",
|
||||
"",
|
||||
" Update a vault-generic-type credential library given its ID. Example:",
|
||||
"",
|
||||
` $ boundary credential-libraries update vault-generic -id clvlt_1234567890 -name devops -description "For DevOps usage"`,
|
||||
"",
|
||||
"",
|
||||
})
|
||||
}
|
||||
return helpStr + c.Flags().Help()
|
||||
}
|
||||
+275
@@ -0,0 +1,275 @@
|
||||
// Code generated by "make cli"; DO NOT EDIT.
|
||||
package credentiallibrariescmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/api"
|
||||
"github.com/hashicorp/boundary/api/credentiallibraries"
|
||||
"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 initVaultSshCertificateFlags() {
|
||||
flagsOnce.Do(func() {
|
||||
extraFlags := extraVaultSshCertificateActionsFlagsMapFunc()
|
||||
for k, v := range extraFlags {
|
||||
flagsVaultSshCertificateMap[k] = append(flagsVaultSshCertificateMap[k], v...)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
var (
|
||||
_ cli.Command = (*VaultSshCertificateCommand)(nil)
|
||||
_ cli.CommandAutocomplete = (*VaultSshCertificateCommand)(nil)
|
||||
)
|
||||
|
||||
type VaultSshCertificateCommand struct {
|
||||
*base.Command
|
||||
|
||||
Func string
|
||||
|
||||
plural string
|
||||
|
||||
extraVaultSshCertificateCmdVars
|
||||
}
|
||||
|
||||
func (c *VaultSshCertificateCommand) AutocompleteArgs() complete.Predictor {
|
||||
initVaultSshCertificateFlags()
|
||||
return complete.PredictAnything
|
||||
}
|
||||
|
||||
func (c *VaultSshCertificateCommand) AutocompleteFlags() complete.Flags {
|
||||
initVaultSshCertificateFlags()
|
||||
return c.Flags().Completions()
|
||||
}
|
||||
|
||||
func (c *VaultSshCertificateCommand) Synopsis() string {
|
||||
if extra := extraVaultSshCertificateSynopsisFunc(c); extra != "" {
|
||||
return extra
|
||||
}
|
||||
|
||||
synopsisStr := "credential library"
|
||||
|
||||
synopsisStr = fmt.Sprintf("%s %s", "vault-ssh-certificate-type", synopsisStr)
|
||||
|
||||
return common.SynopsisFunc(c.Func, synopsisStr)
|
||||
}
|
||||
|
||||
func (c *VaultSshCertificateCommand) Help() string {
|
||||
initVaultSshCertificateFlags()
|
||||
|
||||
var helpStr string
|
||||
helpMap := common.HelpMap("credential library")
|
||||
|
||||
switch c.Func {
|
||||
|
||||
default:
|
||||
|
||||
helpStr = c.extraVaultSshCertificateHelpFunc(helpMap)
|
||||
|
||||
}
|
||||
|
||||
// Keep linter from complaining if we don't actually generate code using it
|
||||
_ = helpMap
|
||||
return helpStr
|
||||
}
|
||||
|
||||
var flagsVaultSshCertificateMap = map[string][]string{
|
||||
|
||||
"create": {"credential-store-id", "name", "description"},
|
||||
|
||||
"update": {"id", "name", "description", "version"},
|
||||
}
|
||||
|
||||
func (c *VaultSshCertificateCommand) Flags() *base.FlagSets {
|
||||
if len(flagsVaultSshCertificateMap[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, "vault-ssh-certificate-type credential library", flagsVaultSshCertificateMap, c.Func)
|
||||
|
||||
extraVaultSshCertificateFlagsFunc(c, set, f)
|
||||
|
||||
return set
|
||||
}
|
||||
|
||||
func (c *VaultSshCertificateCommand) Run(args []string) int {
|
||||
initVaultSshCertificateFlags()
|
||||
|
||||
switch c.Func {
|
||||
case "":
|
||||
return cli.RunResultHelp
|
||||
|
||||
}
|
||||
|
||||
c.plural = "vault-ssh-certificate-type credential library"
|
||||
switch c.Func {
|
||||
case "list":
|
||||
c.plural = "vault-ssh-certificate-type credential librarys"
|
||||
}
|
||||
|
||||
f := c.Flags()
|
||||
|
||||
if err := f.Parse(args); err != nil {
|
||||
c.PrintCliError(err)
|
||||
return base.CommandUserError
|
||||
}
|
||||
|
||||
if strutil.StrListContains(flagsVaultSshCertificateMap[c.Func], "id") && c.FlagId == "" {
|
||||
c.PrintCliError(errors.New("ID is required but not passed in via -id"))
|
||||
return base.CommandUserError
|
||||
}
|
||||
|
||||
var opts []credentiallibraries.Option
|
||||
|
||||
if strutil.StrListContains(flagsVaultSshCertificateMap[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
|
||||
}
|
||||
credentiallibrariesClient := credentiallibraries.NewClient(client)
|
||||
|
||||
switch c.FlagName {
|
||||
case "":
|
||||
case "null":
|
||||
opts = append(opts, credentiallibraries.DefaultName())
|
||||
default:
|
||||
opts = append(opts, credentiallibraries.WithName(c.FlagName))
|
||||
}
|
||||
|
||||
switch c.FlagDescription {
|
||||
case "":
|
||||
case "null":
|
||||
opts = append(opts, credentiallibraries.DefaultDescription())
|
||||
default:
|
||||
opts = append(opts, credentiallibraries.WithDescription(c.FlagDescription))
|
||||
}
|
||||
|
||||
if c.FlagFilter != "" {
|
||||
opts = append(opts, credentiallibraries.WithFilter(c.FlagFilter))
|
||||
}
|
||||
|
||||
var version uint32
|
||||
|
||||
switch c.Func {
|
||||
|
||||
case "update":
|
||||
switch c.FlagVersion {
|
||||
case 0:
|
||||
opts = append(opts, credentiallibraries.WithAutomaticVersioning(true))
|
||||
default:
|
||||
version = uint32(c.FlagVersion)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ok := extraVaultSshCertificateFlagsHandlingFunc(c, f, &opts); !ok {
|
||||
return base.CommandUserError
|
||||
}
|
||||
|
||||
var resp *api.Response
|
||||
var item *credentiallibraries.CredentialLibrary
|
||||
|
||||
var createResult *credentiallibraries.CredentialLibraryCreateResult
|
||||
|
||||
var updateResult *credentiallibraries.CredentialLibraryUpdateResult
|
||||
|
||||
switch c.Func {
|
||||
|
||||
case "create":
|
||||
createResult, err = credentiallibrariesClient.Create(c.Context, "vault-ssh-certificate", c.FlagCredentialStoreId, opts...)
|
||||
if exitCode := c.checkFuncError(err); exitCode > 0 {
|
||||
return exitCode
|
||||
}
|
||||
resp = createResult.GetResponse()
|
||||
item = createResult.GetItem()
|
||||
|
||||
case "update":
|
||||
updateResult, err = credentiallibrariesClient.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 = executeExtraVaultSshCertificateActions(c, resp, item, err, credentiallibrariesClient, version, opts)
|
||||
if exitCode := c.checkFuncError(err); exitCode > 0 {
|
||||
return exitCode
|
||||
}
|
||||
|
||||
output, err := printCustomVaultSshCertificateActionOutput(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 *VaultSshCertificateCommand) 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 (
|
||||
extraVaultSshCertificateActionsFlagsMapFunc = func() map[string][]string { return nil }
|
||||
extraVaultSshCertificateSynopsisFunc = func(*VaultSshCertificateCommand) string { return "" }
|
||||
extraVaultSshCertificateFlagsFunc = func(*VaultSshCertificateCommand, *base.FlagSets, *base.FlagSet) {}
|
||||
extraVaultSshCertificateFlagsHandlingFunc = func(*VaultSshCertificateCommand, *base.FlagSets, *[]credentiallibraries.Option) bool { return true }
|
||||
executeExtraVaultSshCertificateActions = func(_ *VaultSshCertificateCommand, inResp *api.Response, inItem *credentiallibraries.CredentialLibrary, inErr error, _ *credentiallibraries.Client, _ uint32, _ []credentiallibraries.Option) (*api.Response, *credentiallibraries.CredentialLibrary, error) {
|
||||
return inResp, inItem, inErr
|
||||
}
|
||||
printCustomVaultSshCertificateActionOutput = func(*VaultSshCertificateCommand) (bool, error) { return false, nil }
|
||||
)
|
||||
@@ -0,0 +1,264 @@
|
||||
package credentiallibrariescmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/hashicorp/boundary/api/credentiallibraries"
|
||||
"github.com/hashicorp/boundary/internal/cmd/base"
|
||||
"github.com/hashicorp/boundary/internal/cmd/common"
|
||||
)
|
||||
|
||||
func init() {
|
||||
extraVaultSshCertificateFlagsFunc = extraVaultSshCertificateFlagsFuncImpl
|
||||
extraVaultSshCertificateActionsFlagsMapFunc = extraVaultSshCertificateActionsFlagsMapFuncImpl
|
||||
extraVaultSshCertificateFlagsHandlingFunc = extraVaultSshCertificateFlagHandlingFuncImpl
|
||||
}
|
||||
|
||||
const (
|
||||
usernameName = "username"
|
||||
keyTypeName = "key-type"
|
||||
keyBitsName = "key-bits"
|
||||
ttlName = "ttl"
|
||||
keyIdName = "key-id"
|
||||
criticalOptionsName = "critical-options"
|
||||
piecewiseCriticalOptionsName = "critical-option"
|
||||
extensionsName = "extensions"
|
||||
piecewiseExtensionName = "extension"
|
||||
)
|
||||
|
||||
type extraVaultSshCertificateCmdVars struct {
|
||||
flagPath string
|
||||
flagUsername string
|
||||
flagKeyType string
|
||||
flagKeyBits string
|
||||
flagTtl string
|
||||
flagKeyId string
|
||||
flagCriticalOptions string
|
||||
flagCriticalOpts []base.CombinedSliceFlagValue
|
||||
flagExtensions string
|
||||
flagExtens []base.CombinedSliceFlagValue
|
||||
}
|
||||
|
||||
func extraVaultSshCertificateActionsFlagsMapFuncImpl() map[string][]string {
|
||||
flags := map[string][]string{
|
||||
"create": {
|
||||
pathFlagName,
|
||||
usernameName,
|
||||
keyTypeName,
|
||||
keyBitsName,
|
||||
ttlName,
|
||||
keyIdName,
|
||||
criticalOptionsName,
|
||||
piecewiseCriticalOptionsName,
|
||||
extensionsName,
|
||||
piecewiseExtensionName,
|
||||
},
|
||||
"update": {
|
||||
pathFlagName,
|
||||
usernameName,
|
||||
keyTypeName,
|
||||
keyBitsName,
|
||||
ttlName,
|
||||
keyIdName,
|
||||
criticalOptionsName,
|
||||
piecewiseCriticalOptionsName,
|
||||
extensionsName,
|
||||
piecewiseExtensionName,
|
||||
},
|
||||
}
|
||||
return flags
|
||||
}
|
||||
|
||||
func extraVaultSshCertificateFlagsFuncImpl(c *VaultSshCertificateCommand, set *base.FlagSets, _ *base.FlagSet) {
|
||||
f := set.NewFlagSet("Vault SSH Certificate Credential Library Options")
|
||||
|
||||
for _, name := range flagsVaultSshCertificateMap[c.Func] {
|
||||
switch name {
|
||||
case pathFlagName:
|
||||
f.StringVar(&base.StringVar{
|
||||
Name: pathFlagName,
|
||||
Target: &c.flagPath,
|
||||
Usage: "The path in vault to request credentials from.",
|
||||
})
|
||||
case usernameName:
|
||||
f.StringVar(&base.StringVar{
|
||||
Name: usernameName,
|
||||
Target: &c.flagUsername,
|
||||
Usage: "The username to use with the ssh certificate.",
|
||||
})
|
||||
case keyTypeName:
|
||||
f.StringVar(&base.StringVar{
|
||||
Name: keyTypeName,
|
||||
Target: &c.flagKeyType,
|
||||
Usage: "The key type for the generated ssh private key. One of: ed25519, ecdsa, rsa.",
|
||||
})
|
||||
case keyBitsName:
|
||||
f.StringVar(&base.StringVar{
|
||||
Name: keyBitsName,
|
||||
Target: &c.flagKeyBits,
|
||||
Usage: "The number of bits when generating the ssh private key. Depends on key_type. If ed25519 this should not be set, or set to 0, if ecdsa one of 256, 384, 521, if rsa one of 2048, 3072, 4096.",
|
||||
})
|
||||
case ttlName:
|
||||
f.StringVar(&base.StringVar{
|
||||
Name: ttlName,
|
||||
Target: &c.flagTtl,
|
||||
Usage: "The time-to-live for the generated certificate.",
|
||||
})
|
||||
case keyIdName:
|
||||
f.StringVar(&base.StringVar{
|
||||
Name: keyIdName,
|
||||
Target: &c.flagKeyId,
|
||||
Usage: "The key id that the created certificate should have.",
|
||||
})
|
||||
}
|
||||
}
|
||||
criticalOptsInput := common.CombinedSliceFlagValuePopulationInput{
|
||||
FlagSet: f,
|
||||
FlagNames: flagsVaultSshCertificateMap[c.Func],
|
||||
FullPopulationFlag: &c.flagCriticalOptions,
|
||||
FullPopulationInputName: criticalOptionsName,
|
||||
PiecewisePopulationFlag: &c.flagCriticalOpts,
|
||||
PiecewisePopulationInputBaseName: piecewiseCriticalOptionsName,
|
||||
PiecewiseNoProtoCompat: true,
|
||||
}
|
||||
common.PopulateCombinedSliceFlagValue(criticalOptsInput)
|
||||
|
||||
extensionsInput := common.CombinedSliceFlagValuePopulationInput{
|
||||
FlagSet: f,
|
||||
FlagNames: flagsVaultSshCertificateMap[c.Func],
|
||||
FullPopulationFlag: &c.flagExtensions,
|
||||
FullPopulationInputName: extensionsName,
|
||||
PiecewisePopulationFlag: &c.flagExtens,
|
||||
PiecewisePopulationInputBaseName: piecewiseExtensionName,
|
||||
PiecewiseNoProtoCompat: true,
|
||||
}
|
||||
common.PopulateCombinedSliceFlagValue(extensionsInput)
|
||||
}
|
||||
|
||||
func extraVaultSshCertificateFlagHandlingFuncImpl(c *VaultSshCertificateCommand, _ *base.FlagSets, opts *[]credentiallibraries.Option) bool {
|
||||
switch c.flagPath {
|
||||
case "":
|
||||
default:
|
||||
*opts = append(*opts, credentiallibraries.WithVaultCredentialLibraryPath(c.flagPath))
|
||||
}
|
||||
switch c.flagUsername {
|
||||
case "":
|
||||
default:
|
||||
*opts = append(*opts, credentiallibraries.WithVaultSSHCertificateCredentialLibraryUsername(c.flagUsername))
|
||||
}
|
||||
switch c.flagKeyType {
|
||||
case "":
|
||||
case "null":
|
||||
*opts = append(*opts, credentiallibraries.DefaultVaultSSHCertificateCredentialLibraryKeyType())
|
||||
default:
|
||||
*opts = append(*opts, credentiallibraries.WithVaultSSHCertificateCredentialLibraryKeyType(c.flagKeyType))
|
||||
}
|
||||
switch c.flagKeyBits {
|
||||
case "":
|
||||
case "0", "null":
|
||||
*opts = append(*opts, credentiallibraries.DefaultVaultSSHCertificateCredentialLibraryKeyBits())
|
||||
default:
|
||||
var final uint32
|
||||
keyBits, err := strconv.ParseUint(c.flagKeyBits, 10, 32)
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Sprintf("Error parsing %q: %s", c.flagKeyBits, err))
|
||||
return false
|
||||
}
|
||||
final = uint32(keyBits)
|
||||
*opts = append(*opts, credentiallibraries.WithVaultSSHCertificateCredentialLibraryKeyBits(final))
|
||||
}
|
||||
switch c.flagTtl {
|
||||
case "":
|
||||
case "null":
|
||||
*opts = append(*opts, credentiallibraries.DefaultVaultSSHCertificateCredentialLibraryTtl())
|
||||
default:
|
||||
*opts = append(*opts, credentiallibraries.WithVaultSSHCertificateCredentialLibraryTtl(c.flagTtl))
|
||||
}
|
||||
switch c.flagKeyId {
|
||||
case "":
|
||||
case "null":
|
||||
*opts = append(*opts, credentiallibraries.DefaultVaultSSHCertificateCredentialLibraryKeyId())
|
||||
default:
|
||||
*opts = append(*opts, credentiallibraries.WithVaultSSHCertificateCredentialLibraryKeyId(c.flagKeyId))
|
||||
}
|
||||
|
||||
if err := common.HandleAttributeFlags(
|
||||
c.Command,
|
||||
piecewiseCriticalOptionsName,
|
||||
c.flagCriticalOptions,
|
||||
c.flagCriticalOpts,
|
||||
func() {
|
||||
*opts = append(*opts, credentiallibraries.DefaultVaultSSHCertificateCredentialLibraryCriticalOptions())
|
||||
},
|
||||
func(in map[string]any) {
|
||||
inn := make(map[string]string, len(in))
|
||||
for k, v := range in {
|
||||
switch vv := v.(type) {
|
||||
case nil:
|
||||
inn[k] = ""
|
||||
case string:
|
||||
inn[k] = vv
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
*opts = append(*opts, credentiallibraries.WithVaultSSHCertificateCredentialLibraryCriticalOptions(inn))
|
||||
}); err != nil {
|
||||
return false
|
||||
}
|
||||
if err := common.HandleAttributeFlags(
|
||||
c.Command,
|
||||
piecewiseExtensionName,
|
||||
c.flagExtensions,
|
||||
c.flagExtens,
|
||||
func() {
|
||||
*opts = append(*opts, credentiallibraries.DefaultVaultSSHCertificateCredentialLibraryExtensions())
|
||||
},
|
||||
func(in map[string]any) {
|
||||
inn := make(map[string]string, len(in))
|
||||
for k, v := range in {
|
||||
switch vv := v.(type) {
|
||||
case nil:
|
||||
inn[k] = ""
|
||||
case string:
|
||||
inn[k] = vv
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
*opts = append(*opts, credentiallibraries.WithVaultSSHCertificateCredentialLibraryExtensions(inn))
|
||||
}); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *VaultSshCertificateCommand) extraVaultSshCertificateHelpFunc(_ map[string]func() string) string {
|
||||
var helpStr string
|
||||
switch c.Func {
|
||||
case "create":
|
||||
helpStr = base.WrapForHelpText([]string{
|
||||
"Usage: boundary credential-libraries create vault-ssh-certificate -credential-store-id [options] [args]",
|
||||
"",
|
||||
" Create a vault-ssh-certificate-type credential library. Example:",
|
||||
"",
|
||||
` $ boundary credential-libraries create vault-ssh-certificate -credential-store-id csvlt_1234567890 -vault-path "/ssh/sign/role" -username user`,
|
||||
"",
|
||||
"",
|
||||
})
|
||||
|
||||
case "update":
|
||||
helpStr = base.WrapForHelpText([]string{
|
||||
"Usage: boundary credential-libraries update vault-ssh-certificate [options] [args]",
|
||||
"",
|
||||
" Update a vault-ssh-certificate-type credential library given its ID. Example:",
|
||||
"",
|
||||
` $ boundary credential-libraries update vault-ssh-certificate -id clvsclt_1234567890 -name devops -description "For DevOps usage"`,
|
||||
"",
|
||||
"",
|
||||
})
|
||||
}
|
||||
return helpStr + c.Flags().Help()
|
||||
}
|
||||
@@ -203,7 +203,7 @@ func (c *VaultCommand) Run(args []string) int {
|
||||
switch c.Func {
|
||||
|
||||
case "create":
|
||||
createResult, err = credentiallibrariesClient.Create(c.Context, c.FlagCredentialStoreId, opts...)
|
||||
createResult, err = credentiallibrariesClient.Create(c.Context, "vault", c.FlagCredentialStoreId, opts...)
|
||||
if exitCode := c.checkFuncError(err); exitCode > 0 {
|
||||
return exitCode
|
||||
}
|
||||
|
||||
@@ -12,39 +12,10 @@ func init() {
|
||||
extraVaultFlagsHandlingFunc = extraVaultFlagHandlingFuncImpl
|
||||
}
|
||||
|
||||
const (
|
||||
pathFlagName = "vault-path"
|
||||
httpMethodFlagName = "vault-http-method"
|
||||
httpRequestBodyFlagName = "vault-http-request-body"
|
||||
credentialTypeFlagName = "credential-type"
|
||||
credentialMappingFlagName = "credential-mapping-override"
|
||||
)
|
||||
|
||||
type extraVaultCmdVars struct {
|
||||
flagPath string
|
||||
flagHttpMethod string
|
||||
flagHttpRequestBody string
|
||||
flagCredentialType string
|
||||
flagCredentialMapping []base.CombinedSliceFlagValue
|
||||
}
|
||||
type extraVaultCmdVars extraVaultGenericCmdVars
|
||||
|
||||
func extraVaultActionsFlagsMapFuncImpl() map[string][]string {
|
||||
flags := map[string][]string{
|
||||
"create": {
|
||||
pathFlagName,
|
||||
httpMethodFlagName,
|
||||
httpRequestBodyFlagName,
|
||||
credentialTypeFlagName,
|
||||
credentialMappingFlagName,
|
||||
},
|
||||
"update": {
|
||||
pathFlagName,
|
||||
httpMethodFlagName,
|
||||
httpRequestBodyFlagName,
|
||||
credentialMappingFlagName,
|
||||
},
|
||||
}
|
||||
return flags
|
||||
return extraVaultGenericActionsFlagsMapFuncImpl()
|
||||
}
|
||||
|
||||
func extraVaultFlagsFuncImpl(c *VaultCommand, set *base.FlagSets, _ *base.FlagSet) {
|
||||
@@ -150,6 +121,7 @@ func (c *VaultCommand) extraVaultHelpFunc(_ map[string]func() string) string {
|
||||
switch c.Func {
|
||||
case "create":
|
||||
helpStr = base.WrapForHelpText([]string{
|
||||
"Deprecated: use 'boundary credential-libraries create vault-generic' instead.",
|
||||
"Usage: boundary credential-libraries create vault -credential-store-id [options] [args]",
|
||||
"",
|
||||
" Create a vault-type credential library. Example:",
|
||||
@@ -161,6 +133,7 @@ func (c *VaultCommand) extraVaultHelpFunc(_ map[string]func() string) string {
|
||||
|
||||
case "update":
|
||||
helpStr = base.WrapForHelpText([]string{
|
||||
"Deprecated: use 'boundary credential-libraries update vault-generic' instead.",
|
||||
"Usage: boundary credential-libraries update vault [options] [args]",
|
||||
"",
|
||||
" Update a vault-type credential library given its ID. Example:",
|
||||
|
||||
@@ -139,6 +139,11 @@ type CombinedSliceFlagValuePopulationInput struct {
|
||||
// combination method, e.g. "attr" will be used to build "string-attr"; also
|
||||
// used for generating help texts
|
||||
PiecewisePopulationInputBaseName string
|
||||
|
||||
// If ProtoCompat is true, the key will be validated against proto3 syntax
|
||||
// requirements for identifiers. If the string is split via KeyDelimiter, each
|
||||
// segment will be evaluated independently.
|
||||
PiecewiseNoProtoCompat bool
|
||||
}
|
||||
|
||||
func PopulateCombinedSliceFlagValue(input CombinedSliceFlagValuePopulationInput) {
|
||||
@@ -163,7 +168,7 @@ func PopulateCombinedSliceFlagValue(input CombinedSliceFlagValuePopulationInput)
|
||||
Target: input.PiecewisePopulationFlag,
|
||||
KvSplit: true,
|
||||
KeyDelimiter: &keyDelimiter,
|
||||
ProtoCompatKey: true,
|
||||
ProtoCompatKey: !input.PiecewiseNoProtoCompat,
|
||||
KeyOnlyAllowed: true,
|
||||
Usage: fmt.Sprintf(
|
||||
"A key=value pair to add to the request's %s map. "+
|
||||
|
||||
@@ -227,18 +227,51 @@ var inputStructs = map[string][]*cmdInfo{
|
||||
HasId: true,
|
||||
},
|
||||
{
|
||||
ResourceType: resource.CredentialLibrary.String(),
|
||||
Pkg: "credentiallibraries",
|
||||
StdActions: []string{"create", "update"},
|
||||
SubActionPrefix: "vault",
|
||||
HasExtraCommandVars: true,
|
||||
SkipNormalHelp: true,
|
||||
HasExtraHelpFunc: true,
|
||||
HasId: true,
|
||||
HasName: true,
|
||||
HasDescription: true,
|
||||
Container: "CredentialStore",
|
||||
VersionedActions: []string{"update"},
|
||||
ResourceType: resource.CredentialLibrary.String(),
|
||||
Pkg: "credentiallibraries",
|
||||
StdActions: []string{"create", "update"},
|
||||
SubActionPrefix: "vault",
|
||||
HasExtraCommandVars: true,
|
||||
SkipNormalHelp: true,
|
||||
HasExtraHelpFunc: true,
|
||||
HasId: true,
|
||||
HasName: true,
|
||||
HasDescription: true,
|
||||
NeedsSubtypeInCreate: true,
|
||||
Container: "CredentialStore",
|
||||
VersionedActions: []string{"update"},
|
||||
PrefixAttributeFieldErrorsWithSubactionPrefix: true,
|
||||
},
|
||||
{
|
||||
ResourceType: resource.CredentialLibrary.String(),
|
||||
Pkg: "credentiallibraries",
|
||||
StdActions: []string{"create", "update"},
|
||||
SubActionPrefix: "vault-generic",
|
||||
HasExtraCommandVars: true,
|
||||
SkipNormalHelp: true,
|
||||
HasExtraHelpFunc: true,
|
||||
HasId: true,
|
||||
HasName: true,
|
||||
HasDescription: true,
|
||||
NeedsSubtypeInCreate: true,
|
||||
Container: "CredentialStore",
|
||||
VersionedActions: []string{"update"},
|
||||
PrefixAttributeFieldErrorsWithSubactionPrefix: true,
|
||||
},
|
||||
{
|
||||
ResourceType: resource.CredentialLibrary.String(),
|
||||
Pkg: "credentiallibraries",
|
||||
StdActions: []string{"create", "update"},
|
||||
SubActionPrefix: "vault-ssh-certificate",
|
||||
HasExtraCommandVars: true,
|
||||
SkipNormalHelp: true,
|
||||
HasExtraHelpFunc: true,
|
||||
HasId: true,
|
||||
HasName: true,
|
||||
HasDescription: true,
|
||||
NeedsSubtypeInCreate: true,
|
||||
Container: "CredentialStore",
|
||||
VersionedActions: []string{"update"},
|
||||
PrefixAttributeFieldErrorsWithSubactionPrefix: true,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -30,6 +30,7 @@ const (
|
||||
UnspecifiedType Type = "unspecified"
|
||||
UsernamePasswordType Type = "username_password"
|
||||
SshPrivateKeyType Type = "ssh_private_key"
|
||||
SshCertificateType Type = "ssh_certificate"
|
||||
JsonType Type = "json"
|
||||
)
|
||||
|
||||
@@ -97,6 +98,13 @@ type Request struct {
|
||||
Purpose Purpose
|
||||
}
|
||||
|
||||
// SshCertificate is a credential containing a client certificate, username,
|
||||
// and SSH private key.
|
||||
type SshCertificate interface {
|
||||
SshPrivateKey
|
||||
Certificate() []byte
|
||||
}
|
||||
|
||||
// Issuer issues dynamic credentials.
|
||||
type Issuer interface {
|
||||
// Issue issues dynamic credentials for a session from the requested
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
package vault
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/credential"
|
||||
"github.com/hashicorp/boundary/internal/credential/vault/store"
|
||||
"github.com/hashicorp/boundary/internal/db/sanitize"
|
||||
"github.com/hashicorp/boundary/internal/db/sentinel"
|
||||
@@ -46,14 +44,11 @@ type Credential struct {
|
||||
expiration time.Duration `gorm:"-"`
|
||||
}
|
||||
|
||||
func newCredential(libraryId, sessionId, externalId string, tokenHmac []byte, expiration time.Duration) (*Credential, error) {
|
||||
func newCredential(libraryId, externalId string, tokenHmac []byte, expiration time.Duration) (*Credential, error) {
|
||||
const op = "vault.newCredential"
|
||||
if libraryId == "" {
|
||||
return nil, errors.NewDeprecated(errors.InvalidParameter, op, "no library id")
|
||||
}
|
||||
if sessionId == "" {
|
||||
return nil, errors.NewDeprecated(errors.InvalidParameter, op, "no session id")
|
||||
}
|
||||
if len(tokenHmac) == 0 {
|
||||
return nil, errors.NewDeprecated(errors.InvalidParameter, op, "no tokenHmac")
|
||||
}
|
||||
@@ -69,7 +64,6 @@ func newCredential(libraryId, sessionId, externalId string, tokenHmac []byte, ex
|
||||
expiration: expiration.Round(time.Second),
|
||||
Credential: &store.Credential{
|
||||
LibraryId: libraryId,
|
||||
SessionId: sessionId,
|
||||
ExternalId: externalId,
|
||||
TokenHmac: tokenHmac,
|
||||
Status: status,
|
||||
@@ -117,38 +111,6 @@ func (c *Credential) oplog(op oplog.OpType) oplog.Metadata {
|
||||
return metadata
|
||||
}
|
||||
|
||||
func (c *Credential) insertQuery() (query string, queryValues []any) {
|
||||
queryValues = []any{
|
||||
sql.Named("public_id", c.PublicId),
|
||||
sql.Named("library_id", c.LibraryId),
|
||||
sql.Named("session_id", c.SessionId),
|
||||
sql.Named("token_hmac", c.TokenHmac),
|
||||
sql.Named("external_id", c.ExternalId),
|
||||
sql.Named("is_renewable", c.IsRenewable),
|
||||
sql.Named("status", c.Status),
|
||||
sql.Named("last_renewal_time", "now()"),
|
||||
}
|
||||
switch {
|
||||
case c.expiration == 0:
|
||||
query = insertCredentialWithInfiniteExpirationQuery
|
||||
default:
|
||||
query = insertCredentialWithExpirationQuery
|
||||
queryValues = append(queryValues, sql.Named("expiration_time", int(c.expiration.Round(time.Second).Seconds())))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Credential) updateSessionQuery(purpose credential.Purpose) (query string, queryValues []any) {
|
||||
queryValues = []any{
|
||||
sql.Named("public_id", c.PublicId),
|
||||
sql.Named("library_id", c.LibraryId),
|
||||
sql.Named("session_id", c.SessionId),
|
||||
sql.Named("purpose", string(purpose)),
|
||||
}
|
||||
query = updateSessionCredentialQuery
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Credential) updateExpirationQuery() (query string, queryValues []any) {
|
||||
queryValues = []any{
|
||||
int(c.expiration.Round(time.Second).Seconds()),
|
||||
|
||||
@@ -56,17 +56,6 @@ func TestCredential_New(t *testing.T) {
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "missing-session-id",
|
||||
args: args{
|
||||
libraryId: lib.GetPublicId(),
|
||||
externalId: "some/vault/credential",
|
||||
tokenHmac: token.GetTokenHmac(),
|
||||
expiration: 5 * time.Minute,
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "missing-tokenHmac",
|
||||
args: args{
|
||||
@@ -144,7 +133,7 @@ func TestCredential_New(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
ctx := context.Background()
|
||||
got, err := newCredential(tt.args.libraryId, tt.args.sessionId,
|
||||
got, err := newCredential(tt.args.libraryId,
|
||||
tt.args.externalId, tt.args.tokenHmac, tt.args.expiration)
|
||||
if tt.wantErr {
|
||||
assert.Error(err)
|
||||
@@ -162,7 +151,8 @@ func TestCredential_New(t *testing.T) {
|
||||
tt.want.PublicId = id
|
||||
got.PublicId = id
|
||||
|
||||
query, queryValues := got.insertQuery()
|
||||
query, queryValues := insertQuery(got, tt.args.sessionId)
|
||||
require.NoError(err)
|
||||
|
||||
rows, err2 := rw.Exec(ctx, query, queryValues)
|
||||
assert.Equal(1, rows)
|
||||
|
||||
@@ -9,6 +9,18 @@ const (
|
||||
httpMethodField = "HttpMethod"
|
||||
httpRequestBodyField = "HttpRequestBody"
|
||||
|
||||
usernameField = "Username"
|
||||
keyTypeField = "KeyType"
|
||||
keyBitsField = "KeyBits"
|
||||
ttlField = "Ttl"
|
||||
keyIdField = "KeyId"
|
||||
// CriticalOptionsField represents the field mask indicating a critical option
|
||||
// update has been requested.
|
||||
CriticalOptionsField = "CriticalOptions"
|
||||
// ExtensionsField represents the field mask indicating an extension
|
||||
// update has been requested.
|
||||
ExtensionsField = "Extensions"
|
||||
|
||||
certificateField = "Certificate"
|
||||
certificateKeyField = "CertificateKey"
|
||||
vaultAddressField = "VaultAddress"
|
||||
|
||||
@@ -34,6 +34,13 @@ type options struct {
|
||||
withOverridePrivateKeyAttribute string
|
||||
withOverridePrivateKeyPassphraseAttribute string
|
||||
withMappingOverride MappingOverride
|
||||
|
||||
withKeyType string
|
||||
withKeyBits uint32
|
||||
withTtl string
|
||||
withKeyId string
|
||||
withCriticalOptions string
|
||||
withExtensions string
|
||||
}
|
||||
|
||||
func getDefaultOptions() options {
|
||||
@@ -174,3 +181,48 @@ func WithMappingOverride(m MappingOverride) Option {
|
||||
o.withMappingOverride = m
|
||||
}
|
||||
}
|
||||
|
||||
// WithKeyType provides an optional ssh private key type to use
|
||||
// with a ssh certificate credential library. Must be rsa, ed25519, or ecdsa.
|
||||
func WithKeyType(t string) Option {
|
||||
return func(o *options) {
|
||||
o.withKeyType = t
|
||||
}
|
||||
}
|
||||
|
||||
// WithKeyBits provides an optional number of bits used to generate an ssh private key.
|
||||
func WithKeyBits(b uint32) Option {
|
||||
return func(o *options) {
|
||||
o.withKeyBits = b
|
||||
}
|
||||
}
|
||||
|
||||
// WithTtl provides an optional requested time to live for a generated ssh certificate.
|
||||
func WithTtl(t string) Option {
|
||||
return func(o *options) {
|
||||
o.withTtl = t
|
||||
}
|
||||
}
|
||||
|
||||
// WithKeyId provides an optional key id for a created certificate.
|
||||
func WithKeyId(i string) Option {
|
||||
return func(o *options) {
|
||||
o.withKeyId = i
|
||||
}
|
||||
}
|
||||
|
||||
// WithCriticalOptions provides an optional map of the critical options
|
||||
// that the certificate should be signed for.
|
||||
func WithCriticalOptions(s string) Option {
|
||||
return func(o *options) {
|
||||
o.withCriticalOptions = s
|
||||
}
|
||||
}
|
||||
|
||||
// WithExtensions provides a optional map of the extensions
|
||||
// that the certificate should be signed for.
|
||||
func WithExtensions(s string) Option {
|
||||
return func(o *options) {
|
||||
o.withExtensions = s
|
||||
}
|
||||
}
|
||||
@@ -2,14 +2,25 @@ package vault
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"database/sql"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/credential"
|
||||
"github.com/hashicorp/boundary/internal/credential/vault/internal/sshprivatekey"
|
||||
"github.com/hashicorp/boundary/internal/credential/vault/internal/usernamepassword"
|
||||
"github.com/hashicorp/boundary/internal/db/sentinel"
|
||||
"github.com/hashicorp/boundary/internal/db/timestamp"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
@@ -17,6 +28,8 @@ import (
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
|
||||
"github.com/hashicorp/go-kms-wrapping/v2/extras/structwrapping"
|
||||
vault "github.com/hashicorp/vault/api"
|
||||
"github.com/mikesmitty/edkey"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
@@ -25,14 +38,16 @@ var _ credential.Dynamic = (*baseCred)(nil)
|
||||
type baseCred struct {
|
||||
*Credential
|
||||
|
||||
lib *issueCredentialLibrary
|
||||
lib issuingCredentialLibrary
|
||||
secretData map[string]any
|
||||
}
|
||||
|
||||
func (bc *baseCred) Secret() credential.SecretData { return bc.secretData }
|
||||
func (bc *baseCred) Library() credential.Library { return bc.lib }
|
||||
func (bc *baseCred) Purpose() credential.Purpose { return bc.lib.Purpose }
|
||||
func (bc *baseCred) Purpose() credential.Purpose { return bc.lib.GetPurpose() }
|
||||
func (bc *baseCred) getExpiration() time.Duration { return bc.expiration }
|
||||
func (bc *baseCred) getCredential() *Credential { return bc.Credential }
|
||||
func (bc *baseCred) isRevokable() bool { return bc.ExternalId != sentinel.ExternalIdNone }
|
||||
|
||||
// convert converts bc to a specific credential type if bc is not
|
||||
// UnspecifiedType.
|
||||
@@ -67,7 +82,12 @@ func baseToUsrPass(ctx context.Context, bc *baseCred) (*usrPassCred, error) {
|
||||
return nil, errors.E(ctx, errors.WithCode(errors.InvalidParameter), errors.WithMsg("invalid credential type"))
|
||||
}
|
||||
|
||||
uAttr, pAttr := bc.lib.UsernameAttribute, bc.lib.PasswordAttribute
|
||||
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"))
|
||||
}
|
||||
|
||||
uAttr, pAttr := lib.UsernameAttribute, lib.PasswordAttribute
|
||||
if uAttr == "" {
|
||||
uAttr = "username"
|
||||
}
|
||||
@@ -109,7 +129,12 @@ func baseToSshPriKey(ctx context.Context, bc *baseCred) (*sshPrivateKeyCred, err
|
||||
return nil, errors.E(ctx, errors.WithCode(errors.InvalidParameter), errors.WithMsg("invalid credential type"))
|
||||
}
|
||||
|
||||
uAttr, pkAttr, pAttr := bc.lib.UsernameAttribute, bc.lib.PrivateKeyAttribute, bc.lib.PrivateKeyPassphraseAttribute
|
||||
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"))
|
||||
}
|
||||
|
||||
uAttr, pkAttr, pAttr := lib.UsernameAttribute, lib.PrivateKeyAttribute, lib.PrivateKeyPassphraseAttribute
|
||||
if uAttr == "" {
|
||||
uAttr = "username"
|
||||
}
|
||||
@@ -132,12 +157,25 @@ func baseToSshPriKey(ctx context.Context, bc *baseCred) (*sshPrivateKeyCred, err
|
||||
}, nil
|
||||
}
|
||||
|
||||
var _ credential.Library = (*issueCredentialLibrary)(nil)
|
||||
type sshCertCred struct {
|
||||
*sshPrivateKeyCred
|
||||
certificate []byte
|
||||
}
|
||||
|
||||
// A issueCredentialLibrary contains all the values needed to connect to Vault and
|
||||
func (c *sshCertCred) Certificate() []byte { return c.certificate }
|
||||
|
||||
var _ credential.Library = (*genericIssuingCredentialLibrary)(nil)
|
||||
|
||||
type issuingCredentialLibrary interface {
|
||||
credential.Library
|
||||
GetPurpose() credential.Purpose
|
||||
retrieveCredential(context.Context, errors.Op, ...credential.Option) (dynamicCred, error)
|
||||
}
|
||||
|
||||
// A genericIssuingCredentialLibrary contains all the values needed to connect to Vault and
|
||||
// retrieve credentials.
|
||||
type issueCredentialLibrary struct {
|
||||
PublicId string `gorm:"primary_key"`
|
||||
type genericIssuingCredentialLibrary struct {
|
||||
PublicId string
|
||||
StoreId string
|
||||
Name string
|
||||
Description string
|
||||
@@ -147,7 +185,7 @@ type issueCredentialLibrary struct {
|
||||
VaultPath string
|
||||
HttpMethod string
|
||||
HttpRequestBody []byte
|
||||
CredType string `gorm:"column:credential_type"`
|
||||
CredType string
|
||||
ProjectId string
|
||||
VaultAddress string
|
||||
Namespace string
|
||||
@@ -167,13 +205,13 @@ type issueCredentialLibrary struct {
|
||||
PasswordAttribute string
|
||||
PrivateKeyAttribute string
|
||||
PrivateKeyPassphraseAttribute string
|
||||
Purpose credential.Purpose `gorm:"-"`
|
||||
Purpose credential.Purpose
|
||||
}
|
||||
|
||||
func (pl *issueCredentialLibrary) clone() *issueCredentialLibrary {
|
||||
func (pl *genericIssuingCredentialLibrary) clone() *genericIssuingCredentialLibrary {
|
||||
// The 'append(a[:0:0], a...)' comes from
|
||||
// https://github.com/go101/go101/wiki/How-to-perfectly-clone-a-slice%3F
|
||||
return &issueCredentialLibrary{
|
||||
return &genericIssuingCredentialLibrary{
|
||||
PublicId: pl.PublicId,
|
||||
StoreId: pl.StoreId,
|
||||
CredType: pl.CredType,
|
||||
@@ -208,15 +246,16 @@ func (pl *issueCredentialLibrary) clone() *issueCredentialLibrary {
|
||||
}
|
||||
}
|
||||
|
||||
func (pl *issueCredentialLibrary) GetPublicId() string { return pl.PublicId }
|
||||
func (pl *issueCredentialLibrary) GetStoreId() string { return pl.StoreId }
|
||||
func (pl *issueCredentialLibrary) GetName() string { return pl.Name }
|
||||
func (pl *issueCredentialLibrary) GetDescription() string { return pl.Description }
|
||||
func (pl *issueCredentialLibrary) GetVersion() uint32 { return pl.Version }
|
||||
func (pl *issueCredentialLibrary) GetCreateTime() *timestamp.Timestamp { return pl.CreateTime }
|
||||
func (pl *issueCredentialLibrary) GetUpdateTime() *timestamp.Timestamp { return pl.UpdateTime }
|
||||
func (pl *genericIssuingCredentialLibrary) GetPublicId() string { return pl.PublicId }
|
||||
func (pl *genericIssuingCredentialLibrary) GetStoreId() string { return pl.StoreId }
|
||||
func (pl *genericIssuingCredentialLibrary) GetName() string { return pl.Name }
|
||||
func (pl *genericIssuingCredentialLibrary) GetDescription() string { return pl.Description }
|
||||
func (pl *genericIssuingCredentialLibrary) GetVersion() uint32 { return pl.Version }
|
||||
func (pl *genericIssuingCredentialLibrary) GetCreateTime() *timestamp.Timestamp { return pl.CreateTime }
|
||||
func (pl *genericIssuingCredentialLibrary) GetUpdateTime() *timestamp.Timestamp { return pl.UpdateTime }
|
||||
func (pl *genericIssuingCredentialLibrary) GetPurpose() credential.Purpose { return pl.Purpose }
|
||||
|
||||
func (pl *issueCredentialLibrary) CredentialType() credential.Type {
|
||||
func (pl *genericIssuingCredentialLibrary) CredentialType() credential.Type {
|
||||
switch ct := pl.CredType; ct {
|
||||
case "":
|
||||
return credential.UnspecifiedType
|
||||
@@ -225,41 +264,8 @@ func (pl *issueCredentialLibrary) CredentialType() credential.Type {
|
||||
}
|
||||
}
|
||||
|
||||
func (pl *issueCredentialLibrary) decrypt(ctx context.Context, cipher wrapping.Wrapper) error {
|
||||
const op = "vault.(issueCredentialLibrary).decrypt"
|
||||
|
||||
if pl.CtToken != nil {
|
||||
type ptk struct {
|
||||
Token []byte `wrapping:"pt,token_data"`
|
||||
CtToken []byte `wrapping:"ct,token_data"`
|
||||
}
|
||||
ptkv := &ptk{
|
||||
CtToken: pl.CtToken,
|
||||
}
|
||||
if err := structwrapping.UnwrapStruct(ctx, cipher, ptkv, nil); err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithCode(errors.Decrypt), errors.WithMsg("token"))
|
||||
}
|
||||
pl.Token = ptkv.Token
|
||||
}
|
||||
|
||||
if pl.CtClientKey != nil && pl.ClientCert != nil {
|
||||
type pck struct {
|
||||
Key []byte `wrapping:"pt,key_data"`
|
||||
CtKey []byte `wrapping:"ct,key_data"`
|
||||
}
|
||||
pckv := &pck{
|
||||
CtKey: pl.CtClientKey,
|
||||
}
|
||||
if err := structwrapping.UnwrapStruct(ctx, cipher, pckv, nil); err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithCode(errors.Decrypt), errors.WithMsg("client certificate"))
|
||||
}
|
||||
pl.ClientKey = pckv.Key
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pl *issueCredentialLibrary) client(ctx context.Context) (vaultClient, error) {
|
||||
const op = "vault.(issueCredentialLibrary).client"
|
||||
func (pl *genericIssuingCredentialLibrary) client(ctx context.Context) (vaultClient, error) {
|
||||
const op = "vault.(genericIssuingCredentialLibrary).client"
|
||||
clientConfig := &clientConfig{
|
||||
Addr: pl.VaultAddress,
|
||||
Token: pl.Token,
|
||||
@@ -284,15 +290,15 @@ func (pl *issueCredentialLibrary) client(ctx context.Context) (vaultClient, erro
|
||||
type dynamicCred interface {
|
||||
credential.Dynamic
|
||||
getExpiration() time.Duration
|
||||
insertQuery() (query string, queryValues []any)
|
||||
updateSessionQuery(purpose credential.Purpose) (query string, queryValues []any)
|
||||
getCredential() *Credential
|
||||
isRevokable() bool
|
||||
}
|
||||
|
||||
// retrieveCredential retrieves a dynamic credential from Vault for the
|
||||
// given sessionId.
|
||||
//
|
||||
// Supported options: credential.WithTemplateData
|
||||
func (pl *issueCredentialLibrary) retrieveCredential(ctx context.Context, op errors.Op, sessionId string, opt ...credential.Option) (dynamicCred, error) {
|
||||
func (pl *genericIssuingCredentialLibrary) retrieveCredential(ctx context.Context, op errors.Op, opt ...credential.Option) (dynamicCred, error) {
|
||||
opts, err := credential.GetOpts(opt...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
@@ -358,7 +364,7 @@ func (pl *issueCredentialLibrary) retrieveCredential(ctx context.Context, op err
|
||||
}
|
||||
|
||||
leaseDuration := time.Duration(secret.LeaseDuration) * time.Second
|
||||
cred, err := newCredential(pl.GetPublicId(), sessionId, secret.LeaseID, pl.TokenHmac, leaseDuration)
|
||||
cred, err := newCredential(pl.GetPublicId(), secret.LeaseID, pl.TokenHmac, leaseDuration)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
@@ -374,11 +380,11 @@ func (pl *issueCredentialLibrary) retrieveCredential(ctx context.Context, op err
|
||||
}
|
||||
|
||||
// TableName returns the table name for gorm.
|
||||
func (pl *issueCredentialLibrary) TableName() string {
|
||||
func (pl *genericIssuingCredentialLibrary) TableName() string {
|
||||
return "credential_vault_library_issue_credentials"
|
||||
}
|
||||
|
||||
func (r *Repository) getIssueCredLibraries(ctx context.Context, requests []credential.Request) ([]*issueCredentialLibrary, error) {
|
||||
func (r *Repository) getIssueCredLibraries(ctx context.Context, requests []credential.Request) ([]issuingCredentialLibrary, error) {
|
||||
const op = "vault.(Repository).getIssueCredLibraries"
|
||||
|
||||
mapper, err := newMapper(requests)
|
||||
@@ -405,9 +411,9 @@ func (r *Repository) getIssueCredLibraries(ctx context.Context, requests []crede
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var libs []*issueCredentialLibrary
|
||||
var libs []*privateCredentialLibraryAllTypes
|
||||
for rows.Next() {
|
||||
var lib issueCredentialLibrary
|
||||
var lib privateCredentialLibraryAllTypes
|
||||
if err := r.reader.ScanRows(ctx, rows, &lib); err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("scan row failed"))
|
||||
}
|
||||
@@ -422,6 +428,7 @@ func (r *Repository) getIssueCredLibraries(ctx context.Context, requests []crede
|
||||
}
|
||||
}
|
||||
|
||||
var decryptedLibs []issuingCredentialLibrary
|
||||
for _, pl := range libs {
|
||||
databaseWrapper, err := r.kms.GetWrapper(ctx, pl.ProjectId, kms.KeyPurposeDatabase)
|
||||
if err != nil {
|
||||
@@ -431,9 +438,209 @@ func (r *Repository) getIssueCredLibraries(ctx context.Context, requests []crede
|
||||
if err := pl.decrypt(ctx, databaseWrapper); err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
decryptedLibs = append(decryptedLibs, pl.toTypedIssuingCredentialLibrary())
|
||||
}
|
||||
|
||||
return libs, nil
|
||||
return decryptedLibs, nil
|
||||
}
|
||||
|
||||
// privateCredentialLibraryAllTypes is a clone of genericIssuingCredentialLibrary. Contains
|
||||
// all the values needed to connect to Vault and retrieve credentials.
|
||||
type privateCredentialLibraryAllTypes struct {
|
||||
PublicId string `gorm:"primary_key"`
|
||||
StoreId string
|
||||
Name string
|
||||
Description string
|
||||
CreateTime *timestamp.Timestamp
|
||||
UpdateTime *timestamp.Timestamp
|
||||
Version uint32
|
||||
VaultPath string
|
||||
HttpMethod string
|
||||
HttpRequestBody []byte
|
||||
CredType string `gorm:"column:credential_type"`
|
||||
ProjectId string
|
||||
VaultAddress string
|
||||
Namespace string
|
||||
CaCert []byte
|
||||
TlsServerName string
|
||||
TlsSkipVerify bool
|
||||
WorkerFilter string
|
||||
Token TokenSecret
|
||||
CtToken []byte
|
||||
TokenHmac []byte
|
||||
TokenKeyId string
|
||||
ClientCert []byte
|
||||
ClientKey KeySecret
|
||||
CtClientKey []byte
|
||||
ClientKeyId string
|
||||
UsernameAttribute string
|
||||
PasswordAttribute string
|
||||
PrivateKeyAttribute string
|
||||
PrivateKeyPassphraseAttribute string
|
||||
Purpose credential.Purpose `gorm:"-"`
|
||||
KeyType string
|
||||
KeyBits int
|
||||
Username string
|
||||
Ttl string
|
||||
KeyId string
|
||||
CriticalOptions []byte
|
||||
Extensions []byte
|
||||
CredLibType string
|
||||
}
|
||||
|
||||
func (pl *privateCredentialLibraryAllTypes) GetPublicId() string { return pl.PublicId }
|
||||
|
||||
func (pl *privateCredentialLibraryAllTypes) decrypt(ctx context.Context, cipher wrapping.Wrapper) error {
|
||||
const op = "vault.(privateCredentialLibraryAllTypes).decrypt"
|
||||
|
||||
if pl.CtToken != nil {
|
||||
type ptk struct {
|
||||
Token []byte `wrapping:"pt,token_data"`
|
||||
CtToken []byte `wrapping:"ct,token_data"`
|
||||
}
|
||||
ptkv := &ptk{
|
||||
CtToken: pl.CtToken,
|
||||
}
|
||||
if err := structwrapping.UnwrapStruct(ctx, cipher, ptkv, nil); err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithCode(errors.Decrypt), errors.WithMsg("token"))
|
||||
}
|
||||
pl.Token = ptkv.Token
|
||||
}
|
||||
|
||||
if pl.CtClientKey != nil && pl.ClientCert != nil {
|
||||
type pck struct {
|
||||
Key []byte `wrapping:"pt,key_data"`
|
||||
CtKey []byte `wrapping:"ct,key_data"`
|
||||
}
|
||||
pckv := &pck{
|
||||
CtKey: pl.CtClientKey,
|
||||
}
|
||||
if err := structwrapping.UnwrapStruct(ctx, cipher, pckv, nil); err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithCode(errors.Decrypt), errors.WithMsg("client certificate"))
|
||||
}
|
||||
pl.ClientKey = pckv.Key
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pl *privateCredentialLibraryAllTypes) clone() *privateCredentialLibraryAllTypes {
|
||||
// The 'append(a[:0:0], a...)' comes from
|
||||
// https://github.com/go101/go101/wiki/How-to-perfectly-clone-a-slice%3F
|
||||
return &privateCredentialLibraryAllTypes{
|
||||
PublicId: pl.PublicId,
|
||||
StoreId: pl.StoreId,
|
||||
CredType: pl.CredType,
|
||||
UsernameAttribute: pl.UsernameAttribute,
|
||||
PasswordAttribute: pl.PasswordAttribute,
|
||||
PrivateKeyAttribute: pl.PrivateKeyAttribute,
|
||||
PrivateKeyPassphraseAttribute: pl.PrivateKeyPassphraseAttribute,
|
||||
Name: pl.Name,
|
||||
Description: pl.Description,
|
||||
CreateTime: proto.Clone(pl.CreateTime).(*timestamp.Timestamp),
|
||||
UpdateTime: proto.Clone(pl.UpdateTime).(*timestamp.Timestamp),
|
||||
Version: pl.Version,
|
||||
ProjectId: pl.ProjectId,
|
||||
VaultPath: pl.VaultPath,
|
||||
HttpMethod: pl.HttpMethod,
|
||||
HttpRequestBody: append(pl.HttpRequestBody[:0:0], pl.HttpRequestBody...),
|
||||
VaultAddress: pl.VaultAddress,
|
||||
Namespace: pl.Namespace,
|
||||
CaCert: append(pl.CaCert[:0:0], pl.CaCert...),
|
||||
TlsServerName: pl.TlsServerName,
|
||||
TlsSkipVerify: pl.TlsSkipVerify,
|
||||
WorkerFilter: pl.WorkerFilter,
|
||||
TokenHmac: append(pl.TokenHmac[:0:0], pl.TokenHmac...),
|
||||
Token: append(pl.Token[:0:0], pl.Token...),
|
||||
CtToken: append(pl.CtToken[:0:0], pl.CtToken...),
|
||||
TokenKeyId: pl.TokenKeyId,
|
||||
ClientCert: append(pl.ClientCert[:0:0], pl.ClientCert...),
|
||||
ClientKey: append(pl.ClientKey[:0:0], pl.ClientKey...),
|
||||
CtClientKey: append(pl.CtClientKey[:0:0], pl.CtClientKey...),
|
||||
ClientKeyId: pl.ClientKeyId,
|
||||
Purpose: pl.Purpose,
|
||||
KeyType: pl.KeyType,
|
||||
KeyBits: pl.KeyBits,
|
||||
Username: pl.Username,
|
||||
Ttl: pl.Ttl,
|
||||
KeyId: pl.KeyId,
|
||||
CriticalOptions: pl.CriticalOptions,
|
||||
Extensions: pl.Extensions,
|
||||
CredLibType: pl.CredLibType,
|
||||
}
|
||||
}
|
||||
|
||||
func (pl *privateCredentialLibraryAllTypes) toTypedIssuingCredentialLibrary() issuingCredentialLibrary {
|
||||
switch pl.CredLibType {
|
||||
case "ssh-signed-cert":
|
||||
return &sshCertIssuingCredentialLibrary{
|
||||
PublicId: pl.PublicId,
|
||||
StoreId: pl.StoreId,
|
||||
CredType: pl.CredType,
|
||||
Username: pl.Username,
|
||||
Name: pl.Name,
|
||||
Description: pl.Description,
|
||||
CreateTime: pl.CreateTime,
|
||||
UpdateTime: pl.UpdateTime,
|
||||
Version: pl.Version,
|
||||
ProjectId: pl.ProjectId,
|
||||
VaultPath: pl.VaultPath,
|
||||
VaultAddress: pl.VaultAddress,
|
||||
Namespace: pl.Namespace,
|
||||
CaCert: pl.CaCert,
|
||||
TlsServerName: pl.TlsServerName,
|
||||
TlsSkipVerify: pl.TlsSkipVerify,
|
||||
WorkerFilter: pl.WorkerFilter,
|
||||
TokenHmac: pl.TokenHmac,
|
||||
Token: pl.Token,
|
||||
CtToken: pl.CtToken,
|
||||
TokenKeyId: pl.TokenKeyId,
|
||||
ClientCert: pl.ClientCert,
|
||||
ClientKey: pl.ClientKey,
|
||||
CtClientKey: pl.CtClientKey,
|
||||
ClientKeyId: pl.ClientKeyId,
|
||||
Purpose: pl.Purpose,
|
||||
KeyType: pl.KeyType,
|
||||
KeyBits: pl.KeyBits,
|
||||
Ttl: pl.Ttl,
|
||||
KeyId: pl.KeyId,
|
||||
CriticalOptions: pl.CriticalOptions,
|
||||
Extensions: pl.Extensions,
|
||||
}
|
||||
default:
|
||||
return &genericIssuingCredentialLibrary{
|
||||
PublicId: pl.PublicId,
|
||||
StoreId: pl.StoreId,
|
||||
CredType: pl.CredType,
|
||||
UsernameAttribute: pl.UsernameAttribute,
|
||||
PasswordAttribute: pl.PasswordAttribute,
|
||||
PrivateKeyAttribute: pl.PrivateKeyAttribute,
|
||||
PrivateKeyPassphraseAttribute: pl.PrivateKeyPassphraseAttribute,
|
||||
Name: pl.Name,
|
||||
Description: pl.Description,
|
||||
CreateTime: pl.CreateTime,
|
||||
UpdateTime: pl.UpdateTime,
|
||||
Version: pl.Version,
|
||||
ProjectId: pl.ProjectId,
|
||||
VaultPath: pl.VaultPath,
|
||||
HttpMethod: pl.HttpMethod,
|
||||
HttpRequestBody: pl.HttpRequestBody,
|
||||
VaultAddress: pl.VaultAddress,
|
||||
Namespace: pl.Namespace,
|
||||
CaCert: pl.CaCert,
|
||||
TlsServerName: pl.TlsServerName,
|
||||
TlsSkipVerify: pl.TlsSkipVerify,
|
||||
WorkerFilter: pl.WorkerFilter,
|
||||
TokenHmac: pl.TokenHmac,
|
||||
Token: pl.Token,
|
||||
CtToken: pl.CtToken,
|
||||
TokenKeyId: pl.TokenKeyId,
|
||||
ClientCert: pl.ClientCert,
|
||||
ClientKey: pl.ClientKey,
|
||||
CtClientKey: pl.CtClientKey,
|
||||
ClientKeyId: pl.ClientKeyId,
|
||||
Purpose: pl.Purpose,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// requestMap takes a slice of credential requests and provides of list of
|
||||
@@ -478,3 +685,320 @@ func (m *requestMap) libIds() []string {
|
||||
func (m *requestMap) get(libraryId string) []credential.Purpose {
|
||||
return m.ids[libraryId]
|
||||
}
|
||||
|
||||
type sshCertIssuingCredentialLibrary struct {
|
||||
PublicId string
|
||||
StoreId string
|
||||
Name string
|
||||
Description string
|
||||
CreateTime *timestamp.Timestamp
|
||||
UpdateTime *timestamp.Timestamp
|
||||
Version uint32
|
||||
VaultPath string
|
||||
CredType string
|
||||
ProjectId string
|
||||
VaultAddress string
|
||||
Namespace string
|
||||
CaCert []byte
|
||||
TlsServerName string
|
||||
TlsSkipVerify bool
|
||||
WorkerFilter string
|
||||
Token TokenSecret
|
||||
CtToken []byte
|
||||
TokenHmac []byte
|
||||
TokenKeyId string
|
||||
ClientCert []byte
|
||||
ClientKey KeySecret
|
||||
CtClientKey []byte
|
||||
ClientKeyId string
|
||||
Username string
|
||||
KeyType string
|
||||
KeyBits int
|
||||
KeyId string
|
||||
Ttl string
|
||||
CriticalOptions []byte
|
||||
Extensions []byte
|
||||
Purpose credential.Purpose
|
||||
}
|
||||
|
||||
func (lib *sshCertIssuingCredentialLibrary) GetPublicId() string { return lib.PublicId }
|
||||
func (lib *sshCertIssuingCredentialLibrary) GetStoreId() string { return lib.StoreId }
|
||||
func (lib *sshCertIssuingCredentialLibrary) GetName() string { return lib.Name }
|
||||
func (lib *sshCertIssuingCredentialLibrary) GetDescription() string { return lib.Description }
|
||||
func (lib *sshCertIssuingCredentialLibrary) GetVersion() uint32 { return lib.Version }
|
||||
func (lib *sshCertIssuingCredentialLibrary) GetPurpose() credential.Purpose { return lib.Purpose }
|
||||
func (lib *sshCertIssuingCredentialLibrary) GetCreateTime() *timestamp.Timestamp {
|
||||
return lib.CreateTime
|
||||
}
|
||||
|
||||
func (lib *sshCertIssuingCredentialLibrary) GetUpdateTime() *timestamp.Timestamp {
|
||||
return lib.UpdateTime
|
||||
}
|
||||
|
||||
func (lib *sshCertIssuingCredentialLibrary) CredentialType() credential.Type {
|
||||
switch ct := lib.CredType; ct {
|
||||
case "":
|
||||
return credential.UnspecifiedType
|
||||
default:
|
||||
return credential.Type(ct)
|
||||
}
|
||||
}
|
||||
|
||||
func (lib *sshCertIssuingCredentialLibrary) client(ctx context.Context) (vaultClient, error) {
|
||||
const op = "vault.(genericIssuingCredentialLibrary).client"
|
||||
clientConfig := &clientConfig{
|
||||
Addr: lib.VaultAddress,
|
||||
Token: lib.Token,
|
||||
CaCert: lib.CaCert,
|
||||
TlsServerName: lib.TlsServerName,
|
||||
TlsSkipVerify: lib.TlsSkipVerify,
|
||||
Namespace: lib.Namespace,
|
||||
}
|
||||
|
||||
if lib.ClientKey != nil {
|
||||
clientConfig.ClientCert = lib.ClientCert
|
||||
clientConfig.ClientKey = lib.ClientKey
|
||||
}
|
||||
|
||||
client, err := vaultClientFactoryFn(ctx, clientConfig, WithWorkerFilter(lib.WorkerFilter))
|
||||
if err != nil {
|
||||
return nil, errors.WrapDeprecated(err, op, errors.WithMsg("unable to create vault client"))
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func generatePublicPrivateKeys(ctx context.Context, keyType string, keyBits int) (string, []byte, error) {
|
||||
const op = "vault.generatePublicPrivateKeys"
|
||||
pemBlock := pem.Block{}
|
||||
var sshKey ssh.PublicKey
|
||||
|
||||
switch keyType {
|
||||
case KeyTypeRsa:
|
||||
pemBlock.Type = "RSA PRIVATE KEY" // these values are copied from the crypto ssh library in ssh/keys.go
|
||||
|
||||
key, err := rsa.GenerateKey(rand.Reader, keyBits)
|
||||
if err != nil {
|
||||
return "", nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
if sshKey, err = ssh.NewPublicKey(&key.PublicKey); err != nil {
|
||||
return "", nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
pemBlock.Bytes = x509.MarshalPKCS1PrivateKey(key)
|
||||
|
||||
case KeyTypeEd25519:
|
||||
pemBlock.Type = "OPENSSH PRIVATE KEY" // these values are copied from the crypto ssh library in ssh/keys.go
|
||||
|
||||
pubKey, privKey, err := ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
return "", nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
if sshKey, err = ssh.NewPublicKey(pubKey); err != nil {
|
||||
return "", nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
if pemBlock.Bytes = edkey.MarshalED25519PrivateKey(privKey); pemBlock.Bytes == nil {
|
||||
return "", nil, errors.New(ctx, errors.Encode, op, "failed to marshal ed25519 private key")
|
||||
}
|
||||
|
||||
case KeyTypeEcdsa:
|
||||
pemBlock.Type = "EC PRIVATE KEY" // these values are copied from the crypto ssh library in ssh/keys.go
|
||||
|
||||
var curve elliptic.Curve
|
||||
switch keyBits {
|
||||
case 256:
|
||||
curve = elliptic.P256()
|
||||
case 384:
|
||||
curve = elliptic.P384()
|
||||
case 521:
|
||||
curve = elliptic.P521()
|
||||
default:
|
||||
return "", nil, errors.New(ctx, errors.InvalidParameter, op, "invalid KeyBits. when KeyType=ecdsa, KeyBits must be one of: 256, 384, or 521")
|
||||
}
|
||||
|
||||
key, err := ecdsa.GenerateKey(curve, rand.Reader)
|
||||
if err != nil {
|
||||
return "", nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
if sshKey, err = ssh.NewPublicKey(&key.PublicKey); err != nil {
|
||||
return "", nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
if pemBlock.Bytes, err = x509.MarshalECPrivateKey(key); err != nil {
|
||||
return "", nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
default:
|
||||
return "", nil, errors.New(ctx, errors.InvalidParameter, op, "invalid KeyType, must be one of: \"rsa\", \"ed25519\", or \"ecdsa\"")
|
||||
}
|
||||
|
||||
publicKey := base64.StdEncoding.EncodeToString(sshKey.Marshal())
|
||||
privateKey := pem.EncodeToMemory(&pemBlock)
|
||||
if privateKey == nil {
|
||||
return "", nil, errors.New(ctx, errors.Encode, op, "failed to encode private key to PEM format")
|
||||
}
|
||||
return publicKey, privateKey, nil
|
||||
}
|
||||
|
||||
type sshCertVaultBody struct {
|
||||
KeyType string `json:"key_type,omitempty"` // must be "rsa", "ed25519", or "ecdsa"
|
||||
KeyBits int `json:"key_bits,omitempty"` // with key_type=rsa, allowed values are: 2048 (default), 3072, or 4096; with key_type=ecdsa, allowed values are: 256 (default), 384, or 521; ignored with key_type=ed25519
|
||||
PublicKey string `json:"public_key,omitempty"`
|
||||
TTL string `json:"ttl,omitempty"`
|
||||
ValidPrincipals string `json:"valid_principals,omitempty"` // this needs to be "generated" off of the username provided in config
|
||||
CertType string `json:"cert_type,omitempty"` // this should always be "user"
|
||||
KeyId string `json:"key_id,omitempty"` // this will be loaded directly from lib
|
||||
CriticalOptions map[string]string `json:"critical_options,omitempty"` // this will be loaded directly from lib
|
||||
Extensions map[string]string `json:"extensions,omitempty"` // this will be loaded directly from lib
|
||||
}
|
||||
|
||||
var vaultPathRegexp = regexp.MustCompile(`^.+\/(sign|issue)\/[^\/\\\s]+$`)
|
||||
|
||||
// retrieveCredential retrieves a dynamic connection credential from Vault
|
||||
// for a specific session and connection.
|
||||
//
|
||||
// Supported options: credential.WithTemplateData
|
||||
func (lib *sshCertIssuingCredentialLibrary) retrieveCredential(ctx context.Context, op errors.Op, opt ...credential.Option) (dynamicCred, error) {
|
||||
opts, err := credential.GetOpts(opt...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
// Get the credential ID early. No need to get a secret from Vault
|
||||
// if there is no way to save it in the database.
|
||||
credId, err := newCredentialId()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
client, err := lib.client(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
// build as much of the payload as we can, since sign/issue share many attributes
|
||||
// Template the username
|
||||
tplate, err := template.New(ctx, lib.Username)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
username, err := tplate.Generate(ctx, opts.WithTemplateData)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
var criticalOptions map[string]string
|
||||
if lib.CriticalOptions != nil {
|
||||
if json.Unmarshal(lib.CriticalOptions, &criticalOptions) != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
}
|
||||
|
||||
var extensions map[string]string
|
||||
if lib.Extensions != nil {
|
||||
if json.Unmarshal(lib.Extensions, &extensions) != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
}
|
||||
|
||||
payload := sshCertVaultBody{
|
||||
ValidPrincipals: username,
|
||||
CertType: "user",
|
||||
CriticalOptions: criticalOptions,
|
||||
Extensions: extensions,
|
||||
TTL: lib.Ttl,
|
||||
KeyId: lib.KeyId,
|
||||
}
|
||||
|
||||
var privateKey credential.PrivateKey
|
||||
var secret *vault.Secret
|
||||
|
||||
match := vaultPathRegexp.FindStringSubmatch(lib.VaultPath)
|
||||
if len(match) < 2 {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "vault path was not in an expected format. expected path containing \"sign\" or \"issue\"")
|
||||
}
|
||||
|
||||
// by definition, if match exists, then match[1] == "sign" or "issue"
|
||||
switch match[1] {
|
||||
case "sign":
|
||||
payload.PublicKey, privateKey, err = generatePublicPrivateKeys(ctx, lib.KeyType, lib.KeyBits)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
body, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
secret, err = client.post(ctx, lib.VaultPath, body)
|
||||
if err != nil {
|
||||
// TODO(mgaffney) 05/2021: detect if the error is because of an
|
||||
// expired or invalid token
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
if secret == nil {
|
||||
return nil, errors.E(ctx, errors.WithCode(errors.VaultEmptySecret), errors.WithOp(op))
|
||||
}
|
||||
|
||||
case "issue":
|
||||
payload.KeyBits = lib.KeyBits
|
||||
if lib.KeyType == KeyTypeEcdsa {
|
||||
// this is a special case where internal to boundary, we refer to the crypto
|
||||
// library name, but vault refers to it simply as "ec" for "Elliptic Curve"
|
||||
payload.KeyType = "ec"
|
||||
} else {
|
||||
payload.KeyType = lib.KeyType
|
||||
}
|
||||
|
||||
body, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
secret, err = client.post(ctx, lib.VaultPath, body)
|
||||
if err != nil {
|
||||
// TODO(mgaffney) 05/2021: detect if the error is because of an
|
||||
// expired or invalid token
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
if secret == nil {
|
||||
return nil, errors.E(ctx, errors.WithCode(errors.VaultEmptySecret), errors.WithOp(op))
|
||||
}
|
||||
|
||||
pk, ok := secret.Data["private_key"].(string)
|
||||
if !ok {
|
||||
return nil, errors.New(ctx, errors.VaultInvalidCredentialMapping, op, "vault secret did not contain a private key or response was not in the expected format")
|
||||
}
|
||||
|
||||
privateKey = []byte(pk)
|
||||
}
|
||||
|
||||
leaseDuration := time.Duration(secret.LeaseDuration) * time.Second
|
||||
cred, err := newCredential(lib.GetPublicId(), secret.LeaseID, lib.TokenHmac, leaseDuration)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
cred.PublicId = credId
|
||||
cred.IsRenewable = secret.Renewable // possibly set to just false
|
||||
|
||||
// same location for both
|
||||
cert, ok := secret.Data["signed_key"].(string)
|
||||
if !ok {
|
||||
return nil, errors.New(ctx, errors.VaultInvalidCredentialMapping, op, "vault secret did not contain a signed key or response was not in the expected format")
|
||||
}
|
||||
|
||||
return &sshCertCred{
|
||||
sshPrivateKeyCred: &sshPrivateKeyCred{
|
||||
baseCred: &baseCred{
|
||||
Credential: cred,
|
||||
lib: lib,
|
||||
secretData: secret.Data,
|
||||
},
|
||||
username: username,
|
||||
privateKey: privateKey,
|
||||
},
|
||||
certificate: []byte(cert),
|
||||
}, nil
|
||||
}
|
||||
@@ -2,7 +2,9 @@ package vault
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/credential"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
@@ -10,6 +12,8 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/scheduler"
|
||||
"github.com/hashicorp/boundary/internal/util"
|
||||
"github.com/hashicorp/boundary/internal/util/template"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -63,7 +67,7 @@ func TestRepository_getPrivateLibraries(t *testing.T) {
|
||||
opts = append(opts, WithClientCert(clientCert))
|
||||
}
|
||||
|
||||
_, token := v.CreateToken(t)
|
||||
_, token := v.CreateToken(t, WithTokenPeriod(time.Hour))
|
||||
|
||||
credStoreIn, err := NewCredentialStore(prj.GetPublicId(), v.Addr, []byte(token), opts...)
|
||||
assert.NoError(err)
|
||||
@@ -269,7 +273,10 @@ func TestRepository_getPrivateLibraries(t *testing.T) {
|
||||
require.NotNil(gotLibs)
|
||||
assert.Len(gotLibs, len(libs))
|
||||
|
||||
for _, got := range gotLibs {
|
||||
for _, gotLib := range gotLibs {
|
||||
got, ok := gotLib.(*genericIssuingCredentialLibrary)
|
||||
require.True(ok)
|
||||
|
||||
assert.Equal(TokenSecret(token), got.Token)
|
||||
if tt.tls == TestClientTLS {
|
||||
require.NotNil(got.ClientKey)
|
||||
@@ -414,7 +421,7 @@ func TestBaseToUsrPass(t *testing.T) {
|
||||
{
|
||||
name: "library-not-username-password-type",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.UnspecifiedType),
|
||||
},
|
||||
},
|
||||
@@ -423,7 +430,7 @@ func TestBaseToUsrPass(t *testing.T) {
|
||||
{
|
||||
name: "invalid-no-username-default-password-attribute",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.UsernamePasswordType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
@@ -435,7 +442,7 @@ func TestBaseToUsrPass(t *testing.T) {
|
||||
{
|
||||
name: "invalid-no-password-default-username-attribute",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.UsernamePasswordType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
@@ -447,7 +454,7 @@ func TestBaseToUsrPass(t *testing.T) {
|
||||
{
|
||||
name: "valid-default-attributes",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.UsernamePasswordType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
@@ -463,7 +470,7 @@ func TestBaseToUsrPass(t *testing.T) {
|
||||
{
|
||||
name: "valid-override-attributes",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.UsernamePasswordType),
|
||||
UsernameAttribute: "test-username",
|
||||
PasswordAttribute: "test-password",
|
||||
@@ -483,7 +490,7 @@ func TestBaseToUsrPass(t *testing.T) {
|
||||
{
|
||||
name: "valid-default-username-override-password",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.UsernamePasswordType),
|
||||
PasswordAttribute: "test-password",
|
||||
},
|
||||
@@ -502,7 +509,7 @@ func TestBaseToUsrPass(t *testing.T) {
|
||||
{
|
||||
name: "valid-override-username-default-password",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.UsernamePasswordType),
|
||||
UsernameAttribute: "test-username",
|
||||
},
|
||||
@@ -521,7 +528,7 @@ func TestBaseToUsrPass(t *testing.T) {
|
||||
{
|
||||
name: "invalid-username-override",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.UsernamePasswordType),
|
||||
UsernameAttribute: "missing-username",
|
||||
},
|
||||
@@ -537,7 +544,7 @@ func TestBaseToUsrPass(t *testing.T) {
|
||||
{
|
||||
name: "invalid-password-override",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.UsernamePasswordType),
|
||||
UsernameAttribute: "missing-password",
|
||||
},
|
||||
@@ -553,7 +560,7 @@ func TestBaseToUsrPass(t *testing.T) {
|
||||
{
|
||||
name: "invalid-kv2-no-metadata-field",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.UsernamePasswordType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
@@ -568,7 +575,7 @@ func TestBaseToUsrPass(t *testing.T) {
|
||||
{
|
||||
name: "invalid-kv2-no-data-field",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.UsernamePasswordType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
@@ -580,7 +587,7 @@ func TestBaseToUsrPass(t *testing.T) {
|
||||
{
|
||||
name: "invalid-kv2-no-username-default-password-attribute",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.UsernamePasswordType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
@@ -595,7 +602,7 @@ func TestBaseToUsrPass(t *testing.T) {
|
||||
{
|
||||
name: "invalid-kv2-no-passsword-default-username-attribute",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.UsernamePasswordType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
@@ -610,7 +617,7 @@ func TestBaseToUsrPass(t *testing.T) {
|
||||
{
|
||||
name: "invalid-kv2-invalid-metadata-type",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.UsernamePasswordType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
@@ -626,7 +633,7 @@ func TestBaseToUsrPass(t *testing.T) {
|
||||
{
|
||||
name: "invalid-kv2-invalid-metadata-type",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.UsernamePasswordType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
@@ -639,7 +646,7 @@ func TestBaseToUsrPass(t *testing.T) {
|
||||
{
|
||||
name: "invalid-kv2-additional-field",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.UsernamePasswordType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
@@ -656,7 +663,7 @@ func TestBaseToUsrPass(t *testing.T) {
|
||||
{
|
||||
name: "valid-kv2-default-attributes",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.UsernamePasswordType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
@@ -675,7 +682,7 @@ func TestBaseToUsrPass(t *testing.T) {
|
||||
{
|
||||
name: "valid-kv2-override-attributes",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.UsernamePasswordType),
|
||||
UsernameAttribute: "test-username",
|
||||
PasswordAttribute: "test-password",
|
||||
@@ -698,7 +705,7 @@ func TestBaseToUsrPass(t *testing.T) {
|
||||
{
|
||||
name: "valid-kv2-default-username-override-password",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.UsernamePasswordType),
|
||||
PasswordAttribute: "test-password",
|
||||
},
|
||||
@@ -720,7 +727,7 @@ func TestBaseToUsrPass(t *testing.T) {
|
||||
{
|
||||
name: "valid-kv2-override-username-default-password",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.UsernamePasswordType),
|
||||
UsernameAttribute: "test-username",
|
||||
},
|
||||
@@ -779,7 +786,7 @@ func TestBaseToSshPriKey(t *testing.T) {
|
||||
{
|
||||
name: "library-not-ssh-private-key-type",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.UnspecifiedType),
|
||||
},
|
||||
},
|
||||
@@ -788,7 +795,7 @@ func TestBaseToSshPriKey(t *testing.T) {
|
||||
{
|
||||
name: "invalid-no-username-default-pk-attribute",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.SshPrivateKeyType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
@@ -800,7 +807,7 @@ func TestBaseToSshPriKey(t *testing.T) {
|
||||
{
|
||||
name: "invalid-no-pk-default-username-attribute",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.SshPrivateKeyType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
@@ -812,7 +819,7 @@ func TestBaseToSshPriKey(t *testing.T) {
|
||||
{
|
||||
name: "valid-default-attributes",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.SshPrivateKeyType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
@@ -828,7 +835,7 @@ func TestBaseToSshPriKey(t *testing.T) {
|
||||
{
|
||||
name: "valid-default-attributes-with-passphrase",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.SshPrivateKeyType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
@@ -846,7 +853,7 @@ func TestBaseToSshPriKey(t *testing.T) {
|
||||
{
|
||||
name: "valid-override-attributes",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.SshPrivateKeyType),
|
||||
UsernameAttribute: "test-username",
|
||||
PrivateKeyAttribute: "test-pk",
|
||||
@@ -866,7 +873,7 @@ func TestBaseToSshPriKey(t *testing.T) {
|
||||
{
|
||||
name: "valid-override-attributes-with-passphrase",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.SshPrivateKeyType),
|
||||
UsernameAttribute: "test-username",
|
||||
PrivateKeyAttribute: "test-pk",
|
||||
@@ -890,7 +897,7 @@ func TestBaseToSshPriKey(t *testing.T) {
|
||||
{
|
||||
name: "valid-default-username-override-pk",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.SshPrivateKeyType),
|
||||
PrivateKeyAttribute: "test-pk",
|
||||
},
|
||||
@@ -909,7 +916,7 @@ func TestBaseToSshPriKey(t *testing.T) {
|
||||
{
|
||||
name: "valid-override-username-default-pk",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.SshPrivateKeyType),
|
||||
UsernameAttribute: "test-username",
|
||||
},
|
||||
@@ -928,7 +935,7 @@ func TestBaseToSshPriKey(t *testing.T) {
|
||||
{
|
||||
name: "invalid-username-override",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.SshPrivateKeyType),
|
||||
UsernameAttribute: "missing-username",
|
||||
},
|
||||
@@ -944,7 +951,7 @@ func TestBaseToSshPriKey(t *testing.T) {
|
||||
{
|
||||
name: "invalid-pk-override",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.SshPrivateKeyType),
|
||||
UsernameAttribute: "missing-pk",
|
||||
},
|
||||
@@ -960,7 +967,7 @@ func TestBaseToSshPriKey(t *testing.T) {
|
||||
{
|
||||
name: "invalid-kv2-no-metadata-field",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.SshPrivateKeyType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
@@ -975,7 +982,7 @@ func TestBaseToSshPriKey(t *testing.T) {
|
||||
{
|
||||
name: "invalid-kv2-no-data-field",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.SshPrivateKeyType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
@@ -987,7 +994,7 @@ func TestBaseToSshPriKey(t *testing.T) {
|
||||
{
|
||||
name: "invalid-kv2-no-username-default-pk-attribute",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.SshPrivateKeyType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
@@ -1002,7 +1009,7 @@ func TestBaseToSshPriKey(t *testing.T) {
|
||||
{
|
||||
name: "invalid-kv2-no-pk-default-username-attribute",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.SshPrivateKeyType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
@@ -1017,7 +1024,7 @@ func TestBaseToSshPriKey(t *testing.T) {
|
||||
{
|
||||
name: "invalid-kv2-invalid-metadata-type",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.SshPrivateKeyType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
@@ -1033,7 +1040,7 @@ func TestBaseToSshPriKey(t *testing.T) {
|
||||
{
|
||||
name: "invalid-kv2-invalid-data-type",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.SshPrivateKeyType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
@@ -1046,7 +1053,7 @@ func TestBaseToSshPriKey(t *testing.T) {
|
||||
{
|
||||
name: "invalid-kv2-additional-field",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.SshPrivateKeyType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
@@ -1063,7 +1070,7 @@ func TestBaseToSshPriKey(t *testing.T) {
|
||||
{
|
||||
name: "valid-kv2-default-attributes",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.SshPrivateKeyType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
@@ -1082,7 +1089,7 @@ func TestBaseToSshPriKey(t *testing.T) {
|
||||
{
|
||||
name: "valid-kv2-default-attributes-with-passphrase",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.SshPrivateKeyType),
|
||||
},
|
||||
secretData: map[string]any{
|
||||
@@ -1103,7 +1110,7 @@ func TestBaseToSshPriKey(t *testing.T) {
|
||||
{
|
||||
name: "valid-kv2-override-attributes",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.SshPrivateKeyType),
|
||||
UsernameAttribute: "test-username",
|
||||
PrivateKeyAttribute: "test-pk",
|
||||
@@ -1126,7 +1133,7 @@ func TestBaseToSshPriKey(t *testing.T) {
|
||||
{
|
||||
name: "valid-kv2-override-attributes-with-passphrase",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.SshPrivateKeyType),
|
||||
UsernameAttribute: "test-username",
|
||||
PrivateKeyAttribute: "test-pk",
|
||||
@@ -1153,7 +1160,7 @@ func TestBaseToSshPriKey(t *testing.T) {
|
||||
{
|
||||
name: "valid-kv2-default-username-override-pk",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.SshPrivateKeyType),
|
||||
PrivateKeyAttribute: "test-pk",
|
||||
},
|
||||
@@ -1175,7 +1182,7 @@ func TestBaseToSshPriKey(t *testing.T) {
|
||||
{
|
||||
name: "valid-kv2-override-username-default-pk",
|
||||
given: &baseCred{
|
||||
lib: &issueCredentialLibrary{
|
||||
lib: &genericIssuingCredentialLibrary{
|
||||
CredType: string(credential.SshPrivateKeyType),
|
||||
UsernameAttribute: "test-username",
|
||||
},
|
||||
@@ -1212,3 +1219,171 @@ func TestBaseToSshPriKey(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepository_sshCertIssuingCredentialLibrary_retrieveCredential(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// create test vault server
|
||||
v := NewTestVaultServer(t, WithTestVaultTLS(TestNoTLS), WithVaultVersion("1.12.2"))
|
||||
require.NotNil(t, v)
|
||||
|
||||
vc := v.client(t).cl
|
||||
mounts, err := vc.Sys().ListMounts()
|
||||
assert.NoError(t, err)
|
||||
require.NotEmpty(t, mounts)
|
||||
beforeCount := len(mounts)
|
||||
|
||||
// enable ssh secrets engine
|
||||
v.MountSSH(t, WithAllowedExtension("permit-pty"))
|
||||
mounts, err = vc.Sys().ListMounts()
|
||||
fmt.Printf("mounts: %#v\n", mounts)
|
||||
assert.NoError(t, err)
|
||||
require.NotEmpty(t, mounts)
|
||||
afterCount := len(mounts)
|
||||
assert.Greater(t, afterCount, beforeCount)
|
||||
|
||||
// create and setup db
|
||||
ctx := context.Background()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
sec, token := v.CreateToken(t, WithPolicies([]string{"default", "boundary-controller", "ssh"}), WithTokenPeriod(time.Hour))
|
||||
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := NewRepository(rw, rw, kms, sche)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, repo)
|
||||
|
||||
cs := TestCredentialStore(t, conn, wrapper, prj.GetPublicId(), v.Addr, token, sec.Auth.Accessor)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
username string
|
||||
vaulthPath string
|
||||
opts []Option
|
||||
retOpts []credential.Option
|
||||
expected map[string]string
|
||||
}{
|
||||
{
|
||||
name: "vault sign boundary ed25519 key",
|
||||
username: "username",
|
||||
vaulthPath: "ssh/sign/boundary",
|
||||
opts: []Option{WithKeyType(KeyTypeEd25519)},
|
||||
},
|
||||
{
|
||||
name: "vault sign boundary rsa(4096) key",
|
||||
username: "username-2-electric-boogaloo",
|
||||
vaulthPath: "ssh/sign/boundary",
|
||||
opts: []Option{WithKeyType(KeyTypeRsa), WithKeyBits(4096)},
|
||||
},
|
||||
{
|
||||
name: "vault sign boundary ec(521) key",
|
||||
username: "username-3-the-namening",
|
||||
vaulthPath: "ssh/sign/boundary",
|
||||
opts: []Option{WithKeyType(KeyTypeEcdsa), WithKeyBits(521)},
|
||||
},
|
||||
{
|
||||
name: "vault issue ed25519 cert",
|
||||
username: "username-4-revengeance",
|
||||
vaulthPath: "ssh/issue/boundary",
|
||||
opts: []Option{WithKeyType(KeyTypeEd25519)},
|
||||
},
|
||||
{
|
||||
name: "vault issue rsa(4096) cert",
|
||||
username: "username-5-this-time-its-personal",
|
||||
vaulthPath: "ssh/issue/boundary",
|
||||
opts: []Option{WithKeyType(KeyTypeRsa), WithKeyBits(4096)},
|
||||
},
|
||||
{
|
||||
name: "vault issue ec(521) cert",
|
||||
username: "username-6-the-holiday-episode",
|
||||
vaulthPath: "ssh/issue/boundary",
|
||||
opts: []Option{WithKeyType(KeyTypeEcdsa), WithKeyBits(521)},
|
||||
},
|
||||
{
|
||||
name: "vault issue rsa(2048) cert with critical options and extensions",
|
||||
username: "username-7-because-789",
|
||||
vaulthPath: "ssh/issue/boundary",
|
||||
opts: []Option{WithKeyType(KeyTypeRsa), WithKeyBits(2048), WithCriticalOptions("{ \"force-commnad\": \"/bin/some-script\" }"), WithExtensions("{ \"permit-pty\": \"\" }")},
|
||||
},
|
||||
{
|
||||
name: "vault sign boundary rsa(3072) key with critical options and extensions",
|
||||
username: "username-7-because-789",
|
||||
vaulthPath: "ssh/sign/boundary",
|
||||
opts: []Option{WithKeyType(KeyTypeRsa), WithKeyBits(3072), WithCriticalOptions("{ \"force-commnad\": \"/bin/some-script\" }"), WithExtensions("{ \"permit-pty\": \"\" }")},
|
||||
},
|
||||
{
|
||||
name: "vault issue ec(256) cert with template username",
|
||||
username: "username-8-{{ .User.Name }}",
|
||||
expected: map[string]string{
|
||||
"username": "username-8-revenge-of-the-template",
|
||||
},
|
||||
vaulthPath: "ssh/issue/boundary",
|
||||
opts: []Option{WithKeyType(KeyTypeEcdsa), WithKeyBits(256)},
|
||||
retOpts: []credential.Option{credential.WithTemplateData(template.Data{User: template.User{Name: util.Pointer("revenge-of-the-template")}})},
|
||||
},
|
||||
{
|
||||
name: "vault issue ec(384) cert with disallowed extension",
|
||||
username: "username-10-because-789",
|
||||
expected: map[string]string{
|
||||
"error": "extensions [permit-port-forwarding] are not on allowed list",
|
||||
},
|
||||
vaulthPath: "ssh/issue/boundary",
|
||||
opts: []Option{WithKeyType(KeyTypeEcdsa), WithKeyBits(384), WithExtensions("{ \"permit-port-forwarding\": \"\" }")},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
require := require.New(t)
|
||||
// create ssh library
|
||||
lib, err := NewSSHCertificateCredentialLibrary(cs.GetPublicId(), tt.vaulthPath, tt.username, tt.opts...)
|
||||
require.NoError(err)
|
||||
require.NotNil(lib)
|
||||
lib.PublicId, err = newSSHCertificateCredentialLibraryId()
|
||||
require.NoError(err)
|
||||
|
||||
_, err = rw.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{},
|
||||
func(_ db.Reader, iw db.Writer) error {
|
||||
return iw.Create(ctx, lib)
|
||||
},
|
||||
)
|
||||
require.NoError(err)
|
||||
|
||||
req := credential.Request{
|
||||
SourceId: lib.GetPublicId(),
|
||||
Purpose: "doesn't matter",
|
||||
}
|
||||
|
||||
libs, err := repo.getIssueCredLibraries(ctx, []credential.Request{req})
|
||||
require.NoError(err)
|
||||
require.NotEmpty(libs)
|
||||
require.Equal(1, len(libs))
|
||||
|
||||
cred, err := libs[0].retrieveCredential(ctx, "op", tt.retOpts...)
|
||||
if retErr, ok := tt.expected["error"]; ok {
|
||||
require.ErrorContains(err, retErr)
|
||||
return // retrieveCredential failed (as expected) don't do the rest of the checks
|
||||
}
|
||||
require.NoError(err)
|
||||
|
||||
cert, ok := cred.(*sshCertCred)
|
||||
require.True(ok)
|
||||
|
||||
if usr, ok := tt.expected["username"]; ok {
|
||||
require.Equal(usr, cert.username)
|
||||
}
|
||||
|
||||
// TODO: somehow check that the ssh cert matches the private key
|
||||
// for now, manually check that private key and cert match and that sign/issue both return correct formats
|
||||
fmt.Printf("test: %s\npriv:\n%s\ncert:\n%s\n", tt.name, string(cert.privateKey), string(cert.certificate))
|
||||
|
||||
// ensure credential matches expected SshCertificate
|
||||
_, ok = cred.(credential.SshCertificate)
|
||||
require.True(ok)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -8,18 +8,27 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
if err := subtypes.Register(credential.Domain, Subtype, CredentialStorePrefix, CredentialLibraryPrefix, DynamicCredentialPrefix); err != nil {
|
||||
if err := subtypes.Register(credential.Domain, Subtype, CredentialStorePrefix, DynamicCredentialPrefix); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := subtypes.Register(credential.Domain, GenericLibrarySubtype, CredentialLibraryPrefix); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := subtypes.Register(credential.Domain, SSHCertificateLibrarySubtype, SSHCertificateCredentialLibraryPrefix); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// PublicId prefixes for the resources in the vault package.
|
||||
const (
|
||||
CredentialStorePrefix = "csvlt"
|
||||
CredentialLibraryPrefix = "clvlt"
|
||||
DynamicCredentialPrefix = "cdvlt"
|
||||
CredentialStorePrefix = "csvlt"
|
||||
CredentialLibraryPrefix = "clvlt"
|
||||
DynamicCredentialPrefix = "cdvlt"
|
||||
SSHCertificateCredentialLibraryPrefix = "clvsclt"
|
||||
|
||||
Subtype = subtypes.Subtype("vault")
|
||||
Subtype = subtypes.Subtype("vault")
|
||||
GenericLibrarySubtype = subtypes.Subtype("vault-generic")
|
||||
SSHCertificateLibrarySubtype = subtypes.Subtype("vault-ssh-certificate")
|
||||
)
|
||||
|
||||
func newCredentialStoreId() (string, error) {
|
||||
@@ -45,3 +54,11 @@ func newCredentialLibraryId() (string, error) {
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func newSSHCertificateCredentialLibraryId() (string, error) {
|
||||
id, err := db.NewPublicId(SSHCertificateCredentialLibraryPrefix)
|
||||
if err != nil {
|
||||
return "", errors.WrapDeprecated(err, "vault.newSSHCertificateCredentialLibraryPrefix")
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package vault
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
@@ -13,6 +14,38 @@ import (
|
||||
|
||||
var _ credential.Issuer = (*Repository)(nil)
|
||||
|
||||
func insertQuery(c *Credential, sessionId string) (query string, queryValues []any) {
|
||||
queryValues = []any{
|
||||
sql.Named("public_id", c.PublicId),
|
||||
sql.Named("library_id", c.LibraryId),
|
||||
sql.Named("session_id", sessionId),
|
||||
sql.Named("token_hmac", c.TokenHmac),
|
||||
sql.Named("external_id", c.ExternalId),
|
||||
sql.Named("is_renewable", c.IsRenewable),
|
||||
sql.Named("status", c.Status),
|
||||
sql.Named("last_renewal_time", "now()"),
|
||||
}
|
||||
switch {
|
||||
case c.expiration == 0:
|
||||
query = insertCredentialWithInfiniteExpirationQuery
|
||||
default:
|
||||
query = insertCredentialWithExpirationQuery
|
||||
queryValues = append(queryValues, sql.Named("expiration_time", int(c.expiration.Round(time.Second).Seconds())))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func updateSessionQuery(c *Credential, sessionId string, purpose credential.Purpose) (query string, queryValues []any) {
|
||||
queryValues = []any{
|
||||
sql.Named("public_id", c.PublicId),
|
||||
sql.Named("library_id", c.LibraryId),
|
||||
sql.Named("session_id", sessionId),
|
||||
sql.Named("purpose", string(purpose)),
|
||||
}
|
||||
query = updateSessionCredentialQuery
|
||||
return
|
||||
}
|
||||
|
||||
// Issue issues and returns dynamic credentials from Vault for all of the
|
||||
// requests and assigns them to sessionId.
|
||||
func (r *Repository) Issue(ctx context.Context, sessionId string, requests []credential.Request, opt ...credential.Option) ([]credential.Dynamic, error) {
|
||||
@@ -37,11 +70,17 @@ func (r *Repository) Issue(ctx context.Context, sessionId string, requests []cre
|
||||
var minLease time.Duration
|
||||
runJobsInterval := r.scheduler.GetRunJobsInterval()
|
||||
for _, lib := range libs {
|
||||
cred, err := lib.retrieveCredential(ctx, op, sessionId, opt...)
|
||||
cred, err := lib.retrieveCredential(ctx, op, opt...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
creds = append(creds, cred)
|
||||
if !cred.isRevokable() {
|
||||
// No need to persist since the credential cannot be revoked nor renewed
|
||||
continue
|
||||
}
|
||||
|
||||
if cred.getExpiration() < runJobsInterval {
|
||||
event.WriteError(ctx, op,
|
||||
fmt.Errorf("WARNING: credential will expire before job scheduler can run"),
|
||||
@@ -54,8 +93,18 @@ func (r *Repository) Issue(ctx context.Context, sessionId string, requests []cre
|
||||
if minLease > cred.getExpiration() {
|
||||
minLease = cred.getExpiration()
|
||||
}
|
||||
insertQuery, insertQueryValues := cred.insertQuery()
|
||||
updateQuery, updateQueryValues := cred.updateSessionQuery(lib.Purpose)
|
||||
|
||||
underlyingCred := cred.getCredential()
|
||||
|
||||
insertQuery, insertQueryValues := insertQuery(underlyingCred, sessionId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
updateQuery, updateQueryValues := updateSessionQuery(underlyingCred, sessionId, cred.Purpose())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{},
|
||||
func(_ db.Reader, w db.Writer) error {
|
||||
rowsInserted, err := w.Exec(ctx, insertQuery, insertQueryValues)
|
||||
@@ -80,8 +129,6 @@ func (r *Repository) Issue(ctx context.Context, sessionId string, requests []cre
|
||||
); err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
creds = append(creds, cred)
|
||||
}
|
||||
|
||||
// Best effort update next run time of credential renewal job, but an error should not
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/credential"
|
||||
"github.com/hashicorp/boundary/internal/credential/vault"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/db/timestamp"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/hashicorp/boundary/internal/host/static"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
@@ -21,6 +22,95 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// since we're not using gorm tags to retrieve this, it's faster and easier to just make a new struct than import usrPassCred from the vault package
|
||||
type revokableCred struct {
|
||||
PublicId string
|
||||
LibraryId string
|
||||
SessionId string
|
||||
TokenHmac []byte
|
||||
ExternalId string
|
||||
IsRenewable bool
|
||||
Status string
|
||||
LastRenewalTime *timestamp.Timestamp
|
||||
ExpirationTime *timestamp.Timestamp
|
||||
}
|
||||
|
||||
func lookupDbCred(t *testing.T, ctx context.Context, rw *db.Db, dc credential.Dynamic) *revokableCred {
|
||||
rows, err := rw.Query(ctx, `
|
||||
select
|
||||
public_id,
|
||||
library_id,
|
||||
session_id,
|
||||
token_hmac,
|
||||
external_id,
|
||||
is_renewable,
|
||||
status,
|
||||
last_renewal_time,
|
||||
expiration_time
|
||||
from credential_vault_credential
|
||||
where public_id = ?;
|
||||
`, []any{dc.GetPublicId()})
|
||||
require.NoError(t, err)
|
||||
rowCount := 0
|
||||
|
||||
got := revokableCred{}
|
||||
|
||||
for rows.Next() {
|
||||
rowCount++
|
||||
require.NoError(t, rows.Scan(
|
||||
&got.PublicId,
|
||||
&got.LibraryId,
|
||||
&got.SessionId,
|
||||
&got.TokenHmac,
|
||||
&got.ExternalId,
|
||||
&got.IsRenewable,
|
||||
&got.Status,
|
||||
&got.LastRenewalTime,
|
||||
&got.ExpirationTime,
|
||||
))
|
||||
}
|
||||
// Should never get more than one that matches, but can get 0
|
||||
assert.LessOrEqual(t, rowCount, 1)
|
||||
|
||||
if rowCount == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &got
|
||||
}
|
||||
|
||||
type libT int
|
||||
|
||||
const (
|
||||
libDB libT = iota
|
||||
libUsrPassDB
|
||||
libErrUsrPassDB
|
||||
libPKI
|
||||
libErrPKI
|
||||
libKV
|
||||
libErrKV
|
||||
libUsrPassKV
|
||||
libSshPkKV
|
||||
libExpiredToken
|
||||
)
|
||||
|
||||
type testLib struct {
|
||||
PublicId string
|
||||
HasLease bool
|
||||
IsRenewable bool
|
||||
}
|
||||
|
||||
type testLibMap map[libT]testLib
|
||||
|
||||
func (m testLibMap) GetByPublicId(id string) (testLib, bool) {
|
||||
for _, v := range m {
|
||||
if v.PublicId == id {
|
||||
return v, true
|
||||
}
|
||||
}
|
||||
return testLib{}, false
|
||||
}
|
||||
|
||||
func TestRepository_IssueCredentials(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
@@ -80,21 +170,7 @@ func TestRepository_IssueCredentials(t *testing.T) {
|
||||
assert.Equal(t, 1, num)
|
||||
v.RevokeToken(t, expToken)
|
||||
|
||||
type libT int
|
||||
const (
|
||||
libDB libT = iota
|
||||
libUsrPassDB
|
||||
libErrUsrPassDB
|
||||
libPKI
|
||||
libErrPKI
|
||||
libKV
|
||||
libErrKV
|
||||
libUsrPassKV
|
||||
libSshPkKV
|
||||
libExpiredToken
|
||||
)
|
||||
|
||||
libs := make(map[libT]string)
|
||||
libs := make(testLibMap)
|
||||
{
|
||||
libPath := path.Join("database", "creds", "opened")
|
||||
libIn, err := vault.NewCredentialLibrary(origStore.GetPublicId(), libPath)
|
||||
@@ -103,7 +179,7 @@ func TestRepository_IssueCredentials(t *testing.T) {
|
||||
lib, err := repo.CreateCredentialLibrary(ctx, prj.GetPublicId(), libIn)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, lib)
|
||||
libs[libDB] = lib.GetPublicId()
|
||||
libs[libDB] = testLib{PublicId: lib.GetPublicId(), HasLease: true, IsRenewable: true}
|
||||
}
|
||||
{
|
||||
libPath := path.Join("pki", "issue", "boundary")
|
||||
@@ -113,7 +189,7 @@ func TestRepository_IssueCredentials(t *testing.T) {
|
||||
lib, err := repo.CreateCredentialLibrary(ctx, prj.GetPublicId(), libIn)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, lib)
|
||||
libs[libPKI] = lib.GetPublicId()
|
||||
libs[libPKI] = testLib{PublicId: lib.GetPublicId(), HasLease: false, IsRenewable: false}
|
||||
}
|
||||
{
|
||||
|
||||
@@ -124,7 +200,7 @@ func TestRepository_IssueCredentials(t *testing.T) {
|
||||
lib, err := repo.CreateCredentialLibrary(ctx, prj.GetPublicId(), libIn)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, lib)
|
||||
libs[libErrPKI] = lib.GetPublicId()
|
||||
libs[libErrPKI] = testLib{PublicId: lib.GetPublicId(), HasLease: true, IsRenewable: true}
|
||||
}
|
||||
{
|
||||
libPath := path.Join("secret", "data", "my-up-secret")
|
||||
@@ -134,7 +210,7 @@ func TestRepository_IssueCredentials(t *testing.T) {
|
||||
lib, err := repo.CreateCredentialLibrary(ctx, prj.GetPublicId(), libIn)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, lib)
|
||||
libs[libKV] = lib.GetPublicId()
|
||||
libs[libKV] = testLib{PublicId: lib.GetPublicId(), HasLease: false, IsRenewable: true}
|
||||
}
|
||||
{
|
||||
libPath := path.Join("secret", "data", "fake-secret")
|
||||
@@ -144,7 +220,7 @@ func TestRepository_IssueCredentials(t *testing.T) {
|
||||
lib, err := repo.CreateCredentialLibrary(ctx, prj.GetPublicId(), libIn)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, lib)
|
||||
libs[libErrKV] = lib.GetPublicId()
|
||||
libs[libErrKV] = testLib{PublicId: lib.GetPublicId(), HasLease: false, IsRenewable: true}
|
||||
}
|
||||
{
|
||||
libPath := path.Join("database", "creds", "opened")
|
||||
@@ -157,7 +233,7 @@ func TestRepository_IssueCredentials(t *testing.T) {
|
||||
lib, err := repo.CreateCredentialLibrary(ctx, prj.GetPublicId(), libIn)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, lib)
|
||||
libs[libUsrPassDB] = lib.GetPublicId()
|
||||
libs[libUsrPassDB] = testLib{PublicId: lib.GetPublicId(), HasLease: true, IsRenewable: true}
|
||||
}
|
||||
{
|
||||
libPath := path.Join("database", "creds", "opened")
|
||||
@@ -174,7 +250,7 @@ func TestRepository_IssueCredentials(t *testing.T) {
|
||||
lib, err := repo.CreateCredentialLibrary(ctx, prj.GetPublicId(), libIn)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, lib)
|
||||
libs[libErrUsrPassDB] = lib.GetPublicId()
|
||||
libs[libErrUsrPassDB] = testLib{PublicId: lib.GetPublicId(), HasLease: true, IsRenewable: true}
|
||||
}
|
||||
{
|
||||
libPath := path.Join("secret", "data", "my-up-secret")
|
||||
@@ -187,7 +263,7 @@ func TestRepository_IssueCredentials(t *testing.T) {
|
||||
lib, err := repo.CreateCredentialLibrary(ctx, prj.GetPublicId(), libIn)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, lib)
|
||||
libs[libUsrPassKV] = lib.GetPublicId()
|
||||
libs[libUsrPassKV] = testLib{PublicId: lib.GetPublicId(), HasLease: false}
|
||||
}
|
||||
{
|
||||
libPath := path.Join("secret", "data", "my-sshpk-secret")
|
||||
@@ -200,7 +276,7 @@ func TestRepository_IssueCredentials(t *testing.T) {
|
||||
lib, err := repo.CreateCredentialLibrary(ctx, prj.GetPublicId(), libIn)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, lib)
|
||||
libs[libSshPkKV] = lib.GetPublicId()
|
||||
libs[libSshPkKV] = testLib{PublicId: lib.GetPublicId(), HasLease: false}
|
||||
}
|
||||
{
|
||||
libPath := path.Join("secret", "data", "my-up-secret")
|
||||
@@ -213,7 +289,7 @@ func TestRepository_IssueCredentials(t *testing.T) {
|
||||
lib, err := repo.CreateCredentialLibrary(ctx, prj.GetPublicId(), libIn)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, lib)
|
||||
libs[libExpiredToken] = lib.GetPublicId()
|
||||
libs[libExpiredToken] = testLib{PublicId: lib.GetPublicId(), HasLease: false}
|
||||
}
|
||||
|
||||
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
||||
@@ -250,7 +326,7 @@ func TestRepository_IssueCredentials(t *testing.T) {
|
||||
convertFn: rc2dc,
|
||||
requests: []credential.Request{
|
||||
{
|
||||
SourceId: libs[libDB],
|
||||
SourceId: libs[libDB].PublicId,
|
||||
Purpose: credential.BrokeredPurpose,
|
||||
},
|
||||
},
|
||||
@@ -260,11 +336,11 @@ func TestRepository_IssueCredentials(t *testing.T) {
|
||||
convertFn: rc2dc,
|
||||
requests: []credential.Request{
|
||||
{
|
||||
SourceId: libs[libDB],
|
||||
SourceId: libs[libDB].PublicId,
|
||||
Purpose: credential.BrokeredPurpose,
|
||||
},
|
||||
{
|
||||
SourceId: libs[libPKI],
|
||||
SourceId: libs[libPKI].PublicId,
|
||||
Purpose: credential.BrokeredPurpose,
|
||||
},
|
||||
},
|
||||
@@ -274,7 +350,7 @@ func TestRepository_IssueCredentials(t *testing.T) {
|
||||
convertFn: rc2dc,
|
||||
requests: []credential.Request{
|
||||
{
|
||||
SourceId: libs[libErrPKI],
|
||||
SourceId: libs[libErrPKI].PublicId,
|
||||
Purpose: credential.BrokeredPurpose,
|
||||
},
|
||||
},
|
||||
@@ -285,11 +361,11 @@ func TestRepository_IssueCredentials(t *testing.T) {
|
||||
convertFn: rc2nil,
|
||||
requests: []credential.Request{
|
||||
{
|
||||
SourceId: libs[libDB],
|
||||
SourceId: libs[libDB].PublicId,
|
||||
Purpose: credential.BrokeredPurpose,
|
||||
},
|
||||
{
|
||||
SourceId: libs[libPKI],
|
||||
SourceId: libs[libPKI].PublicId,
|
||||
Purpose: credential.BrokeredPurpose,
|
||||
},
|
||||
},
|
||||
@@ -300,7 +376,7 @@ func TestRepository_IssueCredentials(t *testing.T) {
|
||||
convertFn: rc2dc,
|
||||
requests: []credential.Request{
|
||||
{
|
||||
SourceId: libs[libKV],
|
||||
SourceId: libs[libKV].PublicId,
|
||||
Purpose: credential.BrokeredPurpose,
|
||||
},
|
||||
},
|
||||
@@ -310,7 +386,7 @@ func TestRepository_IssueCredentials(t *testing.T) {
|
||||
convertFn: rc2dc,
|
||||
requests: []credential.Request{
|
||||
{
|
||||
SourceId: libs[libUsrPassDB],
|
||||
SourceId: libs[libUsrPassDB].PublicId,
|
||||
Purpose: credential.BrokeredPurpose,
|
||||
},
|
||||
},
|
||||
@@ -320,7 +396,7 @@ func TestRepository_IssueCredentials(t *testing.T) {
|
||||
convertFn: rc2dc,
|
||||
requests: []credential.Request{
|
||||
{
|
||||
SourceId: libs[libErrKV],
|
||||
SourceId: libs[libErrKV].PublicId,
|
||||
Purpose: credential.BrokeredPurpose,
|
||||
},
|
||||
},
|
||||
@@ -331,7 +407,7 @@ func TestRepository_IssueCredentials(t *testing.T) {
|
||||
convertFn: rc2dc,
|
||||
requests: []credential.Request{
|
||||
{
|
||||
SourceId: libs[libErrUsrPassDB],
|
||||
SourceId: libs[libErrUsrPassDB].PublicId,
|
||||
Purpose: credential.BrokeredPurpose,
|
||||
},
|
||||
},
|
||||
@@ -342,7 +418,7 @@ func TestRepository_IssueCredentials(t *testing.T) {
|
||||
convertFn: rc2dc,
|
||||
requests: []credential.Request{
|
||||
{
|
||||
SourceId: libs[libUsrPassKV],
|
||||
SourceId: libs[libUsrPassKV].PublicId,
|
||||
Purpose: credential.BrokeredPurpose,
|
||||
},
|
||||
},
|
||||
@@ -352,7 +428,7 @@ func TestRepository_IssueCredentials(t *testing.T) {
|
||||
convertFn: rc2dc,
|
||||
requests: []credential.Request{
|
||||
{
|
||||
SourceId: libs[libSshPkKV],
|
||||
SourceId: libs[libSshPkKV].PublicId,
|
||||
Purpose: credential.BrokeredPurpose,
|
||||
},
|
||||
},
|
||||
@@ -362,7 +438,7 @@ func TestRepository_IssueCredentials(t *testing.T) {
|
||||
convertFn: rc2dc,
|
||||
requests: []credential.Request{
|
||||
{
|
||||
SourceId: libs[libExpiredToken],
|
||||
SourceId: libs[libExpiredToken].PublicId,
|
||||
Purpose: credential.BrokeredPurpose,
|
||||
},
|
||||
},
|
||||
@@ -406,6 +482,19 @@ func TestRepository_IssueCredentials(t *testing.T) {
|
||||
assert.Fail("do not want UsernamePassword credential from library with credential type Unspecified")
|
||||
}
|
||||
}
|
||||
if lib, ok := libs.GetByPublicId(dc.Library().GetPublicId()); ok {
|
||||
retrieved := lookupDbCred(t, ctx, rw, dc)
|
||||
if lib.HasLease {
|
||||
// we also want to retrieve the cred from the db and make sure it's the same as the one returned by Issue
|
||||
require.NotNil(retrieved)
|
||||
require.Equal(retrieved.SessionId, sess.GetPublicId())
|
||||
require.Equal(retrieved.PublicId, dc.GetPublicId())
|
||||
assert.NotEmpty(retrieved.ExternalId)
|
||||
assert.Equal(retrieved.IsRenewable, lib.IsRenewable)
|
||||
} else {
|
||||
assert.Nil(retrieved)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,363 @@
|
||||
package vault
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/credential"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/oplog"
|
||||
"github.com/hashicorp/go-dbw"
|
||||
)
|
||||
|
||||
// CreateSSHCertificateCredentialLibrary inserts l into the repository and returns a new
|
||||
// SSHCertificateCredentialLibrary containing the credential library's PublicId. l is not
|
||||
// changed. l must contain a valid StoreId. l must not contain a PublicId.
|
||||
// The PublicId is generated and assigned by this method.
|
||||
//
|
||||
// Both l.Name and l.Description are optional. If l.Name is set, it must be
|
||||
// unique within l.StoreId.
|
||||
//
|
||||
// Both l.CreateTime and l.UpdateTime are ignored.
|
||||
func (r *Repository) CreateSSHCertificateCredentialLibrary(ctx context.Context, projectId string, l *SSHCertificateCredentialLibrary, _ ...Option) (*SSHCertificateCredentialLibrary, error) {
|
||||
const op = "vault.(Repository).CreateSSHCertificateCredentialLibrary"
|
||||
if l == nil {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "nil SSHCertificateCredentialLibrary")
|
||||
}
|
||||
if l.SSHCertificateCredentialLibrary == nil {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "nil embedded l")
|
||||
}
|
||||
if l.StoreId == "" {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "no store id")
|
||||
}
|
||||
if l.VaultPath == "" {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "no vault path")
|
||||
}
|
||||
if l.PublicId != "" {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "public id not empty")
|
||||
}
|
||||
if projectId == "" {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "no project id")
|
||||
}
|
||||
if l.Username == "" {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "no username")
|
||||
}
|
||||
|
||||
l = l.clone()
|
||||
|
||||
if l.KeyType == "" {
|
||||
l.KeyType = KeyTypeEd25519
|
||||
}
|
||||
|
||||
if l.KeyBits == KeyBitsDefault {
|
||||
l.KeyBits = l.getDefaultKeyBits()
|
||||
}
|
||||
|
||||
if l.GetCredentialType() == "" {
|
||||
l.SSHCertificateCredentialLibrary.CredentialType = string(credential.SshCertificateType)
|
||||
}
|
||||
if l.GetCredentialType() != string(credential.SshCertificateType) {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "invalid credential type")
|
||||
}
|
||||
|
||||
id, err := newSSHCertificateCredentialLibraryId()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
l.setId(id)
|
||||
|
||||
oplogWrapper, err := r.kms.GetWrapper(ctx, projectId, kms.KeyPurposeOplog)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper"))
|
||||
}
|
||||
|
||||
var newSSHCertificateCredentialLibrary *SSHCertificateCredentialLibrary
|
||||
_, err = r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{},
|
||||
func(_ db.Reader, w db.Writer) error {
|
||||
var msgs []*oplog.Message
|
||||
ticket, err := w.GetTicket(ctx, l)
|
||||
if err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket"))
|
||||
}
|
||||
|
||||
// insert credential library
|
||||
newSSHCertificateCredentialLibrary = l.clone()
|
||||
var lOplogMsg oplog.Message
|
||||
if err := w.Create(ctx, newSSHCertificateCredentialLibrary, db.NewOplogMsg(&lOplogMsg)); err != nil {
|
||||
return errors.Wrap(ctx, err, op)
|
||||
}
|
||||
msgs = append(msgs, &lOplogMsg)
|
||||
|
||||
metadata := l.oplog(oplog.OpType_OP_TYPE_CREATE)
|
||||
if err := w.WriteOplogEntryWith(ctx, oplogWrapper, ticket, metadata, msgs); err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write oplog"))
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
if errors.IsUniqueError(err) {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in credential store: %s: name %s already exists", l.StoreId, l.Name)))
|
||||
}
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in credential store: %s", l.StoreId)))
|
||||
}
|
||||
return newSSHCertificateCredentialLibrary, nil
|
||||
}
|
||||
|
||||
// UpdateSSHCertificateCredentialLibrary updates the repository entry for l.PublicId with
|
||||
// the values in l for the fields listed in fieldMaskPaths. It returns a
|
||||
// new SSHCertificateCredentialLibrary containing the updated values and a count of the
|
||||
// number of records updated. l is not changed.
|
||||
//
|
||||
// l must contain a valid PublicId. Name, Description, VaultPath, Username,
|
||||
// KeyType, KeyBits, Ttl, KeyId, CriticalOptions, and Extensions can be updated. If
|
||||
// l.Name is set to a non-empty string, it must be unique within l.StoreId.
|
||||
//
|
||||
// An attribute of l will be set to NULL in the database if the attribute
|
||||
// in l is the zero value and it is included in fieldMaskPaths.
|
||||
func (r *Repository) UpdateSSHCertificateCredentialLibrary(ctx context.Context, projectId string, l *SSHCertificateCredentialLibrary, version uint32, fieldMaskPaths []string, _ ...Option) (*SSHCertificateCredentialLibrary, int, error) {
|
||||
const op = "vault.(Repository).UpdateSSHCertificateCredentialLibrary"
|
||||
if l == nil {
|
||||
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing SSHCertificateCredentialLibrary")
|
||||
}
|
||||
if l.SSHCertificateCredentialLibrary == nil {
|
||||
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing embedded SSHCertificateCredentialLibrary")
|
||||
}
|
||||
if l.PublicId == "" {
|
||||
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidPublicId, op, "missing public id")
|
||||
}
|
||||
if version == 0 {
|
||||
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing version")
|
||||
}
|
||||
if projectId == "" {
|
||||
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing project id")
|
||||
}
|
||||
l = l.clone()
|
||||
|
||||
var keyTypeChange, keyBitChangeDefault bool
|
||||
|
||||
for _, f := range fieldMaskPaths {
|
||||
switch {
|
||||
case strings.EqualFold(nameField, f):
|
||||
case strings.EqualFold(descriptionField, f):
|
||||
case strings.EqualFold(vaultPathField, f):
|
||||
case strings.EqualFold(usernameField, f):
|
||||
case strings.EqualFold(keyTypeField, f):
|
||||
keyTypeChange = true
|
||||
case strings.EqualFold(keyBitsField, f):
|
||||
keyBitChangeDefault = l.KeyBits == KeyBitsDefault
|
||||
case strings.EqualFold(ttlField, f):
|
||||
case strings.EqualFold(keyIdField, f):
|
||||
case strings.EqualFold(CriticalOptionsField, f):
|
||||
case strings.EqualFold(ExtensionsField, f):
|
||||
|
||||
default:
|
||||
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidFieldMask, op, f)
|
||||
}
|
||||
}
|
||||
|
||||
if keyTypeChange && l.KeyType == "" {
|
||||
l.KeyType = KeyTypeEd25519
|
||||
}
|
||||
|
||||
if keyTypeChange && keyBitChangeDefault {
|
||||
l.KeyBits = l.getDefaultKeyBits()
|
||||
}
|
||||
|
||||
origLib, err := r.LookupSSHCertificateCredentialLibrary(ctx, l.PublicId)
|
||||
switch {
|
||||
case err != nil:
|
||||
return nil, db.NoRowsAffected, errors.Wrap(ctx, err, op)
|
||||
case origLib == nil:
|
||||
return nil, db.NoRowsAffected, errors.New(ctx, errors.RecordNotFound, op, fmt.Sprintf("credential library %s", l.PublicId))
|
||||
}
|
||||
|
||||
if keyBitChangeDefault && !keyTypeChange {
|
||||
l.KeyBits = origLib.getDefaultKeyBits()
|
||||
}
|
||||
|
||||
var dbMask, nullFields []string
|
||||
dbMask, nullFields = dbw.BuildUpdatePaths(
|
||||
map[string]any{
|
||||
nameField: l.Name,
|
||||
descriptionField: l.Description,
|
||||
vaultPathField: l.VaultPath,
|
||||
usernameField: l.Username,
|
||||
keyTypeField: l.KeyType,
|
||||
keyBitsField: l.KeyBits,
|
||||
ttlField: l.Ttl,
|
||||
keyIdField: l.KeyId,
|
||||
CriticalOptionsField: l.CriticalOptions,
|
||||
ExtensionsField: l.Extensions,
|
||||
},
|
||||
fieldMaskPaths,
|
||||
[]string{keyBitsField},
|
||||
)
|
||||
|
||||
if len(dbMask) == 0 && len(nullFields) == 0 {
|
||||
return nil, db.NoRowsAffected, errors.New(ctx, errors.EmptyFieldMask, op, "missing field mask")
|
||||
}
|
||||
|
||||
oplogWrapper, err := r.kms.GetWrapper(ctx, projectId, kms.KeyPurposeOplog)
|
||||
if err != nil {
|
||||
return nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithCode(errors.Encrypt),
|
||||
errors.WithMsg("unable to get oplog wrapper"))
|
||||
}
|
||||
|
||||
var rowsUpdated int
|
||||
var returnedCredentialLibrary *SSHCertificateCredentialLibrary
|
||||
_, err = r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{},
|
||||
func(rr db.Reader, w db.Writer) error {
|
||||
var msgs []*oplog.Message
|
||||
ticket, err := w.GetTicket(ctx, l)
|
||||
if err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket"))
|
||||
}
|
||||
|
||||
l := l.clone()
|
||||
var lOplogMsg oplog.Message
|
||||
|
||||
// Update the credential library table
|
||||
switch {
|
||||
case len(dbMask) == 0 && len(nullFields) == 0:
|
||||
// the credential library's fields are not being updated,
|
||||
// just one of it's child objects, so we just need to
|
||||
// update the library's version.
|
||||
l.Version = version + 1
|
||||
rowsUpdated, err = w.Update(ctx, l, []string{"Version"}, nil, db.NewOplogMsg(&lOplogMsg), db.WithVersion(&version))
|
||||
if err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to update credential library version"))
|
||||
}
|
||||
switch rowsUpdated {
|
||||
case 1:
|
||||
case 0:
|
||||
return nil
|
||||
default:
|
||||
return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("updated credential library version and %d rows updated", rowsUpdated))
|
||||
}
|
||||
default:
|
||||
rowsUpdated, err = w.Update(ctx, l, dbMask, nullFields, db.NewOplogMsg(&lOplogMsg), db.WithVersion(&version))
|
||||
if err != nil {
|
||||
if errors.IsUniqueError(err) {
|
||||
return errors.New(ctx, errors.NotUnique, op,
|
||||
fmt.Sprintf("name %s already exists: %s", l.Name, l.PublicId))
|
||||
}
|
||||
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to update credential library"))
|
||||
}
|
||||
switch rowsUpdated {
|
||||
case 1:
|
||||
case 0:
|
||||
return nil
|
||||
default:
|
||||
return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("updated credential library and %d rows updated", rowsUpdated))
|
||||
}
|
||||
}
|
||||
msgs = append(msgs, &lOplogMsg)
|
||||
|
||||
metadata := l.oplog(oplog.OpType_OP_TYPE_UPDATE)
|
||||
if err := w.WriteOplogEntryWith(ctx, oplogWrapper, ticket, metadata, msgs); err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write oplog"))
|
||||
}
|
||||
|
||||
accl := allocSSHCertificateCredentialLibrary()
|
||||
accl.PublicId = l.PublicId
|
||||
if err = rr.LookupById(ctx, accl); err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to retrieve updated credential library"))
|
||||
}
|
||||
returnedCredentialLibrary = accl
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
if errors.IsUniqueError(err) {
|
||||
return nil, db.NoRowsAffected, errors.New(ctx, errors.NotUnique, op,
|
||||
fmt.Sprintf("name %s already exists: %s", l.Name, l.PublicId))
|
||||
}
|
||||
return nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(l.PublicId))
|
||||
}
|
||||
|
||||
return returnedCredentialLibrary, rowsUpdated, nil
|
||||
}
|
||||
|
||||
// LookupSSHCertificateCredentialLibrary returns the SSHCertificateCredentialLibrary for publicId.
|
||||
// Returns nil, nil if no SSHCertificateCredentialLibrary is found for publicId.
|
||||
func (r *Repository) LookupSSHCertificateCredentialLibrary(ctx context.Context, publicId string, _ ...Option) (*SSHCertificateCredentialLibrary, error) {
|
||||
const op = "vault.(Repository).LookupCredentialLibrary"
|
||||
if publicId == "" {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "no public id")
|
||||
}
|
||||
l := allocSSHCertificateCredentialLibrary()
|
||||
l.PublicId = publicId
|
||||
if err := r.reader.LookupByPublicId(ctx, l); err != nil {
|
||||
if errors.IsNotFoundError(err) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for: %s", publicId)))
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// ListSSHCertificateCredentialLibraries returns a slice of SSHCertificateCredentialLibraries for the
|
||||
// storeId. WithLimit is the only option supported.
|
||||
func (r *Repository) ListSSHCertificateCredentialLibraries(ctx context.Context, storeId string, opt ...Option) ([]*SSHCertificateCredentialLibrary, error) {
|
||||
const op = "vault.(Repository).ListSSHCertificateCredentialLibraries"
|
||||
if storeId == "" {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "no storeId")
|
||||
}
|
||||
opts := getOpts(opt...)
|
||||
limit := r.defaultLimit
|
||||
if opts.withLimit != 0 {
|
||||
// non-zero signals an override of the default limit for the repo.
|
||||
limit = opts.withLimit
|
||||
}
|
||||
var libs []*SSHCertificateCredentialLibrary
|
||||
err := r.reader.SearchWhere(ctx, &libs, "store_id = ?", []any{storeId}, db.WithLimit(limit))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
return libs, nil
|
||||
}
|
||||
|
||||
// DeleteSSHCertificateCredentialLibrary deletes publicId from the repository and returns
|
||||
// the number of records deleted.
|
||||
func (r *Repository) DeleteSSHCertificateCredentialLibrary(ctx context.Context, projectId string, publicId string, _ ...Option) (int, error) {
|
||||
const op = "vault.(Repository).DeleteSSHCertificateCredentialLibrary"
|
||||
if publicId == "" {
|
||||
return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "no public id")
|
||||
}
|
||||
if projectId == "" {
|
||||
return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "no project id")
|
||||
}
|
||||
|
||||
l := allocSSHCertificateCredentialLibrary()
|
||||
l.PublicId = publicId
|
||||
|
||||
oplogWrapper, err := r.kms.GetWrapper(ctx, projectId, kms.KeyPurposeOplog)
|
||||
if err != nil {
|
||||
return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper"))
|
||||
}
|
||||
|
||||
var rowsDeleted int
|
||||
_, err = r.writer.DoTx(
|
||||
ctx, db.StdRetryCnt, db.ExpBackoff{},
|
||||
func(_ db.Reader, w db.Writer) (err error) {
|
||||
dl := l.clone()
|
||||
rowsDeleted, err = w.Delete(ctx, dl, db.WithOplog(oplogWrapper, l.oplog(oplog.OpType_OP_TYPE_DELETE)))
|
||||
if err == nil && rowsDeleted > 1 {
|
||||
return errors.New(ctx, errors.MultipleRecords, op, "more than 1 CredentialLibrary would have been deleted")
|
||||
}
|
||||
return err
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("delete failed for %s", l.PublicId)))
|
||||
}
|
||||
|
||||
return rowsDeleted, nil
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -74,7 +74,7 @@ func TestSetupSleepyDevEnvironment(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, store)
|
||||
libClient := credentiallibraries.NewClient(client)
|
||||
lib, err := libClient.Create(ctx, store.Item.Id, credentiallibraries.WithVaultCredentialLibraryPath(path.Join("database", "creds", "opened")),
|
||||
lib, err := libClient.Create(ctx, store.Item.Type, store.Item.Id, credentiallibraries.WithVaultCredentialLibraryPath(path.Join("database", "creds", "opened")),
|
||||
credentiallibraries.WithVaultCredentialLibraryHttpMethod("GET"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
package vault
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/boundary/internal/credential"
|
||||
"github.com/hashicorp/boundary/internal/credential/vault/store"
|
||||
"github.com/hashicorp/boundary/internal/oplog"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
KeyTypeEcdsa = "ecdsa"
|
||||
KeyTypeEd25519 = "ed25519"
|
||||
KeyTypeRsa = "rsa"
|
||||
|
||||
KeyBitsDefault = 0
|
||||
|
||||
KeyBitsEcdsa256 = 256
|
||||
KeyBitsEcdsa384 = 384
|
||||
KeyBitsEcdsa521 = 521
|
||||
|
||||
KeyBitsRsa2048 = 2048
|
||||
KeyBitsRsa3072 = 3072
|
||||
KeyBitsRsa4096 = 4096
|
||||
)
|
||||
|
||||
// SSHCertificateCredentialLibrary is a credential library that issues
|
||||
// ssh certificate using the vault ssh secret engine.
|
||||
// See: https://developer.hashicorp.com/vault/api-docs/secret/ssh#sign-ssh-key
|
||||
type SSHCertificateCredentialLibrary struct {
|
||||
*store.SSHCertificateCredentialLibrary
|
||||
tableName string `gorm:"-"`
|
||||
}
|
||||
|
||||
// NewSSHCertificateCredentialLibrary creates a new in memory SSHCertificateCredentialLibrary
|
||||
// for a Vault backend at vaultPath assigned to storeId. The SSH username field must be set.
|
||||
// Name, description, key type, key bits, ttl, key id, critical options, and extensions
|
||||
// are the only valid options. All other options are ignored.
|
||||
func NewSSHCertificateCredentialLibrary(storeId string, vaultPath string, username string, opt ...Option) (*SSHCertificateCredentialLibrary, error) {
|
||||
const op = "vault.NewSSHCertificateCredentialLibrary"
|
||||
opts := getOpts(opt...)
|
||||
|
||||
l := &SSHCertificateCredentialLibrary{
|
||||
SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{
|
||||
StoreId: storeId,
|
||||
Name: opts.withName,
|
||||
Description: opts.withDescription,
|
||||
VaultPath: vaultPath,
|
||||
Username: username,
|
||||
KeyType: opts.withKeyType,
|
||||
KeyBits: opts.withKeyBits,
|
||||
Ttl: opts.withTtl,
|
||||
KeyId: opts.withKeyId,
|
||||
CriticalOptions: opts.withCriticalOptions,
|
||||
Extensions: opts.withExtensions,
|
||||
CredentialType: string(credential.SshCertificateType),
|
||||
},
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func allocSSHCertificateCredentialLibrary() *SSHCertificateCredentialLibrary {
|
||||
return &SSHCertificateCredentialLibrary{
|
||||
SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{},
|
||||
}
|
||||
}
|
||||
|
||||
func (l *SSHCertificateCredentialLibrary) clone() *SSHCertificateCredentialLibrary {
|
||||
cp := proto.Clone(l.SSHCertificateCredentialLibrary)
|
||||
return &SSHCertificateCredentialLibrary{
|
||||
SSHCertificateCredentialLibrary: cp.(*store.SSHCertificateCredentialLibrary),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *SSHCertificateCredentialLibrary) setId(i string) {
|
||||
l.PublicId = i
|
||||
}
|
||||
|
||||
// TableName returns the table name.
|
||||
func (l *SSHCertificateCredentialLibrary) TableName() string {
|
||||
if l.tableName != "" {
|
||||
return l.tableName
|
||||
}
|
||||
return "credential_vault_ssh_cert_library"
|
||||
}
|
||||
|
||||
// SetTableName sets the table name.
|
||||
func (l *SSHCertificateCredentialLibrary) SetTableName(n string) {
|
||||
l.tableName = n
|
||||
}
|
||||
|
||||
func (l *SSHCertificateCredentialLibrary) oplog(op oplog.OpType) oplog.Metadata {
|
||||
metadata := oplog.Metadata{
|
||||
"resource-public-id": []string{l.PublicId},
|
||||
"resource-type": []string{"credential-vault-ssh-cert-library"},
|
||||
"op-type": []string{op.String()},
|
||||
}
|
||||
if l.StoreId != "" {
|
||||
metadata["store-id"] = []string{l.StoreId}
|
||||
}
|
||||
return metadata
|
||||
}
|
||||
|
||||
func (l *SSHCertificateCredentialLibrary) getDefaultKeyBits() uint32 {
|
||||
switch l.KeyType {
|
||||
case KeyTypeEcdsa:
|
||||
return KeyBitsEcdsa256
|
||||
case KeyTypeRsa:
|
||||
return KeyBitsRsa2048
|
||||
default:
|
||||
return KeyBitsDefault
|
||||
}
|
||||
}
|
||||
|
||||
// CredentialType returns the type of credential the library retrieves.
|
||||
func (l *SSHCertificateCredentialLibrary) CredentialType() credential.Type {
|
||||
return credential.Type(l.SSHCertificateCredentialLibrary.CredentialType)
|
||||
}
|
||||
|
||||
var _ credential.Library = (*SSHCertificateCredentialLibrary)(nil)
|
||||
@@ -629,6 +629,210 @@ func (x *CredentialLibrary) GetCredentialType() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
type SSHCertificateCredentialLibrary struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// public_id is a surrogate key suitable for use in a public API.
|
||||
// @inject_tag: `gorm:"primary_key"`
|
||||
PublicId string `protobuf:"bytes,1,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"`
|
||||
// create_time is set by the database.
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
CreateTime *timestamp.Timestamp `protobuf:"bytes,2,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"`
|
||||
// update_time is set by the database.
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
UpdateTime *timestamp.Timestamp `protobuf:"bytes,3,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty" gorm:"default:current_timestamp"`
|
||||
// name is optional. If set, it must be unique within project_id.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"`
|
||||
// description is optional.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty" gorm:"default:null"`
|
||||
// store_id of the owning vault credential store.
|
||||
// It must be set.
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
StoreId string `protobuf:"bytes,6,opt,name=store_id,json=storeId,proto3" json:"store_id,omitempty" gorm:"not_null"`
|
||||
// version allows optimistic locking of the resource.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Version uint32 `protobuf:"varint,7,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"`
|
||||
// vault_path is the path in Vault to request credentials from.
|
||||
// It must be set.
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
VaultPath string `protobuf:"bytes,8,opt,name=vault_path,json=vaultPath,proto3" json:"vault_path,omitempty" gorm:"not_null"`
|
||||
// username is the username to use when making an SSH connection.
|
||||
// It must be set.
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
Username string `protobuf:"bytes,9,opt,name=username,proto3" json:"username,omitempty" gorm:"not_null"`
|
||||
// key_type specifies the key type to use when generating an SSH private key.
|
||||
// Values must be "rsa", "ed25519", or "ecdsa".
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
KeyType string `protobuf:"bytes,10,opt,name=key_type,json=keyType,proto3" json:"key_type,omitempty" gorm:"not_null"`
|
||||
// key_bits specifies the number of bits to use to generate an SSH private key.
|
||||
// Not used if key_type is ed25519.
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
KeyBits uint32 `protobuf:"varint,11,opt,name=key_bits,json=keyBits,proto3" json:"key_bits,omitempty" gorm:"not_null"`
|
||||
// ttl specifies the requested time to live for the certificate.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Ttl string `protobuf:"bytes,12,opt,name=ttl,proto3" json:"ttl,omitempty" gorm:"default:null"`
|
||||
// key_id specifies the key id that the created certificate should have.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
KeyId string `protobuf:"bytes,13,opt,name=key_id,json=keyId,proto3" json:"key_id,omitempty" gorm:"default:null"`
|
||||
// critical_options specifies a map of the critical options that the certificate should be signed for.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
CriticalOptions string `protobuf:"bytes,14,opt,name=critical_options,json=criticalOptions,proto3" json:"critical_options,omitempty" gorm:"default:null"`
|
||||
// extensions specifies a map of the extensions that the certificate should be signed for.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Extensions string `protobuf:"bytes,15,opt,name=extensions,proto3" json:"extensions,omitempty" gorm:"default:null"`
|
||||
// credential_type is always ssh_certificate
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
CredentialType string `protobuf:"bytes,16,opt,name=credential_type,json=credentialType,proto3" json:"credential_type,omitempty" gorm:"default:null"`
|
||||
}
|
||||
|
||||
func (x *SSHCertificateCredentialLibrary) Reset() {
|
||||
*x = SSHCertificateCredentialLibrary{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *SSHCertificateCredentialLibrary) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SSHCertificateCredentialLibrary) ProtoMessage() {}
|
||||
|
||||
func (x *SSHCertificateCredentialLibrary) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[4]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SSHCertificateCredentialLibrary.ProtoReflect.Descriptor instead.
|
||||
func (*SSHCertificateCredentialLibrary) Descriptor() ([]byte, []int) {
|
||||
return file_controller_storage_credential_vault_store_v1_vault_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *SSHCertificateCredentialLibrary) GetPublicId() string {
|
||||
if x != nil {
|
||||
return x.PublicId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SSHCertificateCredentialLibrary) GetCreateTime() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.CreateTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *SSHCertificateCredentialLibrary) GetUpdateTime() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.UpdateTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *SSHCertificateCredentialLibrary) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SSHCertificateCredentialLibrary) GetDescription() string {
|
||||
if x != nil {
|
||||
return x.Description
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SSHCertificateCredentialLibrary) GetStoreId() string {
|
||||
if x != nil {
|
||||
return x.StoreId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SSHCertificateCredentialLibrary) GetVersion() uint32 {
|
||||
if x != nil {
|
||||
return x.Version
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SSHCertificateCredentialLibrary) GetVaultPath() string {
|
||||
if x != nil {
|
||||
return x.VaultPath
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SSHCertificateCredentialLibrary) GetUsername() string {
|
||||
if x != nil {
|
||||
return x.Username
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SSHCertificateCredentialLibrary) GetKeyType() string {
|
||||
if x != nil {
|
||||
return x.KeyType
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SSHCertificateCredentialLibrary) GetKeyBits() uint32 {
|
||||
if x != nil {
|
||||
return x.KeyBits
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SSHCertificateCredentialLibrary) GetTtl() string {
|
||||
if x != nil {
|
||||
return x.Ttl
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SSHCertificateCredentialLibrary) GetKeyId() string {
|
||||
if x != nil {
|
||||
return x.KeyId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SSHCertificateCredentialLibrary) GetCriticalOptions() string {
|
||||
if x != nil {
|
||||
return x.CriticalOptions
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SSHCertificateCredentialLibrary) GetExtensions() string {
|
||||
if x != nil {
|
||||
return x.Extensions
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SSHCertificateCredentialLibrary) GetCredentialType() string {
|
||||
if x != nil {
|
||||
return x.CredentialType
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Credential struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -688,7 +892,7 @@ type Credential struct {
|
||||
func (x *Credential) Reset() {
|
||||
*x = Credential{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[4]
|
||||
mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -701,7 +905,7 @@ func (x *Credential) String() string {
|
||||
func (*Credential) ProtoMessage() {}
|
||||
|
||||
func (x *Credential) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[4]
|
||||
mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[5]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -714,7 +918,7 @@ func (x *Credential) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use Credential.ProtoReflect.Descriptor instead.
|
||||
func (*Credential) Descriptor() ([]byte, []int) {
|
||||
return file_controller_storage_credential_vault_store_v1_vault_proto_rawDescGZIP(), []int{4}
|
||||
return file_controller_storage_credential_vault_store_v1_vault_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *Credential) GetPublicId() string {
|
||||
@@ -832,7 +1036,7 @@ type UsernamePasswordOverride struct {
|
||||
func (x *UsernamePasswordOverride) Reset() {
|
||||
*x = UsernamePasswordOverride{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[5]
|
||||
mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -845,7 +1049,7 @@ func (x *UsernamePasswordOverride) String() string {
|
||||
func (*UsernamePasswordOverride) ProtoMessage() {}
|
||||
|
||||
func (x *UsernamePasswordOverride) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[5]
|
||||
mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[6]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -858,7 +1062,7 @@ func (x *UsernamePasswordOverride) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use UsernamePasswordOverride.ProtoReflect.Descriptor instead.
|
||||
func (*UsernamePasswordOverride) Descriptor() ([]byte, []int) {
|
||||
return file_controller_storage_credential_vault_store_v1_vault_proto_rawDescGZIP(), []int{5}
|
||||
return file_controller_storage_credential_vault_store_v1_vault_proto_rawDescGZIP(), []int{6}
|
||||
}
|
||||
|
||||
func (x *UsernamePasswordOverride) GetLibraryId() string {
|
||||
@@ -923,7 +1127,7 @@ type SshPrivateKeyOverride struct {
|
||||
func (x *SshPrivateKeyOverride) Reset() {
|
||||
*x = SshPrivateKeyOverride{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[6]
|
||||
mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[7]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -936,7 +1140,7 @@ func (x *SshPrivateKeyOverride) String() string {
|
||||
func (*SshPrivateKeyOverride) ProtoMessage() {}
|
||||
|
||||
func (x *SshPrivateKeyOverride) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[6]
|
||||
mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[7]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -949,7 +1153,7 @@ func (x *SshPrivateKeyOverride) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use SshPrivateKeyOverride.ProtoReflect.Descriptor instead.
|
||||
func (*SshPrivateKeyOverride) Descriptor() ([]byte, []int) {
|
||||
return file_controller_storage_credential_vault_store_v1_vault_proto_rawDescGZIP(), []int{6}
|
||||
return file_controller_storage_credential_vault_store_v1_vault_proto_rawDescGZIP(), []int{7}
|
||||
}
|
||||
|
||||
func (x *SshPrivateKeyOverride) GetLibraryId() string {
|
||||
@@ -1146,72 +1350,131 @@ var file_controller_storage_credential_vault_store_v1_vault_proto_rawDesc = []by
|
||||
0x0f, 0x68, 0x74, 0x74, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6f, 0x64, 0x79,
|
||||
0x12, 0x27, 0x0a, 0x0f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x74,
|
||||
0x79, 0x70, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x72, 0x65, 0x64, 0x65,
|
||||
0x6e, 0x74, 0x69, 0x61, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x22, 0xc3, 0x04, 0x0a, 0x0a, 0x43, 0x72,
|
||||
0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c,
|
||||
0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62,
|
||||
0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79,
|
||||
0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6c, 0x69, 0x62, 0x72, 0x61,
|
||||
0x72, 0x79, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f,
|
||||
0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x68, 0x6d, 0x61,
|
||||
0x63, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x48, 0x6d,
|
||||
0x61, 0x63, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d,
|
||||
0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
|
||||
0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d,
|
||||
0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74,
|
||||
0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12,
|
||||
0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06,
|
||||
0x6e, 0x74, 0x69, 0x61, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x22, 0xaa, 0x07, 0x0a, 0x1f, 0x53, 0x53,
|
||||
0x48, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64,
|
||||
0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x12, 0x1b, 0x0a,
|
||||
0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72,
|
||||
0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f,
|
||||
0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76,
|
||||
0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65,
|
||||
0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74,
|
||||
0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63,
|
||||
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67,
|
||||
0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54,
|
||||
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65,
|
||||
0x54, 0x69, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01,
|
||||
0x28, 0x09, 0x42, 0x10, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x04,
|
||||
0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x40, 0x0a, 0x0b, 0x64, 0x65,
|
||||
0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42,
|
||||
0x1e, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52,
|
||||
0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08,
|
||||
0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
|
||||
0x73, 0x74, 0x6f, 0x72, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69,
|
||||
0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x12, 0x3f, 0x0a, 0x0a, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18,
|
||||
0x08, 0x20, 0x01, 0x28, 0x09, 0x42, 0x20, 0xc2, 0xdd, 0x29, 0x1c, 0x0a, 0x09, 0x56, 0x61, 0x75,
|
||||
0x6c, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x0f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74,
|
||||
0x65, 0x73, 0x2e, 0x70, 0x61, 0x74, 0x68, 0x52, 0x09, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x50, 0x61,
|
||||
0x74, 0x68, 0x12, 0x3f, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x09,
|
||||
0x20, 0x01, 0x28, 0x09, 0x42, 0x23, 0xc2, 0xdd, 0x29, 0x1f, 0x0a, 0x08, 0x55, 0x73, 0x65, 0x72,
|
||||
0x6e, 0x61, 0x6d, 0x65, 0x12, 0x13, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73,
|
||||
0x2e, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e,
|
||||
0x61, 0x6d, 0x65, 0x12, 0x3d, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18,
|
||||
0x0a, 0x20, 0x01, 0x28, 0x09, 0x42, 0x22, 0xc2, 0xdd, 0x29, 0x1e, 0x0a, 0x07, 0x4b, 0x65, 0x79,
|
||||
0x54, 0x79, 0x70, 0x65, 0x12, 0x13, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73,
|
||||
0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x54, 0x79,
|
||||
0x70, 0x65, 0x12, 0x3d, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x18, 0x0b,
|
||||
0x20, 0x01, 0x28, 0x0d, 0x42, 0x22, 0xc2, 0xdd, 0x29, 0x1e, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x42,
|
||||
0x69, 0x74, 0x73, 0x12, 0x13, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e,
|
||||
0x6b, 0x65, 0x79, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x42, 0x69, 0x74,
|
||||
0x73, 0x12, 0x2b, 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x42, 0x19,
|
||||
0xc2, 0xdd, 0x29, 0x15, 0x0a, 0x03, 0x54, 0x74, 0x6c, 0x12, 0x0e, 0x61, 0x74, 0x74, 0x72, 0x69,
|
||||
0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x74, 0x74, 0x6c, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x12, 0x35,
|
||||
0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1e,
|
||||
0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x05, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x11, 0x61, 0x74, 0x74,
|
||||
0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x52, 0x05,
|
||||
0x6b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x5d, 0x0a, 0x10, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61,
|
||||
0x6c, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x42,
|
||||
0x32, 0xc2, 0xdd, 0x29, 0x2e, 0x0a, 0x0f, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x4f,
|
||||
0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1b, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74,
|
||||
0x65, 0x73, 0x2e, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x6f, 0x70, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x73, 0x52, 0x0f, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x4f, 0x70, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x73, 0x12, 0x47, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x42, 0x27, 0xc2, 0xdd, 0x29, 0x23, 0x0a, 0x0a,
|
||||
0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x15, 0x61, 0x74, 0x74, 0x72,
|
||||
0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
|
||||
0x73, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x27, 0x0a,
|
||||
0x0f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x74, 0x79, 0x70, 0x65,
|
||||
0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69,
|
||||
0x61, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x22, 0xc3, 0x04, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x64, 0x65,
|
||||
0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f,
|
||||
0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,
|
||||
0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x69, 0x64,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x49,
|
||||
0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18,
|
||||
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64,
|
||||
0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x68, 0x6d, 0x61, 0x63, 0x18, 0x04,
|
||||
0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x48, 0x6d, 0x61, 0x63, 0x12,
|
||||
0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65,
|
||||
0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
|
||||
0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
|
||||
0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07,
|
||||
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76,
|
||||
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e,
|
||||
0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x74,
|
||||
0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x56, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f,
|
||||
0x72, 0x65, 0x6e, 0x65, 0x77, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01,
|
||||
0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e,
|
||||
0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
|
||||
0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0f,
|
||||
0x6c, 0x61, 0x73, 0x74, 0x52, 0x65, 0x6e, 0x65, 0x77, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x12,
|
||||
0x53, 0x0a, 0x0f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69,
|
||||
0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72,
|
||||
0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69,
|
||||
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73,
|
||||
0x74, 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x54, 0x69, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x6e, 0x65, 0x77,
|
||||
0x61, 0x62, 0x6c, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x73, 0x52, 0x65,
|
||||
0x6e, 0x65, 0x77, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,
|
||||
0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22,
|
||||
0x97, 0x01, 0x0a, 0x18, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x50, 0x61, 0x73, 0x73,
|
||||
0x77, 0x6f, 0x72, 0x64, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a,
|
||||
0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x09, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x75,
|
||||
0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74,
|
||||
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d,
|
||||
0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x2d, 0x0a, 0x12, 0x70, 0x61,
|
||||
0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65,
|
||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64,
|
||||
0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x22, 0xe2, 0x01, 0x0a, 0x15, 0x53, 0x73,
|
||||
0x68, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x4f, 0x76, 0x65, 0x72, 0x72,
|
||||
0x69, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x69,
|
||||
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79,
|
||||
0x49, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x61,
|
||||
0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11,
|
||||
0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74,
|
||||
0x65, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79,
|
||||
0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x13, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x74, 0x74, 0x72,
|
||||
0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x47, 0x0a, 0x20, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65,
|
||||
0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x5f,
|
||||
0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x1d, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x73, 0x73, 0x70,
|
||||
0x68, 0x72, 0x61, 0x73, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x42, 0x45,
|
||||
0x5a, 0x43, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73,
|
||||
0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f,
|
||||
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74,
|
||||
0x69, 0x61, 0x6c, 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b,
|
||||
0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b,
|
||||
0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73,
|
||||
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
|
||||
0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75,
|
||||
0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72,
|
||||
0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73,
|
||||
0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f,
|
||||
0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e,
|
||||
0x61, 0x6c, 0x49, 0x64, 0x12, 0x56, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x72, 0x65, 0x6e,
|
||||
0x65, 0x77, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f,
|
||||
0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76,
|
||||
0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0f, 0x6c, 0x61, 0x73,
|
||||
0x74, 0x52, 0x65, 0x6e, 0x65, 0x77, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x53, 0x0a, 0x0f,
|
||||
0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18,
|
||||
0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c,
|
||||
0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73,
|
||||
0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
|
||||
0x70, 0x52, 0x0e, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d,
|
||||
0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x6e, 0x65, 0x77, 0x61, 0x62, 0x6c,
|
||||
0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x73, 0x52, 0x65, 0x6e, 0x65, 0x77,
|
||||
0x61, 0x62, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0c,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x97, 0x01, 0x0a,
|
||||
0x18, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72,
|
||||
0x64, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x69, 0x62,
|
||||
0x72, 0x61, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6c,
|
||||
0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x75, 0x73, 0x65, 0x72,
|
||||
0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x41, 0x74,
|
||||
0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x2d, 0x0a, 0x12, 0x70, 0x61, 0x73, 0x73, 0x77,
|
||||
0x6f, 0x72, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x03, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x11, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x41, 0x74, 0x74,
|
||||
0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x22, 0xe2, 0x01, 0x0a, 0x15, 0x53, 0x73, 0x68, 0x50, 0x72,
|
||||
0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65,
|
||||
0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x49, 0x64, 0x12,
|
||||
0x2d, 0x0a, 0x12, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72,
|
||||
0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x75, 0x73, 0x65,
|
||||
0x72, 0x6e, 0x61, 0x6d, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x32,
|
||||
0x0a, 0x15, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x74,
|
||||
0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70,
|
||||
0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75,
|
||||
0x74, 0x65, 0x12, 0x47, 0x0a, 0x20, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65,
|
||||
0x79, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x5f, 0x61, 0x74, 0x74,
|
||||
0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1d, 0x70, 0x72,
|
||||
0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61,
|
||||
0x73, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x42, 0x45, 0x5a, 0x43, 0x67,
|
||||
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63,
|
||||
0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74,
|
||||
0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c,
|
||||
0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f,
|
||||
0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -1226,36 +1489,39 @@ func file_controller_storage_credential_vault_store_v1_vault_proto_rawDescGZIP()
|
||||
return file_controller_storage_credential_vault_store_v1_vault_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
|
||||
var file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
|
||||
var file_controller_storage_credential_vault_store_v1_vault_proto_goTypes = []interface{}{
|
||||
(*CredentialStore)(nil), // 0: controller.storage.credential.vault.store.v1.CredentialStore
|
||||
(*Token)(nil), // 1: controller.storage.credential.vault.store.v1.Token
|
||||
(*ClientCertificate)(nil), // 2: controller.storage.credential.vault.store.v1.ClientCertificate
|
||||
(*CredentialLibrary)(nil), // 3: controller.storage.credential.vault.store.v1.CredentialLibrary
|
||||
(*Credential)(nil), // 4: controller.storage.credential.vault.store.v1.Credential
|
||||
(*UsernamePasswordOverride)(nil), // 5: controller.storage.credential.vault.store.v1.UsernamePasswordOverride
|
||||
(*SshPrivateKeyOverride)(nil), // 6: controller.storage.credential.vault.store.v1.SshPrivateKeyOverride
|
||||
(*timestamp.Timestamp)(nil), // 7: controller.storage.timestamp.v1.Timestamp
|
||||
(*CredentialStore)(nil), // 0: controller.storage.credential.vault.store.v1.CredentialStore
|
||||
(*Token)(nil), // 1: controller.storage.credential.vault.store.v1.Token
|
||||
(*ClientCertificate)(nil), // 2: controller.storage.credential.vault.store.v1.ClientCertificate
|
||||
(*CredentialLibrary)(nil), // 3: controller.storage.credential.vault.store.v1.CredentialLibrary
|
||||
(*SSHCertificateCredentialLibrary)(nil), // 4: controller.storage.credential.vault.store.v1.SSHCertificateCredentialLibrary
|
||||
(*Credential)(nil), // 5: controller.storage.credential.vault.store.v1.Credential
|
||||
(*UsernamePasswordOverride)(nil), // 6: controller.storage.credential.vault.store.v1.UsernamePasswordOverride
|
||||
(*SshPrivateKeyOverride)(nil), // 7: controller.storage.credential.vault.store.v1.SshPrivateKeyOverride
|
||||
(*timestamp.Timestamp)(nil), // 8: controller.storage.timestamp.v1.Timestamp
|
||||
}
|
||||
var file_controller_storage_credential_vault_store_v1_vault_proto_depIdxs = []int32{
|
||||
7, // 0: controller.storage.credential.vault.store.v1.CredentialStore.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
7, // 1: controller.storage.credential.vault.store.v1.CredentialStore.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
7, // 2: controller.storage.credential.vault.store.v1.CredentialStore.delete_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
7, // 3: controller.storage.credential.vault.store.v1.Token.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
7, // 4: controller.storage.credential.vault.store.v1.Token.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
7, // 5: controller.storage.credential.vault.store.v1.Token.last_renewal_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
7, // 6: controller.storage.credential.vault.store.v1.Token.expiration_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
7, // 7: controller.storage.credential.vault.store.v1.CredentialLibrary.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
7, // 8: controller.storage.credential.vault.store.v1.CredentialLibrary.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
7, // 9: controller.storage.credential.vault.store.v1.Credential.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
7, // 10: controller.storage.credential.vault.store.v1.Credential.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
7, // 11: controller.storage.credential.vault.store.v1.Credential.last_renewal_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
7, // 12: controller.storage.credential.vault.store.v1.Credential.expiration_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
13, // [13:13] is the sub-list for method output_type
|
||||
13, // [13:13] is the sub-list for method input_type
|
||||
13, // [13:13] is the sub-list for extension type_name
|
||||
13, // [13:13] is the sub-list for extension extendee
|
||||
0, // [0:13] is the sub-list for field type_name
|
||||
8, // 0: controller.storage.credential.vault.store.v1.CredentialStore.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
8, // 1: controller.storage.credential.vault.store.v1.CredentialStore.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
8, // 2: controller.storage.credential.vault.store.v1.CredentialStore.delete_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
8, // 3: controller.storage.credential.vault.store.v1.Token.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
8, // 4: controller.storage.credential.vault.store.v1.Token.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
8, // 5: controller.storage.credential.vault.store.v1.Token.last_renewal_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
8, // 6: controller.storage.credential.vault.store.v1.Token.expiration_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
8, // 7: controller.storage.credential.vault.store.v1.CredentialLibrary.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
8, // 8: controller.storage.credential.vault.store.v1.CredentialLibrary.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
8, // 9: controller.storage.credential.vault.store.v1.SSHCertificateCredentialLibrary.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
8, // 10: controller.storage.credential.vault.store.v1.SSHCertificateCredentialLibrary.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
8, // 11: controller.storage.credential.vault.store.v1.Credential.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
8, // 12: controller.storage.credential.vault.store.v1.Credential.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
8, // 13: controller.storage.credential.vault.store.v1.Credential.last_renewal_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
8, // 14: controller.storage.credential.vault.store.v1.Credential.expiration_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
15, // [15:15] is the sub-list for method output_type
|
||||
15, // [15:15] is the sub-list for method input_type
|
||||
15, // [15:15] is the sub-list for extension type_name
|
||||
15, // [15:15] is the sub-list for extension extendee
|
||||
0, // [0:15] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_controller_storage_credential_vault_store_v1_vault_proto_init() }
|
||||
@@ -1313,7 +1579,7 @@ func file_controller_storage_credential_vault_store_v1_vault_proto_init() {
|
||||
}
|
||||
}
|
||||
file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Credential); i {
|
||||
switch v := v.(*SSHCertificateCredentialLibrary); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
@@ -1325,7 +1591,7 @@ func file_controller_storage_credential_vault_store_v1_vault_proto_init() {
|
||||
}
|
||||
}
|
||||
file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*UsernamePasswordOverride); i {
|
||||
switch v := v.(*Credential); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
@@ -1337,6 +1603,18 @@ func file_controller_storage_credential_vault_store_v1_vault_proto_init() {
|
||||
}
|
||||
}
|
||||
file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*UsernamePasswordOverride); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*SshPrivateKeyOverride); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@@ -1355,7 +1633,7 @@ func file_controller_storage_credential_vault_store_v1_vault_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_controller_storage_credential_vault_store_v1_vault_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 7,
|
||||
NumMessages: 8,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
||||
@@ -75,9 +75,14 @@ func gotNewServer(t testing.TB, opt ...TestOption) *TestVaultServer {
|
||||
pool: pool,
|
||||
}
|
||||
|
||||
vaultVersion := DefaultVaultVersion
|
||||
if opts.vaultVersion != "" {
|
||||
vaultVersion = opts.vaultVersion
|
||||
}
|
||||
|
||||
dockerOptions := &dockertest.RunOptions{
|
||||
Repository: "vault",
|
||||
Tag: DefaultVaultVersion,
|
||||
Tag: vaultVersion,
|
||||
Env: []string{fmt.Sprintf("VAULT_DEV_ROOT_TOKEN_ID=%s", server.RootToken)},
|
||||
}
|
||||
|
||||
|
||||
@@ -140,6 +140,38 @@ func TestCredentialLibraries(t testing.TB, conn *db.DB, _ wrapping.Wrapper, stor
|
||||
return libs
|
||||
}
|
||||
|
||||
// TestSSHCertificateCredentialLibraries creates count number of vault ssh
|
||||
// certificate credential libraries in the provided DB with the provided store
|
||||
// id. If any errors are encountered during the creation of the credential
|
||||
// libraries, the test will fail.
|
||||
func TestSSHCertificateCredentialLibraries(t testing.TB, conn *db.DB, _ wrapping.Wrapper, storeId string, count int) []*SSHCertificateCredentialLibrary {
|
||||
t.Helper()
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
w := db.New(conn)
|
||||
var libs []*SSHCertificateCredentialLibrary
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
lib, err := NewSSHCertificateCredentialLibrary(storeId, fmt.Sprintf("ssh/sign/role-%d", i), "username", WithKeyType(KeyTypeEd25519))
|
||||
assert.NoError(err)
|
||||
require.NotNil(lib)
|
||||
id, err := newSSHCertificateCredentialLibraryId()
|
||||
assert.NoError(err)
|
||||
require.NotEmpty(id)
|
||||
lib.PublicId = id
|
||||
|
||||
ctx := context.Background()
|
||||
_, err2 := w.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{},
|
||||
func(_ db.Reader, iw db.Writer) error {
|
||||
return iw.Create(ctx, lib)
|
||||
},
|
||||
)
|
||||
|
||||
require.NoError(err2)
|
||||
libs = append(libs, lib)
|
||||
}
|
||||
return libs
|
||||
}
|
||||
|
||||
// TestCredentials creates count number of vault credentials in the provided DB with
|
||||
// the provided library id and session id. If any errors are encountered
|
||||
// during the creation of the credentials, the test will fail.
|
||||
@@ -168,7 +200,7 @@ func TestCredentials(t testing.TB, conn *db.DB, wrapper wrapping.Wrapper, librar
|
||||
|
||||
var credentials []*Credential
|
||||
for i := 0; i < count; i++ {
|
||||
credential, err := newCredential(lib.GetPublicId(), sessionId, fmt.Sprintf("vault/credential/%d", i), token.GetTokenHmac(), 5*time.Minute)
|
||||
credential, err := newCredential(lib.GetPublicId(), fmt.Sprintf("vault/credential/%d", i), token.GetTokenHmac(), 5*time.Minute)
|
||||
assert.NoError(err)
|
||||
require.NotNil(credential)
|
||||
|
||||
@@ -177,7 +209,7 @@ func TestCredentials(t testing.TB, conn *db.DB, wrapper wrapping.Wrapper, librar
|
||||
require.NotNil(id)
|
||||
credential.PublicId = id
|
||||
|
||||
query, queryValues := credential.insertQuery()
|
||||
query, queryValues := insertQuery(credential, sessionId)
|
||||
rows, err2 := rw.Exec(ctx, query, queryValues)
|
||||
assert.Equal(1, rows)
|
||||
assert.NoError(err2)
|
||||
@@ -477,17 +509,19 @@ type TestOption func(testing.TB, *testOptions)
|
||||
|
||||
// options = how options are represented
|
||||
type testOptions struct {
|
||||
orphan bool
|
||||
periodic bool
|
||||
renewable bool
|
||||
policies []string
|
||||
mountPath string
|
||||
roleName string
|
||||
vaultTLS TestVaultTLS
|
||||
dockerNetwork bool
|
||||
skipCleanup bool
|
||||
tokenPeriod time.Duration
|
||||
clientKey *ecdsa.PrivateKey
|
||||
orphan bool
|
||||
periodic bool
|
||||
renewable bool
|
||||
policies []string
|
||||
allowedExtensions []string
|
||||
mountPath string
|
||||
roleName string
|
||||
vaultTLS TestVaultTLS
|
||||
dockerNetwork bool
|
||||
skipCleanup bool
|
||||
tokenPeriod time.Duration
|
||||
clientKey *ecdsa.PrivateKey
|
||||
vaultVersion string
|
||||
}
|
||||
|
||||
func getDefaultTestOptions(t testing.TB) testOptions {
|
||||
@@ -593,6 +627,24 @@ func WithDontCleanUp() TestOption {
|
||||
}
|
||||
}
|
||||
|
||||
// WithVaultVersion sets the version of vault that will be started.
|
||||
// defaults to the value stored in vault.supported.DefaultVaultVersion
|
||||
func WithVaultVersion(s string) TestOption {
|
||||
return func(t testing.TB, o *testOptions) {
|
||||
t.Helper()
|
||||
o.vaultVersion = s
|
||||
}
|
||||
}
|
||||
|
||||
// WithAllowedExtension tells vault to allow a specific SSH extension
|
||||
// to be used by vault's ssh secrets engine
|
||||
func WithAllowedExtension(e string) TestOption {
|
||||
return func(t testing.TB, o *testOptions) {
|
||||
t.Helper()
|
||||
o.allowedExtensions = append(o.allowedExtensions, e)
|
||||
}
|
||||
}
|
||||
|
||||
// TestOrphanToken sets the token orphan option to b.
|
||||
// The orphan option is true by default.
|
||||
func TestOrphanToken(b bool) TestOption {
|
||||
@@ -752,6 +804,95 @@ func (v *TestVaultServer) addPolicy(t testing.TB, name string, pc pathCapabiliti
|
||||
require.NoError(vc.Sys().PutPolicy(name, policy))
|
||||
}
|
||||
|
||||
// MountSSH mounts the Vault SSH secret engine and initializes it by
|
||||
// generating a root certificate authority and creating a default role on
|
||||
// the mount. The root CA is returned.
|
||||
//
|
||||
// The default mount path is ssh and the default role name is boundary.
|
||||
// WithTestMountPath, WithTestRoleName, and WithAllowedExtension are the
|
||||
// test options supported. extensions are combined into allowed_extensions.
|
||||
// allowed_extensions defaults to "*" (allow all)
|
||||
// The role is defined as:
|
||||
//
|
||||
// {
|
||||
// "key_type": "ca",
|
||||
// "allowed_users": "*",
|
||||
// "allowed_extensions": "*",
|
||||
// "allow_user_certificates": true,
|
||||
// "ttl": "12h0m0s"
|
||||
// }
|
||||
//
|
||||
// MountSSH also adds a Vault policy named 'ssh' to v and adds it to the
|
||||
// standard set of polices attached to tokens created with v.CreateToken.
|
||||
// The policy is defined as:
|
||||
//
|
||||
// path "mountPath/*" {
|
||||
// capabilities = ["create", "read", "update", "delete", "list"]
|
||||
// }
|
||||
func (v *TestVaultServer) MountSSH(t testing.TB, opt ...TestOption) *vault.Secret {
|
||||
require := require.New(t)
|
||||
opts := getTestOpts(t, opt...)
|
||||
vc := v.client(t).cl
|
||||
|
||||
// Mount SSH
|
||||
maxTTL := 24 * time.Hour
|
||||
if t, ok := t.(*testing.T); ok {
|
||||
if deadline, ok := t.Deadline(); ok {
|
||||
maxTTL = time.Until(deadline) * 2
|
||||
}
|
||||
}
|
||||
|
||||
defaultTTL := maxTTL / 2
|
||||
t.Logf("maxTTL: %s, defaultTTL: %s", maxTTL, defaultTTL)
|
||||
mountInput := &vault.MountInput{
|
||||
Type: "ssh",
|
||||
Description: t.Name(),
|
||||
Config: vault.MountConfigInput{
|
||||
DefaultLeaseTTL: defaultTTL.String(),
|
||||
MaxLeaseTTL: maxTTL.String(),
|
||||
},
|
||||
}
|
||||
mountPath := opts.mountPath
|
||||
if mountPath == "" {
|
||||
mountPath = "ssh/"
|
||||
}
|
||||
// this is the call that actually enables the secrets engine
|
||||
require.NoError(vc.Sys().Mount(mountPath, mountInput))
|
||||
|
||||
policyPath := fmt.Sprintf("%s*", mountPath)
|
||||
pc := pathCapabilities{
|
||||
policyPath: createCapability | readCapability | updateCapability | deleteCapability | listCapability,
|
||||
}
|
||||
v.addPolicy(t, "ssh", pc)
|
||||
|
||||
// Generate a root CA
|
||||
caPath := path.Join(mountPath, "config/ca")
|
||||
caOptions := map[string]any{
|
||||
"generate_signing_key": true,
|
||||
}
|
||||
s, err := vc.Logical().Write(caPath, caOptions)
|
||||
require.NoError(err)
|
||||
require.NotEmpty(s)
|
||||
|
||||
// Create default role
|
||||
rolePath := path.Join(mountPath, "roles", opts.roleName)
|
||||
allowExtensions := "*"
|
||||
if len(opts.allowedExtensions) > 0 {
|
||||
allowExtensions = strings.Join(opts.allowedExtensions, ",")
|
||||
}
|
||||
roleOptions := map[string]any{
|
||||
"key_type": "ca",
|
||||
"allowed_users": "*",
|
||||
"allowed_extensions": allowExtensions,
|
||||
"allow_user_certificates": true,
|
||||
"ttl": defaultTTL.String(),
|
||||
}
|
||||
_, err = vc.Logical().Write(rolePath, roleOptions)
|
||||
require.NoError(err)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// MountPKI mounts the Vault PKI secret engine and initializes it by
|
||||
// generating a root certificate authority and creating a default role on
|
||||
// the mount. The root CA is returned.
|
||||
|
||||
+418
-71
@@ -2,6 +2,7 @@ package credentiallibraries
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
@@ -33,6 +34,11 @@ const (
|
||||
httpMethodField = "attributes.http_method"
|
||||
httpRequestBodyField = "attributes.http_request_body"
|
||||
credentialMappingPathField = "credential_mapping_overrides"
|
||||
sshCertUsernameField = "attributes.username"
|
||||
keyTypeField = "attributes.key_type"
|
||||
keyBitsField = "attributes.key_bits"
|
||||
criticalOptionsField = "attributes.critical_options"
|
||||
extensionsField = "attributes.extensions"
|
||||
domain = "credential"
|
||||
)
|
||||
|
||||
@@ -45,7 +51,8 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
maskManager handlers.MaskManager
|
||||
maskManager handlers.MaskManager
|
||||
sshCertMaskManager handlers.MaskManager
|
||||
|
||||
// IdActions contains the set of actions that can be performed on
|
||||
// individual resources
|
||||
@@ -62,6 +69,18 @@ var (
|
||||
action.Create,
|
||||
action.List,
|
||||
}
|
||||
|
||||
validCredentialTypesVaultGeneric = []credential.Type{
|
||||
credential.UsernamePasswordType,
|
||||
credential.SshPrivateKeyType,
|
||||
credential.UnspecifiedType,
|
||||
}
|
||||
|
||||
validKeyTypes = []string{
|
||||
vault.KeyTypeEcdsa,
|
||||
vault.KeyTypeEd25519,
|
||||
vault.KeyTypeRsa,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -70,6 +89,10 @@ func init() {
|
||||
handlers.MaskSource{&pb.CredentialLibrary{}, &pb.VaultCredentialLibraryAttributes{}}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if sshCertMaskManager, err = handlers.NewMaskManager(handlers.MaskDestination{&store.SSHCertificateCredentialLibrary{}},
|
||||
handlers.MaskSource{&pb.CredentialLibrary{}, &pb.VaultSSHCertificateCredentialLibraryAttributes{}}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Service handles request as described by the pbs.CredentialLibraryServiceServer interface.
|
||||
@@ -242,11 +265,23 @@ func (s Service) UpdateCredentialLibrary(ctx context.Context, req *pbs.UpdateCre
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cur, err := repo.LookupCredentialLibrary(ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
var currentCredentialType credential.Type
|
||||
var mo vault.MappingOverride
|
||||
switch subtypes.SubtypeFromId(domain, req.GetId()) {
|
||||
case vault.SSHCertificateLibrarySubtype:
|
||||
cur, err := repo.LookupSSHCertificateCredentialLibrary(ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
currentCredentialType = credential.Type(cur.GetCredentialType())
|
||||
default:
|
||||
cur, err := repo.LookupCredentialLibrary(ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
currentCredentialType = credential.Type(cur.GetCredentialType())
|
||||
mo = cur.MappingOverride
|
||||
}
|
||||
currentCredentialType := credential.Type(cur.GetCredentialType())
|
||||
|
||||
if err := validateUpdateRequest(req, currentCredentialType); err != nil {
|
||||
return nil, err
|
||||
@@ -255,7 +290,7 @@ func (s Service) UpdateCredentialLibrary(ctx context.Context, req *pbs.UpdateCre
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
cl, err := s.updateInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem(), currentCredentialType, cur.MappingOverride)
|
||||
cl, err := s.updateInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem(), currentCredentialType, mo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -298,16 +333,28 @@ func (s Service) DeleteCredentialLibrary(ctx context.Context, req *pbs.DeleteCre
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s Service) listFromRepo(ctx context.Context, storeId string) ([]*vault.CredentialLibrary, error) {
|
||||
func (s Service) listFromRepo(ctx context.Context, storeId string) ([]credential.Library, error) {
|
||||
const op = "credentiallibraries.(Service).listFromRepo"
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
csl, err := repo.ListCredentialLibraries(ctx, storeId)
|
||||
genCsl, err := repo.ListCredentialLibraries(ctx, storeId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
certCsl, err := repo.ListSSHCertificateCredentialLibraries(ctx, storeId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
csl := make([]credential.Library, 0, len(genCsl)+len(certCsl))
|
||||
for _, s := range genCsl {
|
||||
csl = append(csl, s)
|
||||
}
|
||||
for _, s := range certCsl {
|
||||
csl = append(csl, s)
|
||||
}
|
||||
return csl, nil
|
||||
}
|
||||
|
||||
@@ -317,32 +364,67 @@ func (s Service) getFromRepo(ctx context.Context, id string) (credential.Library
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
cs, err := repo.LookupCredentialLibrary(ctx, id)
|
||||
if err != nil && !errors.IsNotFoundError(err) {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
switch subtypes.SubtypeFromId(domain, id) {
|
||||
case vault.GenericLibrarySubtype:
|
||||
cs, err := repo.LookupCredentialLibrary(ctx, id)
|
||||
if err != nil && !errors.IsNotFoundError(err) {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
if cs == nil {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("credential library %q not found", id))
|
||||
}
|
||||
return cs, err
|
||||
case vault.SSHCertificateLibrarySubtype:
|
||||
cs, err := repo.LookupSSHCertificateCredentialLibrary(ctx, id)
|
||||
if err != nil && !errors.IsNotFoundError(err) {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
if cs == nil {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("ssh certificate credential library %q not found", id))
|
||||
}
|
||||
return cs, err
|
||||
}
|
||||
if cs == nil {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("credential library %q not found", id))
|
||||
}
|
||||
return cs, err
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "unrecognized credential library subtype")
|
||||
}
|
||||
|
||||
func (s Service) createInRepo(ctx context.Context, scopeId string, item *pb.CredentialLibrary) (credential.Library, error) {
|
||||
const op = "credentiallibraries.(Service).createInRepo"
|
||||
cl, err := toStorageVaultLibrary(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.CreateCredentialLibrary(ctx, scopeId, cl)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create credential library"))
|
||||
}
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create credential library but no error returned from repository.")
|
||||
var out credential.Library
|
||||
switch subtypes.SubtypeFromType(domain, item.GetType()) {
|
||||
case vault.SSHCertificateLibrarySubtype:
|
||||
cl, err := toStorageVaultSSHCertificateLibrary(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)
|
||||
}
|
||||
rl, err := repo.CreateSSHCertificateCredentialLibrary(ctx, scopeId, cl)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create ssh certificate credential library"))
|
||||
}
|
||||
if rl == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create ssh certificate credential library but no error returned from repository.")
|
||||
}
|
||||
out = rl
|
||||
default:
|
||||
cl, err := toStorageVaultLibrary(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)
|
||||
}
|
||||
rl, err := repo.CreateCredentialLibrary(ctx, scopeId, cl)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create credential library"))
|
||||
}
|
||||
if rl == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create credential library but no error returned from repository.")
|
||||
}
|
||||
out = rl
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
@@ -372,26 +454,53 @@ func (s Service) updateInRepo(
|
||||
item.CredentialMappingOverrides = mappingStruct
|
||||
}
|
||||
|
||||
cl, err := toStorageVaultLibrary(item.GetCredentialStoreId(), item)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
cl.PublicId = id
|
||||
|
||||
dbMasks = append(dbMasks, maskManager.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."})
|
||||
}
|
||||
var out credential.Library
|
||||
rowsUpdated := 0
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
out, rowsUpdated, err := repo.UpdateCredentialLibrary(ctx, projId, cl, item.GetVersion(), dbMasks)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to update credential library"))
|
||||
}
|
||||
if rowsUpdated == 0 {
|
||||
return nil, handlers.NotFoundErrorf("Credential Library %q doesn't exist or incorrect version provided.", id)
|
||||
switch subtypes.SubtypeFromId(domain, id) {
|
||||
case vault.SSHCertificateLibrarySubtype:
|
||||
dbMasks = append(dbMasks, sshCertMaskManager.Translate(masks)...)
|
||||
if getMapUpdate(criticalOptionsField, masks) {
|
||||
dbMasks = append(dbMasks, vault.CriticalOptionsField)
|
||||
}
|
||||
if getMapUpdate(extensionsField, masks) {
|
||||
dbMasks = append(dbMasks, vault.ExtensionsField)
|
||||
}
|
||||
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."})
|
||||
}
|
||||
cl, err := toStorageVaultSSHCertificateLibrary(item.GetCredentialStoreId(), item)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
cl.PublicId = id
|
||||
out, rowsUpdated, err = repo.UpdateSSHCertificateCredentialLibrary(ctx, projId, cl, item.GetVersion(), dbMasks)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to update credential library"))
|
||||
}
|
||||
if rowsUpdated == 0 {
|
||||
return nil, handlers.NotFoundErrorf("Credential Library %q doesn't exist or incorrect version provided.", id)
|
||||
}
|
||||
default:
|
||||
dbMasks = append(dbMasks, maskManager.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."})
|
||||
}
|
||||
cl, err := toStorageVaultLibrary(item.GetCredentialStoreId(), item)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
cl.PublicId = id
|
||||
out, rowsUpdated, err = repo.UpdateCredentialLibrary(ctx, projId, cl, item.GetVersion(), dbMasks)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to update credential library"))
|
||||
}
|
||||
if rowsUpdated == 0 {
|
||||
return nil, handlers.NotFoundErrorf("Credential Library %q doesn't exist or incorrect version provided.", id)
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
@@ -402,7 +511,13 @@ func (s Service) deleteFromRepo(ctx context.Context, scopeId, id string) (bool,
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
rows, err := repo.DeleteCredentialLibrary(ctx, scopeId, id)
|
||||
rows := 0
|
||||
switch subtypes.SubtypeFromId(domain, id) {
|
||||
case vault.SSHCertificateLibrarySubtype:
|
||||
rows, err = repo.DeleteSSHCertificateCredentialLibrary(ctx, scopeId, id)
|
||||
default:
|
||||
rows, err = repo.DeleteCredentialLibrary(ctx, scopeId, id)
|
||||
}
|
||||
if err != nil {
|
||||
if errors.IsNotFoundError(err) {
|
||||
return false, nil
|
||||
@@ -428,9 +543,9 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
parentId = id
|
||||
default:
|
||||
opts = append(opts, auth.WithId(id))
|
||||
|
||||
parentId = id
|
||||
switch subtypes.SubtypeFromId(domain, id) {
|
||||
case vault.Subtype:
|
||||
case vault.GenericLibrarySubtype:
|
||||
cl, err := repo.LookupCredentialLibrary(ctx, id)
|
||||
if err != nil {
|
||||
res.Error = err
|
||||
@@ -441,6 +556,17 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
return res
|
||||
}
|
||||
parentId = cl.GetStoreId()
|
||||
case vault.SSHCertificateLibrarySubtype:
|
||||
cl, err := repo.LookupSSHCertificateCredentialLibrary(ctx, id)
|
||||
if err != nil {
|
||||
res.Error = err
|
||||
return res
|
||||
}
|
||||
if cl == nil {
|
||||
res.Error = handlers.NotFoundError()
|
||||
return res
|
||||
}
|
||||
parentId = cl.GetStoreId()
|
||||
default:
|
||||
res.Error = errors.New(ctx, errors.InvalidParameter, op, "unrecognized credential library subtype from id")
|
||||
return res
|
||||
@@ -515,7 +641,7 @@ func toProto(in credential.Library, opt ...handlers.Option) (*pb.CredentialLibra
|
||||
out.AuthorizedActions = opts.WithAuthorizedActions
|
||||
}
|
||||
switch subtypes.SubtypeFromId(domain, in.GetPublicId()) {
|
||||
case vault.Subtype:
|
||||
case vault.GenericLibrarySubtype:
|
||||
vaultIn, ok := in.(*vault.CredentialLibrary)
|
||||
if !ok {
|
||||
return nil, errors.NewDeprecated(errors.Internal, op, "unable to cast to vault credential library")
|
||||
@@ -568,6 +694,47 @@ func toProto(in credential.Library, opt ...handlers.Option) (*pb.CredentialLibra
|
||||
VaultCredentialLibraryAttributes: attrs,
|
||||
}
|
||||
}
|
||||
case vault.SSHCertificateLibrarySubtype:
|
||||
vaultIn, ok := in.(*vault.SSHCertificateCredentialLibrary)
|
||||
if !ok {
|
||||
return nil, errors.NewDeprecated(errors.Internal, op, "unable to cast to vault ssh certificate credential library")
|
||||
}
|
||||
// We don't check for mapping overrides here -- this subtype does not currently support them.
|
||||
out.CredentialType = vaultIn.GetCredentialType()
|
||||
if outputFields.Has(globals.AttributesField) {
|
||||
attrs := &pb.VaultSSHCertificateCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String(vaultIn.GetVaultPath()),
|
||||
}
|
||||
if vaultIn.GetUsername() != "" {
|
||||
attrs.Username = wrapperspb.String(vaultIn.GetUsername())
|
||||
}
|
||||
if vaultIn.GetKeyType() != "" {
|
||||
attrs.KeyType = wrapperspb.String(vaultIn.GetKeyType())
|
||||
}
|
||||
if vaultIn.GetKeyBits() != 0 {
|
||||
attrs.KeyBits = &wrapperspb.UInt32Value{Value: vaultIn.GetKeyBits()}
|
||||
}
|
||||
if vaultIn.GetTtl() != "" {
|
||||
attrs.Ttl = wrapperspb.String(vaultIn.GetTtl())
|
||||
}
|
||||
if vaultIn.GetKeyId() != "" {
|
||||
attrs.KeyId = wrapperspb.String(vaultIn.GetKeyId())
|
||||
}
|
||||
if vaultIn.GetCriticalOptions() != "" {
|
||||
co := make(map[string]string)
|
||||
json.Unmarshal([]byte(vaultIn.GetCriticalOptions()), &co)
|
||||
attrs.CriticalOptions = co
|
||||
}
|
||||
if vaultIn.GetExtensions() != "" {
|
||||
e := make(map[string]string)
|
||||
json.Unmarshal([]byte(vaultIn.GetExtensions()), &e)
|
||||
attrs.Extensions = e
|
||||
}
|
||||
out.Attrs = &pb.CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes{
|
||||
VaultSshCertificateCredentialLibraryAttributes: attrs,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
@@ -582,7 +749,11 @@ func toStorageVaultLibrary(storeId string, in *pb.CredentialLibrary) (out *vault
|
||||
opts = append(opts, vault.WithDescription(in.GetDescription().GetValue()))
|
||||
}
|
||||
|
||||
attrs := in.GetVaultCredentialLibraryAttributes()
|
||||
attrs := in.GetVaultGenericCredentialLibraryAttributes()
|
||||
if attrs == nil {
|
||||
// fallback to attributes for older subtype
|
||||
attrs = in.GetVaultCredentialLibraryAttributes()
|
||||
}
|
||||
if attrs.GetHttpMethod() != nil {
|
||||
opts = append(opts, vault.WithMethod(vault.Method(strings.ToUpper(attrs.GetHttpMethod().GetValue()))))
|
||||
}
|
||||
@@ -631,13 +802,66 @@ func toStorageVaultLibrary(storeId string, in *pb.CredentialLibrary) (out *vault
|
||||
return cs, err
|
||||
}
|
||||
|
||||
func toStorageVaultSSHCertificateLibrary(storeId string, in *pb.CredentialLibrary) (out *vault.SSHCertificateCredentialLibrary, err error) {
|
||||
const op = "credentiallibraries.toStorageVaultSSHCertificateLibrary"
|
||||
var opts []vault.Option
|
||||
if in.GetName() != nil {
|
||||
opts = append(opts, vault.WithName(in.GetName().GetValue()))
|
||||
}
|
||||
if in.GetDescription() != nil {
|
||||
opts = append(opts, vault.WithDescription(in.GetDescription().GetValue()))
|
||||
}
|
||||
opts = append(opts, vault.WithCredentialType(credential.Type(in.GetCredentialType())))
|
||||
|
||||
attrs := in.GetVaultSshCertificateCredentialLibraryAttributes()
|
||||
if attrs.GetKeyType() != nil {
|
||||
opts = append(opts, vault.WithKeyType(attrs.GetKeyType().GetValue()))
|
||||
}
|
||||
if attrs.GetKeyBits() != nil {
|
||||
opts = append(opts, vault.WithKeyBits(attrs.GetKeyBits().GetValue()))
|
||||
}
|
||||
if attrs.GetTtl() != nil {
|
||||
opts = append(opts, vault.WithTtl(attrs.GetTtl().GetValue()))
|
||||
}
|
||||
if attrs.GetKeyId() != nil {
|
||||
opts = append(opts, vault.WithKeyId(attrs.GetKeyId().GetValue()))
|
||||
}
|
||||
if attrs.GetCriticalOptions() != nil {
|
||||
co, err := json.Marshal(attrs.GetCriticalOptions())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts = append(opts, vault.WithCriticalOptions(string(co)))
|
||||
}
|
||||
if attrs.GetExtensions() != nil {
|
||||
e, err := json.Marshal(attrs.GetExtensions())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts = append(opts, vault.WithExtensions(string(e)))
|
||||
}
|
||||
|
||||
cs, err := vault.NewSSHCertificateCredentialLibrary(storeId, attrs.GetPath().GetValue(), attrs.GetUsername().GetValue(), opts...)
|
||||
if err != nil {
|
||||
return nil, errors.WrapDeprecated(err, op, errors.WithMsg("unable to build credential library"))
|
||||
}
|
||||
return cs, err
|
||||
}
|
||||
|
||||
// A validateX method should exist for each method above. These methods do not make calls to any backing service but enforce
|
||||
// requirements on the structure of the request. They verify that:
|
||||
// - The path passed in is correctly formatted
|
||||
// - All required parameters are set
|
||||
// - There are no conflicting parameters provided
|
||||
func validateGetRequest(req *pbs.GetCredentialLibraryRequest) error {
|
||||
return handlers.ValidateGetRequest(handlers.NoopValidatorFn, req, vault.CredentialLibraryPrefix)
|
||||
prefix := ""
|
||||
switch subtypes.SubtypeFromId(domain, req.GetId()) {
|
||||
case vault.SSHCertificateLibrarySubtype:
|
||||
prefix = vault.SSHCertificateCredentialLibraryPrefix
|
||||
default:
|
||||
prefix = vault.CredentialLibraryPrefix
|
||||
}
|
||||
return handlers.ValidateGetRequest(handlers.NoopValidatorFn, req, prefix)
|
||||
}
|
||||
|
||||
func validateCreateRequest(req *pbs.CreateCredentialLibraryRequest) error {
|
||||
@@ -645,23 +869,82 @@ func validateCreateRequest(req *pbs.CreateCredentialLibraryRequest) error {
|
||||
badFields := map[string]string{}
|
||||
switch subtypes.SubtypeFromId(domain, req.GetItem().GetCredentialStoreId()) {
|
||||
case vault.Subtype:
|
||||
if t := req.GetItem().GetType(); t != "" && subtypes.SubtypeFromType(domain, t) != vault.Subtype {
|
||||
t := req.GetItem().GetType()
|
||||
if t == "" {
|
||||
badFields[globals.TypeField] = "This is a required field."
|
||||
}
|
||||
|
||||
// support older vault generic libraries with 'vault.Subtype'.
|
||||
if subtypes.SubtypeFromType(domain, t) == vault.Subtype {
|
||||
req.GetItem().Type = vault.GenericLibrarySubtype.String()
|
||||
t = vault.GenericLibrarySubtype.String()
|
||||
}
|
||||
|
||||
if subtypes.SubtypeFromType(domain, t) != vault.GenericLibrarySubtype &&
|
||||
subtypes.SubtypeFromType(domain, t) != vault.SSHCertificateLibrarySubtype {
|
||||
badFields[globals.CredentialStoreIdField] = "If included, type must match that of the credential store."
|
||||
}
|
||||
attrs := req.GetItem().GetVaultCredentialLibraryAttributes()
|
||||
if attrs == nil {
|
||||
badFields[attributesPathField] = "This is a required field."
|
||||
|
||||
switch subtypes.SubtypeFromType(domain, req.GetItem().GetType()) {
|
||||
case vault.GenericLibrarySubtype:
|
||||
isValidCred := false
|
||||
ct := req.GetItem().GetCredentialType()
|
||||
for _, t := range validCredentialTypesVaultGeneric {
|
||||
if ct == "" || ct == string(t) {
|
||||
isValidCred = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isValidCred {
|
||||
badFields[globals.CredentialTypeField] = fmt.Sprintf("Unknown credential type %q", ct)
|
||||
}
|
||||
|
||||
attrs := req.GetItem().GetVaultGenericCredentialLibraryAttributes()
|
||||
if attrs == nil {
|
||||
// fallback to attributes for older subtype
|
||||
attrs = req.GetItem().GetVaultCredentialLibraryAttributes()
|
||||
if attrs == nil {
|
||||
badFields[attributesPathField] = "This is a required field."
|
||||
}
|
||||
}
|
||||
if attrs.GetPath().GetValue() == "" {
|
||||
badFields[vaultPathField] = "This is a required field."
|
||||
}
|
||||
if m := attrs.GetHttpMethod(); m != nil && !strutil.StrListContains([]string{"GET", "POST"}, strings.ToUpper(m.GetValue())) {
|
||||
badFields[httpMethodField] = "If set, value must be 'GET' or 'POST'."
|
||||
}
|
||||
if b := attrs.GetHttpRequestBody(); b != nil && strings.ToUpper(attrs.GetHttpMethod().GetValue()) != "POST" {
|
||||
badFields[httpRequestBodyField] = fmt.Sprintf("Field can only be set if %q is set to the value 'POST'.", httpMethodField)
|
||||
}
|
||||
validateMapping(badFields, credential.Type(req.GetItem().GetCredentialType()), req.GetItem().CredentialMappingOverrides.AsMap())
|
||||
case vault.SSHCertificateLibrarySubtype:
|
||||
if req.GetItem().GetCredentialType() != "" {
|
||||
badFields[globals.CredentialTypeField] = fmt.Sprintf("This field is read only and cannot be set.")
|
||||
}
|
||||
|
||||
attrs := req.GetItem().GetVaultSshCertificateCredentialLibraryAttributes()
|
||||
if attrs == nil {
|
||||
badFields[attributesPathField] = "This is a required field."
|
||||
}
|
||||
if attrs.GetPath().GetValue() == "" {
|
||||
badFields[vaultPathField] = "This is a required field."
|
||||
}
|
||||
if attrs.GetUsername().GetValue() == "" {
|
||||
badFields[sshCertUsernameField] = "This is a required field."
|
||||
}
|
||||
if (attrs.GetKeyType() == nil) != (attrs.GetKeyBits() == nil) {
|
||||
if attrs.GetKeyType() != nil && attrs.GetKeyType().GetValue() != vault.KeyTypeEd25519 {
|
||||
badFields[keyTypeField] = fmt.Sprintf("If set, %q must also be set.", keyBitsField)
|
||||
}
|
||||
if attrs.GetKeyBits() != nil {
|
||||
badFields[keyBitsField] = fmt.Sprintf("If set, %q must also be set.", keyTypeField)
|
||||
}
|
||||
}
|
||||
if t := attrs.GetKeyType(); t != nil && !strutil.StrListContains(validKeyTypes, strings.ToLower(t.GetValue())) {
|
||||
badFields[keyTypeField] = "If set, value must be 'ed25519', 'ecdsa', or 'rsa'."
|
||||
}
|
||||
validateKeyBits(badFields, attrs.GetKeyBits().GetValue(), attrs.GetKeyType().GetValue())
|
||||
}
|
||||
if attrs.GetPath().GetValue() == "" {
|
||||
badFields[vaultPathField] = "This is a required field."
|
||||
}
|
||||
if m := attrs.GetHttpMethod(); m != nil && !strutil.StrListContains([]string{"GET", "POST"}, strings.ToUpper(m.GetValue())) {
|
||||
badFields[httpMethodField] = "If set, value must be 'GET' or 'POST'."
|
||||
}
|
||||
if b := attrs.GetHttpRequestBody(); b != nil && strings.ToUpper(attrs.GetHttpMethod().GetValue()) != "POST" {
|
||||
badFields[httpRequestBodyField] = fmt.Sprintf("Field can only be set if %q is set to the value 'POST'.", httpMethodField)
|
||||
}
|
||||
validateMapping(badFields, credential.Type(req.GetItem().GetCredentialType()), req.GetItem().CredentialMappingOverrides.AsMap())
|
||||
default:
|
||||
badFields[globals.CredentialStoreIdField] = "This field must be a valid credential store id."
|
||||
}
|
||||
@@ -670,17 +953,29 @@ func validateCreateRequest(req *pbs.CreateCredentialLibraryRequest) error {
|
||||
}
|
||||
|
||||
func validateUpdateRequest(req *pbs.UpdateCredentialLibraryRequest, currentCredentialType credential.Type) error {
|
||||
prefix := ""
|
||||
st := subtypes.SubtypeFromId(domain, req.GetId())
|
||||
switch st {
|
||||
case vault.GenericLibrarySubtype:
|
||||
prefix = vault.CredentialLibraryPrefix
|
||||
case vault.SSHCertificateLibrarySubtype:
|
||||
prefix = vault.SSHCertificateCredentialLibraryPrefix
|
||||
}
|
||||
return handlers.ValidateUpdateRequest(req, req.GetItem(), func() map[string]string {
|
||||
badFields := map[string]string{}
|
||||
switch subtypes.SubtypeFromId(domain, req.GetId()) {
|
||||
case vault.Subtype:
|
||||
if req.GetItem().GetType() != "" && subtypes.SubtypeFromType(domain, req.GetItem().GetType()) != vault.Subtype {
|
||||
switch st {
|
||||
case vault.GenericLibrarySubtype:
|
||||
if req.GetItem().GetType() != "" && subtypes.SubtypeFromType(domain, req.GetItem().GetType()) != vault.GenericLibrarySubtype {
|
||||
badFields[globals.TypeField] = "Cannot modify resource type."
|
||||
}
|
||||
if req.GetItem().GetCredentialType() != "" && req.GetItem().GetCredentialType() != string(currentCredentialType) {
|
||||
badFields[globals.CredentialTypeField] = "Cannot modify credential type."
|
||||
}
|
||||
attrs := req.GetItem().GetVaultCredentialLibraryAttributes()
|
||||
attrs := req.GetItem().GetVaultGenericCredentialLibraryAttributes()
|
||||
if attrs == nil {
|
||||
// fallback to attributes for older subtype
|
||||
attrs = req.GetItem().GetVaultCredentialLibraryAttributes()
|
||||
}
|
||||
if attrs != nil {
|
||||
if handlers.MaskContains(req.GetUpdateMask().GetPaths(), vaultPathField) && attrs.GetPath().GetValue() == "" {
|
||||
badFields[vaultPathField] = "This is a required field and cannot be set to empty."
|
||||
@@ -693,13 +988,33 @@ func validateUpdateRequest(req *pbs.UpdateCredentialLibraryRequest, currentCrede
|
||||
}
|
||||
validateMapping(badFields, currentCredentialType, req.GetItem().CredentialMappingOverrides.AsMap())
|
||||
}
|
||||
case vault.SSHCertificateLibrarySubtype:
|
||||
if req.GetItem().GetType() != "" && subtypes.SubtypeFromType(domain, req.GetItem().GetType()) != vault.SSHCertificateLibrarySubtype {
|
||||
badFields[globals.TypeField] = "Cannot modify resource type."
|
||||
}
|
||||
if req.GetItem().GetCredentialType() != "" && req.GetItem().GetCredentialType() != string(currentCredentialType) {
|
||||
badFields[globals.CredentialTypeField] = "Cannot modify credential type."
|
||||
}
|
||||
attrs := req.GetItem().GetVaultSshCertificateCredentialLibraryAttributes()
|
||||
if attrs != nil {
|
||||
if handlers.MaskContains(req.GetUpdateMask().GetPaths(), vaultPathField) && attrs.GetPath().GetValue() == "" {
|
||||
badFields[vaultPathField] = "This is a required field and cannot be set to empty."
|
||||
}
|
||||
if u := attrs.GetUsername().GetValue(); handlers.MaskContains(req.GetUpdateMask().GetPaths(), sshCertUsernameField) && u == "" {
|
||||
badFields[sshCertUsernameField] = "This is a required field and cannot be set to empty."
|
||||
}
|
||||
if t := attrs.GetKeyType(); t != nil && !strutil.StrListContains(validKeyTypes, strings.ToLower(t.GetValue())) {
|
||||
badFields[keyTypeField] = "If set, value must be 'ed25519', 'ecdsa', or 'rsa'."
|
||||
}
|
||||
validateKeyBits(badFields, attrs.GetKeyBits().GetValue(), attrs.GetKeyType().GetValue())
|
||||
}
|
||||
}
|
||||
return badFields
|
||||
}, vault.CredentialLibraryPrefix)
|
||||
}, prefix)
|
||||
}
|
||||
|
||||
func validateDeleteRequest(req *pbs.DeleteCredentialLibraryRequest) error {
|
||||
return handlers.ValidateDeleteRequest(handlers.NoopValidatorFn, req, vault.CredentialLibraryPrefix)
|
||||
return handlers.ValidateDeleteRequest(handlers.NoopValidatorFn, req, vault.CredentialLibraryPrefix, vault.SSHCertificateCredentialLibraryPrefix)
|
||||
}
|
||||
|
||||
func validateListRequest(req *pbs.ListCredentialLibrariesRequest) error {
|
||||
@@ -747,6 +1062,38 @@ func validateMapping(badFields map[string]string, credentialType credential.Type
|
||||
}
|
||||
}
|
||||
|
||||
// validateKeyBits appends to badFields if keyBits and keyType aren't accepted combinations for an SSHCertificateCredentialLibrary.
|
||||
// If keyType is an empty string, validateKeyBits only validates keyBits.
|
||||
func validateKeyBits(badFields map[string]string, keyBits uint32, keyType string) {
|
||||
switch keyBits {
|
||||
case vault.KeyBitsDefault:
|
||||
case vault.KeyBitsEcdsa256, vault.KeyBitsEcdsa384, vault.KeyBitsEcdsa521:
|
||||
if keyType != "" && keyType != vault.KeyTypeEcdsa {
|
||||
badFields[keyBitsField] = fmt.Sprintf("Invalid bit size %d for key type %s", keyBits, keyType)
|
||||
}
|
||||
case vault.KeyBitsRsa2048, vault.KeyBitsRsa3072, vault.KeyBitsRsa4096:
|
||||
if keyType != "" && keyType != vault.KeyTypeRsa {
|
||||
badFields[keyBitsField] = fmt.Sprintf("Invalid bit size %d for key type %s", keyBits, keyType)
|
||||
}
|
||||
default:
|
||||
badFields[keyBitsField] = fmt.Sprintf("Invalid bit size %d", keyBits)
|
||||
}
|
||||
}
|
||||
|
||||
func getMapUpdate(field string, apiMasks []string) bool {
|
||||
for _, m := range apiMasks {
|
||||
if m == field {
|
||||
return true
|
||||
}
|
||||
|
||||
fieldPrefix := fmt.Sprintf("%v.", field)
|
||||
if s := strings.SplitN(m, fieldPrefix, 2); len(s) == 2 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getMappingUpdates(credentialType credential.Type, current vault.MappingOverride, new map[string]any, apiMasks []string) (map[string]any, bool) {
|
||||
ret := make(map[string]any)
|
||||
masks := make(map[string]bool)
|
||||
|
||||
+893
-19
File diff suppressed because it is too large.
Load diff
@@ -33,7 +33,16 @@ func dynamicToWorkerCredential(ctx context.Context, cred credential.Dynamic) (se
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
case credential.SshCertificate:
|
||||
workerCred = &serverpb.Credential{
|
||||
Credential: &serverpb.Credential_SshCertificate{
|
||||
SshCertificate: &serverpb.SshCertificate{
|
||||
Username: c.Username(),
|
||||
PrivateKey: string(c.PrivateKey()),
|
||||
Certificate: string(c.Certificate()),
|
||||
},
|
||||
},
|
||||
}
|
||||
case credential.SshPrivateKey:
|
||||
workerCred = &serverpb.Credential{
|
||||
Credential: &serverpb.Credential_SshPrivateKey{
|
||||
|
||||
@@ -1810,6 +1810,7 @@ func validateAddCredentialSourcesRequest(req *pbs.AddTargetCredentialSourcesRequ
|
||||
for _, cl := range req.GetInjectedApplicationCredentialSourceIds() {
|
||||
if !handlers.ValidId(handlers.Id(cl),
|
||||
vault.CredentialLibraryPrefix,
|
||||
vault.SSHCertificateCredentialLibraryPrefix,
|
||||
credential.UsernamePasswordCredentialPrefix,
|
||||
credential.PreviousUsernamePasswordCredentialPrefix,
|
||||
credential.SshPrivateKeyCredentialPrefix) {
|
||||
@@ -1857,6 +1858,7 @@ func validateSetCredentialSourcesRequest(req *pbs.SetTargetCredentialSourcesRequ
|
||||
for _, cl := range req.GetInjectedApplicationCredentialSourceIds() {
|
||||
if !handlers.ValidId(handlers.Id(cl),
|
||||
vault.CredentialLibraryPrefix,
|
||||
vault.SSHCertificateCredentialLibraryPrefix,
|
||||
credential.UsernamePasswordCredentialPrefix,
|
||||
credential.PreviousUsernamePasswordCredentialPrefix,
|
||||
credential.SshPrivateKeyCredentialPrefix) {
|
||||
@@ -1908,6 +1910,7 @@ func validateRemoveCredentialSourcesRequest(req *pbs.RemoveTargetCredentialSourc
|
||||
for _, cl := range req.GetInjectedApplicationCredentialSourceIds() {
|
||||
if !handlers.ValidId(handlers.Id(cl),
|
||||
vault.CredentialLibraryPrefix,
|
||||
vault.SSHCertificateCredentialLibraryPrefix,
|
||||
credential.UsernamePasswordCredentialPrefix,
|
||||
credential.PreviousUsernamePasswordCredentialPrefix,
|
||||
credential.SshPrivateKeyCredentialPrefix,
|
||||
|
||||
@@ -2516,6 +2516,7 @@ func TestAuthorizeSession(t *testing.T) {
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Name: wrapperspb.String("Library Name"),
|
||||
Description: wrapperspb.String("Library Description"),
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
|
||||
Path: &wrapperspb.StringValue{
|
||||
@@ -2629,7 +2630,7 @@ func TestAuthorizeSession(t *testing.T) {
|
||||
Name: clsResp.GetItem().GetName().GetValue(),
|
||||
Description: clsResp.GetItem().GetDescription().GetValue(),
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Type: vault.Subtype.String(),
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
},
|
||||
}},
|
||||
// TODO: validate the contents of the authorization token is what is expected
|
||||
@@ -2758,6 +2759,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Name: wrapperspb.String("Usernamepassword Library"),
|
||||
Description: wrapperspb.String("Usernamepassword Library Description"),
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String(path.Join("secret", "data", "default-userpass")),
|
||||
@@ -2772,6 +2774,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Name: wrapperspb.String("Unspecified Library"),
|
||||
Description: wrapperspb.String("Unspecified Library Description"),
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String(path.Join("secret", "data", "default-userpass")),
|
||||
@@ -2789,6 +2792,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Name: wrapperspb.String("Usernamepassword Mapping Library"),
|
||||
Description: wrapperspb.String("Usernamepassword Mapping Library Description"),
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String(path.Join("secret", "data", "non-default-userpass")),
|
||||
@@ -2860,6 +2864,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Name: wrapperspb.String("SSH Private Key Library"),
|
||||
Description: wrapperspb.String("SSH Private Key Library Description"),
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String(path.Join("secret", "data", "default-sshpk")),
|
||||
@@ -2878,6 +2883,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Name: wrapperspb.String("SSH Private Key Mapping Library"),
|
||||
Description: wrapperspb.String("SSH Private Key Mapping Library Description"),
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String(path.Join("secret", "data", "non-default-sshpk")),
|
||||
@@ -2900,6 +2906,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Name: wrapperspb.String("SSH Private Key With Passphrase Library"),
|
||||
Description: wrapperspb.String("SSH Private Key Library Description"),
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String(path.Join("secret", "data", "default-sshpk-with-pass")),
|
||||
@@ -2919,6 +2926,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Name: wrapperspb.String("SSH Private Key With Passphrase Mapping Library"),
|
||||
Description: wrapperspb.String("SSH Private Key Mapping Library Description"),
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String(path.Join("secret", "data", "non-default-sshpk-with-pass")),
|
||||
@@ -2955,7 +2963,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
Name: clsRespUnspecified.GetItem().GetName().GetValue(),
|
||||
Description: clsRespUnspecified.GetItem().GetDescription().GetValue(),
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Type: vault.Subtype.String(),
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2971,7 +2979,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
Name: clsRespUsernamePassword.GetItem().GetName().GetValue(),
|
||||
Description: clsRespUsernamePassword.GetItem().GetDescription().GetValue(),
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Type: vault.Subtype.String(),
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
CredentialType: string(credential.UsernamePasswordType),
|
||||
},
|
||||
Credential: func() *structpb.Struct {
|
||||
@@ -2997,7 +3005,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
Name: clsRespUsernamePasswordWithMapping.GetItem().GetName().GetValue(),
|
||||
Description: clsRespUsernamePasswordWithMapping.GetItem().GetDescription().GetValue(),
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Type: vault.Subtype.String(),
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
CredentialType: string(credential.UsernamePasswordType),
|
||||
},
|
||||
Credential: func() *structpb.Struct {
|
||||
@@ -3075,7 +3083,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
Name: clsRespSshPrivateKey.GetItem().GetName().GetValue(),
|
||||
Description: clsRespSshPrivateKey.GetItem().GetDescription().GetValue(),
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Type: vault.Subtype.String(),
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
CredentialType: string(credential.SshPrivateKeyType),
|
||||
},
|
||||
Credential: func() *structpb.Struct {
|
||||
@@ -3101,7 +3109,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
Name: clsRespSshPrivateKeyWithMapping.GetItem().GetName().GetValue(),
|
||||
Description: clsRespSshPrivateKeyWithMapping.GetItem().GetDescription().GetValue(),
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Type: vault.Subtype.String(),
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
CredentialType: string(credential.SshPrivateKeyType),
|
||||
},
|
||||
Credential: func() *structpb.Struct {
|
||||
@@ -3154,7 +3162,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
Name: clsRespSshPrivateKeyWithPass.GetItem().GetName().GetValue(),
|
||||
Description: clsRespSshPrivateKeyWithPass.GetItem().GetDescription().GetValue(),
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Type: vault.Subtype.String(),
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
CredentialType: string(credential.SshPrivateKeyType),
|
||||
},
|
||||
Credential: func() *structpb.Struct {
|
||||
@@ -3181,7 +3189,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
Name: clsRespSshPrivateKeyWithPassWithMapping.GetItem().GetName().GetValue(),
|
||||
Description: clsRespSshPrivateKeyWithPassWithMapping.GetItem().GetDescription().GetValue(),
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Type: vault.Subtype.String(),
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
CredentialType: string(credential.SshPrivateKeyType),
|
||||
},
|
||||
Credential: func() *structpb.Struct {
|
||||
@@ -3392,6 +3400,7 @@ func TestAuthorizeSession_Errors(t *testing.T) {
|
||||
clsResp, err := credService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Description: wrapperspb.String(fmt.Sprintf("Library Description for target %q", tar.GetName())),
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String(path.Join("database", "creds", "opened")),
|
||||
@@ -3416,6 +3425,7 @@ func TestAuthorizeSession_Errors(t *testing.T) {
|
||||
clsResp, err := credService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Description: wrapperspb.String(fmt.Sprintf("Library Description for target %q", tar.GetName())),
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String("bad path"),
|
||||
@@ -3440,6 +3450,7 @@ func TestAuthorizeSession_Errors(t *testing.T) {
|
||||
clsResp, err := credService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{
|
||||
CredentialStoreId: expiredStore.GetPublicId(),
|
||||
Description: wrapperspb.String(fmt.Sprintf("Library Description for target %q", tar.GetName())),
|
||||
Type: vault.GenericLibrarySubtype.String(),
|
||||
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String(path.Join("database", "creds", "opened")),
|
||||
|
||||
@@ -65,6 +65,7 @@ begin;
|
||||
and o.public_id = p.parent_id
|
||||
and o.type = 'org';
|
||||
|
||||
-- Replaced in 63/03_wh_ssh_cert_library.up.sql
|
||||
create view whx_credential_dimension_target as
|
||||
select key,
|
||||
credential_purpose,
|
||||
|
||||
@@ -4,6 +4,7 @@ begin;
|
||||
-- with the current values in the operational tables for the given parameters. IF the values
|
||||
-- between operational tables and the wh_credential_dimension differ, a new row is inserted in
|
||||
-- the wh_credential_dimension to match the current values in the operational tables.
|
||||
-- Replaced in 63/03_wh_ssh_cert_library.up.sql
|
||||
create function wh_upsert_credential_dimension(p_session_id wt_public_id, p_library_id wt_public_id, p_credential_purpose wh_dim_text) returns wh_dim_key
|
||||
as $$
|
||||
declare
|
||||
|
||||
@@ -6,6 +6,7 @@ begin;
|
||||
|
||||
-- Add new constraint that only allows known types
|
||||
-- This replaces the constraint defined in 36/01_credential_type_up
|
||||
-- Replaced in 63/01_credential_vault_ssh_cert_library
|
||||
alter table credential_type_enm
|
||||
add constraint only_predefined_credential_types_allowed
|
||||
check (
|
||||
|
||||
@@ -157,6 +157,7 @@ begin;
|
||||
-- The whx_credential_dimension_source view shows the current values in the
|
||||
-- operational tables of the credential dimension.
|
||||
-- Replaces whx_credential_dimension_source defined in 16/02_wh_credential_dimension.up.sql
|
||||
-- Replaced in 63/03_wh_ssh_cert_library.up.sql
|
||||
drop view whx_credential_dimension_source;
|
||||
create view whx_credential_dimension_source as
|
||||
select -- id is the first column in the target view
|
||||
|
||||
+1
@@ -188,6 +188,7 @@ begin;
|
||||
'The view returns the current token for the store, if the Vault token has expired this view will return an empty token_hmac and a token_status of ''expired'' '
|
||||
'Each row may contain encrypted data. This view should not be used to retrieve data which will be returned external to boundary.';
|
||||
|
||||
-- Replaced in 63/02_add_ssh_cert_to_vault_cred_library_view.up.sql
|
||||
create view credential_vault_library_issue_credentials as
|
||||
with
|
||||
password_override (library_id, username_attribute, password_attribute) as (
|
||||
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
begin;
|
||||
|
||||
-- drop constraint so we can add ssh_certificate
|
||||
alter table credential_type_enm
|
||||
drop constraint only_predefined_credential_types_allowed;
|
||||
|
||||
-- Add new constraint that only allows known types
|
||||
-- This replaces the constraint defined in 39/01_static_ssh_private_key_creds_up
|
||||
alter table credential_type_enm
|
||||
add constraint only_predefined_credential_types_allowed
|
||||
check (
|
||||
name in (
|
||||
'unspecified',
|
||||
'username_password',
|
||||
'ssh_private_key',
|
||||
'ssh_certificate'
|
||||
)
|
||||
);
|
||||
|
||||
insert into credential_type_enm (name)
|
||||
values ('ssh_certificate');
|
||||
|
||||
create table credential_vault_ssh_cert_key_type_enm (
|
||||
name text primary key
|
||||
constraint only_predefined_key_types_allowed
|
||||
check (
|
||||
name in (
|
||||
'ed25519',
|
||||
'ecdsa',
|
||||
'rsa'
|
||||
)
|
||||
)
|
||||
);
|
||||
comment on table credential_vault_ssh_cert_key_type_enm is
|
||||
'credential_vault_ssh_cert_key_type_enm is an enumeration table for the ssh key type. ';
|
||||
|
||||
insert into credential_vault_ssh_cert_key_type_enm (name)
|
||||
values
|
||||
('ed25519'),
|
||||
('ecdsa'),
|
||||
('rsa');
|
||||
|
||||
create table credential_vault_ssh_cert_key_bits_enm (
|
||||
bits int primary key
|
||||
constraint only_predefined_key_bits_allowed
|
||||
check (
|
||||
bits in (
|
||||
0,
|
||||
2048,
|
||||
3072,
|
||||
4096,
|
||||
256,
|
||||
384,
|
||||
521
|
||||
)
|
||||
)
|
||||
);
|
||||
comment on table credential_vault_ssh_cert_key_bits_enm is
|
||||
'credential_vault_ssh_cert_key_bits_enm is an enumeration table for the ssh key bits. ';
|
||||
|
||||
insert into credential_vault_ssh_cert_key_bits_enm (bits)
|
||||
values
|
||||
(0),
|
||||
(2048),
|
||||
(3072),
|
||||
(4096),
|
||||
(256),
|
||||
(384),
|
||||
(521);
|
||||
|
||||
create table credential_vault_ssh_cert_valid_key_type_key_bits (
|
||||
key_type text not null
|
||||
constraint credential_vault_ssh_cert_key_type_enm_fkey
|
||||
references credential_vault_ssh_cert_key_type_enm (name),
|
||||
key_bits int not null
|
||||
constraint credential_vault_ssh_cert_key_bits_enm_fkey
|
||||
references credential_vault_ssh_cert_key_bits_enm (bits),
|
||||
constraint credential_vault_ssh_cert_valid_key_type_key_bits_uq
|
||||
unique(key_type, key_bits)
|
||||
);
|
||||
|
||||
insert into credential_vault_ssh_cert_valid_key_type_key_bits (key_type, key_bits)
|
||||
values
|
||||
('ed25519', 0),
|
||||
('ecdsa', 256),
|
||||
('ecdsa', 384),
|
||||
('ecdsa', 521),
|
||||
('rsa', 2048),
|
||||
('rsa', 3072),
|
||||
('rsa', 4096);
|
||||
|
||||
create table credential_vault_ssh_cert_library (
|
||||
public_id wt_public_id primary key,
|
||||
store_id wt_public_id not null
|
||||
constraint credential_vault_store_fkey
|
||||
references credential_vault_store (public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
name wt_name,
|
||||
description wt_description,
|
||||
create_time wt_timestamp,
|
||||
update_time wt_timestamp,
|
||||
version wt_version,
|
||||
vault_path text not null
|
||||
constraint vault_path_must_not_be_empty
|
||||
check(length(trim(vault_path)) > 0)
|
||||
constraint vault_path_must_be_sign_or_issue
|
||||
check(vault_path ~ '^.+\/(sign|issue)\/[^\/\\\s]+$'),
|
||||
username text not null
|
||||
constraint username_must_not_be_empty
|
||||
check(length(trim(username)) > 0),
|
||||
key_type text not null,
|
||||
key_bits int not null,
|
||||
ttl text,
|
||||
key_id text,
|
||||
critical_options bytea,
|
||||
extensions bytea,
|
||||
credential_type text,
|
||||
project_id wt_public_id not null,
|
||||
constraint credential_vault_ssh_cert_library_store_id_name_uq
|
||||
unique(store_id, name),
|
||||
constraint credential_vault_ssh_cert_library_store_id_public_id_uq
|
||||
unique(store_id, public_id),
|
||||
constraint credential_library_fkey
|
||||
foreign key (project_id, store_id, public_id, credential_type)
|
||||
references credential_library (project_id, store_id, public_id, credential_type)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
constraint credential_vault_ssh_cert_valid_key_type_key_bits_fkey
|
||||
foreign key (key_type, key_bits)
|
||||
references credential_vault_ssh_cert_valid_key_type_key_bits(key_type, key_bits)
|
||||
);
|
||||
comment on table credential_vault_ssh_cert_library is
|
||||
'credential_vault_ssh_cert_library a credential library that issues credentials from a vault ssh secret backend.';
|
||||
|
||||
create function default_ssh_certificate_credential_type() returns trigger
|
||||
as $$
|
||||
begin
|
||||
if new.credential_type is distinct from 'ssh_certificate' then
|
||||
raise warning 'credential_vault_ssh_cert_library only supports ssh_certificate credentials';
|
||||
new.credential_type = 'ssh_certificate';
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
comment on function default_ssh_certificate_credential_type is
|
||||
'default_ssh_certificate_credential_type ensures the credential_type is set to ssh_certificate';
|
||||
|
||||
create trigger default_ssh_certificate_credential_type before insert on credential_vault_ssh_cert_library
|
||||
for each row execute procedure default_ssh_certificate_credential_type();
|
||||
create trigger insert_credential_library_subtype before insert on credential_vault_ssh_cert_library
|
||||
for each row execute procedure insert_credential_library_subtype();
|
||||
create trigger default_create_time_column before insert on credential_vault_ssh_cert_library
|
||||
for each row execute procedure default_create_time();
|
||||
create trigger delete_credential_library_subtype after delete on credential_vault_ssh_cert_library
|
||||
for each row execute procedure delete_credential_library_subtype();
|
||||
create trigger immutable_columns before update on credential_vault_ssh_cert_library
|
||||
for each row execute procedure immutable_columns('public_id', 'store_id', 'project_id', 'credential_type', 'create_time');
|
||||
create trigger update_time_column before update on credential_vault_ssh_cert_library
|
||||
for each row execute procedure update_time_column();
|
||||
create trigger update_version_column after update on credential_vault_ssh_cert_library
|
||||
for each row execute procedure update_version_column();
|
||||
create trigger before_insert_credential_vault_library before insert on credential_vault_ssh_cert_library
|
||||
for each row execute procedure before_insert_credential_vault_library();
|
||||
|
||||
insert into oplog_ticket (name, version)
|
||||
values
|
||||
('credential_vault_ssh_cert_library', 1);
|
||||
|
||||
commit;
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
begin;
|
||||
|
||||
-- Replaces view from 56/02_add_data_key_foreign_key_references.up.sql
|
||||
drop view credential_vault_library_issue_credentials;
|
||||
create view credential_vault_library_issue_credentials as
|
||||
with
|
||||
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_library_username_password_mapping_override
|
||||
),
|
||||
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_library_ssh_private_key_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)
|
||||
as username_attribute,
|
||||
upasso.password_attribute as password_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
|
||||
from credential_vault_library library
|
||||
join credential_vault_store_client store
|
||||
on library.store_id = store.public_id
|
||||
left join password_override upasso
|
||||
on library.public_id = upasso.library_id
|
||||
left join ssh_private_key_override sshpk
|
||||
on library.public_id = sshpk.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 private_key_attribute,
|
||||
null as private_key_passphrase_attribute,
|
||||
'ssh-signed-cert' as cred_lib_type -- used to switch on
|
||||
from credential_vault_ssh_cert_library library
|
||||
join credential_vault_store_client store
|
||||
on library.store_id = store.public_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.';
|
||||
|
||||
commit;
|
||||
@@ -0,0 +1,254 @@
|
||||
begin;
|
||||
alter table wh_credential_dimension
|
||||
add column credential_library_username text,
|
||||
add column credential_library_key_type_and_bits text
|
||||
;
|
||||
|
||||
update wh_credential_dimension set
|
||||
credential_library_username = 'Not Applicable',
|
||||
credential_library_key_type_and_bits = 'Not Applicable';
|
||||
|
||||
update wh_credential_dimension set
|
||||
credential_library_vault_http_request_body = 'Not Applicable'
|
||||
where
|
||||
credential_library_vault_http_method = 'GET'
|
||||
and
|
||||
credential_library_vault_http_request_body = 'None';
|
||||
|
||||
|
||||
alter table wh_credential_dimension
|
||||
alter column credential_library_username type wh_dim_text,
|
||||
alter column credential_library_key_type_and_bits type wh_dim_text
|
||||
;
|
||||
|
||||
update wh_credential_dimension set
|
||||
credential_library_type = 'vault generic credential library'
|
||||
where
|
||||
credential_library_type = 'vault credential library';
|
||||
|
||||
-- The whx_credential_dimension_source view shows the current values in the
|
||||
-- operational tables of the credential dimension.
|
||||
-- Replaces whx_credential_dimension_source defined in 44/03_targets.up.sql
|
||||
drop view whx_credential_dimension_source;
|
||||
create view whx_credential_dimension_source as
|
||||
with vault_generic_library as (
|
||||
select vcl.public_id as public_id,
|
||||
'vault generic credential library' as type,
|
||||
coalesce(vcl.name, 'None') as name,
|
||||
coalesce(vcl.description, 'None') as description,
|
||||
vcl.vault_path as vault_path,
|
||||
vcl.http_method as http_method,
|
||||
case
|
||||
when vcl.http_method = 'GET' then 'Not Applicable'
|
||||
else coalesce(vcl.http_request_body::text, 'None')
|
||||
end as http_request_body,
|
||||
'Not Applicable' as username,
|
||||
'Not Applicable' as key_type_and_bits
|
||||
from credential_vault_library as vcl
|
||||
),
|
||||
vault_ssh_cert_library as (
|
||||
select vsccl.public_id as public_id,
|
||||
'vault ssh certificate credential library' as type,
|
||||
coalesce(vsccl.name, 'None') as name,
|
||||
coalesce(vsccl.description, 'None') as description,
|
||||
vsccl.vault_path as vault_path,
|
||||
'Not Applicable' as http_method,
|
||||
'Not Applicable' as http_request_body,
|
||||
vsccl.username as username,
|
||||
case
|
||||
when vsccl.key_type = 'ed25519' then vsccl.key_type
|
||||
else vsccl.key_type || '-' || vsccl.key_bits::text
|
||||
end as key_type_and_bits
|
||||
from credential_vault_ssh_cert_library as vsccl
|
||||
),
|
||||
final as (
|
||||
select s.public_id as session_id,
|
||||
scd.credential_purpose as credential_purpose,
|
||||
cl.public_id as credential_library_id,
|
||||
coalesce(vcl.type, vsccl.type) as credential_library_type,
|
||||
coalesce(vcl.name, vsccl.name) as credential_library_name,
|
||||
coalesce(vcl.description, vsccl.description) as credential_library_description,
|
||||
coalesce(vcl.vault_path, vsccl.vault_path) as credential_library_vault_path,
|
||||
coalesce(vcl.http_method, vsccl.http_method) as credential_library_vault_http_method,
|
||||
coalesce(vcl.http_request_body, vsccl.http_request_body) as credential_library_vault_http_request_body,
|
||||
coalesce(vcl.username, vsccl.username) as credential_library_username,
|
||||
coalesce(vcl.key_type_and_bits, vsccl.key_type_and_bits) as credential_library_key_type_and_bits,
|
||||
cs.public_id as credential_store_id,
|
||||
case
|
||||
when vcs is null then 'None'
|
||||
else 'vault credential store'
|
||||
end as credential_store_type,
|
||||
coalesce(vcs.name, 'None') as credential_store_name,
|
||||
coalesce(vcs.description, 'None') as credential_store_description,
|
||||
coalesce(vcs.namespace, 'None') as credential_store_vault_namespace,
|
||||
coalesce(vcs.vault_address, 'None') as credential_store_vault_address,
|
||||
t.public_id as target_id,
|
||||
'tcp target' as target_type,
|
||||
coalesce(tt.name, 'None') as target_name,
|
||||
coalesce(tt.description, 'None') as target_description,
|
||||
coalesce(tt.default_port, 0) as target_default_port_number,
|
||||
tt.session_max_seconds as target_session_max_seconds,
|
||||
tt.session_connection_limit as target_session_connection_limit,
|
||||
p.public_id as project_id,
|
||||
coalesce(p.name, 'None') as project_name,
|
||||
coalesce(p.description, 'None') as project_description,
|
||||
o.public_id as organization_id,
|
||||
coalesce(o.name, 'None') as organization_name,
|
||||
coalesce(o.description, 'None') as organization_description
|
||||
from session_credential_dynamic as scd
|
||||
join session as s on scd.session_id = s.public_id
|
||||
join credential_library as cl on scd.library_id = cl.public_id
|
||||
join credential_store as cs on cl.store_id = cs.public_id
|
||||
join target as t on s.target_id = t.public_id
|
||||
join iam_scope as p on p.public_id = t.project_id and p.type = 'project'
|
||||
join iam_scope as o on p.parent_id = o.public_id and o.type = 'org'
|
||||
left join vault_generic_library as vcl on cl.public_id = vcl.public_id
|
||||
left join vault_ssh_cert_library as vsccl on cl.public_id = vsccl.public_id
|
||||
left join credential_vault_store as vcs on cs.public_id = vcs.public_id
|
||||
left join target_tcp as tt on t.public_id = tt.public_id
|
||||
)
|
||||
select session_id,
|
||||
credential_purpose,
|
||||
credential_library_id,
|
||||
credential_library_type,
|
||||
credential_library_name,
|
||||
credential_library_description,
|
||||
credential_library_vault_path,
|
||||
credential_library_vault_http_method,
|
||||
credential_library_vault_http_request_body,
|
||||
credential_library_username,
|
||||
credential_library_key_type_and_bits,
|
||||
credential_store_id,
|
||||
credential_store_type,
|
||||
credential_store_name,
|
||||
credential_store_description,
|
||||
credential_store_vault_namespace,
|
||||
credential_store_vault_address,
|
||||
target_id,
|
||||
target_type,
|
||||
target_name,
|
||||
target_description,
|
||||
target_default_port_number,
|
||||
target_session_max_seconds,
|
||||
target_session_connection_limit,
|
||||
project_id,
|
||||
project_name,
|
||||
project_description,
|
||||
organization_id,
|
||||
organization_name,
|
||||
organization_description
|
||||
from final;
|
||||
|
||||
-- Replaces view in 16/02_wh_credential_dimension.up.sql
|
||||
drop view whx_credential_dimension_target;
|
||||
create view whx_credential_dimension_target as
|
||||
select key,
|
||||
credential_purpose,
|
||||
credential_library_id,
|
||||
credential_library_type,
|
||||
credential_library_name,
|
||||
credential_library_description,
|
||||
credential_library_vault_path,
|
||||
credential_library_vault_http_method,
|
||||
credential_library_vault_http_request_body,
|
||||
credential_library_username,
|
||||
credential_library_key_type_and_bits,
|
||||
credential_store_id,
|
||||
credential_store_type,
|
||||
credential_store_name,
|
||||
credential_store_description,
|
||||
credential_store_vault_namespace,
|
||||
credential_store_vault_address,
|
||||
target_id,
|
||||
target_type,
|
||||
target_name,
|
||||
target_description,
|
||||
target_default_port_number,
|
||||
target_session_max_seconds,
|
||||
target_session_connection_limit,
|
||||
project_id,
|
||||
project_name,
|
||||
project_description,
|
||||
organization_id,
|
||||
organization_name,
|
||||
organization_description
|
||||
from wh_credential_dimension
|
||||
where current_row_indicator = 'Current'
|
||||
;
|
||||
|
||||
-- Replaces function in 16/03_wh_credential_dimension.up.sql
|
||||
drop function wh_upsert_credential_dimension;
|
||||
create function wh_upsert_credential_dimension(p_session_id wt_public_id, p_library_id wt_public_id, p_credential_purpose wh_dim_text) returns wh_dim_key
|
||||
as $$
|
||||
declare
|
||||
src whx_credential_dimension_target%rowtype;
|
||||
target whx_credential_dimension_target%rowtype;
|
||||
new_row wh_credential_dimension%rowtype;
|
||||
t_id wt_public_id;
|
||||
begin
|
||||
select s.target_id into strict t_id
|
||||
from session as s
|
||||
where s.public_id = p_session_id;
|
||||
|
||||
select * into target
|
||||
from whx_credential_dimension_target as t
|
||||
where t.credential_library_id = p_library_id
|
||||
and t.target_id = t_id
|
||||
and t.credential_purpose = p_credential_purpose;
|
||||
|
||||
select
|
||||
target.key, t.credential_purpose,
|
||||
t.credential_library_id, t.credential_library_type, t.credential_library_name, t.credential_library_description, t.credential_library_vault_path, t.credential_library_vault_http_method, t.credential_library_vault_http_request_body,
|
||||
t.credential_library_username, t.credential_library_key_type_and_bits,
|
||||
t.credential_store_id, t.credential_store_type, t.credential_store_name, t.credential_store_description, t.credential_store_vault_namespace, t.credential_store_vault_address,
|
||||
t.target_id, t.target_type, t.target_name, t.target_description, t.target_default_port_number, t.target_session_max_seconds, t.target_session_connection_limit,
|
||||
t.project_id, t.project_name, t.project_description,
|
||||
t.organization_id, t.organization_name, t.organization_description
|
||||
into src
|
||||
from whx_credential_dimension_source as t
|
||||
where t.credential_library_id = p_library_id
|
||||
and t.session_id = p_session_id
|
||||
and t.target_id = t_id
|
||||
and t.credential_purpose = p_credential_purpose;
|
||||
|
||||
if src is distinct from target then
|
||||
update wh_credential_dimension
|
||||
set current_row_indicator = 'Expired',
|
||||
row_expiration_time = current_timestamp
|
||||
where credential_library_id = p_library_id
|
||||
and target_id = t_id
|
||||
and credential_purpose = p_credential_purpose
|
||||
and current_row_indicator = 'Current';
|
||||
|
||||
insert into wh_credential_dimension (
|
||||
credential_purpose,
|
||||
credential_library_id, credential_library_type, credential_library_name, credential_library_description, credential_library_vault_path, credential_library_vault_http_method, credential_library_vault_http_request_body,
|
||||
credential_library_username, credential_library_key_type_and_bits,
|
||||
credential_store_id, credential_store_type, credential_store_name, credential_store_description, credential_store_vault_namespace, credential_store_vault_address,
|
||||
target_id, target_type, target_name, target_description, target_default_port_number, target_session_max_seconds, target_session_connection_limit,
|
||||
project_id, project_name, project_description,
|
||||
organization_id, organization_name, organization_description,
|
||||
current_row_indicator, row_effective_time, row_expiration_time
|
||||
)
|
||||
select credential_purpose,
|
||||
credential_library_id, credential_library_type, credential_library_name, credential_library_description, credential_library_vault_path, credential_library_vault_http_method, credential_library_vault_http_request_body,
|
||||
credential_library_username, credential_library_key_type_and_bits,
|
||||
credential_store_id, credential_store_type, credential_store_name, credential_store_description, credential_store_vault_namespace, credential_store_vault_address,
|
||||
target_id, target_type, target_name, target_description, target_default_port_number, target_session_max_seconds, target_session_connection_limit,
|
||||
project_id, project_name, project_description,
|
||||
organization_id, organization_name, organization_description,
|
||||
'Current', current_timestamp, 'infinity'::timestamptz
|
||||
from whx_credential_dimension_source
|
||||
where credential_library_id = p_library_id
|
||||
and session_id = p_session_id
|
||||
and target_id = t_id
|
||||
and credential_purpose = p_credential_purpose
|
||||
returning * into new_row;
|
||||
|
||||
return new_row.key;
|
||||
end if;
|
||||
|
||||
return target.key;
|
||||
end
|
||||
$$ language plpgsql;
|
||||
commit;
|
||||
@@ -315,7 +315,9 @@ begin;
|
||||
(project_id, public_id, name)
|
||||
values
|
||||
('p____bwidget', 't_________wb', 'Big Widget Target'),
|
||||
('p____swidget', 't_________ws', 'Small Widget Target');
|
||||
('p____swidget', 't_________ws', 'Small Widget Target'),
|
||||
('p____swidget', 't________ws2', 'Small Widget Target 2'),
|
||||
('p____swidget', 't________ws3', 'Small Widget Target 3');
|
||||
|
||||
insert into target_host_set
|
||||
(project_id, target_id, host_set_id)
|
||||
@@ -327,7 +329,9 @@ begin;
|
||||
('p____bwidget', 't_________wb', 's___1wb-plghs'),
|
||||
('p____bwidget', 't_________wb', 's___2wb-plghs'),
|
||||
('p____swidget', 't_________ws', 's___1ws-plghs'),
|
||||
('p____swidget', 't_________ws', 's___2ws-plghs');
|
||||
('p____swidget', 't_________ws', 's___2ws-plghs'),
|
||||
('p____swidget', 't________ws2', 's___1ws-sths'),
|
||||
('p____swidget', 't________ws3', 's___1ws-sths');
|
||||
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
@@ -336,9 +340,10 @@ begin;
|
||||
as $$
|
||||
begin
|
||||
insert into credential_vault_store
|
||||
(project_id, public_id, name, description, vault_address, namespace)
|
||||
(project_id, public_id, name, description, vault_address, namespace)
|
||||
values
|
||||
('p____bwidget', 'vs_______wvs', 'widget vault store', 'None', 'https://vault.widget', 'default');
|
||||
('p____bwidget', 'vs_______wvs', 'widget vault store', 'None', 'https://vault.widget', 'default'),
|
||||
('p____swidget', 'vs______swvs', 'small widget vault store', 'None', 'https://small.vault.widget', 'default');
|
||||
|
||||
insert into credential_vault_token
|
||||
(store_id, key_id, status, token_hmac, token, last_renewal_time, expiration_time)
|
||||
@@ -401,13 +406,23 @@ begin;
|
||||
values
|
||||
('vl______wvl12', 'my_username', 'my_private_key', 'my_passphrase');
|
||||
|
||||
insert into credential_vault_ssh_cert_library
|
||||
(store_id, public_id, name, description, vault_path, username, key_type, key_bits)
|
||||
values
|
||||
('vs______swvs', 'vscl____wvl1', 'widget ssh admin cert', 'None', '/ssh/issue/admin', 'admin', 'rsa', 4096),
|
||||
('vs______swvs', 'vscl____wvl2', 'widget ssh ecdsa', 'None', '/ssh/sign/user', 'user', 'ecdsa', 521),
|
||||
('vs______swvs', 'vscl____wvl3', 'widget ssh ed25519', 'None', '/ssh/sign/user', 'user', 'ed25519', 0);
|
||||
|
||||
insert into target_credential_library
|
||||
(project_id, target_id, credential_library_id, credential_purpose)
|
||||
values
|
||||
('p____bwidget', 't_________wb', 'vl______wvl1', 'brokered'),
|
||||
('p____bwidget', 't_________wb', 'vl______wvl2', 'brokered'),
|
||||
('p____bwidget', 't_________wb', 'vl______wvl3', 'brokered'),
|
||||
('p____bwidget', 't_________wb', 'vl______wvl3', 'injected_application');
|
||||
('p____bwidget', 't_________wb', 'vl______wvl3', 'injected_application'),
|
||||
('p____bwidget', 't_________ws', 'vscl____wvl1', 'injected_application'),
|
||||
('p____bwidget', 't________ws2', 'vscl____wvl2', 'injected_application'),
|
||||
('p____bwidget', 't________ws3', 'vscl____wvl3', 'injected_application');
|
||||
|
||||
insert into credential_static_store
|
||||
(project_id, public_id, name, description)
|
||||
@@ -432,9 +447,9 @@ create function _wtt_load_widgets_sessions() returns void
|
||||
as $$
|
||||
begin
|
||||
insert into session
|
||||
( project_id , target_id , host_set_id , host_id , user_id , auth_token_id , certificate , endpoint , public_id)
|
||||
(project_id, target_id, host_set_id, host_id, user_id, auth_token_id, certificate, endpoint, public_id)
|
||||
values
|
||||
('p____bwidget' , 't_________wb' , 's___1wb-sths' , 'h_____wb__01' , 'u_____warren' , 'tok___warren' , 'abc'::bytea , 'ep1' , 's1____warren');
|
||||
('p____swidget', 't_________wb', 's___1wb-sths', 'h_____wb__01', 'u_____warren', 'tok___warren', 'abc'::bytea, 'ep1', 's1____warren');
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
begin;
|
||||
select plan(16);
|
||||
select wtt_load('widgets', 'iam', 'kms', 'auth', 'hosts', 'targets', 'credentials');
|
||||
|
||||
-- validate default values
|
||||
prepare insert_valid as
|
||||
insert into credential_vault_ssh_cert_library
|
||||
(store_id, public_id, vault_path, username, key_type, key_bits)
|
||||
values
|
||||
('vs_______wvs', 'vl______vsc1', '/ssh/sign/foo', 'bar', 'ed25519', 0),
|
||||
('vs_______wvs', 'vl______vsc2', '/ssh/sign/foo', 'bar', 'ecdsa', 256),
|
||||
('vs_______wvs', 'vl______vsc3', '/ssh/sign/foo', 'bar', 'ecdsa', 384),
|
||||
('vs_______wvs', 'vl______vsc4', '/ssh/sign/foo', 'bar', 'ecdsa', 521),
|
||||
('vs_______wvs', 'vl______vsc5', '/ssh/sign/foo', 'bar', 'rsa', 2048),
|
||||
('vs_______wvs', 'vl______vsc6', '/ssh/sign/foo', 'bar', 'rsa', 3072),
|
||||
('vs_______wvs', 'vl______vsc7', '/ssh/sign/foo', 'bar', 'rsa', 4096);
|
||||
|
||||
prepare select_vault_ssh_cert_libraries as
|
||||
select public_id::text, store_id::text, name::text, description::text, vault_path, username, key_type, key_bits, ttl, key_id, critical_options, extensions, credential_type, project_id::text
|
||||
from credential_vault_ssh_cert_library
|
||||
where public_id like 'vl______vsc%'
|
||||
order by public_id;
|
||||
|
||||
prepare select_libraries as
|
||||
select public_id::text, store_id::text, credential_type, project_id::text
|
||||
from credential_library
|
||||
where public_id in ('vl______vsc1', 'vl______vsc3')
|
||||
order by public_id;
|
||||
|
||||
select lives_ok('insert_valid');
|
||||
select results_eq(
|
||||
'select_vault_ssh_cert_libraries',
|
||||
$$VALUES
|
||||
('vl______vsc1', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'ed25519', 0, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'),
|
||||
('vl______vsc2', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'ecdsa', 256, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'),
|
||||
('vl______vsc3', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'ecdsa', 384, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'),
|
||||
('vl______vsc4', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'ecdsa', 521, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'),
|
||||
('vl______vsc5', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'rsa', 2048, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'),
|
||||
('vl______vsc6', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'rsa', 3072, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'),
|
||||
('vl______vsc7', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'rsa', 4096, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget')$$
|
||||
);
|
||||
|
||||
|
||||
prepare insert_invalid_key_type as
|
||||
insert into credential_vault_ssh_cert_library
|
||||
(store_id, public_id, vault_path, username, key_type, key_bits)
|
||||
values
|
||||
('vs_______wvs', 'vl______vsc8', '/ssh/sign/foo', 'bar', 'unknown', 256);
|
||||
select throws_ok('insert_invalid_key_type', 'insert or update on table "credential_vault_ssh_cert_library" violates foreign key constraint "credential_vault_ssh_cert_valid_key_type_key_bits_fkey"');
|
||||
|
||||
prepare insert_invalid_key_bits as
|
||||
insert into credential_vault_ssh_cert_library
|
||||
(store_id, public_id, vault_path, username, key_type, key_bits)
|
||||
values
|
||||
('vs_______wvs', 'vl______vsc8', '/ssh/sign/foo', 'bar', 'rsa', 99);
|
||||
select throws_ok('insert_invalid_key_bits', 'insert or update on table "credential_vault_ssh_cert_library" violates foreign key constraint "credential_vault_ssh_cert_valid_key_type_key_bits_fkey"');
|
||||
|
||||
prepare insert_invalid_rsa_key_type_bits_combo as
|
||||
insert into credential_vault_ssh_cert_library
|
||||
(store_id, public_id, vault_path, username, key_type, key_bits)
|
||||
values
|
||||
('vs_______wvs', 'vl______vsc8', '/ssh/sign/foo', 'bar', 'rsa', 384);
|
||||
select throws_ok('insert_invalid_rsa_key_type_bits_combo', 'insert or update on table "credential_vault_ssh_cert_library" violates foreign key constraint "credential_vault_ssh_cert_valid_key_type_key_bits_fkey"');
|
||||
|
||||
prepare insert_invalid_ecdsa_key_type_bits_combo as
|
||||
insert into credential_vault_ssh_cert_library
|
||||
(store_id, public_id, vault_path, username, key_type, key_bits)
|
||||
values
|
||||
('vs_______wvs', 'vl______vsc8', '/ssh/sign/foo', 'bar', 'ecdsa', 2048);
|
||||
select throws_ok('insert_invalid_ecdsa_key_type_bits_combo', 'insert or update on table "credential_vault_ssh_cert_library" violates foreign key constraint "credential_vault_ssh_cert_valid_key_type_key_bits_fkey"');
|
||||
|
||||
prepare insert_invalid_ed25519_key_type_bits_combo as
|
||||
insert into credential_vault_ssh_cert_library
|
||||
(store_id, public_id, vault_path, username, key_type, key_bits)
|
||||
values
|
||||
('vs_______wvs', 'vl______vsc8', '/ssh/sign/foo', 'bar', 'ed25519', 2048);
|
||||
select throws_ok('insert_invalid_ed25519_key_type_bits_combo', 'insert or update on table "credential_vault_ssh_cert_library" violates foreign key constraint "credential_vault_ssh_cert_valid_key_type_key_bits_fkey"');
|
||||
|
||||
prepare insert_invalid_vault_path as
|
||||
insert into credential_vault_ssh_cert_library
|
||||
(store_id, public_id, vault_path, username, key_type, key_bits)
|
||||
values
|
||||
('vs_______wvs', 'vl______vsc8', '/ssh/not-sign/foo', 'bar', 'ed25519', 0);
|
||||
select throws_ok('insert_invalid_vault_path', 'new row for relation "credential_vault_ssh_cert_library" violates check constraint "vault_path_must_be_sign_or_issue"');
|
||||
|
||||
prepare insert_invalid_vault_path_empty as
|
||||
insert into credential_vault_ssh_cert_library
|
||||
(store_id, public_id, vault_path, username, key_type, key_bits)
|
||||
values
|
||||
('vs_______wvs', 'vl______vsc8', '', 'bar', 'ed25519', 0);
|
||||
select throws_ok('insert_invalid_vault_path', 'new row for relation "credential_vault_ssh_cert_library" violates check constraint "vault_path_must_be_sign_or_issue"');
|
||||
|
||||
prepare insert_invalid_username as
|
||||
insert into credential_vault_ssh_cert_library
|
||||
(store_id, public_id, vault_path, username, key_type, key_bits)
|
||||
values
|
||||
('vs_______wvs', 'vl______vsc8', '/ssh/sign/foo', '', 'ed25519', 0);
|
||||
select throws_ok('insert_invalid_username', 'new row for relation "credential_vault_ssh_cert_library" violates check constraint "username_must_not_be_empty"');
|
||||
|
||||
prepare insert_invalid_credential_type as
|
||||
insert into credential_vault_ssh_cert_library
|
||||
(store_id, public_id, vault_path, username, credential_type, key_type, key_bits)
|
||||
values
|
||||
('vs_______wvs', 'vl______vsc8', '/ssh/sign/foo', 'bar', 'username_password', 'ed25519', 0);
|
||||
select lives_ok('insert_invalid_credential_type');
|
||||
select results_eq(
|
||||
'select_vault_ssh_cert_libraries',
|
||||
$$VALUES
|
||||
('vl______vsc1', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'ed25519', 0, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'),
|
||||
('vl______vsc2', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'ecdsa', 256, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'),
|
||||
('vl______vsc3', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'ecdsa', 384, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'),
|
||||
('vl______vsc4', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'ecdsa', 521, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'),
|
||||
('vl______vsc5', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'rsa', 2048, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'),
|
||||
('vl______vsc6', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'rsa', 3072, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'),
|
||||
('vl______vsc7', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'rsa', 4096, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'),
|
||||
('vl______vsc8', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'ed25519', 0, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget')$$
|
||||
);
|
||||
|
||||
select results_eq(
|
||||
'select_libraries',
|
||||
$$VALUES
|
||||
('vl______vsc1', 'vs_______wvs', 'ssh_certificate', 'p____bwidget'),
|
||||
('vl______vsc3', 'vs_______wvs', 'ssh_certificate', 'p____bwidget')$$
|
||||
);
|
||||
|
||||
prepare delete_ssh_cred_library as
|
||||
delete from credential_vault_ssh_cert_library where public_id = 'vl______vsc3';
|
||||
|
||||
select lives_ok('delete_ssh_cred_library');
|
||||
select results_eq(
|
||||
'select_vault_ssh_cert_libraries',
|
||||
$$VALUES
|
||||
('vl______vsc1', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'ed25519', 0, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'),
|
||||
('vl______vsc2', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'ecdsa', 256, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'),
|
||||
('vl______vsc4', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'ecdsa', 521, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'),
|
||||
('vl______vsc5', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'rsa', 2048, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'),
|
||||
('vl______vsc6', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'rsa', 3072, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'),
|
||||
('vl______vsc7', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'rsa', 4096, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget'),
|
||||
('vl______vsc8', 'vs_______wvs', null, null, '/ssh/sign/foo', 'bar', 'ed25519', 0, null, null, null::bytea, null::bytea, 'ssh_certificate', 'p____bwidget')$$
|
||||
);
|
||||
|
||||
select results_eq(
|
||||
'select_libraries',
|
||||
$$VALUES
|
||||
('vl______vsc1', 'vs_______wvs', 'ssh_certificate', 'p____bwidget')$$
|
||||
);
|
||||
|
||||
rollback;
|
||||
@@ -3,20 +3,22 @@ begin;
|
||||
|
||||
insert into wh_credential_dimension (
|
||||
credential_purpose,
|
||||
credential_library_id, credential_library_type, credential_library_name, credential_library_description, credential_library_vault_path, credential_library_vault_http_method, credential_library_vault_http_request_body,
|
||||
credential_store_id, credential_store_type, credential_store_name, credential_store_description, credential_store_vault_namespace, credential_store_vault_address,
|
||||
target_id, target_type, target_name, target_description, target_default_port_number, target_session_max_seconds, target_session_connection_limit,
|
||||
project_id, project_name, project_description,
|
||||
organization_id, organization_name, organization_description,
|
||||
current_row_indicator, row_effective_time, row_expiration_time
|
||||
credential_library_id, credential_library_type, credential_library_name, credential_library_description, credential_library_vault_path, credential_library_vault_http_method, credential_library_vault_http_request_body,
|
||||
credential_library_username, credential_library_key_type_and_bits,
|
||||
credential_store_id, credential_store_type, credential_store_name, credential_store_description, credential_store_vault_namespace, credential_store_vault_address,
|
||||
target_id, target_type, target_name, target_description, target_default_port_number, target_session_max_seconds, target_session_connection_limit,
|
||||
project_id, project_name, project_description,
|
||||
organization_id, organization_name, organization_description,
|
||||
current_row_indicator, row_effective_time, row_expiration_time
|
||||
) values (
|
||||
'brokered',
|
||||
'vl______wvl1', 'vault credential library', 'gidget vault library', 'None', '/secrets', 'GET', '\x4e6f6e65',
|
||||
'vs_______wvs', 'vault credential store', 'widget vault store', 'None', 'default', 'https://vault.widget',
|
||||
't_________wb', 'tcp target', 'Big Widget Target', 'None', 0, 28800, 1,
|
||||
'p____bwidget', 'Big Widget Factory', 'None',
|
||||
'o_____widget', 'Widget Inc', 'None',
|
||||
'Current', current_timestamp, 'infinity'::timestamptz
|
||||
'vl______wvl1', 'vault generic credential library', 'gidget vault library', 'None', '/secrets', 'GET', '\x4e6f6e65',
|
||||
'Not Applicable', 'Not Applicable',
|
||||
'vs_______wvs', 'vault credential store', 'widget vault store', 'None', 'default', 'https://vault.widget',
|
||||
't_________wb', 'tcp target', 'Big Widget Target', 'None', 0, 28800, 1,
|
||||
'p____bwidget', 'Big Widget Factory', 'None',
|
||||
'o_____widget', 'Widget Inc', 'None',
|
||||
'Current', current_timestamp, 'infinity'::timestamptz
|
||||
);
|
||||
|
||||
update wh_credential_dimension
|
||||
@@ -30,20 +32,22 @@ begin;
|
||||
|
||||
insert into wh_credential_dimension (
|
||||
credential_purpose,
|
||||
credential_library_id, credential_library_type, credential_library_name, credential_library_description, credential_library_vault_path, credential_library_vault_http_method, credential_library_vault_http_request_body,
|
||||
credential_store_id, credential_store_type, credential_store_name, credential_store_description, credential_store_vault_namespace, credential_store_vault_address,
|
||||
target_id, target_type, target_name, target_description, target_default_port_number, target_session_max_seconds, target_session_connection_limit,
|
||||
project_id, project_name, project_description,
|
||||
organization_id, organization_name, organization_description,
|
||||
current_row_indicator, row_effective_time, row_expiration_time
|
||||
credential_library_id, credential_library_type, credential_library_name, credential_library_description, credential_library_vault_path, credential_library_vault_http_method, credential_library_vault_http_request_body,
|
||||
credential_library_username, credential_library_key_type_and_bits,
|
||||
credential_store_id, credential_store_type, credential_store_name, credential_store_description, credential_store_vault_namespace, credential_store_vault_address,
|
||||
target_id, target_type, target_name, target_description, target_default_port_number, target_session_max_seconds, target_session_connection_limit,
|
||||
project_id, project_name, project_description,
|
||||
organization_id, organization_name, organization_description,
|
||||
current_row_indicator, row_effective_time, row_expiration_time
|
||||
) values (
|
||||
'brokered',
|
||||
'vl______wvl1', 'vault credential library', 'gidget vault library', 'None', '/secrets', 'GET', '\x4e6f6e65',
|
||||
'vs_______wvs', 'vault credential store', 'widget vault store', 'None', 'default', 'https://vault.widget',
|
||||
't_________wb', 'tcp target', 'Big Widget Target', 'None', 0, 28800, 1,
|
||||
'p____bwidget', 'Big Widget Factory', 'None',
|
||||
'o_____widget', 'Widget Inc', 'None',
|
||||
'Current', current_timestamp, 'infinity'::timestamptz
|
||||
'vl______wvl1', 'vault generic credential library', 'gidget vault library', 'None', '/secrets', 'GET', '\x4e6f6e65',
|
||||
'Not Applicable', 'Not Applicable',
|
||||
'vs_______wvs', 'vault credential store', 'widget vault store', 'None', 'default', 'https://vault.widget',
|
||||
't_________wb', 'tcp target', 'Big Widget Target', 'None', 0, 28800, 1,
|
||||
'p____bwidget', 'Big Widget Factory', 'None',
|
||||
'o_____widget', 'Widget Inc', 'None',
|
||||
'Current', current_timestamp, 'infinity'::timestamptz
|
||||
);
|
||||
|
||||
select is(count(*), 2::bigint) from wh_credential_dimension where organization_id = 'o_____widget';
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
-- ssh_certificate_cred_library tests the wh_credential_dimension when
|
||||
-- sessions are created with targets that use a vault ssh cert credential library with ecdsa key type
|
||||
begin;
|
||||
select plan(3);
|
||||
|
||||
select wtt_load('widgets', 'iam', 'kms', 'auth', 'hosts', 'targets', 'credentials');
|
||||
|
||||
-- ensure no existing dimensions
|
||||
select is(count(*), 0::bigint) from wh_credential_dimension where organization_id = 'o_____widget';
|
||||
|
||||
-- insert first session, should result in a new credentials dimension
|
||||
insert into session
|
||||
( project_id, target_id, user_id, auth_token_id, certificate, endpoint, public_id)
|
||||
values
|
||||
('p____swidget', 't________ws2', 'u_____walter', 'tok___walter', 'abc'::bytea, 'ep1', 's2____walter');
|
||||
insert into session_host_set_host
|
||||
(session_id, host_set_id, host_id)
|
||||
values
|
||||
('s2____walter', 's___1ws-sths', 'h_____ws__01');
|
||||
insert into session_credential_dynamic
|
||||
( session_id, library_id, credential_id, credential_purpose)
|
||||
values
|
||||
('s2____walter', 'vscl____wvl2', null, 'injected_application');
|
||||
select is(count(*), 1::bigint) from wh_credential_dimension where organization_id = 'o_____widget';
|
||||
|
||||
prepare select_credential_dimensions as
|
||||
select
|
||||
credential_purpose::text,
|
||||
credential_library_id::text, credential_library_type::text, credential_library_name::text, credential_library_description::text, credential_library_vault_path::text, credential_library_vault_http_method::text, credential_library_vault_http_request_body::text,
|
||||
credential_library_username::text, credential_library_key_type_and_bits::text,
|
||||
credential_store_id::text, credential_store_type::text, credential_store_name::text, credential_store_description::text, credential_store_vault_namespace::text, credential_store_vault_address::text,
|
||||
target_id::text, target_type::text, target_name::text, target_description::text, target_default_port_number, target_session_max_seconds, target_session_connection_limit,
|
||||
project_id::text, project_name::text, project_description::text,
|
||||
organization_id::text, organization_name::text, organization_description::text,
|
||||
current_row_indicator::text
|
||||
from wh_credential_dimension
|
||||
where organization_id = 'o_____widget';
|
||||
|
||||
select results_eq(
|
||||
'select_credential_dimensions',
|
||||
$$VALUES
|
||||
(
|
||||
'injected_application',
|
||||
'vscl____wvl2', 'vault ssh certificate credential library', 'widget ssh ecdsa', 'None', '/ssh/sign/user', 'Not Applicable', 'Not Applicable',
|
||||
'user', 'ecdsa-521',
|
||||
'vs______swvs', 'vault credential store', 'small widget vault store', 'None', 'default', 'https://small.vault.widget',
|
||||
't________ws2', 'tcp target', 'Small Widget Target 2', 'None', 0, 28800, -1,
|
||||
'p____swidget', 'Small Widget Factory', 'None',
|
||||
'o_____widget', 'Widget Inc', 'None',
|
||||
'Current'
|
||||
)$$
|
||||
);
|
||||
|
||||
select * from finish();
|
||||
rollback;
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
-- ssh_certificate_cred_library tests the wh_credential_dimension when
|
||||
-- sessions are created with targets that use a vault ssh cert credential library
|
||||
begin;
|
||||
select plan(3);
|
||||
|
||||
select wtt_load('widgets', 'iam', 'kms', 'auth', 'hosts', 'targets', 'credentials');
|
||||
|
||||
-- ensure no existing dimensions
|
||||
select is(count(*), 0::bigint) from wh_credential_dimension where organization_id = 'o_____widget';
|
||||
|
||||
-- insert first session, should result in a new credentials dimension
|
||||
insert into session
|
||||
( project_id, target_id, user_id, auth_token_id, certificate, endpoint, public_id)
|
||||
values
|
||||
('p____swidget', 't________ws3', 'u_____walter', 'tok___walter', 'abc'::bytea, 'ep1', 's3____walter');
|
||||
insert into session_host_set_host
|
||||
(session_id, host_set_id, host_id)
|
||||
values
|
||||
('s3____walter', 's___1ws-sths', 'h_____ws__01');
|
||||
insert into session_credential_dynamic
|
||||
( session_id, library_id, credential_id, credential_purpose)
|
||||
values
|
||||
('s3____walter', 'vscl____wvl3', null, 'injected_application');
|
||||
select is(count(*), 1::bigint) from wh_credential_dimension where organization_id = 'o_____widget';
|
||||
|
||||
prepare select_credential_dimensions as
|
||||
select
|
||||
credential_purpose::text,
|
||||
credential_library_id::text, credential_library_type::text, credential_library_name::text, credential_library_description::text, credential_library_vault_path::text, credential_library_vault_http_method::text, credential_library_vault_http_request_body::text,
|
||||
credential_library_username::text, credential_library_key_type_and_bits::text,
|
||||
credential_store_id::text, credential_store_type::text, credential_store_name::text, credential_store_description::text, credential_store_vault_namespace::text, credential_store_vault_address::text,
|
||||
target_id::text, target_type::text, target_name::text, target_description::text, target_default_port_number, target_session_max_seconds, target_session_connection_limit,
|
||||
project_id::text, project_name::text, project_description::text,
|
||||
organization_id::text, organization_name::text, organization_description::text,
|
||||
current_row_indicator::text
|
||||
from wh_credential_dimension
|
||||
where organization_id = 'o_____widget';
|
||||
|
||||
select results_eq(
|
||||
'select_credential_dimensions',
|
||||
$$VALUES
|
||||
(
|
||||
'injected_application',
|
||||
'vscl____wvl3', 'vault ssh certificate credential library', 'widget ssh ed25519', 'None', '/ssh/sign/user', 'Not Applicable', 'Not Applicable',
|
||||
'user', 'ed25519',
|
||||
'vs______swvs', 'vault credential store', 'small widget vault store', 'None', 'default', 'https://small.vault.widget',
|
||||
't________ws3', 'tcp target', 'Small Widget Target 3', 'None', 0, 28800, -1,
|
||||
'p____swidget', 'Small Widget Factory', 'None',
|
||||
'o_____widget', 'Widget Inc', 'None',
|
||||
'Current'
|
||||
)$$
|
||||
);
|
||||
|
||||
select * from finish();
|
||||
rollback;
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
-- ssh_certificate_cred_library tests the wh_credential_dimension when
|
||||
-- sessions are created with targets that use a vault ssh cert credential library with rsa key type
|
||||
begin;
|
||||
select plan(3);
|
||||
|
||||
select wtt_load('widgets', 'iam', 'kms', 'auth', 'hosts', 'targets', 'credentials');
|
||||
|
||||
-- ensure no existing dimensions
|
||||
select is(count(*), 0::bigint) from wh_credential_dimension where organization_id = 'o_____widget';
|
||||
|
||||
-- insert first session, should result in a new credentials dimension
|
||||
insert into session
|
||||
( project_id, target_id, user_id, auth_token_id, certificate, endpoint, public_id)
|
||||
values
|
||||
('p____swidget', 't_________ws', 'u_____walter', 'tok___walter', 'abc'::bytea, 'ep1', 's1____walter');
|
||||
insert into session_host_set_host
|
||||
(session_id, host_set_id, host_id)
|
||||
values
|
||||
('s1____walter', 's___1ws-sths', 'h_____ws__01');
|
||||
insert into session_credential_dynamic
|
||||
( session_id, library_id, credential_id, credential_purpose)
|
||||
values
|
||||
('s1____walter', 'vscl____wvl1', null, 'injected_application');
|
||||
select is(count(*), 1::bigint) from wh_credential_dimension where organization_id = 'o_____widget';
|
||||
|
||||
prepare select_credential_dimensions as
|
||||
select
|
||||
credential_purpose::text,
|
||||
credential_library_id::text, credential_library_type::text, credential_library_name::text, credential_library_description::text, credential_library_vault_path::text, credential_library_vault_http_method::text, credential_library_vault_http_request_body::text,
|
||||
credential_library_username::text, credential_library_key_type_and_bits::text,
|
||||
credential_store_id::text, credential_store_type::text, credential_store_name::text, credential_store_description::text, credential_store_vault_namespace::text, credential_store_vault_address::text,
|
||||
target_id::text, target_type::text, target_name::text, target_description::text, target_default_port_number, target_session_max_seconds, target_session_connection_limit,
|
||||
project_id::text, project_name::text, project_description::text,
|
||||
organization_id::text, organization_name::text, organization_description::text,
|
||||
current_row_indicator::text
|
||||
from wh_credential_dimension
|
||||
where organization_id = 'o_____widget';
|
||||
|
||||
select results_eq(
|
||||
'select_credential_dimensions',
|
||||
$$VALUES
|
||||
(
|
||||
'injected_application',
|
||||
'vscl____wvl1', 'vault ssh certificate credential library', 'widget ssh admin cert', 'None', '/ssh/issue/admin', 'Not Applicable', 'Not Applicable',
|
||||
'admin', 'rsa-4096',
|
||||
'vs______swvs', 'vault credential store', 'small widget vault store', 'None', 'default', 'https://small.vault.widget',
|
||||
't_________ws', 'tcp target', 'Small Widget Target', 'None', 0, 28800, -1,
|
||||
'p____swidget', 'Small Widget Factory', 'None',
|
||||
'o_____widget', 'Widget Inc', 'None',
|
||||
'Current'
|
||||
)$$
|
||||
);
|
||||
|
||||
select * from finish();
|
||||
rollback;
|
||||
|
||||
@@ -18,36 +18,39 @@ begin;
|
||||
|
||||
select is(s.*, row(
|
||||
's1____walter',
|
||||
'brokered', -- credential_purpose,
|
||||
'vl______wvl1', -- credential_library_id,
|
||||
'vault credential library', -- credential_library_type,
|
||||
'widget vault library', -- credential_library_name,
|
||||
'None', -- credential_library_description,
|
||||
'/secrets', -- credential_library_vault_path,
|
||||
'GET', -- credential_library_vault_http_method,
|
||||
'None', -- credential_library_vault_http_request_body,
|
||||
'brokered', -- credential_purpose,
|
||||
'vl______wvl1', -- credential_library_id,
|
||||
'vault generic credential library', -- credential_library_type,
|
||||
'widget vault library', -- credential_library_name,
|
||||
'None', -- credential_library_description,
|
||||
'/secrets', -- credential_library_vault_path,
|
||||
'GET', -- credential_library_vault_http_method,
|
||||
'Not Applicable', -- credential_library_vault_http_request_body,
|
||||
|
||||
'vs_______wvs', -- credential_store_id,
|
||||
'vault credential store', -- credential_store_type,
|
||||
'widget vault store', -- credential_store_name,
|
||||
'None', -- credential_store_description,
|
||||
'default', -- credential_store_vault_namespace,
|
||||
'https://vault.widget', -- credential_store_vault_address,
|
||||
'Not Applicable', -- credential_library_username,
|
||||
'Not Applicable', -- credential_library_key_type_and_bits,
|
||||
|
||||
't_________wb', -- target_id,
|
||||
'tcp target', -- target_type,
|
||||
'Big Widget Target', -- target_name,
|
||||
'None', -- target_description,
|
||||
0, -- target_default_port_number,
|
||||
28800, -- target_session_max_seconds,
|
||||
-1, -- target_session_connection_limit,
|
||||
'vs_______wvs', -- credential_store_id,
|
||||
'vault credential store', -- credential_store_type,
|
||||
'widget vault store', -- credential_store_name,
|
||||
'None', -- credential_store_description,
|
||||
'default', -- credential_store_vault_namespace,
|
||||
'https://vault.widget', -- credential_store_vault_address,
|
||||
|
||||
'p____bwidget', -- project_id,
|
||||
'Big Widget Factory', -- project_name,
|
||||
'None', -- project_description,
|
||||
'o_____widget', -- organization_id,
|
||||
'Widget Inc', -- organization_name,
|
||||
'None' -- organization_description
|
||||
't_________wb', -- target_id,
|
||||
'tcp target', -- target_type,
|
||||
'Big Widget Target', -- target_name,
|
||||
'None', -- target_description,
|
||||
0, -- target_default_port_number,
|
||||
28800, -- target_session_max_seconds,
|
||||
-1, -- target_session_connection_limit,
|
||||
|
||||
'p____bwidget', -- project_id,
|
||||
'Big Widget Factory', -- project_name,
|
||||
'None', -- project_description,
|
||||
'o_____widget', -- organization_id,
|
||||
'Widget Inc', -- organization_name,
|
||||
'None' -- organization_description
|
||||
)::whx_credential_dimension_source)
|
||||
from whx_credential_dimension_source as s
|
||||
where s.target_id = 't_________wb';
|
||||
|
||||
@@ -8,33 +8,36 @@ begin;
|
||||
(
|
||||
key,
|
||||
credential_purpose,
|
||||
credential_library_id, credential_library_type, credential_library_name, credential_library_description, credential_library_vault_path, credential_library_vault_http_method, credential_library_vault_http_request_body,
|
||||
credential_store_id, credential_store_type, credential_store_name, credential_store_description, credential_store_vault_namespace, credential_store_vault_address,
|
||||
target_id, target_type, target_name, target_description, target_default_port_number, target_session_max_seconds, target_session_connection_limit,
|
||||
project_id, project_name, project_description,
|
||||
organization_id, organization_name, organization_description,
|
||||
current_row_indicator, row_effective_time, row_expiration_time
|
||||
credential_library_id, credential_library_type, credential_library_name, credential_library_description, credential_library_vault_path, credential_library_vault_http_method, credential_library_vault_http_request_body,
|
||||
credential_library_username, credential_library_key_type_and_bits,
|
||||
credential_store_id, credential_store_type, credential_store_name, credential_store_description, credential_store_vault_namespace, credential_store_vault_address,
|
||||
target_id, target_type, target_name, target_description, target_default_port_number, target_session_max_seconds, target_session_connection_limit,
|
||||
project_id, project_name, project_description,
|
||||
organization_id, organization_name, organization_description,
|
||||
current_row_indicator, row_effective_time, row_expiration_time
|
||||
)
|
||||
values
|
||||
(
|
||||
'wcd________1',
|
||||
'brokered',
|
||||
'vl_______wvl', 'vault credential library', 'widget vault library', 'None', '/secrets', 'GET', 'None',
|
||||
'vs_______wvs', 'vault credential store', 'widget vault store', 'None', 'blue', 'https://vault.widget',
|
||||
't_________wb', 'tcp target', 'Big Widget Target', 'None', 0, 28800, 1,
|
||||
'p____bwidget', 'Big Widget Factory', 'None',
|
||||
'o_____widget', 'Widget Inc', 'None',
|
||||
'Current', '2021-07-21T12:01'::timestamptz, 'infinity'::timestamptz
|
||||
'vl_______wvl', 'vault generic credential library', 'widget vault library', 'None', '/secrets', 'GET', 'None',
|
||||
'Not Applicable', 'Not Applicable',
|
||||
'vs_______wvs', 'vault credential store', 'widget vault store', 'None', 'blue', 'https://vault.widget',
|
||||
't_________wb', 'tcp target', 'Big Widget Target', 'None', 0, 28800, 1,
|
||||
'p____bwidget', 'Big Widget Factory', 'None',
|
||||
'o_____widget', 'Widget Inc', 'None',
|
||||
'Current', '2021-07-21T12:01'::timestamptz, 'infinity'::timestamptz
|
||||
);
|
||||
|
||||
select is(t.*, row(
|
||||
'wcd________1',
|
||||
'brokered',
|
||||
'vl_______wvl', 'vault credential library', 'widget vault library', 'None', '/secrets', 'GET', 'None',
|
||||
'vs_______wvs', 'vault credential store', 'widget vault store', 'None', 'blue', 'https://vault.widget',
|
||||
't_________wb', 'tcp target', 'Big Widget Target', 'None', 0, 28800, 1,
|
||||
'p____bwidget', 'Big Widget Factory', 'None',
|
||||
'o_____widget', 'Widget Inc', 'None'
|
||||
'vl_______wvl', 'vault generic credential library', 'widget vault library', 'None', '/secrets', 'GET', 'None',
|
||||
'Not Applicable', 'Not Applicable',
|
||||
'vs_______wvs', 'vault credential store', 'widget vault store', 'None', 'blue', 'https://vault.widget',
|
||||
't_________wb', 'tcp target', 'Big Widget Target', 'None', 0, 28800, 1,
|
||||
'p____bwidget', 'Big Widget Factory', 'None',
|
||||
'o_____widget', 'Widget Inc', 'None'
|
||||
)::whx_credential_dimension_target)
|
||||
from whx_credential_dimension_target as t
|
||||
where t.target_id = 't_________wb';
|
||||
|
||||
@@ -29,6 +29,7 @@ type Credential struct {
|
||||
//
|
||||
// *Credential_UsernamePassword
|
||||
// *Credential_SshPrivateKey
|
||||
// *Credential_SshCertificate
|
||||
Credential isCredential_Credential `protobuf_oneof:"credential"`
|
||||
}
|
||||
|
||||
@@ -85,6 +86,13 @@ func (x *Credential) GetSshPrivateKey() *SshPrivateKey {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Credential) GetSshCertificate() *SshCertificate {
|
||||
if x, ok := x.GetCredential().(*Credential_SshCertificate); ok {
|
||||
return x.SshCertificate
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type isCredential_Credential interface {
|
||||
isCredential_Credential()
|
||||
}
|
||||
@@ -97,10 +105,16 @@ type Credential_SshPrivateKey struct {
|
||||
SshPrivateKey *SshPrivateKey `protobuf:"bytes,3,opt,name=ssh_private_key,json=sshPrivateKey,proto3,oneof"`
|
||||
}
|
||||
|
||||
type Credential_SshCertificate struct {
|
||||
SshCertificate *SshCertificate `protobuf:"bytes,4,opt,name=ssh_certificate,json=sshCertificate,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*Credential_UsernamePassword) isCredential_Credential() {}
|
||||
|
||||
func (*Credential_SshPrivateKey) isCredential_Credential() {}
|
||||
|
||||
func (*Credential_SshCertificate) isCredential_Credential() {}
|
||||
|
||||
// UsernamePassword is a credential containing a username and a password.
|
||||
type UsernamePassword struct {
|
||||
state protoimpl.MessageState
|
||||
@@ -227,6 +241,74 @@ func (x *SshPrivateKey) GetPrivateKeyPassphrase() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// SshCertificate is a credential containing a username, private key, and
|
||||
// client certificate.
|
||||
type SshCertificate struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// The username of the credential
|
||||
Username string `protobuf:"bytes,10,opt,name=username,proto3" json:"username,omitempty"` // @gotags: `class:"public"`
|
||||
// The private key of the credential
|
||||
PrivateKey string `protobuf:"bytes,20,opt,name=private_key,json=privateKey,proto3" json:"private_key,omitempty"` // @gotags: `class:"secret"`
|
||||
// The client certificate signed by a CA to establish trust of the private key.
|
||||
Certificate string `protobuf:"bytes,30,opt,name=certificate,proto3" json:"certificate,omitempty"` // @gotags: `class:"public"`
|
||||
}
|
||||
|
||||
func (x *SshCertificate) Reset() {
|
||||
*x = SshCertificate{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_controller_servers_services_v1_credential_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *SshCertificate) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SshCertificate) ProtoMessage() {}
|
||||
|
||||
func (x *SshCertificate) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_servers_services_v1_credential_proto_msgTypes[3]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SshCertificate.ProtoReflect.Descriptor instead.
|
||||
func (*SshCertificate) Descriptor() ([]byte, []int) {
|
||||
return file_controller_servers_services_v1_credential_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *SshCertificate) GetUsername() string {
|
||||
if x != nil {
|
||||
return x.Username
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SshCertificate) GetPrivateKey() string {
|
||||
if x != nil {
|
||||
return x.PrivateKey
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SshCertificate) GetCertificate() string {
|
||||
if x != nil {
|
||||
return x.Certificate
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_controller_servers_services_v1_credential_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_controller_servers_services_v1_credential_proto_rawDesc = []byte{
|
||||
@@ -235,7 +317,7 @@ var file_controller_servers_services_v1_credential_proto_rawDesc = []byte{
|
||||
0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x12, 0x1e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65,
|
||||
0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76,
|
||||
0x31, 0x22, 0xe9, 0x01, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c,
|
||||
0x31, 0x22, 0xc4, 0x02, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c,
|
||||
0x12, 0x5f, 0x0a, 0x11, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x70, 0x61, 0x73,
|
||||
0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x63, 0x6f,
|
||||
0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73,
|
||||
@@ -247,28 +329,41 @@ var file_controller_servers_services_v1_credential_proto_rawDesc = []byte{
|
||||
0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e,
|
||||
0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x73, 0x68, 0x50,
|
||||
0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x48, 0x00, 0x52, 0x0d, 0x73, 0x73, 0x68,
|
||||
0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x42, 0x0c, 0x0a, 0x0a, 0x63, 0x72,
|
||||
0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x52, 0x0d,
|
||||
0x75, 0x73, 0x65, 0x72, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x4a, 0x0a,
|
||||
0x10, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72,
|
||||
0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0a, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a,
|
||||
0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x82, 0x01, 0x0a, 0x0d, 0x53, 0x73,
|
||||
0x68, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x75,
|
||||
0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x59, 0x0a, 0x0f, 0x73, 0x73,
|
||||
0x68, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72,
|
||||
0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
|
||||
0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x73, 0x68, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
|
||||
0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x0e, 0x73, 0x73, 0x68, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66,
|
||||
0x69, 0x63, 0x61, 0x74, 0x65, 0x42, 0x0c, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74,
|
||||
0x69, 0x61, 0x6c, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x52, 0x0d, 0x75, 0x73, 0x65, 0x72, 0x5f,
|
||||
0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x4a, 0x0a, 0x10, 0x55, 0x73, 0x65, 0x72,
|
||||
0x6e, 0x61, 0x6d, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1a, 0x0a, 0x08,
|
||||
0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
|
||||
0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73,
|
||||
0x77, 0x6f, 0x72, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73,
|
||||
0x77, 0x6f, 0x72, 0x64, 0x22, 0x82, 0x01, 0x0a, 0x0d, 0x53, 0x73, 0x68, 0x50, 0x72, 0x69, 0x76,
|
||||
0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61,
|
||||
0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61,
|
||||
0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65,
|
||||
0x79, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65,
|
||||
0x4b, 0x65, 0x79, 0x12, 0x34, 0x0a, 0x16, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b,
|
||||
0x65, 0x79, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x18, 0x1e, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x14, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x50,
|
||||
0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x22, 0x6f, 0x0a, 0x0e, 0x53, 0x73, 0x68,
|
||||
0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x75,
|
||||
0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75,
|
||||
0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x76, 0x61,
|
||||
0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72,
|
||||
0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x34, 0x0a, 0x16, 0x70, 0x72, 0x69, 0x76,
|
||||
0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61,
|
||||
0x73, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74,
|
||||
0x65, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x42, 0x51,
|
||||
0x5a, 0x4f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73,
|
||||
0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f,
|
||||
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e,
|
||||
0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2f,
|
||||
0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x3b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
|
||||
0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x65, 0x72, 0x74,
|
||||
0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63,
|
||||
0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x42, 0x51, 0x5a, 0x4f, 0x67, 0x69,
|
||||
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f,
|
||||
0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65,
|
||||
0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
|
||||
0x6c, 0x65, 0x72, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x73, 0x65, 0x72, 0x76,
|
||||
0x69, 0x63, 0x65, 0x73, 0x3b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x62, 0x06, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -283,20 +378,22 @@ func file_controller_servers_services_v1_credential_proto_rawDescGZIP() []byte {
|
||||
return file_controller_servers_services_v1_credential_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_controller_servers_services_v1_credential_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
|
||||
var file_controller_servers_services_v1_credential_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
|
||||
var file_controller_servers_services_v1_credential_proto_goTypes = []interface{}{
|
||||
(*Credential)(nil), // 0: controller.servers.services.v1.Credential
|
||||
(*UsernamePassword)(nil), // 1: controller.servers.services.v1.UsernamePassword
|
||||
(*SshPrivateKey)(nil), // 2: controller.servers.services.v1.SshPrivateKey
|
||||
(*SshCertificate)(nil), // 3: controller.servers.services.v1.SshCertificate
|
||||
}
|
||||
var file_controller_servers_services_v1_credential_proto_depIdxs = []int32{
|
||||
1, // 0: controller.servers.services.v1.Credential.username_password:type_name -> controller.servers.services.v1.UsernamePassword
|
||||
2, // 1: controller.servers.services.v1.Credential.ssh_private_key:type_name -> controller.servers.services.v1.SshPrivateKey
|
||||
2, // [2:2] is the sub-list for method output_type
|
||||
2, // [2:2] is the sub-list for method input_type
|
||||
2, // [2:2] is the sub-list for extension type_name
|
||||
2, // [2:2] is the sub-list for extension extendee
|
||||
0, // [0:2] is the sub-list for field type_name
|
||||
3, // 2: controller.servers.services.v1.Credential.ssh_certificate:type_name -> controller.servers.services.v1.SshCertificate
|
||||
3, // [3:3] is the sub-list for method output_type
|
||||
3, // [3:3] is the sub-list for method input_type
|
||||
3, // [3:3] is the sub-list for extension type_name
|
||||
3, // [3:3] is the sub-list for extension extendee
|
||||
0, // [0:3] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_controller_servers_services_v1_credential_proto_init() }
|
||||
@@ -341,10 +438,23 @@ func file_controller_servers_services_v1_credential_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_controller_servers_services_v1_credential_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*SshCertificate); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
file_controller_servers_services_v1_credential_proto_msgTypes[0].OneofWrappers = []interface{}{
|
||||
(*Credential_UsernamePassword)(nil),
|
||||
(*Credential_SshPrivateKey)(nil),
|
||||
(*Credential_SshCertificate)(nil),
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
@@ -352,7 +462,7 @@ func file_controller_servers_services_v1_credential_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_controller_servers_services_v1_credential_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 3,
|
||||
NumMessages: 4,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
||||
+86
-4
@@ -17,10 +17,7 @@ message CredentialLibrary {
|
||||
string id = 10; // @gotags: `class:"public"`
|
||||
|
||||
// The ID of the Credential Store of which this Credential Library is a part.
|
||||
string credential_store_id = 20 [
|
||||
json_name = "credential_store_id",
|
||||
(controller.custom_options.v1.subtype_source_id) = true
|
||||
]; // @gotags: `class:"public"`
|
||||
string credential_store_id = 20 [json_name = "credential_store_id"]; // @gotags: `class:"public"`
|
||||
|
||||
// Output only. Scope information for this Credential Library.
|
||||
resources.scopes.v1.ScopeInfo scope = 30;
|
||||
@@ -67,6 +64,16 @@ message CredentialLibrary {
|
||||
(custom_options.v1.generate_sdk_option) = true,
|
||||
(custom_options.v1.subtype) = "vault"
|
||||
];
|
||||
VaultSSHCertificateCredentialLibraryAttributes vault_ssh_certificate_credential_library_attributes = 102 [
|
||||
(google.api.field_visibility).restriction = "INTERNAL",
|
||||
(custom_options.v1.generate_sdk_option) = true,
|
||||
(custom_options.v1.subtype) = "vault-ssh-certificate"
|
||||
];
|
||||
VaultCredentialLibraryAttributes vault_generic_credential_library_attributes = 103 [
|
||||
(google.api.field_visibility).restriction = "INTERNAL",
|
||||
(custom_options.v1.generate_sdk_option) = true,
|
||||
(custom_options.v1.subtype) = "vault-generic"
|
||||
];
|
||||
}
|
||||
|
||||
// Output only. The available actions on this resource for this user.
|
||||
@@ -116,3 +123,78 @@ message VaultCredentialLibraryAttributes {
|
||||
}
|
||||
]; // @gotags: `class:"secret"`
|
||||
}
|
||||
|
||||
// The attributes of a vault SSH Certificate Credential Library.
|
||||
message VaultSSHCertificateCredentialLibraryAttributes {
|
||||
// The path in Vault to request credentials from.
|
||||
google.protobuf.StringValue path = 10 [
|
||||
(custom_options.v1.generate_sdk_option) = true,
|
||||
(custom_options.v1.mask_mapping) = {
|
||||
this: "attributes.path"
|
||||
that: "VaultPath"
|
||||
}
|
||||
]; // @gotags: `class:"public"`
|
||||
|
||||
// The username to use when making an SSH connection.
|
||||
google.protobuf.StringValue username = 20 [
|
||||
(custom_options.v1.generate_sdk_option) = true,
|
||||
(custom_options.v1.mask_mapping) = {
|
||||
this: "attributes.username"
|
||||
that: "Username"
|
||||
}
|
||||
]; // @gotags: `class:"sensitive"`
|
||||
|
||||
// The key type to use when generating an SSH private key.
|
||||
google.protobuf.StringValue key_type = 30 [
|
||||
(custom_options.v1.generate_sdk_option) = true,
|
||||
(custom_options.v1.mask_mapping) = {
|
||||
this: "attributes.key_type"
|
||||
that: "KeyType"
|
||||
}
|
||||
]; // @gotags: `class:"public"`
|
||||
|
||||
// The number of bits to use to generate an SSH private key.
|
||||
google.protobuf.UInt32Value key_bits = 40 [
|
||||
(custom_options.v1.generate_sdk_option) = true,
|
||||
(custom_options.v1.mask_mapping) = {
|
||||
this: "attributes.key_bits"
|
||||
that: "KeyBits"
|
||||
}
|
||||
]; // @gotags: `class:"public"`
|
||||
|
||||
// The requested time to live for the certificate.
|
||||
google.protobuf.StringValue ttl = 50 [
|
||||
(custom_options.v1.generate_sdk_option) = true,
|
||||
(custom_options.v1.mask_mapping) = {
|
||||
this: "attributes.ttl"
|
||||
that: "Ttl"
|
||||
}
|
||||
]; // @gotags: `class:"public"`
|
||||
|
||||
// The key id that the created certificate should have.
|
||||
google.protobuf.StringValue key_id = 60 [
|
||||
(custom_options.v1.generate_sdk_option) = true,
|
||||
(custom_options.v1.mask_mapping) = {
|
||||
this: "attributes.key_id"
|
||||
that: "KeyId"
|
||||
}
|
||||
]; // @gotags: `class:"public"`
|
||||
|
||||
// The critical options that the certificate should be signed for.
|
||||
map<string, string> critical_options = 70 [
|
||||
(custom_options.v1.generate_sdk_option) = true,
|
||||
(custom_options.v1.mask_mapping) = {
|
||||
this: "attributes.critical_options"
|
||||
that: "CriticalOptions"
|
||||
}
|
||||
]; // @gotags: `class:"public"`
|
||||
|
||||
// The extensions that the certificate should be signed for.
|
||||
map<string, string> extensions = 80 [
|
||||
(custom_options.v1.generate_sdk_option) = true,
|
||||
(custom_options.v1.mask_mapping) = {
|
||||
this: "attributes.extensions"
|
||||
that: "Extensions"
|
||||
}
|
||||
]; // @gotags: `class:"public"`
|
||||
}
|
||||
@@ -11,6 +11,7 @@ message Credential {
|
||||
oneof credential {
|
||||
UsernamePassword username_password = 2;
|
||||
SshPrivateKey ssh_private_key = 3;
|
||||
SshCertificate ssh_certificate = 4;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,3 +36,16 @@ message SshPrivateKey {
|
||||
// The optional passphrase of the private_key
|
||||
string private_key_passphrase = 30; // @gotags: `class:"secret"`
|
||||
}
|
||||
|
||||
// SshCertificate is a credential containing a username, private key, and
|
||||
// client certificate.
|
||||
message SshCertificate {
|
||||
// The username of the credential
|
||||
string username = 10; // @gotags: `class:"public"`
|
||||
|
||||
// The private key of the credential
|
||||
string private_key = 20; // @gotags: `class:"secret"`
|
||||
|
||||
// The client certificate signed by a CA to establish trust of the private key.
|
||||
string certificate = 30; // @gotags: `class:"public"`
|
||||
}
|
||||
@@ -265,6 +265,107 @@ message CredentialLibrary {
|
||||
string credential_type = 11;
|
||||
}
|
||||
|
||||
message SSHCertificateCredentialLibrary {
|
||||
// public_id is a surrogate key suitable for use in a public API.
|
||||
// @inject_tag: `gorm:"primary_key"`
|
||||
string public_id = 1;
|
||||
|
||||
// create_time is set by the database.
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
timestamp.v1.Timestamp create_time = 2;
|
||||
|
||||
// update_time is set by the database.
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
timestamp.v1.Timestamp update_time = 3;
|
||||
|
||||
// name is optional. If set, it must be unique within project_id.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string name = 4 [(custom_options.v1.mask_mapping) = {
|
||||
this: "Name"
|
||||
that: "name"
|
||||
}];
|
||||
|
||||
// description is optional.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string description = 5 [(custom_options.v1.mask_mapping) = {
|
||||
this: "Description"
|
||||
that: "description"
|
||||
}];
|
||||
|
||||
// store_id of the owning vault credential store.
|
||||
// It must be set.
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
string store_id = 6;
|
||||
|
||||
// version allows optimistic locking of the resource.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
uint32 version = 7;
|
||||
|
||||
// vault_path is the path in Vault to request credentials from.
|
||||
// It must be set.
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
string vault_path = 8 [(custom_options.v1.mask_mapping) = {
|
||||
this: "VaultPath"
|
||||
that: "attributes.path"
|
||||
}];
|
||||
|
||||
// username is the username to use when making an SSH connection.
|
||||
// It must be set.
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
string username = 9 [(custom_options.v1.mask_mapping) = {
|
||||
this: "Username"
|
||||
that: "attributes.username"
|
||||
}];
|
||||
|
||||
// key_type specifies the key type to use when generating an SSH private key.
|
||||
// Values must be "rsa", "ed25519", or "ecdsa".
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
string key_type = 10 [(custom_options.v1.mask_mapping) = {
|
||||
this: "KeyType"
|
||||
that: "attributes.key_type"
|
||||
}];
|
||||
|
||||
// key_bits specifies the number of bits to use to generate an SSH private key.
|
||||
// Not used if key_type is ed25519.
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
uint32 key_bits = 11 [(custom_options.v1.mask_mapping) = {
|
||||
this: "KeyBits"
|
||||
that: "attributes.key_bits"
|
||||
}];
|
||||
|
||||
// ttl specifies the requested time to live for the certificate.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string ttl = 12 [(custom_options.v1.mask_mapping) = {
|
||||
this: "Ttl"
|
||||
that: "attributes.ttl"
|
||||
}];
|
||||
|
||||
// key_id specifies the key id that the created certificate should have.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string key_id = 13 [(custom_options.v1.mask_mapping) = {
|
||||
this: "KeyId"
|
||||
that: "attributes.key_id"
|
||||
}];
|
||||
|
||||
// critical_options specifies a map of the critical options that the certificate should be signed for.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string critical_options = 14 [(custom_options.v1.mask_mapping) = {
|
||||
this: "CriticalOptions"
|
||||
that: "attributes.critical_options"
|
||||
}];
|
||||
|
||||
// extensions specifies a map of the extensions that the certificate should be signed for.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string extensions = 15 [(custom_options.v1.mask_mapping) = {
|
||||
this: "Extensions"
|
||||
that: "attributes.extensions"
|
||||
}];
|
||||
|
||||
// credential_type is always ssh_certificate
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string credential_type = 16;
|
||||
}
|
||||
|
||||
message Credential {
|
||||
// public_id is a surrogate key suitable for use in a public API.
|
||||
// @inject_tag: `gorm:"primary_key"`
|
||||
|
||||
@@ -44,7 +44,7 @@ func TestList(t *testing.T) {
|
||||
expected = append(expected, &credentiallibraries.CredentialLibrary{Name: fmt.Sprint(i), Attributes: map[string]any{"vault_path": "something"}})
|
||||
}
|
||||
|
||||
cl, err := lClient.Create(tc.Context(), cs.Item.Id, credentiallibraries.WithName(expected[0].Name), credentiallibraries.WithVaultCredentialLibraryPath("something"))
|
||||
cl, err := lClient.Create(tc.Context(), cs.Item.Type, cs.Item.Id, credentiallibraries.WithName(expected[0].Name), credentiallibraries.WithVaultCredentialLibraryPath("something"))
|
||||
require.NoError(err)
|
||||
expected[0] = cl.Item
|
||||
|
||||
@@ -53,7 +53,7 @@ func TestList(t *testing.T) {
|
||||
assert.ElementsMatch(comparableSetSlice(expected[:1]), comparableSetSlice(ul.Items))
|
||||
|
||||
for i := 1; i < 10; i++ {
|
||||
cl, err = lClient.Create(tc.Context(), cs.Item.Id, credentiallibraries.WithName(expected[i].Name), credentiallibraries.WithVaultCredentialLibraryPath("something"))
|
||||
cl, err = lClient.Create(tc.Context(), cs.Item.Type, cs.Item.Id, credentiallibraries.WithName(expected[i].Name), credentiallibraries.WithVaultCredentialLibraryPath("something"))
|
||||
require.NoError(err)
|
||||
expected[i] = cl.Item
|
||||
}
|
||||
@@ -115,8 +115,9 @@ func TestCrud(t *testing.T) {
|
||||
|
||||
lClient := credentiallibraries.NewClient(client)
|
||||
|
||||
r, err := lClient.Create(tc.Context(), cs.Item.Id, credentiallibraries.WithName("foo"),
|
||||
r, err := lClient.Create(tc.Context(), cs.Item.Type, cs.Item.Id, credentiallibraries.WithName("foo"),
|
||||
credentiallibraries.WithVaultCredentialLibraryPath("something"))
|
||||
require.Nil(err)
|
||||
checkResource(t, "create", r.Item, err, "foo", 1)
|
||||
|
||||
r, err = lClient.Read(tc.Context(), r.Item.Id)
|
||||
@@ -157,7 +158,7 @@ func TestErrors(t *testing.T) {
|
||||
|
||||
lClient := credentiallibraries.NewClient(client)
|
||||
|
||||
l, err := lClient.Create(tc.Context(), cs.Item.Id, credentiallibraries.WithName("foo"),
|
||||
l, err := lClient.Create(tc.Context(), cs.Item.Type, cs.Item.Id, credentiallibraries.WithName("foo"),
|
||||
credentiallibraries.WithVaultCredentialLibraryPath("something"))
|
||||
require.NoError(err)
|
||||
assert.NotNil(l)
|
||||
@@ -178,7 +179,7 @@ func TestErrors(t *testing.T) {
|
||||
assert.NotNil(apiErr)
|
||||
assert.EqualValues(http.StatusNotFound, apiErr.Response().StatusCode())
|
||||
|
||||
l, err = lClient.Create(tc.Context(), cs.Item.Id, credentiallibraries.WithName("foo"))
|
||||
l, err = lClient.Create(tc.Context(), cs.Item.Type, cs.Item.Id, credentiallibraries.WithName("foo"))
|
||||
require.Error(err)
|
||||
apiErr = api.AsServerError(err)
|
||||
assert.NotNil(apiErr)
|
||||
|
||||
@@ -86,11 +86,11 @@ func TestCredentialSourcesASD(t *testing.T) {
|
||||
require.NotNil(csVault)
|
||||
|
||||
lClient := credentiallibraries.NewClient(client)
|
||||
lib1, err := lClient.Create(tc.Context(), csVault.Item.Id, credentiallibraries.WithVaultCredentialLibraryPath("something1"))
|
||||
lib1, err := lClient.Create(tc.Context(), csVault.Item.Type, csVault.Item.Id, credentiallibraries.WithVaultCredentialLibraryPath("something1"))
|
||||
require.NoError(err)
|
||||
require.NotNil(lib1)
|
||||
|
||||
lib2, err := lClient.Create(tc.Context(), csVault.Item.Id, credentiallibraries.WithVaultCredentialLibraryPath("something2"))
|
||||
lib2, err := lClient.Create(tc.Context(), csVault.Item.Type, csVault.Item.Id, credentiallibraries.WithVaultCredentialLibraryPath("something2"))
|
||||
require.NoError(err)
|
||||
require.NotNil(lib2)
|
||||
|
||||
@@ -204,11 +204,11 @@ func TestDeprecatedCredentialSourcesASD(t *testing.T) {
|
||||
require.NotNil(csVault)
|
||||
|
||||
lClient := credentiallibraries.NewClient(client)
|
||||
lib1, err := lClient.Create(tc.Context(), csVault.Item.Id, credentiallibraries.WithVaultCredentialLibraryPath("something1"))
|
||||
lib1, err := lClient.Create(tc.Context(), csVault.Item.Type, csVault.Item.Id, credentiallibraries.WithVaultCredentialLibraryPath("something1"))
|
||||
require.NoError(err)
|
||||
require.NotNil(lib1)
|
||||
|
||||
lib2, err := lClient.Create(tc.Context(), csVault.Item.Id, credentiallibraries.WithVaultCredentialLibraryPath("something2"))
|
||||
lib2, err := lClient.Create(tc.Context(), csVault.Item.Type, csVault.Item.Id, credentiallibraries.WithVaultCredentialLibraryPath("something2"))
|
||||
require.NoError(err)
|
||||
require.NotNil(lib2)
|
||||
|
||||
|
||||
@@ -1,5 +1,36 @@
|
||||
all: test
|
||||
|
||||
REGISTRY_NAME ?= docker.io/hashicorp
|
||||
TEST_VAULT_IMAGE_NAME=vault
|
||||
TEST_VAULT_IMAGE ?= $(REGISTRY_NAME)/$(TEST_VAULT_IMAGE_NAME):1.12.2
|
||||
TEST_VAULT_PORT ?= 8200
|
||||
TEST_VAULT_CONTAINER_NAME ?= boundary-cli-tests-vault
|
||||
|
||||
VAULT_ADDR ?= "http://127.0.0.1:$(TEST_VAULT_PORT)"
|
||||
VAULT_TOKEN ?= boundarytok
|
||||
|
||||
.PHONY: test-vault-up
|
||||
test-vault-up:
|
||||
@echo "Using image: $(TEST_VAULT_IMAGE)"
|
||||
@echo " vault addr: $(VAULT_ADDR)"
|
||||
@echo "vault token: $(VAULT_TOKEN)"
|
||||
@docker run \
|
||||
-d \
|
||||
--name $(TEST_VAULT_CONTAINER_NAME) \
|
||||
-e VAULT_DEV_ROOT_TOKEN_ID=$(VAULT_TOKEN) \
|
||||
-p $(TEST_VAULT_PORT):8200 \
|
||||
--cap-add IPC_LOCK \
|
||||
$(TEST_VAULT_IMAGE)
|
||||
@echo "Container name: $(TEST_VAULT_CONTAINER_NAME)"
|
||||
@echo "Test vault available at: $(VAULT_ADDR)"
|
||||
@echo "For vault logs run:"
|
||||
@echo " docker logs $(TEST_VAULT_CONTAINER_NAME)"
|
||||
|
||||
.PHONY: test-vault-down
|
||||
test-vault-down:
|
||||
docker stop $(TEST_VAULT_CONTAINER_NAME) || true
|
||||
docker rm -v $(TEST_VAULT_CONTAINER_NAME) || true
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
./test.sh
|
||||
@@ -0,0 +1,36 @@
|
||||
load _authorized_actions
|
||||
|
||||
function create_vault_ssh_certificate_library() {
|
||||
boundary credential-libraries \
|
||||
create vault-ssh-certificate $@
|
||||
}
|
||||
|
||||
function create_vault_generic_library() {
|
||||
boundary credential-libraries \
|
||||
create vault-generic $@
|
||||
}
|
||||
|
||||
function create_vault_library() {
|
||||
boundary credential-libraries \
|
||||
create vault $@
|
||||
}
|
||||
|
||||
function read_credential_library() {
|
||||
boundary credential-libraries read -id $1 -format json
|
||||
}
|
||||
|
||||
function delete_credential_library() {
|
||||
boundary credential-libraries delete -id $1
|
||||
}
|
||||
|
||||
function list_credential_libraries() {
|
||||
local csid=$1
|
||||
boundary credential-libraries list -credential-store-id $1 -format json
|
||||
}
|
||||
|
||||
function credential_library_id() {
|
||||
local name=$1
|
||||
local csid=$2
|
||||
|
||||
strip $(list_credential_libraries $csid | jq -c ".items[] | select(.name | contains(\"$name\")) | .[\"id\"]")
|
||||
}
|
||||
@@ -10,6 +10,10 @@ function create_static_credential_store() {
|
||||
-scope-id $sid
|
||||
}
|
||||
|
||||
function create_vault_credential_store() {
|
||||
boundary credential-stores create vault $@
|
||||
}
|
||||
|
||||
function read_credential_store() {
|
||||
boundary credential-stores read -id $1 -format json
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
|
||||
function vault_write_boundary_policy() {
|
||||
(
|
||||
cat <<EOF
|
||||
path "auth/token/lookup-self" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
|
||||
path "auth/token/renew-self" {
|
||||
capabilities = ["update"]
|
||||
}
|
||||
|
||||
path "auth/token/revoke-self" {
|
||||
capabilities = ["update"]
|
||||
}
|
||||
|
||||
path "sys/leases/renew" {
|
||||
capabilities = ["update"]
|
||||
}
|
||||
|
||||
path "sys/leases/revoke" {
|
||||
capabilities = ["update"]
|
||||
}
|
||||
|
||||
path "sys/capabilities-self" {
|
||||
capabilities = ["update"]
|
||||
}
|
||||
EOF
|
||||
) | vault policy write boundary-controller -
|
||||
}
|
||||
|
||||
function create_vault_token() {
|
||||
vault token create \
|
||||
-format=json \
|
||||
-no-default-policy=true \
|
||||
-policy="boundary-controller" \
|
||||
-orphan=true \
|
||||
-period=2m \
|
||||
-renewable=true | \
|
||||
jq -r '.auth.client_token'
|
||||
}
|
||||
|
||||
function skip_if_no_vault() {
|
||||
if [[ -z $VAULT_ADDR ]] || [[ -z $VAULT_TOKEN ]]; then
|
||||
skip "vault environment variables \$VAULT_ADDR and \$VAULT_TOKEN not set"
|
||||
fi
|
||||
}
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
load _auth
|
||||
load _helpers
|
||||
load _vault
|
||||
load _credential_stores
|
||||
load _credential_libraries
|
||||
|
||||
export NEW_STORE='test_vault'
|
||||
export NEW_VAULT_LIB="test_vault"
|
||||
|
||||
@test "boundary/login: can login as default user" {
|
||||
run login $DEFAULT_LOGIN
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "boundary/credential-libraries: setup vault-generic credential store $NEW_STORE in default project" {
|
||||
skip_if_no_vault
|
||||
|
||||
vault_write_boundary_policy
|
||||
local vault_token=$(create_vault_token)
|
||||
run create_vault_credential_store \
|
||||
-name $NEW_STORE -scope-id $DEFAULT_P_ID \
|
||||
-vault-address $VAULT_ADDR \
|
||||
-vault-token $vault_token
|
||||
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "boundary/credential-libraries: can create $NEW_VAULT_LIB vault-generic library in credential store $NEW_STORE" {
|
||||
skip_if_no_vault
|
||||
|
||||
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
|
||||
run create_vault_generic_library \
|
||||
-name $NEW_VAULT_LIB -credential-store-id $csid \
|
||||
-vault-path /kv/secret \
|
||||
-vault-http-method GET
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "boundary/credential-libraries: can not create already created $NEW_VAULT_LIB vault-generic libary" {
|
||||
skip_if_no_vault
|
||||
|
||||
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
|
||||
run create_vault_generic_library \
|
||||
-name $NEW_VAULT_LIB -credential-store-id $csid \
|
||||
-vault-path /kv/secret \
|
||||
-vault-http-method GET
|
||||
echo "$output"
|
||||
[ "$status" -eq 1 ]
|
||||
}
|
||||
|
||||
@test "boundary/credential-libraries: can read $NEW_VAULT_LIB vault-generic library" {
|
||||
skip_if_no_vault
|
||||
|
||||
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
|
||||
local clid=$(credential_library_id $NEW_VAULT_LIB $csid)
|
||||
run read_credential_library $clid
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "boundary/credential-libraries: can delete $NEW_VAULT_LIB vault-generic library" {
|
||||
skip_if_no_vault
|
||||
|
||||
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
|
||||
local clid=$(credential_library_id $NEW_VAULT_LIB $csid)
|
||||
run delete_credential_library $clid
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run has_status_code "$output" "204"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "boundary/credential-libraries: can create $NEW_VAULT_LIB vault library in credential store $NEW_STORE deprecated subcommand" {
|
||||
skip_if_no_vault
|
||||
|
||||
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
|
||||
run create_vault_library \
|
||||
-name $NEW_VAULT_LIB -credential-store-id $csid \
|
||||
-vault-path /kv/secret \
|
||||
-vault-http-method GET
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "boundary/credential-libraries: can not create already created $NEW_VAULT_LIB vault libary deprecated subcommand" {
|
||||
skip_if_no_vault
|
||||
|
||||
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
|
||||
run create_vault_library \
|
||||
-name $NEW_VAULT_LIB -credential-store-id $csid \
|
||||
-vault-path /kv/secret \
|
||||
-vault-http-method GET
|
||||
echo "$output"
|
||||
[ "$status" -eq 1 ]
|
||||
}
|
||||
|
||||
@test "boundary/credential-libraries: can read $NEW_VAULT_LIB vault library depcrecated subcommand" {
|
||||
skip_if_no_vault
|
||||
|
||||
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
|
||||
local clid=$(credential_library_id $NEW_VAULT_LIB $csid)
|
||||
run read_credential_library $clid
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "boundary/credential-libraries: can delete $NEW_VAULT_LIB vault library depcrecated subcommand" {
|
||||
skip_if_no_vault
|
||||
|
||||
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
|
||||
local clid=$(credential_library_id $NEW_VAULT_LIB $csid)
|
||||
run delete_credential_library $clid
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run has_status_code "$output" "204"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "boundary/credential-libraries: can create $NEW_VAULT_LIB vault-ssh-certificate library in credential store $NEW_STORE" {
|
||||
skip_if_no_vault
|
||||
|
||||
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
|
||||
run create_vault_ssh_certificate_library \
|
||||
-name $NEW_VAULT_LIB -credential-store-id $csid \
|
||||
-vault-path /ssh/sign/foo \
|
||||
-username foo
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "boundary/credential-libraries: can not create already created $NEW_VAULT_LIB vault-ssh-certificate libary" {
|
||||
skip_if_no_vault
|
||||
|
||||
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
|
||||
run create_vault_ssh_certificate_library \
|
||||
-name $NEW_VAULT_LIB -credential-store-id $csid \
|
||||
-vault-path /ssh/sign/foo \
|
||||
-username foo
|
||||
echo "$output"
|
||||
[ "$status" -eq 1 ]
|
||||
}
|
||||
|
||||
@test "boundary/credential-libraries: can read $NEW_VAULT_LIB vault-ssh-certificate library" {
|
||||
skip_if_no_vault
|
||||
|
||||
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
|
||||
local clid=$(credential_library_id $NEW_VAULT_LIB $csid)
|
||||
run read_credential_library $clid
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "boundary/credential-libraries: can delete $NEW_VAULT_LIB vault-ssh-certificate library" {
|
||||
skip_if_no_vault
|
||||
|
||||
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
|
||||
local clid=$(credential_library_id $NEW_VAULT_LIB $csid)
|
||||
run delete_credential_library $clid
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run has_status_code "$output" "204"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
# Note, deleting the cred store will revoke the vault token
|
||||
@test "boundary/credential-stores: cleanup can delete $NEW_STORE vault store" {
|
||||
skip_if_no_vault
|
||||
|
||||
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
|
||||
run delete_credential_store $csid
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run has_status_code "$output" "204"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@@ -55,6 +55,8 @@ type CredentialLibrary struct {
|
||||
//
|
||||
// *CredentialLibrary_Attributes
|
||||
// *CredentialLibrary_VaultCredentialLibraryAttributes
|
||||
// *CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes
|
||||
// *CredentialLibrary_VaultGenericCredentialLibraryAttributes
|
||||
Attrs isCredentialLibrary_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"`
|
||||
@@ -180,6 +182,20 @@ func (x *CredentialLibrary) GetVaultCredentialLibraryAttributes() *VaultCredenti
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *CredentialLibrary) GetVaultSshCertificateCredentialLibraryAttributes() *VaultSSHCertificateCredentialLibraryAttributes {
|
||||
if x, ok := x.GetAttrs().(*CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes); ok {
|
||||
return x.VaultSshCertificateCredentialLibraryAttributes
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *CredentialLibrary) GetVaultGenericCredentialLibraryAttributes() *VaultCredentialLibraryAttributes {
|
||||
if x, ok := x.GetAttrs().(*CredentialLibrary_VaultGenericCredentialLibraryAttributes); ok {
|
||||
return x.VaultGenericCredentialLibraryAttributes
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *CredentialLibrary) GetAuthorizedActions() []string {
|
||||
if x != nil {
|
||||
return x.AuthorizedActions
|
||||
@@ -214,10 +230,23 @@ type CredentialLibrary_VaultCredentialLibraryAttributes struct {
|
||||
VaultCredentialLibraryAttributes *VaultCredentialLibraryAttributes `protobuf:"bytes,101,opt,name=vault_credential_library_attributes,json=vaultCredentialLibraryAttributes,proto3,oneof"`
|
||||
}
|
||||
|
||||
type CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes struct {
|
||||
VaultSshCertificateCredentialLibraryAttributes *VaultSSHCertificateCredentialLibraryAttributes `protobuf:"bytes,102,opt,name=vault_ssh_certificate_credential_library_attributes,json=vaultSshCertificateCredentialLibraryAttributes,proto3,oneof"`
|
||||
}
|
||||
|
||||
type CredentialLibrary_VaultGenericCredentialLibraryAttributes struct {
|
||||
VaultGenericCredentialLibraryAttributes *VaultCredentialLibraryAttributes `protobuf:"bytes,103,opt,name=vault_generic_credential_library_attributes,json=vaultGenericCredentialLibraryAttributes,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*CredentialLibrary_Attributes) isCredentialLibrary_Attrs() {}
|
||||
|
||||
func (*CredentialLibrary_VaultCredentialLibraryAttributes) isCredentialLibrary_Attrs() {}
|
||||
|
||||
func (*CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes) isCredentialLibrary_Attrs() {
|
||||
}
|
||||
|
||||
func (*CredentialLibrary_VaultGenericCredentialLibraryAttributes) isCredentialLibrary_Attrs() {}
|
||||
|
||||
// The attributes of a vault typed Credential Library.
|
||||
type VaultCredentialLibraryAttributes struct {
|
||||
state protoimpl.MessageState
|
||||
@@ -285,6 +314,118 @@ func (x *VaultCredentialLibraryAttributes) GetHttpRequestBody() *wrapperspb.Stri
|
||||
return nil
|
||||
}
|
||||
|
||||
// The attributes of a vault SSH Certificate Credential Library.
|
||||
type VaultSSHCertificateCredentialLibraryAttributes struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// The path in Vault to request credentials from.
|
||||
Path *wrapperspb.StringValue `protobuf:"bytes,10,opt,name=path,proto3" json:"path,omitempty" class:"public"` // @gotags: `class:"public"`
|
||||
// The username to use when making an SSH connection.
|
||||
Username *wrapperspb.StringValue `protobuf:"bytes,20,opt,name=username,proto3" json:"username,omitempty" class:"sensitive"` // @gotags: `class:"sensitive"`
|
||||
// The key type to use when generating an SSH private key.
|
||||
KeyType *wrapperspb.StringValue `protobuf:"bytes,30,opt,name=key_type,json=keyType,proto3" json:"key_type,omitempty" class:"public"` // @gotags: `class:"public"`
|
||||
// The number of bits to use to generate an SSH private key.
|
||||
KeyBits *wrapperspb.UInt32Value `protobuf:"bytes,40,opt,name=key_bits,json=keyBits,proto3" json:"key_bits,omitempty" class:"public"` // @gotags: `class:"public"`
|
||||
// The requested time to live for the certificate.
|
||||
Ttl *wrapperspb.StringValue `protobuf:"bytes,50,opt,name=ttl,proto3" json:"ttl,omitempty" class:"public"` // @gotags: `class:"public"`
|
||||
// The key id that the created certificate should have.
|
||||
KeyId *wrapperspb.StringValue `protobuf:"bytes,60,opt,name=key_id,json=keyId,proto3" json:"key_id,omitempty" class:"public"` // @gotags: `class:"public"`
|
||||
// The critical options that the certificate should be signed for.
|
||||
CriticalOptions map[string]string `protobuf:"bytes,70,rep,name=critical_options,json=criticalOptions,proto3" json:"critical_options,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3" class:"public"` // @gotags: `class:"public"`
|
||||
// The extensions that the certificate should be signed for.
|
||||
Extensions map[string]string `protobuf:"bytes,80,rep,name=extensions,proto3" json:"extensions,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3" class:"public"` // @gotags: `class:"public"`
|
||||
}
|
||||
|
||||
func (x *VaultSSHCertificateCredentialLibraryAttributes) Reset() {
|
||||
*x = VaultSSHCertificateCredentialLibraryAttributes{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_controller_api_resources_credentiallibraries_v1_credential_library_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *VaultSSHCertificateCredentialLibraryAttributes) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*VaultSSHCertificateCredentialLibraryAttributes) ProtoMessage() {}
|
||||
|
||||
func (x *VaultSSHCertificateCredentialLibraryAttributes) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_api_resources_credentiallibraries_v1_credential_library_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use VaultSSHCertificateCredentialLibraryAttributes.ProtoReflect.Descriptor instead.
|
||||
func (*VaultSSHCertificateCredentialLibraryAttributes) Descriptor() ([]byte, []int) {
|
||||
return file_controller_api_resources_credentiallibraries_v1_credential_library_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *VaultSSHCertificateCredentialLibraryAttributes) GetPath() *wrapperspb.StringValue {
|
||||
if x != nil {
|
||||
return x.Path
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *VaultSSHCertificateCredentialLibraryAttributes) GetUsername() *wrapperspb.StringValue {
|
||||
if x != nil {
|
||||
return x.Username
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *VaultSSHCertificateCredentialLibraryAttributes) GetKeyType() *wrapperspb.StringValue {
|
||||
if x != nil {
|
||||
return x.KeyType
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *VaultSSHCertificateCredentialLibraryAttributes) GetKeyBits() *wrapperspb.UInt32Value {
|
||||
if x != nil {
|
||||
return x.KeyBits
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *VaultSSHCertificateCredentialLibraryAttributes) GetTtl() *wrapperspb.StringValue {
|
||||
if x != nil {
|
||||
return x.Ttl
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *VaultSSHCertificateCredentialLibraryAttributes) GetKeyId() *wrapperspb.StringValue {
|
||||
if x != nil {
|
||||
return x.KeyId
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *VaultSSHCertificateCredentialLibraryAttributes) GetCriticalOptions() map[string]string {
|
||||
if x != nil {
|
||||
return x.CriticalOptions
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *VaultSSHCertificateCredentialLibraryAttributes) GetExtensions() map[string]string {
|
||||
if x != nil {
|
||||
return x.Extensions
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_controller_api_resources_credentiallibraries_v1_credential_library_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_controller_api_resources_credentiallibraries_v1_credential_library_proto_rawDesc = []byte{
|
||||
@@ -309,99 +450,199 @@ var file_controller_api_resources_credentiallibraries_v1_credential_library_prot
|
||||
0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x22, 0xd9, 0x07, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69,
|
||||
0x6f, 0x74, 0x6f, 0x22, 0xae, 0x0b, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69,
|
||||
0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18,
|
||||
0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x36, 0x0a, 0x13, 0x63, 0x72, 0x65,
|
||||
0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x30, 0x0a, 0x13, 0x63, 0x72, 0x65,
|
||||
0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x69, 0x64,
|
||||
0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x42, 0x04, 0xa0, 0xe3, 0x29, 0x01, 0x52, 0x13, 0x63, 0x72,
|
||||
0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x69,
|
||||
0x64, 0x12, 0x43, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70,
|
||||
0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f, 0x70,
|
||||
0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52,
|
||||
0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x46, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x28,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c,
|
||||
0x75, 0x65, 0x42, 0x14, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x6e, 0x61,
|
||||
0x6d, 0x65, 0x12, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x62,
|
||||
0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x32, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75,
|
||||
0x65, 0x42, 0x22, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x64, 0x65, 0x73,
|
||||
0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69,
|
||||
0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69,
|
||||
0x6d, 0x65, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
|
||||
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73,
|
||||
0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69,
|
||||
0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69,
|
||||
0x6d, 0x65, 0x18, 0x46, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
|
||||
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73,
|
||||
0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69,
|
||||
0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x50, 0x20,
|
||||
0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04,
|
||||
0x74, 0x79, 0x70, 0x65, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65,
|
||||
0x12, 0x4a, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x64,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, 0x0f, 0xa0,
|
||||
0xda, 0x29, 0x01, 0x9a, 0xe3, 0x29, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x48, 0x00,
|
||||
0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0xc1, 0x01, 0x0a,
|
||||
0x23, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61,
|
||||
0x6c, 0x5f, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62,
|
||||
0x75, 0x74, 0x65, 0x73, 0x18, 0x65, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x51, 0x2e, 0x63, 0x6f, 0x6e,
|
||||
0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69,
|
||||
0x61, 0x6c, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x69, 0x64, 0x12, 0x43, 0x0a, 0x05, 0x73,
|
||||
0x63, 0x6f, 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e,
|
||||
0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f,
|
||||
0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c,
|
||||
0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x61, 0x75,
|
||||
0x6c, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72,
|
||||
0x61, 0x72, 0x79, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x42, 0x1d, 0xa0,
|
||||
0xda, 0x29, 0x01, 0x9a, 0xe3, 0x29, 0x05, 0x76, 0x61, 0x75, 0x6c, 0x74, 0xfa, 0xd2, 0xe4, 0x93,
|
||||
0x02, 0x0a, 0x12, 0x08, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x48, 0x00, 0x52, 0x20,
|
||||
0x76, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c,
|
||||
0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73,
|
||||
0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61,
|
||||
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61,
|
||||
0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x73, 0x12, 0x2f, 0x0a, 0x0f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f,
|
||||
0x74, 0x79, 0x70, 0x65, 0x18, 0xb6, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x04, 0xa0, 0xda, 0x29,
|
||||
0x01, 0x52, 0x0f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x74, 0x79,
|
||||
0x70, 0x65, 0x12, 0x62, 0x0a, 0x1c, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c,
|
||||
0x5f, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64,
|
||||
0x65, 0x73, 0x18, 0xc0, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
|
||||
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75,
|
||||
0x63, 0x74, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, 0x1c, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e,
|
||||
0x74, 0x69, 0x61, 0x6c, 0x5f, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x6f, 0x76, 0x65,
|
||||
0x72, 0x72, 0x69, 0x64, 0x65, 0x73, 0x42, 0x07, 0x0a, 0x05, 0x61, 0x74, 0x74, 0x72, 0x73, 0x22,
|
||||
0xee, 0x02, 0x0a, 0x20, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74,
|
||||
0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62,
|
||||
0x75, 0x74, 0x65, 0x73, 0x12, 0x56, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x0a, 0x20, 0x01,
|
||||
0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e,
|
||||
0x53, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65,
|
||||
0x12, 0x46, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c,
|
||||
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
||||
0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x14, 0xa0, 0xda,
|
||||
0x29, 0x01, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x4e, 0x61,
|
||||
0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x62, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63,
|
||||
0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e,
|
||||
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
|
||||
0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x22, 0xa0, 0xda, 0x29,
|
||||
0x01, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x12, 0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52,
|
||||
0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x0c,
|
||||
0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x3c, 0x20, 0x01,
|
||||
0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c,
|
||||
0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x0c,
|
||||
0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x46, 0x20, 0x01,
|
||||
0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c,
|
||||
0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07,
|
||||
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76,
|
||||
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x5a,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x4a, 0x0a, 0x0a, 0x61, 0x74,
|
||||
0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17,
|
||||
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
||||
0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, 0x0f, 0xa0, 0xda, 0x29, 0x01, 0x9a, 0xe3, 0x29,
|
||||
0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x48, 0x00, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72,
|
||||
0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0xc1, 0x01, 0x0a, 0x23, 0x76, 0x61, 0x75, 0x6c, 0x74,
|
||||
0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x6c, 0x69, 0x62, 0x72,
|
||||
0x61, 0x72, 0x79, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x65,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x51, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65,
|
||||
0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e,
|
||||
0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72,
|
||||
0x69, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x72, 0x65, 0x64,
|
||||
0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x41, 0x74, 0x74,
|
||||
0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x42, 0x1d, 0xa0, 0xda, 0x29, 0x01, 0x9a, 0xe3, 0x29,
|
||||
0x05, 0x76, 0x61, 0x75, 0x6c, 0x74, 0xfa, 0xd2, 0xe4, 0x93, 0x02, 0x0a, 0x12, 0x08, 0x49, 0x4e,
|
||||
0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x48, 0x00, 0x52, 0x20, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x43,
|
||||
0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79,
|
||||
0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0xfd, 0x01, 0x0a, 0x33, 0x76,
|
||||
0x61, 0x75, 0x6c, 0x74, 0x5f, 0x73, 0x73, 0x68, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
|
||||
0x63, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f,
|
||||
0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74,
|
||||
0x65, 0x73, 0x18, 0x66, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x5f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72,
|
||||
0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72,
|
||||
0x63, 0x65, 0x73, 0x2e, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x6c, 0x69,
|
||||
0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x61, 0x75, 0x6c, 0x74,
|
||||
0x53, 0x53, 0x48, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x43, 0x72,
|
||||
0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x41,
|
||||
0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x42, 0x2d, 0xa0, 0xda, 0x29, 0x01, 0x9a,
|
||||
0xe3, 0x29, 0x15, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2d, 0x73, 0x73, 0x68, 0x2d, 0x63, 0x65, 0x72,
|
||||
0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0xfa, 0xd2, 0xe4, 0x93, 0x02, 0x0a, 0x12, 0x08,
|
||||
0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x48, 0x00, 0x52, 0x2e, 0x76, 0x61, 0x75, 0x6c,
|
||||
0x74, 0x53, 0x73, 0x68, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x43,
|
||||
0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79,
|
||||
0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0xd8, 0x01, 0x0a, 0x2b, 0x76,
|
||||
0x61, 0x75, 0x6c, 0x74, 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x5f, 0x63, 0x72, 0x65,
|
||||
0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f,
|
||||
0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x67, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x51, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70,
|
||||
0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x63, 0x72, 0x65, 0x64,
|
||||
0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x2e,
|
||||
0x76, 0x31, 0x2e, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69,
|
||||
0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75,
|
||||
0x74, 0x65, 0x73, 0x42, 0x25, 0xa0, 0xda, 0x29, 0x01, 0x9a, 0xe3, 0x29, 0x0d, 0x76, 0x61, 0x75,
|
||||
0x6c, 0x74, 0x2d, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0xfa, 0xd2, 0xe4, 0x93, 0x02, 0x0a,
|
||||
0x12, 0x08, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x48, 0x00, 0x52, 0x27, 0x76, 0x61,
|
||||
0x75, 0x6c, 0x74, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e,
|
||||
0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x41, 0x74, 0x74, 0x72, 0x69,
|
||||
0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
|
||||
0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03,
|
||||
0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61,
|
||||
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2f, 0x0a, 0x0f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e,
|
||||
0x74, 0x69, 0x61, 0x6c, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0xb6, 0x02, 0x20, 0x01, 0x28, 0x09,
|
||||
0x42, 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, 0x0f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69,
|
||||
0x61, 0x6c, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x12, 0x62, 0x0a, 0x1c, 0x63, 0x72, 0x65, 0x64, 0x65,
|
||||
0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x6f, 0x76,
|
||||
0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x73, 0x18, 0xc0, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17,
|
||||
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
||||
0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, 0x1c, 0x63,
|
||||
0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e,
|
||||
0x67, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x73, 0x42, 0x07, 0x0a, 0x05, 0x61,
|
||||
0x74, 0x74, 0x72, 0x73, 0x22, 0xee, 0x02, 0x0a, 0x20, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x72,
|
||||
0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x41,
|
||||
0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x56, 0x0a, 0x04, 0x70, 0x61, 0x74,
|
||||
0x68, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67,
|
||||
0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x24, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1c, 0x0a,
|
||||
0x0f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x70, 0x61, 0x74, 0x68,
|
||||
0x12, 0x09, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04, 0x70, 0x61, 0x74,
|
||||
0x68, 0x12, 0x6c, 0x0a, 0x0b, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64,
|
||||
0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56,
|
||||
0x61, 0x6c, 0x75, 0x65, 0x42, 0x2c, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x24, 0x0a, 0x16,
|
||||
0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x5f,
|
||||
0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x0a, 0x48, 0x74, 0x74, 0x70, 0x4d, 0x65, 0x74, 0x68,
|
||||
0x6f, 0x64, 0x52, 0x0b, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12,
|
||||
0x83, 0x01, 0x0a, 0x11, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x5f, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f,
|
||||
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74,
|
||||
0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x37, 0xa0, 0xda, 0x29, 0x01, 0xc2,
|
||||
0xdd, 0x29, 0x2f, 0x0a, 0x1c, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e,
|
||||
0x68, 0x74, 0x74, 0x70, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x62, 0x6f, 0x64,
|
||||
0x79, 0x12, 0x0f, 0x48, 0x74, 0x74, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6f,
|
||||
0x64, 0x79, 0x52, 0x11, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x5f, 0x62, 0x6f, 0x64, 0x79, 0x22, 0xf1, 0x08, 0x0a, 0x2e, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x53,
|
||||
0x53, 0x48, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x43, 0x72, 0x65,
|
||||
0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x41, 0x74,
|
||||
0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x56, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68,
|
||||
0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56,
|
||||
0x61, 0x6c, 0x75, 0x65, 0x42, 0x24, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1c, 0x0a, 0x0f,
|
||||
0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x70, 0x61, 0x74, 0x68, 0x12,
|
||||
0x09, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68,
|
||||
0x12, 0x61, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01,
|
||||
0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65,
|
||||
0x42, 0x24, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1c, 0x0a, 0x0f, 0x61, 0x74, 0x74, 0x72,
|
||||
0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x70, 0x61, 0x74, 0x68, 0x12, 0x09, 0x56, 0x61, 0x75,
|
||||
0x6c, 0x74, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x6c, 0x0a, 0x0b,
|
||||
0x68, 0x74, 0x74, 0x70, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42,
|
||||
0x2c, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x24, 0x0a, 0x16, 0x61, 0x74, 0x74, 0x72, 0x69,
|
||||
0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f,
|
||||
0x64, 0x12, 0x0a, 0x48, 0x74, 0x74, 0x70, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x0b, 0x68,
|
||||
0x74, 0x74, 0x70, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x83, 0x01, 0x0a, 0x11, 0x68,
|
||||
0x74, 0x74, 0x70, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x62, 0x6f, 0x64, 0x79,
|
||||
0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56,
|
||||
0x61, 0x6c, 0x75, 0x65, 0x42, 0x37, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x2f, 0x0a, 0x1c,
|
||||
0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x5f,
|
||||
0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x0f, 0x48, 0x74,
|
||||
0x74, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x11, 0x68,
|
||||
0x74, 0x74, 0x70, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x62, 0x6f, 0x64, 0x79,
|
||||
0x42, 0x68, 0x5a, 0x66, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68,
|
||||
0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72,
|
||||
0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
|
||||
0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
|
||||
0x65, 0x73, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x6c, 0x69, 0x62,
|
||||
0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x3b, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61,
|
||||
0x6c, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x33,
|
||||
0x42, 0x27, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1f, 0x0a, 0x13, 0x61, 0x74, 0x74, 0x72,
|
||||
0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12,
|
||||
0x08, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e,
|
||||
0x61, 0x6d, 0x65, 0x12, 0x5f, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18,
|
||||
0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61,
|
||||
0x6c, 0x75, 0x65, 0x42, 0x26, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1e, 0x0a, 0x13, 0x61,
|
||||
0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x74, 0x79,
|
||||
0x70, 0x65, 0x12, 0x07, 0x4b, 0x65, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, 0x6b, 0x65, 0x79,
|
||||
0x54, 0x79, 0x70, 0x65, 0x12, 0x5f, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x62, 0x69, 0x74, 0x73,
|
||||
0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56,
|
||||
0x61, 0x6c, 0x75, 0x65, 0x42, 0x26, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1e, 0x0a, 0x13,
|
||||
0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x62,
|
||||
0x69, 0x74, 0x73, 0x12, 0x07, 0x4b, 0x65, 0x79, 0x42, 0x69, 0x74, 0x73, 0x52, 0x07, 0x6b, 0x65,
|
||||
0x79, 0x42, 0x69, 0x74, 0x73, 0x12, 0x4d, 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x32, 0x20, 0x01,
|
||||
0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65,
|
||||
0x42, 0x1d, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x15, 0x0a, 0x0e, 0x61, 0x74, 0x74, 0x72,
|
||||
0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x74, 0x74, 0x6c, 0x12, 0x03, 0x54, 0x74, 0x6c, 0x52,
|
||||
0x03, 0x74, 0x74, 0x6c, 0x12, 0x57, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x3c,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c,
|
||||
0x75, 0x65, 0x42, 0x22, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x11, 0x61, 0x74,
|
||||
0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x12,
|
||||
0x05, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x12, 0xd7, 0x01,
|
||||
0x0a, 0x10, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x73, 0x18, 0x46, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x74, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72,
|
||||
0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72,
|
||||
0x63, 0x65, 0x73, 0x2e, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x6c, 0x69,
|
||||
0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x61, 0x75, 0x6c, 0x74,
|
||||
0x53, 0x53, 0x48, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x43, 0x72,
|
||||
0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x41,
|
||||
0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63,
|
||||
0x61, 0x6c, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x36,
|
||||
0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x2e, 0x0a, 0x1b, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62,
|
||||
0x75, 0x74, 0x65, 0x73, 0x2e, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x6f, 0x70,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x0f, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x4f,
|
||||
0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0f, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c,
|
||||
0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0xbc, 0x01, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65,
|
||||
0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x50, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x6f, 0x2e, 0x63,
|
||||
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65,
|
||||
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69,
|
||||
0x61, 0x6c, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x56,
|
||||
0x61, 0x75, 0x6c, 0x74, 0x53, 0x53, 0x48, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
|
||||
0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72,
|
||||
0x61, 0x72, 0x79, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x45, 0x78,
|
||||
0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x2b, 0xa0,
|
||||
0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x23, 0x0a, 0x15, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75,
|
||||
0x74, 0x65, 0x73, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x0a,
|
||||
0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65,
|
||||
0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x42, 0x0a, 0x14, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63,
|
||||
0x61, 0x6c, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10,
|
||||
0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79,
|
||||
0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3d, 0x0a, 0x0f, 0x45, 0x78,
|
||||
0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
|
||||
0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
|
||||
0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
|
||||
0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x68, 0x5a, 0x66, 0x67, 0x69, 0x74,
|
||||
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72,
|
||||
0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70,
|
||||
0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70,
|
||||
0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x63, 0x72, 0x65, 0x64,
|
||||
0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x3b,
|
||||
0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72,
|
||||
0x69, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -416,32 +657,46 @@ func file_controller_api_resources_credentiallibraries_v1_credential_library_pro
|
||||
return file_controller_api_resources_credentiallibraries_v1_credential_library_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_controller_api_resources_credentiallibraries_v1_credential_library_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_controller_api_resources_credentiallibraries_v1_credential_library_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
|
||||
var file_controller_api_resources_credentiallibraries_v1_credential_library_proto_goTypes = []interface{}{
|
||||
(*CredentialLibrary)(nil), // 0: controller.api.resources.credentiallibraries.v1.CredentialLibrary
|
||||
(*VaultCredentialLibraryAttributes)(nil), // 1: controller.api.resources.credentiallibraries.v1.VaultCredentialLibraryAttributes
|
||||
(*scopes.ScopeInfo)(nil), // 2: controller.api.resources.scopes.v1.ScopeInfo
|
||||
(*wrapperspb.StringValue)(nil), // 3: google.protobuf.StringValue
|
||||
(*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp
|
||||
(*structpb.Struct)(nil), // 5: google.protobuf.Struct
|
||||
(*CredentialLibrary)(nil), // 0: controller.api.resources.credentiallibraries.v1.CredentialLibrary
|
||||
(*VaultCredentialLibraryAttributes)(nil), // 1: controller.api.resources.credentiallibraries.v1.VaultCredentialLibraryAttributes
|
||||
(*VaultSSHCertificateCredentialLibraryAttributes)(nil), // 2: controller.api.resources.credentiallibraries.v1.VaultSSHCertificateCredentialLibraryAttributes
|
||||
nil, // 3: controller.api.resources.credentiallibraries.v1.VaultSSHCertificateCredentialLibraryAttributes.CriticalOptionsEntry
|
||||
nil, // 4: controller.api.resources.credentiallibraries.v1.VaultSSHCertificateCredentialLibraryAttributes.ExtensionsEntry
|
||||
(*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
|
||||
(*wrapperspb.UInt32Value)(nil), // 9: google.protobuf.UInt32Value
|
||||
}
|
||||
var file_controller_api_resources_credentiallibraries_v1_credential_library_proto_depIdxs = []int32{
|
||||
2, // 0: controller.api.resources.credentiallibraries.v1.CredentialLibrary.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
|
||||
3, // 1: controller.api.resources.credentiallibraries.v1.CredentialLibrary.name:type_name -> google.protobuf.StringValue
|
||||
3, // 2: controller.api.resources.credentiallibraries.v1.CredentialLibrary.description:type_name -> google.protobuf.StringValue
|
||||
4, // 3: controller.api.resources.credentiallibraries.v1.CredentialLibrary.created_time:type_name -> google.protobuf.Timestamp
|
||||
4, // 4: controller.api.resources.credentiallibraries.v1.CredentialLibrary.updated_time:type_name -> google.protobuf.Timestamp
|
||||
5, // 5: controller.api.resources.credentiallibraries.v1.CredentialLibrary.attributes:type_name -> google.protobuf.Struct
|
||||
5, // 0: controller.api.resources.credentiallibraries.v1.CredentialLibrary.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
|
||||
6, // 1: controller.api.resources.credentiallibraries.v1.CredentialLibrary.name:type_name -> google.protobuf.StringValue
|
||||
6, // 2: controller.api.resources.credentiallibraries.v1.CredentialLibrary.description:type_name -> google.protobuf.StringValue
|
||||
7, // 3: controller.api.resources.credentiallibraries.v1.CredentialLibrary.created_time:type_name -> google.protobuf.Timestamp
|
||||
7, // 4: controller.api.resources.credentiallibraries.v1.CredentialLibrary.updated_time:type_name -> google.protobuf.Timestamp
|
||||
8, // 5: controller.api.resources.credentiallibraries.v1.CredentialLibrary.attributes:type_name -> google.protobuf.Struct
|
||||
1, // 6: controller.api.resources.credentiallibraries.v1.CredentialLibrary.vault_credential_library_attributes:type_name -> controller.api.resources.credentiallibraries.v1.VaultCredentialLibraryAttributes
|
||||
5, // 7: controller.api.resources.credentiallibraries.v1.CredentialLibrary.credential_mapping_overrides:type_name -> google.protobuf.Struct
|
||||
3, // 8: controller.api.resources.credentiallibraries.v1.VaultCredentialLibraryAttributes.path:type_name -> google.protobuf.StringValue
|
||||
3, // 9: controller.api.resources.credentiallibraries.v1.VaultCredentialLibraryAttributes.http_method:type_name -> google.protobuf.StringValue
|
||||
3, // 10: controller.api.resources.credentiallibraries.v1.VaultCredentialLibraryAttributes.http_request_body:type_name -> google.protobuf.StringValue
|
||||
11, // [11:11] is the sub-list for method output_type
|
||||
11, // [11:11] is the sub-list for method input_type
|
||||
11, // [11:11] is the sub-list for extension type_name
|
||||
11, // [11:11] is the sub-list for extension extendee
|
||||
0, // [0:11] is the sub-list for field type_name
|
||||
2, // 7: controller.api.resources.credentiallibraries.v1.CredentialLibrary.vault_ssh_certificate_credential_library_attributes:type_name -> controller.api.resources.credentiallibraries.v1.VaultSSHCertificateCredentialLibraryAttributes
|
||||
1, // 8: controller.api.resources.credentiallibraries.v1.CredentialLibrary.vault_generic_credential_library_attributes:type_name -> controller.api.resources.credentiallibraries.v1.VaultCredentialLibraryAttributes
|
||||
8, // 9: controller.api.resources.credentiallibraries.v1.CredentialLibrary.credential_mapping_overrides:type_name -> google.protobuf.Struct
|
||||
6, // 10: controller.api.resources.credentiallibraries.v1.VaultCredentialLibraryAttributes.path:type_name -> google.protobuf.StringValue
|
||||
6, // 11: controller.api.resources.credentiallibraries.v1.VaultCredentialLibraryAttributes.http_method:type_name -> google.protobuf.StringValue
|
||||
6, // 12: controller.api.resources.credentiallibraries.v1.VaultCredentialLibraryAttributes.http_request_body:type_name -> google.protobuf.StringValue
|
||||
6, // 13: controller.api.resources.credentiallibraries.v1.VaultSSHCertificateCredentialLibraryAttributes.path:type_name -> google.protobuf.StringValue
|
||||
6, // 14: controller.api.resources.credentiallibraries.v1.VaultSSHCertificateCredentialLibraryAttributes.username:type_name -> google.protobuf.StringValue
|
||||
6, // 15: controller.api.resources.credentiallibraries.v1.VaultSSHCertificateCredentialLibraryAttributes.key_type:type_name -> google.protobuf.StringValue
|
||||
9, // 16: controller.api.resources.credentiallibraries.v1.VaultSSHCertificateCredentialLibraryAttributes.key_bits:type_name -> google.protobuf.UInt32Value
|
||||
6, // 17: controller.api.resources.credentiallibraries.v1.VaultSSHCertificateCredentialLibraryAttributes.ttl:type_name -> google.protobuf.StringValue
|
||||
6, // 18: controller.api.resources.credentiallibraries.v1.VaultSSHCertificateCredentialLibraryAttributes.key_id:type_name -> google.protobuf.StringValue
|
||||
3, // 19: controller.api.resources.credentiallibraries.v1.VaultSSHCertificateCredentialLibraryAttributes.critical_options:type_name -> controller.api.resources.credentiallibraries.v1.VaultSSHCertificateCredentialLibraryAttributes.CriticalOptionsEntry
|
||||
4, // 20: controller.api.resources.credentiallibraries.v1.VaultSSHCertificateCredentialLibraryAttributes.extensions:type_name -> controller.api.resources.credentiallibraries.v1.VaultSSHCertificateCredentialLibraryAttributes.ExtensionsEntry
|
||||
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_credentiallibraries_v1_credential_library_proto_init() }
|
||||
@@ -474,10 +729,24 @@ func file_controller_api_resources_credentiallibraries_v1_credential_library_pro
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_controller_api_resources_credentiallibraries_v1_credential_library_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*VaultSSHCertificateCredentialLibraryAttributes); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
file_controller_api_resources_credentiallibraries_v1_credential_library_proto_msgTypes[0].OneofWrappers = []interface{}{
|
||||
(*CredentialLibrary_Attributes)(nil),
|
||||
(*CredentialLibrary_VaultCredentialLibraryAttributes)(nil),
|
||||
(*CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes)(nil),
|
||||
(*CredentialLibrary_VaultGenericCredentialLibraryAttributes)(nil),
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
@@ -485,7 +754,7 @@ func file_controller_api_resources_credentiallibraries_v1_credential_library_pro
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_controller_api_resources_credentiallibraries_v1_credential_library_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 2,
|
||||
NumMessages: 5,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
||||
@@ -256,7 +256,7 @@ func TestApiVaultCredentialStore(t *testing.T) {
|
||||
|
||||
// Create a credential library for the private key
|
||||
clClient := credentiallibraries.NewClient(client)
|
||||
newCredentialLibraryResult, err := clClient.Create(ctx, newCredentialStoreId,
|
||||
newCredentialLibraryResult, err := clClient.Create(ctx, "vault", newCredentialStoreId,
|
||||
credentiallibraries.WithVaultCredentialLibraryPath(c.VaultSecretPath+"/data/"+privateKeySecretName),
|
||||
credentiallibraries.WithCredentialType("ssh_private_key"),
|
||||
)
|
||||
@@ -265,7 +265,7 @@ func TestApiVaultCredentialStore(t *testing.T) {
|
||||
t.Logf("Created Credential Library: %s", newPrivateKeyCredentialLibraryId)
|
||||
|
||||
// Create a credential library for the password
|
||||
newCredentialLibraryResult, err = clClient.Create(ctx, newCredentialStoreId,
|
||||
newCredentialLibraryResult, err = clClient.Create(ctx, "vault", newCredentialStoreId,
|
||||
credentiallibraries.WithVaultCredentialLibraryPath(c.VaultSecretPath+"/data/"+passwordSecretName),
|
||||
credentiallibraries.WithCredentialType("username_password"),
|
||||
)
|
||||
|
||||
Reference in new issue
Block a user