Merge pull request #6222 from hashicorp/llb-vault-ldap
Vault LDAP Credential Library
This commit is contained in:
81 files changed
+7674
-408
No files matched your search
@@ -74,26 +74,47 @@ jobs:
|
||||
To understand how these diffs are generated and some limitations see the
|
||||
[documentation](https://github.com/hashicorp/boundary/blob/main/scripts/schema-diff.sh) of the script.
|
||||
|
||||
#### Functions
|
||||
$(getdiff .schema-diff/funcs.diff)
|
||||
<details>
|
||||
<summary><b>Functions</b></summary>
|
||||
|
||||
#### Tables
|
||||
$(getdiff .schema-diff/tables.diff)
|
||||
$(getdiff .schema-diff/funcs.diff)
|
||||
</details>
|
||||
|
||||
#### Views
|
||||
$(getdiff .schema-diff/views.diff)
|
||||
<details>
|
||||
<summary><b>Tables</b></summary>
|
||||
|
||||
#### Triggers
|
||||
$(getdiff .schema-diff/triggers.diff)
|
||||
$(getdiff .schema-diff/tables.diff)
|
||||
</details>
|
||||
|
||||
#### Indexes
|
||||
$(getdiff .schema-diff/indexes.diff)
|
||||
<details>
|
||||
<summary><b>Views</b></summary>
|
||||
|
||||
#### Constraints
|
||||
$(getdiff .schema-diff/constraints.diff)
|
||||
$(getdiff .schema-diff/views.diff)
|
||||
</details>
|
||||
|
||||
#### Foreign Key Constraints
|
||||
$(getdiff .schema-diff/fk_constraints.diff)
|
||||
<details>
|
||||
<summary><b>Triggers</b></summary>
|
||||
|
||||
$(getdiff .schema-diff/triggers.diff)
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Indexes</b></summary>
|
||||
|
||||
$(getdiff .schema-diff/indexes.diff)
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Constraints</b></summary>
|
||||
|
||||
$(getdiff .schema-diff/constraints.diff)
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Foreign Key Constraints</b></summary>
|
||||
|
||||
$(getdiff .schema-diff/fk_constraints.diff)
|
||||
</details>
|
||||
EOF
|
||||
|
||||
# Parse it through jq to build a valid json object.
|
||||
|
||||
@@ -383,6 +383,18 @@ func WithVaultCredentialLibraryPath(inPath string) Option {
|
||||
}
|
||||
}
|
||||
|
||||
func WithVaultLdapCredentialLibraryPath(inPath string) Option {
|
||||
return func(o *options) {
|
||||
raw, ok := o.postMap["attributes"]
|
||||
if !ok {
|
||||
raw = any(map[string]any{})
|
||||
}
|
||||
val := raw.(map[string]any)
|
||||
val["path"] = inPath
|
||||
o.postMap["attributes"] = val
|
||||
}
|
||||
}
|
||||
|
||||
func WithVaultSSHCertificateCredentialLibraryPath(inPath string) Option {
|
||||
return func(o *options) {
|
||||
raw, ok := o.postMap["attributes"]
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
// Code generated by "make api"; DO NOT EDIT.
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package credentiallibraries
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
type VaultLdapCredentialLibraryAttributes struct {
|
||||
Path string `json:"path,omitempty"`
|
||||
}
|
||||
|
||||
func AttributesMapToVaultLdapCredentialLibraryAttributes(in map[string]any) (*VaultLdapCredentialLibraryAttributes, error) {
|
||||
if in == nil {
|
||||
return nil, fmt.Errorf("nil input map")
|
||||
}
|
||||
var out VaultLdapCredentialLibraryAttributes
|
||||
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) GetVaultLdapCredentialLibraryAttributes() (*VaultLdapCredentialLibraryAttributes, error) {
|
||||
if pt.Type != "vault-ldap" {
|
||||
return nil, fmt.Errorf("asked to fetch %s-type attributes but credential-library is of type %s", "vault-ldap", pt.Type)
|
||||
}
|
||||
return AttributesMapToVaultLdapCredentialLibraryAttributes(pt.Attributes)
|
||||
}
|
||||
@@ -113,6 +113,17 @@ scenario "e2e_docker_base_with_vault" {
|
||||
}
|
||||
}
|
||||
|
||||
step "create_ldap_server" {
|
||||
module = module.docker_ldap
|
||||
depends_on = [
|
||||
step.create_docker_network
|
||||
]
|
||||
variables {
|
||||
image_name = "${var.docker_mirror}/osixia/openldap:latest"
|
||||
network_name = [local.network_cluster]
|
||||
}
|
||||
}
|
||||
|
||||
step "run_e2e_test" {
|
||||
module = module.test_e2e_docker
|
||||
depends_on = [
|
||||
@@ -141,6 +152,13 @@ scenario "e2e_docker_base_with_vault" {
|
||||
vault_root_token = step.create_vault.token
|
||||
vault_port = step.create_vault.port
|
||||
max_page_size = step.create_boundary.max_page_size
|
||||
ldap_address = step.create_ldap_server.address
|
||||
ldap_domain_dn = step.create_ldap_server.domain_dn
|
||||
ldap_admin_dn = step.create_ldap_server.admin_dn
|
||||
ldap_admin_password = step.create_ldap_server.admin_password
|
||||
ldap_user_name = step.create_ldap_server.user_name
|
||||
ldap_user_password = step.create_ldap_server.user_password
|
||||
ldap_group_name = step.create_ldap_server.group_name
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -71,6 +71,8 @@ const (
|
||||
VaultSshCertificateCredentialLibraryPrefix = "clvsclt"
|
||||
// DynamicCredentialPrefix is the prefix for Vault dynamic credentials
|
||||
VaultDynamicCredentialPrefix = "cdvlt"
|
||||
// VaultLdapCredentialLibraryPrefix is the prefix for Vault Ldap credential libraries
|
||||
VaultLdapCredentialLibraryPrefix = "clvllt"
|
||||
|
||||
// UsernamePasswordCredentialPrefix is the prefix for username/password
|
||||
// creds
|
||||
@@ -243,6 +245,10 @@ var prefixToResourceType = map[string]ResourceInfo{
|
||||
Type: resource.CredentialLibrary,
|
||||
Subtype: UnknownSubtype,
|
||||
},
|
||||
VaultLdapCredentialLibraryPrefix: {
|
||||
Type: resource.CredentialLibrary,
|
||||
Subtype: UnknownSubtype,
|
||||
},
|
||||
VaultDynamicCredentialPrefix: {
|
||||
Type: resource.Credential,
|
||||
Subtype: UnknownSubtype,
|
||||
|
||||
@@ -16,6 +16,7 @@ require (
|
||||
github.com/fatih/structs v1.1.0
|
||||
github.com/favadi/protoc-go-inject-tag v1.4.0
|
||||
github.com/glebarez/sqlite v1.11.0
|
||||
github.com/go-ldap/ldap/v3 v3.4.11
|
||||
github.com/golang-migrate/migrate/v4 v4.19.0
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9
|
||||
github.com/golang/protobuf v1.5.4
|
||||
@@ -139,10 +140,9 @@ require (
|
||||
github.com/dvsekhvalnov/jose2go v1.6.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/glebarez/go-sqlite v1.22.0 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.4 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.1.2 // indirect
|
||||
github.com/go-ldap/ldap/v3 v3.4.8 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
|
||||
@@ -128,16 +128,15 @@ github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec
|
||||
github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
|
||||
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
|
||||
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.7 h1:DTX+lbVTWaTw1hQ+PbZPlnDZPEIs0SS/GCZAl535dDk=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.7/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY=
|
||||
github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
|
||||
github.com/go-jose/go-jose/v4 v4.1.2 h1:TK/7NqRQZfgAh+Td8AlsrvtPoUyiHh0LqVvokh+1vHI=
|
||||
github.com/go-jose/go-jose/v4 v4.1.2/go.mod h1:22cg9HWM1pOlnRiY+9cQYJ9XHmya1bYW8OeDM6Ku6Oo=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-ldap/ldap/v3 v3.4.8 h1:loKJyspcRezt2Q3ZRMq2p/0v8iOurlmeXDPw6fikSvQ=
|
||||
github.com/go-ldap/ldap/v3 v3.4.8/go.mod h1:qS3Sjlu76eHfHGpUdWkAXQTw4beih+cHsco2jXlIXrk=
|
||||
github.com/go-ldap/ldap/v3 v3.4.11 h1:4k0Yxweg+a3OyBLjdYn5OKglv18JNvfDykSoI8bW0gU=
|
||||
github.com/go-ldap/ldap/v3 v3.4.11/go.mod h1:bY7t0FLK8OAVpp/vV6sSlpz3EQDGcQwc8pF0ujLgKvM=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
@@ -184,8 +183,6 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg=
|
||||
@@ -494,8 +491,6 @@ github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
@@ -504,10 +499,7 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0=
|
||||
@@ -577,9 +569,7 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
|
||||
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -603,7 +593,6 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
@@ -612,10 +601,7 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
|
||||
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@@ -656,7 +642,6 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8 h1:LvzTn0GQhWuvKH/kVRS3R3bVAsdQWI7hvfLHGgh9+lU=
|
||||
@@ -667,7 +652,6 @@ golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=
|
||||
golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
||||
@@ -641,6 +641,22 @@ var inputStructs = []*structInfo{
|
||||
mapstructureConversionTemplate,
|
||||
},
|
||||
},
|
||||
{
|
||||
inProto: &credentiallibraries.VaultLdapCredentialLibraryAttributes{},
|
||||
outFile: "credentiallibraries/vault_ldap_credential_library_attributes.gen.go",
|
||||
subtypeName: "VaultLdapCredentialLibrary",
|
||||
subtype: "vault-ldap",
|
||||
fieldOverrides: []fieldInfo{
|
||||
{
|
||||
Name: "Path",
|
||||
SkipDefault: true,
|
||||
},
|
||||
},
|
||||
parentTypeName: "CredentialLibrary",
|
||||
templates: []*template.Template{
|
||||
mapstructureConversionTemplate,
|
||||
},
|
||||
},
|
||||
{
|
||||
inProto: &credentiallibraries.CredentialLibrary{},
|
||||
outFile: "credentiallibraries/credential_library.gen.go",
|
||||
|
||||
@@ -495,6 +495,12 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
||||
Func: "create",
|
||||
}
|
||||
}),
|
||||
"credential-libraries create vault-ldap": wrapper.Wrap(func() wrapper.WrappableCommand {
|
||||
return &credentiallibrariescmd.VaultLdapCommand{
|
||||
Command: base.NewCommand(ui, opts...),
|
||||
Func: "create",
|
||||
}
|
||||
}),
|
||||
"credential-libraries update": wrapper.Wrap(func() wrapper.WrappableCommand {
|
||||
return &credentiallibrariescmd.Command{
|
||||
Command: base.NewCommand(ui, opts...),
|
||||
@@ -519,6 +525,12 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
||||
Func: "update",
|
||||
}
|
||||
}),
|
||||
"credential-libraries update vault-ldap": wrapper.Wrap(func() wrapper.WrappableCommand {
|
||||
return &credentiallibrariescmd.VaultLdapCommand{
|
||||
Command: base.NewCommand(ui, opts...),
|
||||
Func: "update",
|
||||
}
|
||||
}),
|
||||
|
||||
"credential-stores": func() (cli.Command, error) {
|
||||
return &credentialstorescmd.Command{
|
||||
|
||||
@@ -151,6 +151,8 @@ func printItemTable(item *credentiallibraries.CredentialLibrary, resp *api.Respo
|
||||
keySubstMap = genericKeySubstMap
|
||||
case "vault-ssh-certificate":
|
||||
keySubstMap = sshCertKeySubstMap
|
||||
case "vault-ldap":
|
||||
keySubstMap = ldapKeySubstMap
|
||||
}
|
||||
|
||||
maxLength := base.MaxAttributesLength(nonAttributeMap, item.Attributes, keySubstMap)
|
||||
@@ -218,3 +220,7 @@ var sshCertKeySubstMap = map[string]string{
|
||||
"critical_options": "Critical Options",
|
||||
"extensions": "Extensions",
|
||||
}
|
||||
|
||||
var ldapKeySubstMap = map[string]string{
|
||||
"path": "Path",
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
// Code generated by "make cli"; DO NOT EDIT.
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
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 initVaultLdapFlags() {
|
||||
flagsOnce.Do(func() {
|
||||
extraFlags := extraVaultLdapActionsFlagsMapFunc()
|
||||
for k, v := range extraFlags {
|
||||
flagsVaultLdapMap[k] = append(flagsVaultLdapMap[k], v...)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
var (
|
||||
_ cli.Command = (*VaultLdapCommand)(nil)
|
||||
_ cli.CommandAutocomplete = (*VaultLdapCommand)(nil)
|
||||
)
|
||||
|
||||
type VaultLdapCommand struct {
|
||||
*base.Command
|
||||
|
||||
Func string
|
||||
|
||||
plural string
|
||||
|
||||
extraVaultLdapCmdVars
|
||||
}
|
||||
|
||||
func (c *VaultLdapCommand) AutocompleteArgs() complete.Predictor {
|
||||
initVaultLdapFlags()
|
||||
return complete.PredictAnything
|
||||
}
|
||||
|
||||
func (c *VaultLdapCommand) AutocompleteFlags() complete.Flags {
|
||||
initVaultLdapFlags()
|
||||
return c.Flags().Completions()
|
||||
}
|
||||
|
||||
func (c *VaultLdapCommand) Synopsis() string {
|
||||
if extra := extraVaultLdapSynopsisFunc(c); extra != "" {
|
||||
return extra
|
||||
}
|
||||
|
||||
synopsisStr := "credential-library"
|
||||
|
||||
synopsisStr = fmt.Sprintf("%s %s", "vault-ldap-type", synopsisStr)
|
||||
|
||||
return common.SynopsisFunc(c.Func, synopsisStr)
|
||||
}
|
||||
|
||||
func (c *VaultLdapCommand) Help() string {
|
||||
initVaultLdapFlags()
|
||||
|
||||
var helpStr string
|
||||
helpMap := common.HelpMap("credential library")
|
||||
|
||||
switch c.Func {
|
||||
|
||||
default:
|
||||
|
||||
helpStr = c.extraVaultLdapHelpFunc(helpMap)
|
||||
|
||||
}
|
||||
|
||||
// Keep linter from complaining if we don't actually generate code using it
|
||||
_ = helpMap
|
||||
return helpStr
|
||||
}
|
||||
|
||||
var flagsVaultLdapMap = map[string][]string{
|
||||
|
||||
"create": {"credential-store-id", "name", "description"},
|
||||
|
||||
"update": {"id", "name", "description", "version"},
|
||||
}
|
||||
|
||||
func (c *VaultLdapCommand) Flags() *base.FlagSets {
|
||||
if len(flagsVaultLdapMap[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-ldap-type credential library", flagsVaultLdapMap, c.Func)
|
||||
|
||||
extraVaultLdapFlagsFunc(c, set, f)
|
||||
|
||||
return set
|
||||
}
|
||||
|
||||
func (c *VaultLdapCommand) Run(args []string) int {
|
||||
initVaultLdapFlags()
|
||||
|
||||
switch c.Func {
|
||||
case "":
|
||||
return cli.RunResultHelp
|
||||
|
||||
}
|
||||
|
||||
c.plural = "vault-ldap-type credential library"
|
||||
switch c.Func {
|
||||
case "list":
|
||||
c.plural = "vault-ldap-type credential libraries"
|
||||
}
|
||||
|
||||
f := c.Flags()
|
||||
|
||||
if err := f.Parse(args); err != nil {
|
||||
c.PrintCliError(err)
|
||||
return base.CommandUserError
|
||||
}
|
||||
|
||||
if strutil.StrListContains(flagsVaultLdapMap[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(flagsVaultLdapMap[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 := extraVaultLdapFlagsHandlingFunc(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-ldap", 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 = executeExtraVaultLdapActions(c, resp, item, err, credentiallibrariesClient, version, opts)
|
||||
if exitCode := c.checkFuncError(err); exitCode > 0 {
|
||||
return exitCode
|
||||
}
|
||||
|
||||
output, err := printCustomVaultLdapActionOutput(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 *VaultLdapCommand) 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 (
|
||||
extraVaultLdapActionsFlagsMapFunc = func() map[string][]string { return nil }
|
||||
extraVaultLdapSynopsisFunc = func(*VaultLdapCommand) string { return "" }
|
||||
extraVaultLdapFlagsFunc = func(*VaultLdapCommand, *base.FlagSets, *base.FlagSet) {}
|
||||
extraVaultLdapFlagsHandlingFunc = func(*VaultLdapCommand, *base.FlagSets, *[]credentiallibraries.Option) bool { return true }
|
||||
executeExtraVaultLdapActions = func(_ *VaultLdapCommand, inResp *api.Response, inItem *credentiallibraries.CredentialLibrary, inErr error, _ *credentiallibraries.Client, _ uint32, _ []credentiallibraries.Option) (*api.Response, *credentiallibraries.CredentialLibrary, error) {
|
||||
return inResp, inItem, inErr
|
||||
}
|
||||
printCustomVaultLdapActionOutput = func(*VaultLdapCommand) (bool, error) { return false, nil }
|
||||
)
|
||||
@@ -0,0 +1,83 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package credentiallibrariescmd
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/boundary/api/credentiallibraries"
|
||||
"github.com/hashicorp/boundary/internal/cmd/base"
|
||||
)
|
||||
|
||||
func init() {
|
||||
extraVaultLdapActionsFlagsMapFunc = extraVaultLdapActionsFlagsMapFuncImpl
|
||||
extraVaultLdapFlagsFunc = extraVaultLdapFlagsFuncImpl
|
||||
extraVaultLdapFlagsHandlingFunc = extraVaultLdapFlagHandlingFuncImpl
|
||||
}
|
||||
|
||||
type extraVaultLdapCmdVars struct {
|
||||
flagPath string
|
||||
}
|
||||
|
||||
func extraVaultLdapActionsFlagsMapFuncImpl() map[string][]string {
|
||||
flags := map[string][]string{
|
||||
"create": {
|
||||
pathFlagName,
|
||||
},
|
||||
"update": {
|
||||
pathFlagName,
|
||||
},
|
||||
}
|
||||
return flags
|
||||
}
|
||||
|
||||
func extraVaultLdapFlagsFuncImpl(c *VaultLdapCommand, set *base.FlagSets, _ *base.FlagSet) {
|
||||
f := set.NewFlagSet("Vault Ldap Credential Library Options")
|
||||
|
||||
for _, name := range flagsVaultLdapMap[c.Func] {
|
||||
switch name {
|
||||
case pathFlagName:
|
||||
f.StringVar(&base.StringVar{
|
||||
Name: pathFlagName,
|
||||
Target: &c.flagPath,
|
||||
Usage: "The path in vault to request credentials from.",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func extraVaultLdapFlagHandlingFuncImpl(c *VaultLdapCommand, _ *base.FlagSets, opts *[]credentiallibraries.Option) bool {
|
||||
switch c.flagPath {
|
||||
case "":
|
||||
default:
|
||||
*opts = append(*opts, credentiallibraries.WithVaultLdapCredentialLibraryPath(c.flagPath))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *VaultLdapCommand) extraVaultLdapHelpFunc(_ map[string]func() string) string {
|
||||
var helpStr string
|
||||
switch c.Func {
|
||||
case "create":
|
||||
helpStr = base.WrapForHelpText([]string{
|
||||
"Usage: boundary credential-libraries create vault-ldap -credential-store-id [options] [args]",
|
||||
"",
|
||||
" Create a vault-ldap-type credential library. Example:",
|
||||
"",
|
||||
` $ boundary credential-libraries create vault-ldap -credential-store-id csvlt_1234567890 -vault-path "/ldap/static-cred/einstein"`,
|
||||
"",
|
||||
"",
|
||||
})
|
||||
|
||||
case "update":
|
||||
helpStr = base.WrapForHelpText([]string{
|
||||
"Usage: boundary credential-libraries update vault-ldap [options] [args]",
|
||||
"",
|
||||
" Update a vault-ldap-type credential library given its ID. Example:",
|
||||
"",
|
||||
` $ boundary credential-libraries update vault-ldap -id clvllt_1234567890 -name devops -description "For DevOps usage"`,
|
||||
"",
|
||||
"",
|
||||
})
|
||||
}
|
||||
return helpStr + c.Flags().Help()
|
||||
}
|
||||
@@ -357,6 +357,22 @@ var inputStructs = map[string][]*cmdInfo{
|
||||
VersionedActions: []string{"update"},
|
||||
PrefixAttributeFieldErrorsWithSubactionPrefix: true,
|
||||
},
|
||||
{
|
||||
ResourceType: resource.CredentialLibrary.String(),
|
||||
Pkg: "credentiallibraries",
|
||||
StdActions: []string{"create", "update"},
|
||||
SubActionPrefix: "vault-ldap",
|
||||
HasExtraCommandVars: true,
|
||||
SkipNormalHelp: true,
|
||||
HasExtraHelpFunc: true,
|
||||
HasId: true,
|
||||
HasName: true,
|
||||
HasDescription: true,
|
||||
NeedsSubtypeInCreate: true,
|
||||
Container: "CredentialStore",
|
||||
VersionedActions: []string{"update"},
|
||||
PrefixAttributeFieldErrorsWithSubactionPrefix: true,
|
||||
},
|
||||
},
|
||||
"credentials": {
|
||||
{
|
||||
|
||||
@@ -48,8 +48,10 @@ func TestLibraryService_List(t *testing.T) {
|
||||
vault.TestCredentialLibraries(t, conn, wrapper, credStore.GetPublicId(), globals.UnspecifiedCredentialType, 1)[0],
|
||||
vault.TestSSHCertificateCredentialLibraries(t, conn, wrapper, credStore.GetPublicId(), 1)[0],
|
||||
vault.TestCredentialLibraries(t, conn, wrapper, credStore.GetPublicId(), globals.UnspecifiedCredentialType, 1)[0],
|
||||
vault.TestLdapCredentialLibraries(t, conn, wrapper, credStore.GetPublicId(), 1)[0],
|
||||
vault.TestSSHCertificateCredentialLibraries(t, conn, wrapper, credStore.GetPublicId(), 1)[0],
|
||||
vault.TestSSHCertificateCredentialLibraries(t, conn, wrapper, credStore.GetPublicId(), 1)[0],
|
||||
vault.TestLdapCredentialLibraries(t, conn, wrapper, credStore.GetPublicId(), 1)[0],
|
||||
}
|
||||
|
||||
// since we sort by create time descending, we need to reverse the slice
|
||||
@@ -66,7 +68,9 @@ func TestLibraryService_List(t *testing.T) {
|
||||
cmpopts.IgnoreUnexported(
|
||||
vault.CredentialLibrary{},
|
||||
vault.SSHCertificateCredentialLibrary{},
|
||||
vault.LdapCredentialLibrary{},
|
||||
store.SSHCertificateCredentialLibrary{},
|
||||
store.LdapCredentialLibrary{},
|
||||
store.CredentialLibrary{},
|
||||
timestamp.Timestamp{},
|
||||
timestamppb.Timestamp{},
|
||||
@@ -385,7 +389,7 @@ func TestLibraryService_List(t *testing.T) {
|
||||
require.NotNil(t, resp.ListToken)
|
||||
require.Equal(t, resp.ListToken.GrantsHash, []byte("some hash"))
|
||||
require.False(t, resp.CompleteListing)
|
||||
require.Equal(t, resp.EstimatedItemCount, 5)
|
||||
require.Equal(t, resp.EstimatedItemCount, 7)
|
||||
require.Empty(t, resp.DeletedIds)
|
||||
require.Len(t, resp.Items, 1)
|
||||
require.Empty(t, cmp.Diff(resp.Items[0], libs[0], cmpOpts...))
|
||||
@@ -394,7 +398,7 @@ func TestLibraryService_List(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, resp2.ListToken.GrantsHash, []byte("some hash"))
|
||||
require.False(t, resp2.CompleteListing)
|
||||
require.Equal(t, resp2.EstimatedItemCount, 5)
|
||||
require.Equal(t, resp2.EstimatedItemCount, 7)
|
||||
require.Empty(t, resp2.DeletedIds)
|
||||
require.Len(t, resp2.Items, 1)
|
||||
require.Empty(t, cmp.Diff(resp2.Items[0], libs[1], cmpOpts...))
|
||||
@@ -403,7 +407,7 @@ func TestLibraryService_List(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, resp3.ListToken.GrantsHash, []byte("some hash"))
|
||||
require.False(t, resp3.CompleteListing)
|
||||
require.Equal(t, resp3.EstimatedItemCount, 5)
|
||||
require.Equal(t, resp3.EstimatedItemCount, 7)
|
||||
require.Empty(t, resp3.DeletedIds)
|
||||
require.Len(t, resp3.Items, 1)
|
||||
require.Empty(t, cmp.Diff(resp3.Items[0], libs[2], cmpOpts...))
|
||||
@@ -412,7 +416,7 @@ func TestLibraryService_List(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, resp4.ListToken.GrantsHash, []byte("some hash"))
|
||||
require.False(t, resp4.CompleteListing)
|
||||
require.Equal(t, resp4.EstimatedItemCount, 5)
|
||||
require.Equal(t, resp4.EstimatedItemCount, 7)
|
||||
require.Empty(t, resp4.DeletedIds)
|
||||
require.Len(t, resp4.Items, 1)
|
||||
require.Empty(t, cmp.Diff(resp4.Items[0], libs[3], cmpOpts...))
|
||||
@@ -420,21 +424,39 @@ func TestLibraryService_List(t *testing.T) {
|
||||
resp5, err := credential.ListLibrariesPage(ctx, []byte("some hash"), 1, filterFunc, resp4.ListToken, repo, credStore.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, resp5.ListToken.GrantsHash, []byte("some hash"))
|
||||
require.True(t, resp5.CompleteListing)
|
||||
require.Equal(t, resp5.EstimatedItemCount, 5)
|
||||
require.False(t, resp5.CompleteListing)
|
||||
require.Equal(t, resp5.EstimatedItemCount, 7)
|
||||
require.Empty(t, resp5.DeletedIds)
|
||||
require.Len(t, resp5.Items, 1)
|
||||
require.Empty(t, cmp.Diff(resp5.Items[0], libs[4], cmpOpts...))
|
||||
|
||||
// Finished initial pagination phase, request refresh
|
||||
// Expect no results.
|
||||
resp6, err := credential.ListLibrariesRefresh(ctx, []byte("some hash"), 1, filterFunc, resp5.ListToken, repo, credStore.GetPublicId())
|
||||
resp6, err := credential.ListLibrariesPage(ctx, []byte("some hash"), 1, filterFunc, resp4.ListToken, repo, credStore.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, resp6.ListToken.GrantsHash, []byte("some hash"))
|
||||
require.True(t, resp6.CompleteListing)
|
||||
require.Equal(t, resp6.EstimatedItemCount, 5)
|
||||
require.False(t, resp6.CompleteListing)
|
||||
require.Equal(t, resp6.EstimatedItemCount, 7)
|
||||
require.Empty(t, resp6.DeletedIds)
|
||||
require.Empty(t, resp6.Items)
|
||||
require.Len(t, resp6.Items, 1)
|
||||
require.Empty(t, cmp.Diff(resp6.Items[0], libs[5], cmpOpts...))
|
||||
|
||||
resp7, err := credential.ListLibrariesPage(ctx, []byte("some hash"), 1, filterFunc, resp4.ListToken, repo, credStore.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, resp7.ListToken.GrantsHash, []byte("some hash"))
|
||||
require.True(t, resp7.CompleteListing)
|
||||
require.Equal(t, resp7.EstimatedItemCount, 7)
|
||||
require.Empty(t, resp7.DeletedIds)
|
||||
require.Len(t, resp7.Items, 1)
|
||||
require.Empty(t, cmp.Diff(resp7.Items[0], libs[6], cmpOpts...))
|
||||
|
||||
// Finished initial pagination phase, request refresh
|
||||
// Expect no results.
|
||||
resp8, err := credential.ListLibrariesRefresh(ctx, []byte("some hash"), 1, filterFunc, resp5.ListToken, repo, credStore.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, resp8.ListToken.GrantsHash, []byte("some hash"))
|
||||
require.True(t, resp8.CompleteListing)
|
||||
require.Equal(t, resp8.EstimatedItemCount, 7)
|
||||
require.Empty(t, resp8.DeletedIds)
|
||||
require.Empty(t, resp8.Items)
|
||||
|
||||
// Create some new libraries
|
||||
newLib1 := vault.TestCredentialLibraries(t, conn, wrapper, credStore.GetPublicId(), globals.UnspecifiedCredentialType, 1)[0]
|
||||
@@ -453,33 +475,33 @@ func TestLibraryService_List(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Refresh again, should get newLib2
|
||||
resp7, err := credential.ListLibrariesRefresh(ctx, []byte("some hash"), 1, filterFunc, resp6.ListToken, repo, credStore.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, resp7.ListToken.GrantsHash, []byte("some hash"))
|
||||
require.False(t, resp7.CompleteListing)
|
||||
require.Equal(t, resp7.EstimatedItemCount, 7)
|
||||
require.Empty(t, resp7.DeletedIds)
|
||||
require.Len(t, resp7.Items, 1)
|
||||
require.Empty(t, cmp.Diff(resp7.Items[0], newLib2, cmpOpts...))
|
||||
|
||||
// Refresh again, should get newLib1
|
||||
resp8, err := credential.ListLibrariesRefreshPage(ctx, []byte("some hash"), 1, filterFunc, resp7.ListToken, repo, credStore.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, resp8.ListToken.GrantsHash, []byte("some hash"))
|
||||
require.True(t, resp8.CompleteListing)
|
||||
require.Equal(t, resp8.EstimatedItemCount, 7)
|
||||
require.Empty(t, resp8.DeletedIds)
|
||||
require.Len(t, resp8.Items, 1)
|
||||
require.Empty(t, cmp.Diff(resp8.Items[0], newLib1, cmpOpts...))
|
||||
|
||||
// Refresh again, should get no results
|
||||
resp9, err := credential.ListLibrariesRefresh(ctx, []byte("some hash"), 1, filterFunc, resp8.ListToken, repo, credStore.GetPublicId())
|
||||
resp9, err := credential.ListLibrariesRefresh(ctx, []byte("some hash"), 1, filterFunc, resp6.ListToken, repo, credStore.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, resp9.ListToken.GrantsHash, []byte("some hash"))
|
||||
require.True(t, resp9.CompleteListing)
|
||||
require.Equal(t, resp9.EstimatedItemCount, 7)
|
||||
require.False(t, resp9.CompleteListing)
|
||||
require.Equal(t, resp9.EstimatedItemCount, 9)
|
||||
require.Empty(t, resp9.DeletedIds)
|
||||
require.Empty(t, resp9.Items)
|
||||
require.Len(t, resp9.Items, 1)
|
||||
require.Empty(t, cmp.Diff(resp9.Items[0], newLib2, cmpOpts...))
|
||||
|
||||
// Refresh again, should get newLib1
|
||||
resp10, err := credential.ListLibrariesRefreshPage(ctx, []byte("some hash"), 1, filterFunc, resp7.ListToken, repo, credStore.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, resp10.ListToken.GrantsHash, []byte("some hash"))
|
||||
require.True(t, resp10.CompleteListing)
|
||||
require.Equal(t, resp10.EstimatedItemCount, 9)
|
||||
require.Empty(t, resp10.DeletedIds)
|
||||
require.Len(t, resp10.Items, 1)
|
||||
require.Empty(t, cmp.Diff(resp10.Items[0], newLib1, cmpOpts...))
|
||||
|
||||
// Refresh again, should get no results
|
||||
resp11, err := credential.ListLibrariesRefresh(ctx, []byte("some hash"), 1, filterFunc, resp8.ListToken, repo, credStore.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, resp11.ListToken.GrantsHash, []byte("some hash"))
|
||||
require.True(t, resp11.CompleteListing)
|
||||
require.Equal(t, resp11.EstimatedItemCount, 9)
|
||||
require.Empty(t, resp11.DeletedIds)
|
||||
require.Empty(t, resp11.Items)
|
||||
})
|
||||
|
||||
t.Run("simple pagination with aggressive filtering", func(t *testing.T) {
|
||||
@@ -492,7 +514,7 @@ func TestLibraryService_List(t *testing.T) {
|
||||
require.NotNil(t, resp.ListToken)
|
||||
require.Equal(t, resp.ListToken.GrantsHash, []byte("some hash"))
|
||||
require.False(t, resp.CompleteListing)
|
||||
require.Equal(t, resp.EstimatedItemCount, 5)
|
||||
require.Equal(t, resp.EstimatedItemCount, 7)
|
||||
require.Empty(t, resp.DeletedIds)
|
||||
require.Len(t, resp.Items, 1)
|
||||
require.Empty(t, cmp.Diff(resp.Items[0], libs[1], cmpOpts...))
|
||||
@@ -502,7 +524,7 @@ func TestLibraryService_List(t *testing.T) {
|
||||
require.NotNil(t, resp2.ListToken)
|
||||
require.Equal(t, resp2.ListToken.GrantsHash, []byte("some hash"))
|
||||
require.True(t, resp2.CompleteListing)
|
||||
require.Equal(t, resp2.EstimatedItemCount, 5)
|
||||
require.Equal(t, resp2.EstimatedItemCount, 7)
|
||||
require.Empty(t, resp2.DeletedIds)
|
||||
require.Len(t, resp2.Items, 1)
|
||||
require.Empty(t, cmp.Diff(resp2.Items[0], libs[len(libs)-1], cmpOpts...))
|
||||
@@ -512,7 +534,7 @@ func TestLibraryService_List(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, resp3.ListToken.GrantsHash, []byte("some hash"))
|
||||
require.True(t, resp3.CompleteListing)
|
||||
require.Equal(t, resp3.EstimatedItemCount, 5)
|
||||
require.Equal(t, resp3.EstimatedItemCount, 7)
|
||||
require.Empty(t, resp3.DeletedIds)
|
||||
require.Empty(t, resp3.Items)
|
||||
|
||||
@@ -545,7 +567,7 @@ func TestLibraryService_List(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, resp4.ListToken.GrantsHash, []byte("some hash"))
|
||||
require.False(t, resp4.CompleteListing)
|
||||
require.Equal(t, resp4.EstimatedItemCount, 9)
|
||||
require.Equal(t, resp4.EstimatedItemCount, 11)
|
||||
require.Empty(t, resp4.DeletedIds)
|
||||
require.Len(t, resp4.Items, 1)
|
||||
require.Empty(t, cmp.Diff(resp4.Items[0], newLibs2[0], cmpOpts...))
|
||||
@@ -555,7 +577,7 @@ func TestLibraryService_List(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, resp5.ListToken.GrantsHash, []byte("some hash"))
|
||||
require.True(t, resp5.CompleteListing)
|
||||
require.Equal(t, resp5.EstimatedItemCount, 9)
|
||||
require.Equal(t, resp5.EstimatedItemCount, 11)
|
||||
require.Empty(t, resp5.DeletedIds)
|
||||
require.Len(t, resp5.Items, 1)
|
||||
require.Empty(t, cmp.Diff(resp5.Items[0], newLibs1[0], cmpOpts...))
|
||||
@@ -566,7 +588,7 @@ func TestLibraryService_List(t *testing.T) {
|
||||
return true, nil
|
||||
}
|
||||
deletedCredentialLibraryId := libs[0].GetPublicId()
|
||||
_, err := repo.DeleteSSHCertificateCredentialLibrary(ctx, prj.GetPublicId(), deletedCredentialLibraryId)
|
||||
_, err := repo.DeleteLdapCredentialLibrary(ctx, prj.GetPublicId(), deletedCredentialLibraryId)
|
||||
require.NoError(t, err)
|
||||
libs = libs[1:]
|
||||
|
||||
@@ -579,19 +601,19 @@ func TestLibraryService_List(t *testing.T) {
|
||||
require.NotNil(t, resp.ListToken)
|
||||
require.Equal(t, resp.ListToken.GrantsHash, []byte("some hash"))
|
||||
require.False(t, resp.CompleteListing)
|
||||
require.Equal(t, resp.EstimatedItemCount, 4)
|
||||
require.Equal(t, resp.EstimatedItemCount, 6)
|
||||
require.Empty(t, resp.DeletedIds)
|
||||
require.Len(t, resp.Items, 1)
|
||||
require.Empty(t, cmp.Diff(resp.Items[0], libs[0], cmpOpts...))
|
||||
|
||||
// request remaining results
|
||||
resp2, err := credential.ListLibrariesPage(ctx, []byte("some hash"), 3, filterFunc, resp.ListToken, repo, credStore.GetPublicId())
|
||||
resp2, err := credential.ListLibrariesPage(ctx, []byte("some hash"), 5, filterFunc, resp.ListToken, repo, credStore.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, resp2.ListToken.GrantsHash, []byte("some hash"))
|
||||
require.True(t, resp2.CompleteListing)
|
||||
require.Equal(t, resp2.EstimatedItemCount, 4)
|
||||
require.Equal(t, resp2.EstimatedItemCount, 6)
|
||||
require.Empty(t, resp2.DeletedIds)
|
||||
require.Len(t, resp2.Items, 3)
|
||||
require.Len(t, resp2.Items, 5)
|
||||
require.Empty(t, cmp.Diff(resp2.Items, libs[1:], cmpOpts...))
|
||||
|
||||
deletedCredentialLibraryId = libs[0].GetPublicId()
|
||||
@@ -608,7 +630,7 @@ func TestLibraryService_List(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, resp3.ListToken.GrantsHash, []byte("some hash"))
|
||||
require.True(t, resp3.CompleteListing)
|
||||
require.Equal(t, resp3.EstimatedItemCount, 3)
|
||||
require.Equal(t, resp3.EstimatedItemCount, 5)
|
||||
require.Contains(t, resp3.DeletedIds, deletedCredentialLibraryId)
|
||||
require.Empty(t, resp3.Items)
|
||||
})
|
||||
|
||||
@@ -98,7 +98,7 @@ func (l *CredentialLibrary) TableName() string {
|
||||
if l.tableName != "" {
|
||||
return l.tableName
|
||||
}
|
||||
return "credential_vault_library"
|
||||
return "credential_vault_generic_library"
|
||||
}
|
||||
|
||||
// SetTableName sets the table name.
|
||||
@@ -206,6 +206,20 @@ func (l *listCredentialLibraryResult) toLibrary(ctx context.Context) (credential
|
||||
AdditionalValidPrincipals: l.AdditionalValidPrincipals,
|
||||
},
|
||||
}, nil
|
||||
case "ldap":
|
||||
return &LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{
|
||||
PublicId: l.PublicId,
|
||||
StoreId: l.StoreId,
|
||||
Name: l.Name,
|
||||
Description: l.Description,
|
||||
CreateTime: l.CreateTime,
|
||||
UpdateTime: l.UpdateTime,
|
||||
Version: uint32(l.Version),
|
||||
VaultPath: l.VaultPath,
|
||||
CredentialType: l.CredentialType,
|
||||
},
|
||||
}, nil
|
||||
default:
|
||||
return nil, errors.New(ctx, errors.Internal, op, fmt.Sprintf("unexpected vault credential library type %s returned", l.Type))
|
||||
}
|
||||
@@ -218,5 +232,5 @@ type deletedCredentialLibrary struct {
|
||||
|
||||
// TableName returns the tablename to override the default gorm table name
|
||||
func (s *deletedCredentialLibrary) TableName() string {
|
||||
return "credential_vault_library_deleted"
|
||||
return "credential_vault_generic_library_deleted"
|
||||
}
|
||||
@@ -6,8 +6,9 @@ package vault
|
||||
import "testing"
|
||||
|
||||
var (
|
||||
newVaultServer func(t testing.TB, opt ...TestOption) *TestVaultServer = skipNewServer
|
||||
mountDatabase func(t testing.TB, v *TestVaultServer, opt ...TestOption) *TestDatabase = skipMountDatabase
|
||||
newVaultServer func(t testing.TB, opt ...TestOption) *TestVaultServer = skipNewServer
|
||||
mountLdapServer func(t testing.TB, v *TestVaultServer, opt ...TestOption) *TestLdapServer = skipMountLdapServer
|
||||
mountDatabase func(t testing.TB, v *TestVaultServer, opt ...TestOption) *TestDatabase = skipMountDatabase
|
||||
)
|
||||
|
||||
func skipNewServer(t testing.TB, opt ...TestOption) *TestVaultServer {
|
||||
@@ -19,3 +20,8 @@ func skipMountDatabase(t testing.TB, v *TestVaultServer, opt ...TestOption) *Tes
|
||||
t.Skip("docker not available")
|
||||
return nil
|
||||
}
|
||||
|
||||
func skipMountLdapServer(t testing.TB, v *TestVaultServer, opt ...TestOption) *TestLdapServer {
|
||||
t.Skip("docker not available")
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package vault
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/credential"
|
||||
"github.com/hashicorp/boundary/internal/credential/vault/store"
|
||||
"github.com/hashicorp/boundary/internal/db/timestamp"
|
||||
"github.com/hashicorp/boundary/internal/oplog"
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
var _ credential.Library = (*LdapCredentialLibrary)(nil)
|
||||
|
||||
// LdapCredentialLibrary is a credential library that issues ldap credentials
|
||||
// using the vault ldap secret engine. This credential library always issues
|
||||
// username/password/domain credentials.
|
||||
type LdapCredentialLibrary struct {
|
||||
*store.LdapCredentialLibrary
|
||||
tableName string `gorm:"-"`
|
||||
}
|
||||
|
||||
// NewLdapCredentialLibrary creates a new in memory LdapCredentialLibrary for a
|
||||
// Vault backend at vaultPath assigned to storeId. This credential library
|
||||
// always issues username/password/domain credentials. WithName and
|
||||
// WithDescription are the only valid options. All other options are ignored.
|
||||
func NewLdapCredentialLibrary(storeId string, vaultPath string, opt ...Option) (*LdapCredentialLibrary, error) {
|
||||
const op = "vault.NewLdapCredentialLibrary"
|
||||
opts := getOpts(opt...)
|
||||
|
||||
l := &LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{
|
||||
StoreId: storeId,
|
||||
Name: opts.withName,
|
||||
Description: opts.withDescription,
|
||||
VaultPath: vaultPath,
|
||||
CredentialType: string(globals.UsernamePasswordDomainCredentialType),
|
||||
},
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func allocLdapCredentialLibrary() *LdapCredentialLibrary {
|
||||
return &LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{},
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LdapCredentialLibrary) clone() *LdapCredentialLibrary {
|
||||
cp := proto.Clone(l.LdapCredentialLibrary)
|
||||
return &LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: cp.(*store.LdapCredentialLibrary),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LdapCredentialLibrary) setId(i string) {
|
||||
l.PublicId = i
|
||||
}
|
||||
|
||||
// TableName returns the table name.
|
||||
func (l *LdapCredentialLibrary) TableName() string {
|
||||
if l.tableName != "" {
|
||||
return l.tableName
|
||||
}
|
||||
return "credential_vault_ldap_library"
|
||||
}
|
||||
|
||||
// SetTableName sets the table name.
|
||||
func (l *LdapCredentialLibrary) SetTableName(n string) {
|
||||
l.tableName = n
|
||||
}
|
||||
|
||||
// GetResourceType returns the resource type of the CredentialLibrary
|
||||
func (l *LdapCredentialLibrary) GetResourceType() resource.Type {
|
||||
return resource.CredentialLibrary
|
||||
}
|
||||
|
||||
func (l *LdapCredentialLibrary) oplog(op oplog.OpType) oplog.Metadata {
|
||||
metadata := oplog.Metadata{
|
||||
"resource-public-id": []string{l.PublicId},
|
||||
"resource-type": []string{"credential-vault-ldap-library"},
|
||||
"op-type": []string{op.String()},
|
||||
}
|
||||
if l.StoreId != "" {
|
||||
metadata["store-id"] = []string{l.StoreId}
|
||||
}
|
||||
return metadata
|
||||
}
|
||||
|
||||
// CredentialType returns the type of credential the library retrieves.
|
||||
func (l *LdapCredentialLibrary) CredentialType() globals.CredentialType {
|
||||
return globals.CredentialType(l.LdapCredentialLibrary.CredentialType)
|
||||
}
|
||||
|
||||
type deletedLdapCredentialLibrary struct {
|
||||
PublicId string `gorm:"primary_key"`
|
||||
DeleteTime *timestamp.Timestamp
|
||||
}
|
||||
|
||||
// TableName returns the tablename to override the default gorm table name
|
||||
func (s *deletedLdapCredentialLibrary) TableName() string {
|
||||
return "credential_vault_ldap_library_deleted"
|
||||
}
|
||||
@@ -96,7 +96,7 @@ func (o *UsernamePasswordOverride) TableName() string {
|
||||
if o.tableName != "" {
|
||||
return o.tableName
|
||||
}
|
||||
return "credential_vault_library_username_password_mapping_override"
|
||||
return "credential_vault_generic_library_username_password_mapping_ovrd"
|
||||
}
|
||||
|
||||
// SetTableName sets the table name.
|
||||
@@ -163,7 +163,7 @@ func (o *UsernamePasswordDomainOverride) TableName() string {
|
||||
if o.tableName != "" {
|
||||
return o.tableName
|
||||
}
|
||||
return "credential_vault_library_username_password_domain_mapping_ovrd"
|
||||
return "credential_vault_generic_library_usern_pass_domain_mapping_ovrd"
|
||||
}
|
||||
|
||||
// SetTableName sets the table name.
|
||||
@@ -230,7 +230,7 @@ func (o *SshPrivateKeyOverride) TableName() string {
|
||||
if o.tableName != "" {
|
||||
return o.tableName
|
||||
}
|
||||
return "credential_vault_library_ssh_private_key_mapping_override"
|
||||
return "credential_vault_generic_library_ssh_private_key_mapping_ovrd"
|
||||
}
|
||||
|
||||
// SetTableName sets the table name.
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
ldapv3 "github.com/go-ldap/ldap/v3"
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/credential"
|
||||
"github.com/hashicorp/boundary/internal/credential/vault/internal/sshprivatekey"
|
||||
@@ -231,8 +232,10 @@ type issuingCredentialLibrary interface {
|
||||
retrieveCredential(context.Context, errors.Op, ...credential.Option) (dynamicCred, error)
|
||||
}
|
||||
|
||||
// A genericIssuingCredentialLibrary contains all the values needed to connect to Vault and
|
||||
// retrieve credentials.
|
||||
// genericIssuingCredentialLibrary is a subtype of
|
||||
// privateCredentialLibraryAllTypes specifically for Vault generic credential
|
||||
// libraries. It contains all the values needed to connect to Vault and retrieve
|
||||
// credentials.
|
||||
type genericIssuingCredentialLibrary struct {
|
||||
PublicId string
|
||||
StoreId string
|
||||
@@ -446,11 +449,6 @@ func (pl *genericIssuingCredentialLibrary) retrieveCredential(ctx context.Contex
|
||||
return convert(ctx, dCred)
|
||||
}
|
||||
|
||||
// TableName returns the table name for gorm.
|
||||
func (pl *genericIssuingCredentialLibrary) TableName() string {
|
||||
return "credential_vault_library_issue_credentials"
|
||||
}
|
||||
|
||||
func (r *Repository) getIssueCredLibraries(ctx context.Context, requests []credential.Request) ([]issuingCredentialLibrary, error) {
|
||||
const op = "vault.(Repository).getIssueCredLibraries"
|
||||
|
||||
@@ -514,8 +512,9 @@ func (r *Repository) getIssueCredLibraries(ctx context.Context, requests []crede
|
||||
return decryptedLibs, nil
|
||||
}
|
||||
|
||||
// privateCredentialLibraryAllTypes is a clone of genericIssuingCredentialLibrary. Contains
|
||||
// all the values needed to connect to Vault and retrieve credentials.
|
||||
// privateCredentialLibraryAllTypes is a type that interfaces with the database.
|
||||
// It contains all the values needed to connect to Vault and retrieve
|
||||
// credentials.
|
||||
type privateCredentialLibraryAllTypes struct {
|
||||
PublicId string `gorm:"primary_key"`
|
||||
StoreId string
|
||||
@@ -686,6 +685,42 @@ func (pl *privateCredentialLibraryAllTypes) toTypedIssuingCredentialLibrary() is
|
||||
Extensions: pl.Extensions,
|
||||
AdditionalValidPrincipals: pl.AdditionalValidPrincipals,
|
||||
}
|
||||
case "ldap":
|
||||
return &ldapIssuingCredentialLibrary{
|
||||
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,
|
||||
AdditionalValidPrincipals: pl.AdditionalValidPrincipals,
|
||||
}
|
||||
default:
|
||||
return &genericIssuingCredentialLibrary{
|
||||
PublicId: pl.PublicId,
|
||||
@@ -768,6 +803,10 @@ func (m *requestMap) get(libraryId string) []credential.Purpose {
|
||||
return m.ids[libraryId]
|
||||
}
|
||||
|
||||
// sshCertIssuingCredentialLibrary is a subtype of
|
||||
// privateCredentialLibraryAllTypes specifically for Vault SSH Certificate
|
||||
// credential libraries. It contains all the values needed to connect to Vault
|
||||
// and retrieve credentials.
|
||||
type sshCertIssuingCredentialLibrary struct {
|
||||
PublicId string
|
||||
StoreId string
|
||||
@@ -1108,3 +1147,232 @@ func (lib *sshCertIssuingCredentialLibrary) retrieveCredential(ctx context.Conte
|
||||
certificate: []byte(cert),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type ldapIssuingCredentialLibrary 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
|
||||
AdditionalValidPrincipals string
|
||||
}
|
||||
|
||||
// GetPublicId returns the credential library's id.
|
||||
func (lib *ldapIssuingCredentialLibrary) GetPublicId() string { return lib.PublicId }
|
||||
|
||||
// GetStoreId returns the credential library's credential store id.
|
||||
func (lib *ldapIssuingCredentialLibrary) GetStoreId() string { return lib.StoreId }
|
||||
|
||||
// GetName returns the credential library's name.
|
||||
func (lib *ldapIssuingCredentialLibrary) GetName() string { return lib.Name }
|
||||
|
||||
// GetDescription returns the credential library's description.
|
||||
func (lib *ldapIssuingCredentialLibrary) GetDescription() string { return lib.Description }
|
||||
|
||||
// GetVersion returns the credential library's version.
|
||||
func (lib *ldapIssuingCredentialLibrary) GetVersion() uint32 { return lib.Version }
|
||||
|
||||
// GetPurpose returns the credential library's purpose.
|
||||
func (lib *ldapIssuingCredentialLibrary) GetPurpose() credential.Purpose { return lib.Purpose }
|
||||
|
||||
// GetCreateTime returns the credential library's create time.
|
||||
func (lib *ldapIssuingCredentialLibrary) GetCreateTime() *timestamp.Timestamp { return lib.CreateTime }
|
||||
|
||||
// GetUpdateTime returns the credential library's update time.
|
||||
func (lib *ldapIssuingCredentialLibrary) GetUpdateTime() *timestamp.Timestamp { return lib.UpdateTime }
|
||||
|
||||
// CredentialType returns the credential library's issuing credential type.
|
||||
func (lib *ldapIssuingCredentialLibrary) CredentialType() globals.CredentialType {
|
||||
switch ct := lib.CredType; ct {
|
||||
case "":
|
||||
return globals.UnspecifiedCredentialType
|
||||
default:
|
||||
return globals.CredentialType(ct)
|
||||
}
|
||||
}
|
||||
|
||||
// GetResourceType returns the credential library's resource type.
|
||||
func (lib *ldapIssuingCredentialLibrary) GetResourceType() resource.Type {
|
||||
return resource.CredentialLibrary
|
||||
}
|
||||
|
||||
// client produces a Vault API client using this credential library's data.
|
||||
func (lib *ldapIssuingCredentialLibrary) 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.Wrap(ctx, err, op, errors.WithMsg("unable to create vault client"))
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
var vaultLdapPathRegexp = regexp.MustCompile(`^.+\/(static-cred|creds).+$`)
|
||||
|
||||
// retrieveCredential retrieves a dynamic LDAP credential from Vault for a
|
||||
// specific session and connection.
|
||||
func (lib *ldapIssuingCredentialLibrary) retrieveCredential(ctx context.Context, op errors.Op, _ ...credential.Option) (dynamicCred, error) {
|
||||
credId, err := newCredentialId(ctx)
|
||||
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)
|
||||
}
|
||||
var secret *vault.Secret
|
||||
|
||||
secret, err = client.get(ctx, lib.VaultPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
if secret == nil {
|
||||
return nil, errors.E(ctx, errors.WithCode(errors.VaultEmptySecret), errors.WithOp(op))
|
||||
}
|
||||
|
||||
leaseDuration := time.Duration(secret.LeaseDuration) * time.Second
|
||||
cred, err := newCredential(ctx, lib.GetPublicId(), secret.LeaseID, lib.TokenHmac, leaseDuration)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
cred.PublicId = credId
|
||||
cred.IsRenewable = secret.Renewable
|
||||
|
||||
upd := &usrPassDomainCred{
|
||||
baseCred: &baseCred{
|
||||
Credential: cred,
|
||||
lib: lib,
|
||||
secretData: secret.Data,
|
||||
},
|
||||
}
|
||||
|
||||
username, ok := secret.Data["username"].(string)
|
||||
if !ok {
|
||||
return nil, errors.New(ctx, errors.VaultInvalidCredentialMapping, op, "vault secret did not contain a username or response was not in the expected format")
|
||||
}
|
||||
upd.username = username
|
||||
|
||||
pwData, ok := secret.Data["password"].(string)
|
||||
if !ok {
|
||||
return nil, errors.New(ctx, errors.VaultInvalidCredentialMapping, op, "vault secret did not contain a password or response was not in the expected format")
|
||||
}
|
||||
upd.password = credential.Password(pwData)
|
||||
|
||||
matches := vaultLdapPathRegexp.FindStringSubmatch(lib.VaultPath)
|
||||
if len(matches) < 2 { // [0] is the vault path, [1] is static-cred or creds, if it exists.
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "vault path was not in an expected format. expected path containing \"static-cred\" or \"creds\"")
|
||||
}
|
||||
switch matches[1] {
|
||||
case "static-cred":
|
||||
dn, ok := secret.Data["dn"].(string)
|
||||
if !ok {
|
||||
return nil, errors.New(ctx, errors.VaultInvalidCredentialMapping, op, "vault secret did not contain a dn or response was not in the expected format")
|
||||
}
|
||||
domain, err := extractDomainFromDn(dn)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
upd.domain = domain
|
||||
case "creds":
|
||||
var dns []string
|
||||
dis, ok := secret.Data["distinguished_names"].([]any)
|
||||
if ok {
|
||||
for _, value := range dis {
|
||||
if v, ok := value.(string); ok {
|
||||
dns = append(dns, v)
|
||||
} else {
|
||||
return nil, errors.New(ctx, errors.VaultInvalidCredentialMapping, op, "vault secret returned distinguished_names with non-string member")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return nil, errors.New(ctx, errors.VaultInvalidCredentialMapping, op, "vault secret did not contain distinguished_names or response was not in the expected format")
|
||||
}
|
||||
if len(dns) == 0 {
|
||||
return nil, errors.New(ctx, errors.VaultInvalidCredentialMapping, op, "vault secret returned empty distinguished_names")
|
||||
}
|
||||
|
||||
// In the Vault LDAP secrets engine, no deduplication occurs and order
|
||||
// is maintained from the LDIF statements, so the assumption here is
|
||||
// that the first DN should correspond to the user's creation in the
|
||||
// creation LDIF, which should correspond to the correct domain.
|
||||
// https://developer.hashicorp.com/vault/docs/secrets/ldap#dynamic-credentials
|
||||
domain, err := extractDomainFromDn(dns[0])
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
upd.domain = domain
|
||||
}
|
||||
|
||||
return upd, nil
|
||||
}
|
||||
|
||||
// extractDomainFromDn composes a domain name from all the "dc" attributes in an
|
||||
// LDAP DN.
|
||||
func extractDomainFromDn(dnStr string) (string, error) {
|
||||
if dnStr == "" {
|
||||
return "", fmt.Errorf("empty DN")
|
||||
}
|
||||
|
||||
dn, err := ldapv3.ParseDN(dnStr)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse DN: %w", err)
|
||||
}
|
||||
|
||||
var dcs []string
|
||||
for _, rdn := range dn.RDNs {
|
||||
for _, attr := range rdn.Attributes {
|
||||
// "0.9.2342.19200300.100.1.25" is the OID for the "dc" attribute
|
||||
// and it is a valid representation in a DN string.
|
||||
// (https://docs.ldap.com/specs/rfc4519.txt)
|
||||
if strings.EqualFold(attr.Type, "dc") || strings.EqualFold(attr.Type, "0.9.2342.19200300.100.1.25") {
|
||||
dcs = append(dcs, attr.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(dcs) == 0 {
|
||||
return "", fmt.Errorf("no domain component (dc) attribute found in dn")
|
||||
}
|
||||
|
||||
return strings.Join(dcs, "."), nil
|
||||
}
|
||||
@@ -1879,6 +1879,136 @@ func TestBaseToSshPriKey(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepository_ldapCertIssuingCredentialLibrary_retrieveCredential(t *testing.T) {
|
||||
// Create Vault server
|
||||
v := NewTestVaultServer(t, WithTestVaultTLS(TestNoTLS), WithVaultVersion("1.20.0"), WithDockerNetwork(true))
|
||||
require.NotNil(t, v)
|
||||
|
||||
vc := v.client(t).cl
|
||||
mounts, err := vc.Sys().ListMounts()
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, mounts)
|
||||
|
||||
// Create OpenLDAP server and mount Vault LDAP secrets engine.
|
||||
_ = MountLdapServer(t, v)
|
||||
|
||||
// Create and setup Boundary DB
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
rw := db.New(conn)
|
||||
repo, err := NewRepository(t.Context(), rw, rw, kms, sche)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, repo)
|
||||
|
||||
sec, token := v.CreateToken(t, WithPolicies([]string{"default", "boundary-controller", "ldap"}), WithTokenPeriod(time.Hour))
|
||||
require.NotNil(t, sec)
|
||||
require.NotEmpty(t, token)
|
||||
|
||||
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
cs := TestCredentialStore(t, conn, wrapper, prj.GetPublicId(), v.Addr, token, sec.Auth.Accessor)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
vaultPath string
|
||||
expStaticUsername string
|
||||
expDomain string
|
||||
expDynamicCred bool
|
||||
expErr bool
|
||||
expErrMsg string
|
||||
}{
|
||||
{
|
||||
name: "doesntExistStaticCred",
|
||||
vaultPath: "ldap/static-cred/doesntexist",
|
||||
expErr: true,
|
||||
expErrMsg: "unknown role:",
|
||||
},
|
||||
{
|
||||
name: "doesntExistDynamicCred",
|
||||
vaultPath: "ldap/creds/doesntexist",
|
||||
expErr: true,
|
||||
expErrMsg: "vault secret is empty",
|
||||
},
|
||||
{
|
||||
name: "staticCred",
|
||||
vaultPath: "ldap/static-cred/einstein",
|
||||
expStaticUsername: "einstein",
|
||||
expDomain: "example.org",
|
||||
},
|
||||
{
|
||||
name: "staticCredHierarchical",
|
||||
vaultPath: "ldap/static-cred/myorg/myproject/newton",
|
||||
expStaticUsername: "newton",
|
||||
expDomain: "example.org",
|
||||
},
|
||||
{
|
||||
name: "dynamicCred",
|
||||
vaultPath: "ldap/creds/scientists",
|
||||
expDynamicCred: true,
|
||||
expDomain: "example.org",
|
||||
},
|
||||
{
|
||||
name: "dynamicCredHierarchical",
|
||||
vaultPath: "ldap/creds/myorg/myproject/scientists",
|
||||
expDynamicCred: true,
|
||||
expDomain: "example.org",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
lib, err := NewLdapCredentialLibrary(cs.GetPublicId(), tt.vaultPath)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, lib)
|
||||
lib.PublicId, err = newLdapCredentialLibraryId(t.Context())
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = rw.DoTx(t.Context(), db.StdRetryCnt, db.ExpBackoff{},
|
||||
func(_ db.Reader, iw db.Writer) error {
|
||||
return iw.Create(t.Context(), lib)
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
libs, err := repo.getIssueCredLibraries(t.Context(), []credential.Request{{
|
||||
SourceId: lib.GetPublicId(),
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, libs, 1)
|
||||
|
||||
cred, err := libs[0].retrieveCredential(t.Context(), "TestLdapRetrieveCredential")
|
||||
if tt.expErr {
|
||||
require.ErrorContains(t, err, tt.expErrMsg)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
require.IsType(t, &usrPassDomainCred{}, cred)
|
||||
credUpd := cred.(*usrPassDomainCred)
|
||||
require.NotEmpty(t, credUpd.baseCred)
|
||||
require.NotEmpty(t, credUpd.getCredential())
|
||||
require.EqualValues(t, libs[0], credUpd.lib)
|
||||
require.NotEmpty(t, credUpd.secretData)
|
||||
require.Contains(t, credUpd.secretData, "username")
|
||||
require.Contains(t, credUpd.secretData, "password")
|
||||
|
||||
require.EqualValues(t, credUpd.secretData["password"], credUpd.Password())
|
||||
require.EqualValues(t, tt.expDomain, credUpd.Domain())
|
||||
if !tt.expDynamicCred {
|
||||
require.EqualValues(t, credUpd.secretData["username"], credUpd.Username())
|
||||
require.EqualValues(t, tt.expStaticUsername, credUpd.Username())
|
||||
require.False(t, credUpd.GetIsRenewable())
|
||||
} else {
|
||||
require.EqualValues(t, credUpd.secretData["username"], credUpd.Username())
|
||||
require.Contains(t, credUpd.Username(), "b_token-TestRepository-ldapCertIssuingCredentialLibrary-retrieveCredential_")
|
||||
require.True(t, credUpd.GetIsRenewable())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepository_sshCertIssuingCredentialLibrary_retrieveCredential(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -2093,3 +2223,83 @@ func TestRepository_sshCertIssuingCredentialLibrary_retrieveCredential(t *testin
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractDomainFromDn(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
inDn string
|
||||
expDomain string
|
||||
expErr bool
|
||||
expErrMsgContains string
|
||||
}{
|
||||
{
|
||||
name: "emptyString",
|
||||
inDn: "",
|
||||
expErr: true,
|
||||
expErrMsgContains: "empty DN",
|
||||
},
|
||||
{
|
||||
name: "notADn",
|
||||
inDn: "a string that isn't really a DN",
|
||||
expErr: true,
|
||||
expErrMsgContains: "failed to parse DN:",
|
||||
},
|
||||
{
|
||||
name: "validDn",
|
||||
inDn: "dc=domain,dc=tld",
|
||||
expDomain: "domain.tld",
|
||||
},
|
||||
{
|
||||
name: "validDnSpaces",
|
||||
inDn: "dc = domain , dc = tld",
|
||||
expDomain: "domain.tld",
|
||||
},
|
||||
{
|
||||
name: "validDnCaps",
|
||||
inDn: "DC=DOMAIN,DC=TLD",
|
||||
expDomain: "DOMAIN.TLD",
|
||||
},
|
||||
{
|
||||
name: "validDnOid",
|
||||
inDn: "0.9.2342.19200300.100.1.25=Domain,0.9.2342.19200300.100.1.25=Tld",
|
||||
expDomain: "Domain.Tld",
|
||||
},
|
||||
{
|
||||
name: "validDnOidMixed",
|
||||
inDn: "dc=sub1,0.9.2342.19200300.100.1.25=Domain,dc=Tld",
|
||||
expDomain: "sub1.Domain.Tld",
|
||||
},
|
||||
{
|
||||
name: "validDnWithCn",
|
||||
inDn: "CN=My User,CN=admin,DC=mycorp,DC=domain,DC=TLD",
|
||||
expDomain: "mycorp.domain.TLD",
|
||||
},
|
||||
{
|
||||
name: "validDnWithEscapeChars",
|
||||
inDn: `CN=mycn,OU=HashiCorp\, an IBM Company,DC=domain,DC=TLD`,
|
||||
expDomain: "domain.TLD",
|
||||
},
|
||||
{
|
||||
name: "validDnWithEscapeChars2",
|
||||
inDn: `CN=Before\0DAfter,OU=HashiCorp,DC=domain,DC=TLD`,
|
||||
expDomain: "domain.TLD",
|
||||
},
|
||||
{
|
||||
name: "validDnWithSubdomains",
|
||||
inDn: `dc=foo,dc=bAR,dc=BAZ,dc=domain,dc=Tld`,
|
||||
expDomain: "foo.bAR.BAZ.domain.Tld",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
domain, err := extractDomainFromDn(tt.inDn)
|
||||
if tt.expErr {
|
||||
require.ErrorContains(t, err, tt.expErrMsgContains)
|
||||
require.Empty(t, domain)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tt.expDomain, domain)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ func init() {
|
||||
globals.RegisterPrefixToResourceInfo(globals.VaultDynamicCredentialPrefix, resource.Credential, credential.Domain, Subtype)
|
||||
globals.RegisterPrefixToResourceInfo(globals.VaultCredentialLibraryPrefix, resource.CredentialLibrary, credential.Domain, GenericLibrarySubtype)
|
||||
globals.RegisterPrefixToResourceInfo(globals.VaultSshCertificateCredentialLibraryPrefix, resource.CredentialLibrary, credential.Domain, SSHCertificateLibrarySubtype)
|
||||
globals.RegisterPrefixToResourceInfo(globals.VaultLdapCredentialLibraryPrefix, resource.CredentialLibrary, credential.Domain, LdapCredentialLibrarySubtype)
|
||||
}
|
||||
|
||||
// PublicId prefixes for the resources in the vault package.
|
||||
@@ -25,6 +26,7 @@ const (
|
||||
Subtype = globals.Subtype("vault")
|
||||
GenericLibrarySubtype = globals.Subtype("vault-generic")
|
||||
SSHCertificateLibrarySubtype = globals.Subtype("vault-ssh-certificate")
|
||||
LdapCredentialLibrarySubtype = globals.Subtype("vault-ldap")
|
||||
)
|
||||
|
||||
func newCredentialStoreId(ctx context.Context) (string, error) {
|
||||
@@ -58,3 +60,11 @@ func newSSHCertificateCredentialLibraryId(ctx context.Context) (string, error) {
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func newLdapCredentialLibraryId(ctx context.Context) (string, error) {
|
||||
id, err := db.NewPublicId(ctx, globals.VaultLdapCredentialLibraryPrefix)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(ctx, err, "vault.newLdapCredentialLibraryPrefix")
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
@@ -194,8 +194,9 @@ delete from credential_vault_credential
|
||||
select sum(reltuples::bigint) as estimate
|
||||
from pg_class
|
||||
where oid in (
|
||||
'credential_vault_library'::regclass,
|
||||
'credential_vault_ssh_cert_library'::regclass
|
||||
'credential_vault_generic_library'::regclass,
|
||||
'credential_vault_ssh_cert_library'::regclass,
|
||||
'credential_vault_ldap_library'::regclass
|
||||
)
|
||||
`
|
||||
|
||||
@@ -209,7 +210,7 @@ with libraries as (
|
||||
),
|
||||
generic_libs as (
|
||||
select *
|
||||
from credential_vault_library
|
||||
from credential_vault_generic_library
|
||||
where public_id in (select public_id from libraries)
|
||||
),
|
||||
ssh_cert_libs as (
|
||||
@@ -217,6 +218,11 @@ ssh_cert_libs as (
|
||||
from credential_vault_ssh_cert_library
|
||||
where public_id in (select public_id from libraries)
|
||||
),
|
||||
ldap_libs as (
|
||||
select *
|
||||
from credential_vault_ldap_library
|
||||
where public_id in (select public_id from libraries)
|
||||
),
|
||||
final as (
|
||||
select public_id,
|
||||
store_id,
|
||||
@@ -263,6 +269,29 @@ final as (
|
||||
additional_valid_principals,
|
||||
'ssh' as type
|
||||
from ssh_cert_libs
|
||||
union
|
||||
select public_id,
|
||||
store_id,
|
||||
project_id,
|
||||
name,
|
||||
description,
|
||||
create_time,
|
||||
update_time,
|
||||
version,
|
||||
vault_path,
|
||||
credential_type,
|
||||
null as http_method, -- Add to make union uniform
|
||||
null as http_request_body, -- Add to make union uniform
|
||||
null as username, -- Add to make union uniform
|
||||
null as key_type, -- Add to make union uniform
|
||||
null as key_bits, -- Add to make union uniform
|
||||
null as ttl, -- Add to make union uniform
|
||||
null as key_id, -- Add to make union uniform
|
||||
null as critical_options, -- Add to make union uniform
|
||||
null as extensions, -- Add to make union uniform
|
||||
null as additional_valid_principals, -- Add to make union uniform
|
||||
'ldap' as type
|
||||
from ldap_libs
|
||||
)
|
||||
select *
|
||||
from final
|
||||
@@ -280,7 +309,7 @@ with libraries as (
|
||||
),
|
||||
generic_libs as (
|
||||
select *
|
||||
from credential_vault_library
|
||||
from credential_vault_generic_library
|
||||
where public_id in (select public_id from libraries)
|
||||
),
|
||||
ssh_cert_libs as (
|
||||
@@ -288,6 +317,11 @@ ssh_cert_libs as (
|
||||
from credential_vault_ssh_cert_library
|
||||
where public_id in (select public_id from libraries)
|
||||
),
|
||||
ldap_libs as (
|
||||
select *
|
||||
from credential_vault_ldap_library
|
||||
where public_id in (select public_id from libraries)
|
||||
),
|
||||
final as (
|
||||
select public_id,
|
||||
store_id,
|
||||
@@ -334,6 +368,29 @@ final as (
|
||||
additional_valid_principals,
|
||||
'ssh' as type
|
||||
from ssh_cert_libs
|
||||
union
|
||||
select public_id,
|
||||
store_id,
|
||||
project_id,
|
||||
name,
|
||||
description,
|
||||
create_time,
|
||||
update_time,
|
||||
version,
|
||||
vault_path,
|
||||
credential_type,
|
||||
null as http_method, -- Add to make union uniform
|
||||
null as http_request_body, -- Add to make union uniform
|
||||
null as username, -- Add to make union uniform
|
||||
null as key_type, -- Add to make union uniform
|
||||
null as key_bits, -- Add to make union uniform
|
||||
null as ttl, -- Add to make union uniform
|
||||
null as key_id, -- Add to make union uniform
|
||||
null as critical_options, -- Add to make union uniform
|
||||
null as extensions, -- Add to make union uniform
|
||||
null as additional_valid_principals, -- Add to make union uniform
|
||||
'ldap' as type
|
||||
from ldap_libs
|
||||
)
|
||||
select *
|
||||
from final
|
||||
@@ -351,7 +408,7 @@ with libraries as (
|
||||
),
|
||||
generic_libs as (
|
||||
select *
|
||||
from credential_vault_library
|
||||
from credential_vault_generic_library
|
||||
where public_id in (select public_id from libraries)
|
||||
),
|
||||
ssh_cert_libs as (
|
||||
@@ -359,6 +416,11 @@ ssh_cert_libs as (
|
||||
from credential_vault_ssh_cert_library
|
||||
where public_id in (select public_id from libraries)
|
||||
),
|
||||
ldap_libs as (
|
||||
select *
|
||||
from credential_vault_ldap_library
|
||||
where public_id in (select public_id from libraries)
|
||||
),
|
||||
final as (
|
||||
select public_id,
|
||||
store_id,
|
||||
@@ -405,6 +467,29 @@ final as (
|
||||
additional_valid_principals,
|
||||
'ssh' as type
|
||||
from ssh_cert_libs
|
||||
union
|
||||
select public_id,
|
||||
store_id,
|
||||
project_id,
|
||||
name,
|
||||
description,
|
||||
create_time,
|
||||
update_time,
|
||||
version,
|
||||
vault_path,
|
||||
credential_type,
|
||||
null as http_method, -- Add to make union uniform
|
||||
null as http_request_body, -- Add to make union uniform
|
||||
null as username, -- Add to make union uniform
|
||||
null as key_type, -- Add to make union uniform
|
||||
null as key_bits, -- Add to make union uniform
|
||||
null as ttl, -- Add to make union uniform
|
||||
null as key_id, -- Add to make union uniform
|
||||
null as critical_options, -- Add to make union uniform
|
||||
null as extensions, -- Add to make union uniform
|
||||
null as additional_valid_principals, -- Add to make union uniform
|
||||
'ldap' as type
|
||||
from ldap_libs
|
||||
)
|
||||
select *
|
||||
from final
|
||||
@@ -423,7 +508,7 @@ with libraries as (
|
||||
),
|
||||
generic_libs as (
|
||||
select *
|
||||
from credential_vault_library
|
||||
from credential_vault_generic_library
|
||||
where public_id in (select public_id from libraries)
|
||||
),
|
||||
ssh_cert_libs as (
|
||||
@@ -431,6 +516,11 @@ ssh_cert_libs as (
|
||||
from credential_vault_ssh_cert_library
|
||||
where public_id in (select public_id from libraries)
|
||||
),
|
||||
ldap_libs as (
|
||||
select *
|
||||
from credential_vault_ldap_library
|
||||
where public_id in (select public_id from libraries)
|
||||
),
|
||||
final as (
|
||||
select public_id,
|
||||
store_id,
|
||||
@@ -477,6 +567,29 @@ final as (
|
||||
additional_valid_principals,
|
||||
'ssh' as type
|
||||
from ssh_cert_libs
|
||||
union
|
||||
select public_id,
|
||||
store_id,
|
||||
project_id,
|
||||
name,
|
||||
description,
|
||||
create_time,
|
||||
update_time,
|
||||
version,
|
||||
vault_path,
|
||||
credential_type,
|
||||
null as http_method, -- Add to make union uniform
|
||||
null as http_request_body, -- Add to make union uniform
|
||||
null as username, -- Add to make union uniform
|
||||
null as key_type, -- Add to make union uniform
|
||||
null as key_bits, -- Add to make union uniform
|
||||
null as ttl, -- Add to make union uniform
|
||||
null as key_id, -- Add to make union uniform
|
||||
null as critical_options, -- Add to make union uniform
|
||||
null as extensions, -- Add to make union uniform
|
||||
null as additional_valid_principals, -- Add to make union uniform
|
||||
'ldap' as type
|
||||
from ldap_libs
|
||||
)
|
||||
select *
|
||||
from final
|
||||
|
||||
@@ -421,7 +421,7 @@ func (pl *listLookupLibrary) toCredentialLibrary() *CredentialLibrary {
|
||||
}
|
||||
|
||||
// TableName returns the table name for gorm.
|
||||
func (*listLookupLibrary) TableName() string { return "credential_vault_library_list_lookup" }
|
||||
func (*listLookupLibrary) TableName() string { return "credential_vault_generic_library_list_lookup" }
|
||||
|
||||
// GetPublicId returns the public id.
|
||||
func (pl *listLookupLibrary) GetPublicId() string { return pl.PublicId }
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package vault
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"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"
|
||||
)
|
||||
|
||||
// CreateLdapCredentialLibrary creates a new Vault LDAP credential library. It
|
||||
// inserts the input domain object into the repository and returns the inserted
|
||||
// domain object. No options are supported.
|
||||
//
|
||||
// The input domain object is not mutated, must contain a valid credential store
|
||||
// id and must not contain a public id.
|
||||
func (r *Repository) CreateLdapCredentialLibrary(ctx context.Context, projectId string, l *LdapCredentialLibrary, _ ...Option) (*LdapCredentialLibrary, error) {
|
||||
const op = "vault.(Repository).CreateLdapCredentialLibrary"
|
||||
if projectId == "" {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "no project id")
|
||||
}
|
||||
if l == nil || l.LdapCredentialLibrary == nil {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "nil input domain object")
|
||||
}
|
||||
if l.PublicId != "" {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "public id not empty")
|
||||
}
|
||||
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")
|
||||
}
|
||||
l = l.clone()
|
||||
|
||||
if l.GetCredentialType() == "" {
|
||||
l.LdapCredentialLibrary.CredentialType = string(globals.UsernamePasswordDomainCredentialType)
|
||||
}
|
||||
if l.GetCredentialType() != string(globals.UsernamePasswordDomainCredentialType) {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "invalid credential type")
|
||||
}
|
||||
|
||||
id, err := newLdapCredentialLibraryId(ctx)
|
||||
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 insertedLdapCredLib *LdapCredentialLibrary
|
||||
_, err = r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{},
|
||||
func(_ db.Reader, w db.Writer) error {
|
||||
insertedLdapCredLib = l.clone()
|
||||
err = w.Create(ctx, insertedLdapCredLib,
|
||||
db.WithOplog(oplogWrapper, insertedLdapCredLib.oplog(oplog.OpType_OP_TYPE_CREATE)),
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if errors.IsUniqueError(err) {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("in credential store: %s: name %s already exists", l.StoreId, l.Name))
|
||||
}
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("credential store: %s", l.StoreId))
|
||||
}
|
||||
|
||||
return insertedLdapCredLib, nil
|
||||
}
|
||||
|
||||
// UpdateLdapCredentialLibrary updates an existing Vault LDAP credential library
|
||||
// in the repository. It returns a domain object containing the updated state
|
||||
// and the number of updated records. No options are supported.
|
||||
//
|
||||
// The input domain object is not mutated and must contain a valid public id.
|
||||
func (r *Repository) UpdateLdapCredentialLibrary(ctx context.Context, projectId string, l *LdapCredentialLibrary, version uint32, fieldMaskPaths []string, _ ...Option) (*LdapCredentialLibrary, int, error) {
|
||||
const op = "vault.(Repository).UpdateLdapCredentialLibrary"
|
||||
if projectId == "" {
|
||||
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "no project id")
|
||||
}
|
||||
if version == 0 {
|
||||
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing version")
|
||||
}
|
||||
if l == nil || l.LdapCredentialLibrary == nil {
|
||||
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "nil domain object")
|
||||
}
|
||||
if l.PublicId == "" {
|
||||
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing public id")
|
||||
}
|
||||
|
||||
for _, f := range fieldMaskPaths {
|
||||
switch {
|
||||
case strings.EqualFold(nameField, f):
|
||||
case strings.EqualFold(descriptionField, f):
|
||||
case strings.EqualFold(vaultPathField, f):
|
||||
default:
|
||||
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidFieldMask, op, fmt.Sprintf("%q field mask path is unsupported", f))
|
||||
}
|
||||
}
|
||||
|
||||
dbMask, nullFields := dbw.BuildUpdatePaths(
|
||||
map[string]any{
|
||||
nameField: l.Name,
|
||||
descriptionField: l.Description,
|
||||
vaultPathField: l.VaultPath,
|
||||
}, fieldMaskPaths, nil,
|
||||
)
|
||||
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.WithMsg("unable to get oplog wrapper"))
|
||||
}
|
||||
|
||||
var updatedCredLib *LdapCredentialLibrary
|
||||
var rowsUpdated int
|
||||
_, err = r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{},
|
||||
func(r db.Reader, w db.Writer) error {
|
||||
updated, err := w.Update(ctx, l.clone(), dbMask, nullFields,
|
||||
db.WithOplog(oplogWrapper, l.oplog(oplog.OpType_OP_TYPE_UPDATE)),
|
||||
db.WithVersion(&version),
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithMsg("failed to update credential library"))
|
||||
}
|
||||
if updated > 1 {
|
||||
return errors.New(ctx, errors.MultipleRecords, op, "more than 1 credential library would have been updated")
|
||||
}
|
||||
rowsUpdated = updated
|
||||
|
||||
updatedCredLib = allocLdapCredentialLibrary()
|
||||
updatedCredLib.PublicId = l.GetPublicId()
|
||||
err = r.LookupByPublicId(ctx, updatedCredLib)
|
||||
if err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to retrieve updated credential library"))
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if errors.IsUniqueError(err) {
|
||||
return nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("name %s already exists: %s", l.Name, l.PublicId))
|
||||
}
|
||||
return nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("failed to update credential library %s", l.PublicId))
|
||||
}
|
||||
|
||||
return updatedCredLib, rowsUpdated, nil
|
||||
}
|
||||
|
||||
// LookupLdapCredentialLibrary looks up an existing Vault LDAP credential
|
||||
// library in the repository. It returns the domain object for the given public
|
||||
// id, if it exists. Returns nil, nil if not found. No options are supported.
|
||||
func (r *Repository) LookupLdapCredentialLibrary(ctx context.Context, publicId string, _ ...Option) (*LdapCredentialLibrary, error) {
|
||||
const op = "vault.(Repository).LookupLdapCredentialLibrary"
|
||||
if publicId == "" {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "no public id")
|
||||
}
|
||||
|
||||
l := allocLdapCredentialLibrary()
|
||||
l.PublicId = publicId
|
||||
err := r.reader.LookupByPublicId(ctx, l)
|
||||
if err != nil {
|
||||
if errors.IsNotFoundError(err) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("lookup failed for id %s", publicId))
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// DeleteLdapCredentialLibrary deletes the Vault LDAP credential library with
|
||||
// the given public id from the repository. Returns the number of records
|
||||
// deleted. No options are supported.
|
||||
func (r *Repository) DeleteLdapCredentialLibrary(ctx context.Context, projectId string, publicId string, _ ...Option) (int, error) {
|
||||
const op = "vault.(Repository).DeleteLdapCredentialLibrary"
|
||||
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 := allocLdapCredentialLibrary()
|
||||
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) {
|
||||
rowsDeleted, err = w.Delete(ctx, l, 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 credential library would have been deleted")
|
||||
}
|
||||
return err
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("delete failed for id %s", l.PublicId))
|
||||
}
|
||||
|
||||
return rowsDeleted, nil
|
||||
}
|
||||
@@ -0,0 +1,709 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package vault
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/credential/vault/store"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/scheduler"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/testing/protocmp"
|
||||
)
|
||||
|
||||
func TestCreateLdapCredentialLibrary(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
|
||||
_, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
cs := TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0]
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
inProjectId string
|
||||
inDomainObj *LdapCredentialLibrary
|
||||
expOut *LdapCredentialLibrary
|
||||
expErr bool
|
||||
expErrMsg string
|
||||
}{
|
||||
{
|
||||
name: "noProjectId",
|
||||
inProjectId: "",
|
||||
inDomainObj: nil,
|
||||
expErr: true,
|
||||
expErrMsg: "no project id",
|
||||
},
|
||||
{
|
||||
name: "nilDomainObject",
|
||||
inProjectId: proj.GetPublicId(),
|
||||
inDomainObj: nil,
|
||||
expErr: true,
|
||||
expErrMsg: "nil input domain object",
|
||||
},
|
||||
{
|
||||
name: "nilStoreObject",
|
||||
inProjectId: proj.GetPublicId(),
|
||||
inDomainObj: &LdapCredentialLibrary{LdapCredentialLibrary: nil},
|
||||
expErr: true,
|
||||
expErrMsg: "nil input domain object",
|
||||
},
|
||||
{
|
||||
name: "emptyObject",
|
||||
inProjectId: proj.GetPublicId(),
|
||||
inDomainObj: &LdapCredentialLibrary{LdapCredentialLibrary: &store.LdapCredentialLibrary{}},
|
||||
expErr: true,
|
||||
expErrMsg: "no store id",
|
||||
},
|
||||
{
|
||||
name: "withPublicId",
|
||||
inProjectId: proj.GetPublicId(),
|
||||
inDomainObj: &LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{
|
||||
PublicId: "cvllt_3o2iuhOpZ",
|
||||
StoreId: cs.GetPublicId(),
|
||||
Name: "withPublicId",
|
||||
Description: "withPublicId Subtest",
|
||||
VaultPath: "ldap/creds/dynamic1",
|
||||
},
|
||||
},
|
||||
expErr: true,
|
||||
expErrMsg: "public id not empty",
|
||||
},
|
||||
{
|
||||
name: "noStoreId",
|
||||
inProjectId: proj.GetPublicId(),
|
||||
inDomainObj: &LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{
|
||||
StoreId: "",
|
||||
Name: "noStoreId",
|
||||
Description: "noStoreId Subtest",
|
||||
VaultPath: "ldap/creds/dynamic1",
|
||||
},
|
||||
},
|
||||
expErr: true,
|
||||
expErrMsg: "no store id",
|
||||
},
|
||||
{
|
||||
name: "noVaultPath",
|
||||
inProjectId: proj.GetPublicId(),
|
||||
inDomainObj: &LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
Name: "noVaultPath",
|
||||
Description: "noVaultPath Subtest",
|
||||
VaultPath: "",
|
||||
},
|
||||
},
|
||||
expErr: true,
|
||||
expErrMsg: "no vault path",
|
||||
},
|
||||
{
|
||||
name: "invalidCredentialType",
|
||||
inProjectId: proj.GetPublicId(),
|
||||
inDomainObj: &LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
Name: "invalidCredentialType",
|
||||
Description: "invalidCredentialType Subtest",
|
||||
VaultPath: "ldap/creds/dynamic1",
|
||||
CredentialType: "cred_type_that_doesnt_exist",
|
||||
},
|
||||
},
|
||||
expErr: true,
|
||||
expErrMsg: "invalid credential type",
|
||||
},
|
||||
{
|
||||
name: "invalidVaultPath1",
|
||||
inProjectId: proj.GetPublicId(),
|
||||
inDomainObj: &LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
Name: "invalidVaultPath1",
|
||||
Description: "invalidVaultPath1 Subtest",
|
||||
VaultPath: "ldap/bad/some/test/path",
|
||||
},
|
||||
},
|
||||
expErr: true,
|
||||
expErrMsg: "vault_path_must_have_staticcred_or_creds constraint failed",
|
||||
},
|
||||
{
|
||||
name: "invalidVaultPath2",
|
||||
inProjectId: proj.GetPublicId(),
|
||||
inDomainObj: &LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
Name: "invalidVaultPath2",
|
||||
Description: "invalidVaultPath2 Subtest",
|
||||
VaultPath: "ldap/static-credbad/some/test/path",
|
||||
},
|
||||
},
|
||||
expErr: true,
|
||||
expErrMsg: "vault_path_must_have_staticcred_or_creds constraint failed",
|
||||
},
|
||||
{
|
||||
name: "invalidVaultPath3",
|
||||
inProjectId: proj.GetPublicId(),
|
||||
inDomainObj: &LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
Name: "invalidVaultPath3",
|
||||
Description: "invalidVaultPath3 Subtest",
|
||||
VaultPath: "ldap/badstatic-cred/some/test/path",
|
||||
},
|
||||
},
|
||||
expErr: true,
|
||||
expErrMsg: "vault_path_must_have_staticcred_or_creds constraint failed",
|
||||
},
|
||||
{
|
||||
name: "invalidVaultPath4",
|
||||
inProjectId: proj.GetPublicId(),
|
||||
inDomainObj: &LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
Name: "invalidVaultPath4",
|
||||
Description: "invalidVaultPath4 Subtest",
|
||||
VaultPath: "ldap/credsbad/some/test/path",
|
||||
},
|
||||
},
|
||||
expErr: true,
|
||||
expErrMsg: "vault_path_must_have_staticcred_or_creds constraint failed",
|
||||
},
|
||||
{
|
||||
name: "invalidVaultPath5",
|
||||
inProjectId: proj.GetPublicId(),
|
||||
inDomainObj: &LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
Name: "invalidVaultPath5",
|
||||
Description: "invalidVaultPath5 Subtest",
|
||||
VaultPath: "ldap/badcreds/some/test/path",
|
||||
},
|
||||
},
|
||||
expErr: true,
|
||||
expErrMsg: "vault_path_must_have_staticcred_or_creds constraint failed",
|
||||
},
|
||||
{
|
||||
name: "validStaticCred1",
|
||||
inProjectId: proj.GetPublicId(),
|
||||
inDomainObj: &LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
Name: "validStaticCred1",
|
||||
Description: "validStaticCred1 Subtest",
|
||||
VaultPath: "ldap/static-cred/some/test/path",
|
||||
},
|
||||
},
|
||||
expOut: &LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{
|
||||
Name: "validStaticCred1",
|
||||
Description: "validStaticCred1 Subtest",
|
||||
VaultPath: "ldap/static-cred/some/test/path",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "validStaticCred2",
|
||||
inProjectId: proj.GetPublicId(),
|
||||
inDomainObj: &LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
Name: "validStaticCred2",
|
||||
Description: "validStaticCred2 Subtest",
|
||||
VaultPath: "/ldap/static-cred/path",
|
||||
},
|
||||
},
|
||||
expOut: &LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{
|
||||
Name: "validStaticCred2",
|
||||
Description: "validStaticCred2 Subtest",
|
||||
VaultPath: "/ldap/static-cred/path",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "validDynamicCreds1",
|
||||
inProjectId: proj.GetPublicId(),
|
||||
inDomainObj: &LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
Name: "validDynamicCreds1",
|
||||
Description: "validDynamicCreds1 Subtest",
|
||||
VaultPath: "ldap/creds/some/test/path",
|
||||
},
|
||||
},
|
||||
expOut: &LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{
|
||||
Name: "validDynamicCreds1",
|
||||
Description: "validDynamicCreds1 Subtest",
|
||||
VaultPath: "ldap/creds/some/test/path",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "validDynamicCreds2",
|
||||
inProjectId: proj.GetPublicId(),
|
||||
inDomainObj: &LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
Name: "validDynamicCreds2",
|
||||
Description: "validDynamicCreds2 Subtest",
|
||||
VaultPath: "/ldap/creds/path",
|
||||
},
|
||||
},
|
||||
expOut: &LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{
|
||||
Name: "validDynamicCreds2",
|
||||
Description: "validDynamicCreds2 Subtest",
|
||||
VaultPath: "/ldap/creds/path",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
rw := db.New(conn)
|
||||
|
||||
repo, err := NewRepository(t.Context(), rw, rw, kms, sche)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, repo)
|
||||
|
||||
ldapCredLib, err := repo.CreateLdapCredentialLibrary(t.Context(), tt.inProjectId, tt.inDomainObj)
|
||||
if tt.expErr {
|
||||
require.ErrorContains(t, err, tt.expErrMsg)
|
||||
require.Nil(t, ldapCredLib)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, ldapCredLib)
|
||||
|
||||
require.Contains(t, ldapCredLib.GetPublicId(), globals.VaultLdapCredentialLibraryPrefix)
|
||||
require.NotEmpty(t, ldapCredLib.GetCreateTime())
|
||||
require.NotEmpty(t, ldapCredLib.GetUpdateTime())
|
||||
require.EqualValues(t, cs.GetPublicId(), ldapCredLib.GetStoreId())
|
||||
require.EqualValues(t, 1, ldapCredLib.GetVersion())
|
||||
require.EqualValues(t, string(globals.UsernamePasswordDomainCredentialType), ldapCredLib.GetCredentialType())
|
||||
require.EqualValues(t, tt.expOut.GetName(), ldapCredLib.GetName())
|
||||
require.EqualValues(t, tt.expOut.GetDescription(), ldapCredLib.GetDescription())
|
||||
require.EqualValues(t, tt.expOut.GetVaultPath(), ldapCredLib.GetVaultPath())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateLdapCredentialLibrary(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
|
||||
_, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
cs := TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0]
|
||||
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
rw := db.New(conn)
|
||||
|
||||
repo, err := NewRepository(t.Context(), rw, rw, kms, sche)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, repo)
|
||||
|
||||
t.Run("nilDomainObject", func(t *testing.T) {
|
||||
l, updated, err := repo.UpdateLdapCredentialLibrary(t.Context(), proj.GetPublicId(), nil, 1, []string{})
|
||||
require.ErrorContains(t, err, "nil domain object")
|
||||
require.EqualValues(t, 0, updated)
|
||||
require.Nil(t, l)
|
||||
})
|
||||
|
||||
t.Run("nilStoreObject", func(t *testing.T) {
|
||||
l, updated, err := repo.UpdateLdapCredentialLibrary(t.Context(), proj.GetPublicId(),
|
||||
&LdapCredentialLibrary{LdapCredentialLibrary: nil}, 1, []string{})
|
||||
require.ErrorContains(t, err, "nil domain object")
|
||||
require.EqualValues(t, 0, updated)
|
||||
require.Nil(t, l)
|
||||
})
|
||||
|
||||
t.Run("emptyDomainObject", func(t *testing.T) {
|
||||
l, updated, err := repo.UpdateLdapCredentialLibrary(t.Context(), proj.GetPublicId(),
|
||||
&LdapCredentialLibrary{LdapCredentialLibrary: &store.LdapCredentialLibrary{}},
|
||||
1, []string{},
|
||||
)
|
||||
require.ErrorContains(t, err, "missing public id")
|
||||
require.EqualValues(t, 0, updated)
|
||||
require.Nil(t, l)
|
||||
})
|
||||
|
||||
t.Run("credLibDoesntExist", func(t *testing.T) {
|
||||
l, updated, err := repo.UpdateLdapCredentialLibrary(t.Context(), proj.GetPublicId(),
|
||||
&LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{PublicId: "doesnt_exist"},
|
||||
}, 1, []string{"name", "description"},
|
||||
)
|
||||
require.ErrorContains(t, err, "record not found, search issue")
|
||||
require.EqualValues(t, 0, updated)
|
||||
require.Nil(t, l)
|
||||
})
|
||||
|
||||
updateNameFn := func(name string) func(*LdapCredentialLibrary) *LdapCredentialLibrary {
|
||||
return func(lcl *LdapCredentialLibrary) *LdapCredentialLibrary {
|
||||
lcl.Name = name
|
||||
return lcl
|
||||
}
|
||||
}
|
||||
updateDescriptionFn := func(description string) func(*LdapCredentialLibrary) *LdapCredentialLibrary {
|
||||
return func(lcl *LdapCredentialLibrary) *LdapCredentialLibrary {
|
||||
lcl.Description = description
|
||||
return lcl
|
||||
}
|
||||
}
|
||||
updateVaultPathFn := func(vaultPath string) func(*LdapCredentialLibrary) *LdapCredentialLibrary {
|
||||
return func(lcl *LdapCredentialLibrary) *LdapCredentialLibrary {
|
||||
lcl.VaultPath = vaultPath
|
||||
return lcl
|
||||
}
|
||||
}
|
||||
updateStoreIdFn := func() func(*LdapCredentialLibrary) *LdapCredentialLibrary {
|
||||
return func(lcl *LdapCredentialLibrary) *LdapCredentialLibrary {
|
||||
lcl.StoreId = "this_is_immutable"
|
||||
return lcl
|
||||
}
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
inProjectId string
|
||||
inVersion uint32
|
||||
inFieldMask []string
|
||||
changeFns []func(*LdapCredentialLibrary) *LdapCredentialLibrary
|
||||
expUpdate bool
|
||||
expOut *LdapCredentialLibrary
|
||||
expErr bool
|
||||
expErrStr string
|
||||
}{
|
||||
{
|
||||
name: "noProjectId",
|
||||
inProjectId: "",
|
||||
inVersion: 1,
|
||||
inFieldMask: []string{"somepath"},
|
||||
expErr: true,
|
||||
expErrStr: "no project id",
|
||||
},
|
||||
{
|
||||
name: "noVersion",
|
||||
inProjectId: proj.GetPublicId(),
|
||||
inVersion: 0,
|
||||
inFieldMask: []string{"somepath"},
|
||||
expErr: true,
|
||||
expErrStr: "missing version",
|
||||
},
|
||||
{
|
||||
name: "missingFieldMask",
|
||||
inProjectId: proj.GetPublicId(),
|
||||
inVersion: 1,
|
||||
inFieldMask: []string{},
|
||||
expErr: true,
|
||||
expErrStr: "missing field mask",
|
||||
},
|
||||
{
|
||||
name: "unsupportedFieldInFieldMask",
|
||||
inProjectId: proj.GetPublicId(),
|
||||
inVersion: 1,
|
||||
inFieldMask: []string{"name", "storeId"},
|
||||
expErr: true,
|
||||
expErrStr: `"storeId" field mask path is unsupported`,
|
||||
},
|
||||
{
|
||||
name: "updateToInvalidVaultPath1",
|
||||
inProjectId: proj.GetPublicId(),
|
||||
inVersion: 1,
|
||||
inFieldMask: []string{"vaultPath"},
|
||||
changeFns: []func(*LdapCredentialLibrary) *LdapCredentialLibrary{
|
||||
updateVaultPathFn("ldap/credbad/new"),
|
||||
},
|
||||
expErr: true,
|
||||
expErrStr: "vault_path_must_have_staticcred_or_creds constraint failed",
|
||||
},
|
||||
{
|
||||
name: "updateToInvalidVaultPath2",
|
||||
inProjectId: proj.GetPublicId(),
|
||||
inVersion: 1,
|
||||
inFieldMask: []string{"vaultPath"},
|
||||
changeFns: []func(*LdapCredentialLibrary) *LdapCredentialLibrary{
|
||||
updateVaultPathFn("ldap/static-credbad/new"),
|
||||
},
|
||||
expErr: true,
|
||||
expErrStr: "vault_path_must_have_staticcred_or_creds constraint failed",
|
||||
},
|
||||
{
|
||||
name: "updateAndFieldMaskMismatch",
|
||||
inProjectId: proj.GetPublicId(),
|
||||
inVersion: 1,
|
||||
inFieldMask: []string{"name"},
|
||||
changeFns: []func(*LdapCredentialLibrary) *LdapCredentialLibrary{
|
||||
updateDescriptionFn("updateAndFieldMaskMismatch Updated Description"),
|
||||
},
|
||||
expUpdate: false,
|
||||
expOut: &LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{
|
||||
Name: "TestUpdateLdapCredentialLibrary/updateAndFieldMaskMismatch Original Name",
|
||||
Description: "TestUpdateLdapCredentialLibrary/updateAndFieldMaskMismatch Original Description",
|
||||
VaultPath: "ldap/creds/path",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "validUpdateName",
|
||||
inProjectId: proj.GetPublicId(),
|
||||
inVersion: 1,
|
||||
inFieldMask: []string{"name"},
|
||||
changeFns: []func(*LdapCredentialLibrary) *LdapCredentialLibrary{
|
||||
updateNameFn("validUpdateName Subtest Updated Name"),
|
||||
},
|
||||
expUpdate: true,
|
||||
expOut: &LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{
|
||||
Name: "validUpdateName Subtest Updated Name",
|
||||
Description: "TestUpdateLdapCredentialLibrary/validUpdateName Original Description",
|
||||
VaultPath: "ldap/creds/path",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "validUpdateDescription",
|
||||
inProjectId: proj.GetPublicId(),
|
||||
inVersion: 1,
|
||||
inFieldMask: []string{"description"},
|
||||
changeFns: []func(*LdapCredentialLibrary) *LdapCredentialLibrary{
|
||||
updateDescriptionFn("validUpdateDescription Subtest Updated Description"),
|
||||
},
|
||||
expUpdate: true,
|
||||
expOut: &LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{
|
||||
Name: "TestUpdateLdapCredentialLibrary/validUpdateDescription Original Name",
|
||||
Description: "validUpdateDescription Subtest Updated Description",
|
||||
VaultPath: "ldap/creds/path",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "validUpdateVaultPath",
|
||||
inProjectId: proj.GetPublicId(),
|
||||
inVersion: 1,
|
||||
inFieldMask: []string{"vaultPath"},
|
||||
changeFns: []func(*LdapCredentialLibrary) *LdapCredentialLibrary{
|
||||
updateVaultPathFn("ldap/creds/updated/path/new"),
|
||||
},
|
||||
expUpdate: true,
|
||||
expOut: &LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{
|
||||
Name: "TestUpdateLdapCredentialLibrary/validUpdateVaultPath Original Name",
|
||||
Description: "TestUpdateLdapCredentialLibrary/validUpdateVaultPath Original Description",
|
||||
VaultPath: "ldap/creds/updated/path/new",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "validUpdateMultipleFields",
|
||||
inProjectId: proj.GetPublicId(),
|
||||
inVersion: 1,
|
||||
inFieldMask: []string{"name", "description", "vaultPath"},
|
||||
changeFns: []func(*LdapCredentialLibrary) *LdapCredentialLibrary{
|
||||
updateNameFn("validUpdateMultipleFields Updated Name"),
|
||||
updateDescriptionFn("validUpdateMultipleFields Updated Description"),
|
||||
updateVaultPathFn("ldap/static-cred/updated/path/new"),
|
||||
},
|
||||
expUpdate: true,
|
||||
expOut: &LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{
|
||||
Name: "validUpdateMultipleFields Updated Name",
|
||||
Description: "validUpdateMultipleFields Updated Description",
|
||||
VaultPath: "ldap/static-cred/updated/path/new",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "validUpdateNameExtraChangesNotInFieldMask",
|
||||
inProjectId: proj.GetPublicId(),
|
||||
inVersion: 1,
|
||||
inFieldMask: []string{"name"},
|
||||
changeFns: []func(*LdapCredentialLibrary) *LdapCredentialLibrary{
|
||||
updateNameFn("validUpdateNameExtraChanges Updated Name"),
|
||||
updateDescriptionFn("validUpdateNameExtraChanges Updated Description"),
|
||||
updateVaultPathFn("ldap/creds/updated/path/new"),
|
||||
updateStoreIdFn(),
|
||||
},
|
||||
expUpdate: true,
|
||||
expOut: &LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{
|
||||
Name: "validUpdateNameExtraChanges Updated Name",
|
||||
Description: "TestUpdateLdapCredentialLibrary/validUpdateNameExtraChangesNotInFieldMask Original Description",
|
||||
VaultPath: "ldap/creds/path",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
createdLib, err := repo.CreateLdapCredentialLibrary(t.Context(), proj.GetPublicId(), &LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
Name: t.Name() + " Original Name",
|
||||
Description: t.Name() + " Original Description",
|
||||
VaultPath: "ldap/creds/path",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, createdLib)
|
||||
|
||||
inDomainObj := createdLib.clone()
|
||||
for _, changeFn := range tt.changeFns {
|
||||
inDomainObj = changeFn(inDomainObj)
|
||||
}
|
||||
|
||||
updatedLdapLib, updated, err := repo.UpdateLdapCredentialLibrary(t.Context(),
|
||||
tt.inProjectId, inDomainObj, tt.inVersion, tt.inFieldMask,
|
||||
)
|
||||
if tt.expErr {
|
||||
require.ErrorContains(t, err, tt.expErrStr)
|
||||
require.Nil(t, updatedLdapLib)
|
||||
require.EqualValues(t, 0, updated)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, updatedLdapLib)
|
||||
require.NotNil(t, updatedLdapLib.LdapCredentialLibrary)
|
||||
|
||||
if tt.expUpdate {
|
||||
require.Greater(t, updatedLdapLib.GetUpdateTime().AsTime(), createdLib.GetUpdateTime().AsTime())
|
||||
require.EqualValues(t, createdLib.GetVersion()+1, updatedLdapLib.GetVersion())
|
||||
} else {
|
||||
require.EqualValues(t, createdLib.GetUpdateTime().AsTime(), updatedLdapLib.GetUpdateTime().AsTime())
|
||||
require.EqualValues(t, createdLib.GetVersion(), updatedLdapLib.GetVersion())
|
||||
}
|
||||
require.EqualValues(t, 1, updated)
|
||||
require.EqualValues(t, createdLib.GetPublicId(), updatedLdapLib.GetPublicId())
|
||||
require.EqualValues(t, createdLib.GetCreateTime().AsTime(), updatedLdapLib.GetCreateTime().AsTime())
|
||||
require.EqualValues(t, createdLib.GetStoreId(), updatedLdapLib.GetStoreId())
|
||||
require.EqualValues(t, createdLib.GetCredentialType(), updatedLdapLib.GetCredentialType())
|
||||
require.EqualValues(t, tt.expOut.GetName(), updatedLdapLib.GetName())
|
||||
require.EqualValues(t, tt.expOut.GetDescription(), updatedLdapLib.GetDescription())
|
||||
require.EqualValues(t, tt.expOut.GetVaultPath(), updatedLdapLib.GetVaultPath())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookupLdapCredentialLibrary(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
|
||||
_, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
cs := TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0]
|
||||
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
rw := db.New(conn)
|
||||
|
||||
repo, err := NewRepository(t.Context(), rw, rw, kms, sche)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, repo)
|
||||
|
||||
t.Run("noPublicId", func(t *testing.T) {
|
||||
l, err := repo.LookupLdapCredentialLibrary(t.Context(), "")
|
||||
require.ErrorContains(t, err, "no public id")
|
||||
require.Nil(t, l)
|
||||
})
|
||||
|
||||
t.Run("credLibNotFound", func(t *testing.T) {
|
||||
l, err := repo.LookupLdapCredentialLibrary(t.Context(), "alibthat_doesnt_exist")
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, l)
|
||||
})
|
||||
|
||||
t.Run("success", func(t *testing.T) {
|
||||
createdLib, err := repo.CreateLdapCredentialLibrary(t.Context(), proj.GetPublicId(), &LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
Name: "success",
|
||||
Description: "success Subtest",
|
||||
VaultPath: "ldap/creds/path",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, createdLib)
|
||||
|
||||
lookupLib, err := repo.LookupLdapCredentialLibrary(t.Context(), createdLib.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, cmp.Diff(createdLib, lookupLib, protocmp.Transform()))
|
||||
})
|
||||
}
|
||||
|
||||
func TestDeleteLdapCredentialLibrary(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
|
||||
_, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
cs := TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0]
|
||||
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
rw := db.New(conn)
|
||||
|
||||
repo, err := NewRepository(t.Context(), rw, rw, kms, sche)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, repo)
|
||||
|
||||
t.Run("noPublicId", func(t *testing.T) {
|
||||
deleted, err := repo.DeleteLdapCredentialLibrary(t.Context(), proj.GetPublicId(), "")
|
||||
require.ErrorContains(t, err, "no public id")
|
||||
require.EqualValues(t, 0, deleted)
|
||||
})
|
||||
|
||||
t.Run("noProjectId", func(t *testing.T) {
|
||||
deleted, err := repo.DeleteLdapCredentialLibrary(t.Context(), "", "cvllt_doesntexist")
|
||||
require.ErrorContains(t, err, "no project id")
|
||||
require.EqualValues(t, 0, deleted)
|
||||
})
|
||||
|
||||
t.Run("credLibDoesntExist", func(t *testing.T) {
|
||||
deleted, err := repo.DeleteLdapCredentialLibrary(t.Context(), proj.GetPublicId(), "cvllt_doesntexist")
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, 0, deleted)
|
||||
})
|
||||
|
||||
t.Run("success", func(t *testing.T) {
|
||||
createdLib, err := repo.CreateLdapCredentialLibrary(t.Context(), proj.GetPublicId(), &LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
Name: "success",
|
||||
Description: "success Subtest",
|
||||
VaultPath: "ldap/creds/path",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, createdLib)
|
||||
|
||||
lookupLib, err := repo.LookupLdapCredentialLibrary(t.Context(), createdLib.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, cmp.Diff(createdLib, lookupLib, protocmp.Transform()))
|
||||
|
||||
deleted, err := repo.DeleteLdapCredentialLibrary(t.Context(), proj.GetPublicId(), lookupLib.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, 1, deleted)
|
||||
|
||||
lookupLib2, err := repo.LookupLdapCredentialLibrary(t.Context(), createdLib.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, lookupLib2)
|
||||
})
|
||||
}
|
||||
@@ -1269,6 +1269,134 @@ func (x *SshPrivateKeyOverride) GetPrivateKeyPassphraseAttribute() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
type LdapCredentialLibrary struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// public_id is a surrogate key suitable for use in a public API.
|
||||
// @inject_tag: `gorm:"primary_key"`
|
||||
PublicId string `protobuf:"bytes,1,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"`
|
||||
// create_time is set by the database.
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
CreateTime *timestamp.Timestamp `protobuf:"bytes,2,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"`
|
||||
// update_time is set by the database.
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
UpdateTime *timestamp.Timestamp `protobuf:"bytes,3,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty" gorm:"default:current_timestamp"`
|
||||
// name is optional. If set, it must be unique within project_id.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"`
|
||||
// description is optional.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty" gorm:"default:null"`
|
||||
// store_id of the owning 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"`
|
||||
// credential_type is always ssh_certificate
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
CredentialType string `protobuf:"bytes,9,opt,name=credential_type,json=credentialType,proto3" json:"credential_type,omitempty" gorm:"default:null"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *LdapCredentialLibrary) Reset() {
|
||||
*x = LdapCredentialLibrary{}
|
||||
mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[9]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *LdapCredentialLibrary) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*LdapCredentialLibrary) ProtoMessage() {}
|
||||
|
||||
func (x *LdapCredentialLibrary) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[9]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use LdapCredentialLibrary.ProtoReflect.Descriptor instead.
|
||||
func (*LdapCredentialLibrary) Descriptor() ([]byte, []int) {
|
||||
return file_controller_storage_credential_vault_store_v1_vault_proto_rawDescGZIP(), []int{9}
|
||||
}
|
||||
|
||||
func (x *LdapCredentialLibrary) GetPublicId() string {
|
||||
if x != nil {
|
||||
return x.PublicId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *LdapCredentialLibrary) GetCreateTime() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.CreateTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *LdapCredentialLibrary) GetUpdateTime() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.UpdateTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *LdapCredentialLibrary) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *LdapCredentialLibrary) GetDescription() string {
|
||||
if x != nil {
|
||||
return x.Description
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *LdapCredentialLibrary) GetStoreId() string {
|
||||
if x != nil {
|
||||
return x.StoreId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *LdapCredentialLibrary) GetVersion() uint32 {
|
||||
if x != nil {
|
||||
return x.Version
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *LdapCredentialLibrary) GetVaultPath() string {
|
||||
if x != nil {
|
||||
return x.VaultPath
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *LdapCredentialLibrary) GetCredentialType() string {
|
||||
if x != nil {
|
||||
return x.CredentialType
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_controller_storage_credential_vault_store_v1_vault_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_controller_storage_credential_vault_store_v1_vault_proto_rawDesc = "" +
|
||||
@@ -1424,7 +1552,23 @@ const file_controller_storage_credential_vault_store_v1_vault_proto_rawDesc = ""
|
||||
"library_id\x18\x01 \x01(\tR\tlibraryId\x12-\n" +
|
||||
"\x12username_attribute\x18\x02 \x01(\tR\x11usernameAttribute\x122\n" +
|
||||
"\x15private_key_attribute\x18\x03 \x01(\tR\x13privateKeyAttribute\x12G\n" +
|
||||
" private_key_passphrase_attribute\x18\x04 \x01(\tR\x1dprivateKeyPassphraseAttributeBEZCgithub.com/hashicorp/boundary/internal/credential/vault/store;storeb\x06proto3"
|
||||
" private_key_passphrase_attribute\x18\x04 \x01(\tR\x1dprivateKeyPassphraseAttribute\"\xd5\x03\n" +
|
||||
"\x15LdapCredentialLibrary\x12\x1b\n" +
|
||||
"\tpublic_id\x18\x01 \x01(\tR\bpublicId\x12K\n" +
|
||||
"\vcreate_time\x18\x02 \x01(\v2*.controller.storage.timestamp.v1.TimestampR\n" +
|
||||
"createTime\x12K\n" +
|
||||
"\vupdate_time\x18\x03 \x01(\v2*.controller.storage.timestamp.v1.TimestampR\n" +
|
||||
"updateTime\x12$\n" +
|
||||
"\x04name\x18\x04 \x01(\tB\x10\xc2\xdd)\f\n" +
|
||||
"\x04Name\x12\x04nameR\x04name\x12@\n" +
|
||||
"\vdescription\x18\x05 \x01(\tB\x1e\xc2\xdd)\x1a\n" +
|
||||
"\vDescription\x12\vdescriptionR\vdescription\x12\x19\n" +
|
||||
"\bstore_id\x18\x06 \x01(\tR\astoreId\x12\x18\n" +
|
||||
"\aversion\x18\a \x01(\rR\aversion\x12?\n" +
|
||||
"\n" +
|
||||
"vault_path\x18\b \x01(\tB \xc2\xdd)\x1c\n" +
|
||||
"\tVaultPath\x12\x0fattributes.pathR\tvaultPath\x12'\n" +
|
||||
"\x0fcredential_type\x18\t \x01(\tR\x0ecredentialTypeBEZCgithub.com/hashicorp/boundary/internal/credential/vault/store;storeb\x06proto3"
|
||||
|
||||
var (
|
||||
file_controller_storage_credential_vault_store_v1_vault_proto_rawDescOnce sync.Once
|
||||
@@ -1438,7 +1582,7 @@ func file_controller_storage_credential_vault_store_v1_vault_proto_rawDescGZIP()
|
||||
return file_controller_storage_credential_vault_store_v1_vault_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
|
||||
var file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
|
||||
var file_controller_storage_credential_vault_store_v1_vault_proto_goTypes = []any{
|
||||
(*CredentialStore)(nil), // 0: controller.storage.credential.vault.store.v1.CredentialStore
|
||||
(*Token)(nil), // 1: controller.storage.credential.vault.store.v1.Token
|
||||
@@ -1449,29 +1593,32 @@ var file_controller_storage_credential_vault_store_v1_vault_proto_goTypes = []an
|
||||
(*UsernamePasswordOverride)(nil), // 6: controller.storage.credential.vault.store.v1.UsernamePasswordOverride
|
||||
(*UsernamePasswordDomainOverride)(nil), // 7: controller.storage.credential.vault.store.v1.UsernamePasswordDomainOverride
|
||||
(*SshPrivateKeyOverride)(nil), // 8: controller.storage.credential.vault.store.v1.SshPrivateKeyOverride
|
||||
(*timestamp.Timestamp)(nil), // 9: controller.storage.timestamp.v1.Timestamp
|
||||
(*LdapCredentialLibrary)(nil), // 9: controller.storage.credential.vault.store.v1.LdapCredentialLibrary
|
||||
(*timestamp.Timestamp)(nil), // 10: controller.storage.timestamp.v1.Timestamp
|
||||
}
|
||||
var file_controller_storage_credential_vault_store_v1_vault_proto_depIdxs = []int32{
|
||||
9, // 0: controller.storage.credential.vault.store.v1.CredentialStore.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
9, // 1: controller.storage.credential.vault.store.v1.CredentialStore.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
9, // 2: controller.storage.credential.vault.store.v1.CredentialStore.delete_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
9, // 3: controller.storage.credential.vault.store.v1.Token.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
9, // 4: controller.storage.credential.vault.store.v1.Token.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
9, // 5: controller.storage.credential.vault.store.v1.Token.last_renewal_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
9, // 6: controller.storage.credential.vault.store.v1.Token.expiration_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
9, // 7: controller.storage.credential.vault.store.v1.CredentialLibrary.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
9, // 8: controller.storage.credential.vault.store.v1.CredentialLibrary.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
9, // 9: controller.storage.credential.vault.store.v1.SSHCertificateCredentialLibrary.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
9, // 10: controller.storage.credential.vault.store.v1.SSHCertificateCredentialLibrary.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
9, // 11: controller.storage.credential.vault.store.v1.Credential.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
9, // 12: controller.storage.credential.vault.store.v1.Credential.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
9, // 13: controller.storage.credential.vault.store.v1.Credential.last_renewal_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
9, // 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
|
||||
10, // 0: controller.storage.credential.vault.store.v1.CredentialStore.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 1: controller.storage.credential.vault.store.v1.CredentialStore.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 2: controller.storage.credential.vault.store.v1.CredentialStore.delete_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 3: controller.storage.credential.vault.store.v1.Token.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 4: controller.storage.credential.vault.store.v1.Token.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 5: controller.storage.credential.vault.store.v1.Token.last_renewal_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 6: controller.storage.credential.vault.store.v1.Token.expiration_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 7: controller.storage.credential.vault.store.v1.CredentialLibrary.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 8: controller.storage.credential.vault.store.v1.CredentialLibrary.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 9: controller.storage.credential.vault.store.v1.SSHCertificateCredentialLibrary.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 10: controller.storage.credential.vault.store.v1.SSHCertificateCredentialLibrary.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 11: controller.storage.credential.vault.store.v1.Credential.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 12: controller.storage.credential.vault.store.v1.Credential.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 13: controller.storage.credential.vault.store.v1.Credential.last_renewal_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 14: controller.storage.credential.vault.store.v1.Credential.expiration_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 15: controller.storage.credential.vault.store.v1.LdapCredentialLibrary.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
10, // 16: controller.storage.credential.vault.store.v1.LdapCredentialLibrary.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
17, // [17:17] is the sub-list for method output_type
|
||||
17, // [17:17] is the sub-list for method input_type
|
||||
17, // [17:17] is the sub-list for extension type_name
|
||||
17, // [17:17] is the sub-list for extension extendee
|
||||
0, // [0:17] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_controller_storage_credential_vault_store_v1_vault_proto_init() }
|
||||
@@ -1485,7 +1632,7 @@ func file_controller_storage_credential_vault_store_v1_vault_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_controller_storage_credential_vault_store_v1_vault_proto_rawDesc), len(file_controller_storage_credential_vault_store_v1_vault_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 9,
|
||||
NumMessages: 10,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
package vault
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@@ -32,15 +34,18 @@ const DefaultVaultVersion = "1.15.1"
|
||||
var (
|
||||
vaultRepository = "hashicorp/vault"
|
||||
postgresRepository = "postgres"
|
||||
ldapRepository = "osixia/openldap"
|
||||
)
|
||||
|
||||
func init() {
|
||||
newVaultServer = gotNewServer
|
||||
mountLdapServer = gotLdapServer
|
||||
mountDatabase = gotMountDatabase
|
||||
|
||||
mirror := os.Getenv("DOCKER_MIRROR")
|
||||
if mirror != "" {
|
||||
vaultRepository = strings.Join([]string{mirror, vaultRepository}, "/")
|
||||
ldapRepository = strings.Join([]string{mirror, ldapRepository}, "/")
|
||||
postgresRepository = strings.Join([]string{mirror, postgresRepository}, "/")
|
||||
}
|
||||
}
|
||||
@@ -217,20 +222,233 @@ func gotNewServer(t testing.TB, opt ...TestOption) *TestVaultServer {
|
||||
return server
|
||||
}
|
||||
|
||||
func gotLdapServer(t testing.TB, v *TestVaultServer, opt ...TestOption) *TestLdapServer {
|
||||
require.NotNil(t, v.pool)
|
||||
require.NotNil(t, v.network, "Vault server must be created with docker network")
|
||||
require.NotNil(t, v.vaultContainer, "vault container doesn't exist")
|
||||
require.Nil(t, v.ldapContainer, "ldap container exists")
|
||||
|
||||
pool, ok := v.pool.(*dockertest.Pool)
|
||||
require.True(t, ok)
|
||||
nw, ok := v.network.(*dockertest.Network)
|
||||
require.True(t, ok)
|
||||
|
||||
opts := getTestOpts(t, opt...)
|
||||
if opts.ldapRootDomain == "" {
|
||||
opts.ldapRootDomain = "example.org"
|
||||
}
|
||||
if opts.bindPass == "" {
|
||||
opts.bindPass = "admin"
|
||||
}
|
||||
|
||||
dcl := []string{}
|
||||
for d := range strings.SplitSeq(opts.ldapRootDomain, ".") {
|
||||
dcl = append(dcl, "dc="+d)
|
||||
}
|
||||
dc := strings.Join(dcl, ",")
|
||||
dn := fmt.Sprintf("cn=admin,%s", dc)
|
||||
|
||||
server := &TestLdapServer{
|
||||
BindDomain: opts.ldapRootDomain,
|
||||
BindPass: opts.bindPass,
|
||||
BindDn: dn,
|
||||
}
|
||||
|
||||
userLdif := func(username, dc string) string {
|
||||
template := `
|
||||
dn: cn=%[1]s,%[2]s
|
||||
objectClass: inetOrgPerson
|
||||
cn: %[1]s
|
||||
sn: %[1]s
|
||||
uid: %[1]s
|
||||
userPassword: password
|
||||
`
|
||||
template = fmt.Sprintf(template, username, dc)
|
||||
// LDIF is whitespace sensitive, so remove the leading tab characters we
|
||||
// have from having indented the template.
|
||||
return strings.ReplaceAll(template, "\t", "")
|
||||
}
|
||||
userEinsteinLdif := userLdif("einstein", dc)
|
||||
userNewtonLdif := userLdif("newton", dc)
|
||||
|
||||
groupScientistsLdifTemplate := `
|
||||
dn: cn=scientists,%[1]s
|
||||
objectClass: groupOfUniqueNames
|
||||
cn: scientists
|
||||
uniqueMember: cn=einstein,%[1]s
|
||||
uniqueMember: cn=netwon,%[1]s
|
||||
`
|
||||
groupLdif := fmt.Sprintf(groupScientistsLdifTemplate, dc)
|
||||
// LDIF is whitespace sensitive, so remove the leading tab characters we
|
||||
// have from having indented the template.
|
||||
groupLdif = strings.ReplaceAll(groupLdif, "\t", "")
|
||||
|
||||
dir, err := base62.Random(12)
|
||||
require.NoError(t, err)
|
||||
dir = path.Join("/tmp", dir)
|
||||
outdir := path.Join(dir, "ldap")
|
||||
require.NoError(t, os.Mkdir(dir, os.ModePerm))
|
||||
require.NoError(t, os.Mkdir(outdir, os.ModePerm))
|
||||
require.NoError(t, os.WriteFile(fmt.Sprintf("%s/%s", outdir, "userEinstein.ldif"), []byte(userEinsteinLdif), 0o666))
|
||||
require.NoError(t, os.WriteFile(fmt.Sprintf("%s/%s", outdir, "userNewton.ldif"), []byte(userNewtonLdif), 0o666))
|
||||
require.NoError(t, os.WriteFile(fmt.Sprintf("%s/%s", outdir, "group.ldif"), []byte(groupLdif), 0o666))
|
||||
|
||||
resource, err := pool.RunWithOptions(&dockertest.RunOptions{
|
||||
Repository: ldapRepository,
|
||||
Tag: "latest",
|
||||
Networks: []*dockertest.Network{nw},
|
||||
Mounts: []string{fmt.Sprintf("%s:/tmp/ldap", outdir)},
|
||||
Env: []string{
|
||||
fmt.Sprintf("LDAP_DOMAIN=%s", server.BindDomain),
|
||||
fmt.Sprintf("LDAP_ADMIN_PASSWORD=%s", server.BindPass),
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() { cleanupResource(t, pool, resource) })
|
||||
v.ldapContainer = resource
|
||||
server.Url = fmt.Sprintf("ldap://%s", resource.GetIPInNetwork(nw))
|
||||
|
||||
// Wait until the container is healthy or timeout.
|
||||
timeout := 10 * time.Second
|
||||
ctx, cancel := context.WithTimeout(t.Context(), timeout)
|
||||
t.Cleanup(cancel)
|
||||
for loop := true; loop; {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
t.Fatalf("LDAP server was not healthy after trying for %s", timeout)
|
||||
default:
|
||||
buf := bytes.NewBuffer(nil)
|
||||
exitCode, _ := resource.Exec([]string{
|
||||
"ldapwhoami", "-x", "-H", "ldap://localhost", "-D", server.BindDn, "-w", server.BindPass,
|
||||
}, dockertest.ExecOptions{
|
||||
StdOut: buf,
|
||||
})
|
||||
// ldapwhoami will output the caller's identity in the form of a DN.
|
||||
// Because we're using the admin user here, we can just look for the
|
||||
// BindDn in stdout.
|
||||
if exitCode == 0 || strings.Contains(buf.String(), server.BindDn) {
|
||||
loop = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
ebuf := bytes.NewBuffer(nil)
|
||||
exitCode, err := resource.Exec([]string{"ldapadd", "-x", "-H", "ldap://localhost", "-D", server.BindDn, "-w", server.BindPass, "-f", "/tmp/ldap/userEinstein.ldif"}, dockertest.ExecOptions{
|
||||
StdOut: buf,
|
||||
StdErr: ebuf,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, ebuf)
|
||||
require.Equal(t, 0, exitCode)
|
||||
|
||||
buf.Reset()
|
||||
ebuf.Reset()
|
||||
exitCode, err = resource.Exec([]string{"ldapadd", "-x", "-H", "ldap://localhost", "-D", server.BindDn, "-w", server.BindPass, "-f", "/tmp/ldap/userNewton.ldif"}, dockertest.ExecOptions{
|
||||
StdOut: buf,
|
||||
StdErr: ebuf,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, ebuf)
|
||||
require.Equal(t, 0, exitCode)
|
||||
|
||||
buf.Reset()
|
||||
ebuf.Reset()
|
||||
exitCode, err = resource.Exec([]string{"ldapadd", "-x", "-H", "ldap://localhost", "-D", server.BindDn, "-w", server.BindPass, "-f", "/tmp/ldap/group.ldif"}, dockertest.ExecOptions{
|
||||
StdOut: buf,
|
||||
StdErr: ebuf,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, ebuf)
|
||||
require.Equal(t, 0, exitCode)
|
||||
|
||||
// Mount and configure Vault LDAP secrets engine.
|
||||
v.MountLdap(t, WithBindDn(server.BindDn), WithBindPass(server.BindPass), WithLdapUrl(server.Url))
|
||||
|
||||
vc := v.client(t).cl
|
||||
mounts, err := vc.Sys().ListMounts()
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, mounts, "ldap/")
|
||||
|
||||
// Add static roles that correspond to the LDAP server users.
|
||||
_, err = vc.Logical().Write("ldap/static-role/einstein", map[string]any{
|
||||
"dn": "cn=einstein,dc=example,dc=org",
|
||||
"username": "einstein",
|
||||
"rotation_period": "24h",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = vc.Logical().Write("ldap/static-role/myorg/myproject/newton", map[string]any{
|
||||
"dn": "cn=newton,dc=example,dc=org",
|
||||
"username": "newton",
|
||||
"rotation_period": "24h",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Setup LDAP dynamic credentials.
|
||||
ldifCreateTemplate := `
|
||||
dn: cn={{.Username}},dc=example,dc=org
|
||||
changetype: add
|
||||
objectClass: inetOrgPerson
|
||||
cn: {{.Username}}
|
||||
sn: {{.Username}}
|
||||
uid: {{.Username}}
|
||||
userPassword:{{.Password}}
|
||||
|
||||
dn: cn=scientists,dc=example,dc=org
|
||||
changetype: modify
|
||||
add: uniqueMember
|
||||
uniqueMember: cn={{.Username}},dc=example,dc=org
|
||||
-`
|
||||
ldifCreateTemplate = strings.ReplaceAll(ldifCreateTemplate, "\t", "")
|
||||
|
||||
ldifDeleteTemplate := `
|
||||
dn: cn={{.Username}},dc=example,dc=org
|
||||
changetype: delete
|
||||
`
|
||||
ldifDeleteTemplate = strings.ReplaceAll(ldifDeleteTemplate, "\t", "")
|
||||
|
||||
_, err = vc.Logical().Write("ldap/role/scientists", map[string]any{
|
||||
"username_template": "b_{{.DisplayName}}_{{.RoleName}}_{{random 10}}_{{unix_time}}",
|
||||
"creation_ldif": ldifCreateTemplate,
|
||||
"deletion_ldif": ldifDeleteTemplate,
|
||||
"rollback_ldif": ldifDeleteTemplate,
|
||||
"default_ttl": "1h",
|
||||
"max_ttl": "24h",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = vc.Logical().Write("ldap/role/myorg/myproject/scientists", map[string]any{
|
||||
"username_template": "b_{{.DisplayName}}_{{.RoleName}}_{{random 10}}_{{unix_time}}",
|
||||
"creation_ldif": ldifCreateTemplate,
|
||||
"deletion_ldif": ldifDeleteTemplate,
|
||||
"rollback_ldif": ldifDeleteTemplate,
|
||||
"default_ttl": "1h",
|
||||
"max_ttl": "24h",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
return server
|
||||
}
|
||||
|
||||
func gotMountDatabase(t testing.TB, v *TestVaultServer, opt ...TestOption) *TestDatabase {
|
||||
require := require.New(t)
|
||||
require.Nil(v.postgresContainer, "postgres container exists")
|
||||
require.NotNil(v.pool)
|
||||
require.NotNil(v.network, "Vault server must be created with docker network")
|
||||
require.NotNil(v.vaultContainer, "vault container doesn't exist")
|
||||
require.Nil(v.postgresContainer, "postgres container exists")
|
||||
|
||||
pool, ok := v.pool.(*dockertest.Pool)
|
||||
require.True(ok)
|
||||
network, ok := v.network.(*dockertest.Network)
|
||||
nw, ok := v.network.(*dockertest.Network)
|
||||
require.True(ok)
|
||||
vaultContainer, ok := v.vaultContainer.(*dockertest.Resource)
|
||||
require.True(ok)
|
||||
|
||||
dockerOptions := &dockertest.RunOptions{
|
||||
Repository: postgresRepository,
|
||||
Tag: globals.MinimumSupportedPostgresVersion,
|
||||
Networks: []*dockertest.Network{network},
|
||||
Networks: []*dockertest.Network{nw},
|
||||
Env: []string{"POSTGRES_PASSWORD=password", "POSTGRES_DB=boundarytest"},
|
||||
}
|
||||
|
||||
@@ -316,13 +534,10 @@ func gotMountDatabase(t testing.TB, v *TestVaultServer, opt ...TestOption) *Test
|
||||
}
|
||||
v.addPolicy(t, "database", pc)
|
||||
|
||||
vaultContainer, ok := v.vaultContainer.(*dockertest.Resource)
|
||||
require.True(ok)
|
||||
|
||||
t.Log(vaultContainer.GetIPInNetwork(network))
|
||||
t.Log(vaultContainer.GetIPInNetwork(nw))
|
||||
|
||||
// Configure PostgreSQL secrets engine
|
||||
connUrl := fmt.Sprintf("postgresql://{{username}}:{{password}}@%s:5432/boundarytest?sslmode=disable", resource.GetIPInNetwork(network))
|
||||
connUrl := fmt.Sprintf("postgresql://{{username}}:{{password}}@%s:5432/boundarytest?sslmode=disable", resource.GetIPInNetwork(nw))
|
||||
t.Log(connUrl)
|
||||
|
||||
postgresConfPath := path.Join(mountPath, "config/postgresql")
|
||||
|
||||
@@ -180,6 +180,44 @@ func TestSSHCertificateCredentialLibraries(t testing.TB, conn *db.DB, _ wrapping
|
||||
return libs
|
||||
}
|
||||
|
||||
// TestLdapCredentialLibraries creates `count` number of Vault LDAP credential
|
||||
// libraries in the provided db with the provided credential store. If any
|
||||
// errors are encountered, the test will fail.
|
||||
func TestLdapCredentialLibraries(t testing.TB, conn *db.DB, _ wrapping.Wrapper, storeId string, count int) []*LdapCredentialLibrary {
|
||||
t.Helper()
|
||||
w := db.New(conn)
|
||||
|
||||
var libs []*LdapCredentialLibrary
|
||||
for i := range count {
|
||||
var staticOrDynamic string
|
||||
switch mrand.Intn(2) {
|
||||
case 0:
|
||||
staticOrDynamic = "static-cred"
|
||||
case 1:
|
||||
staticOrDynamic = "creds"
|
||||
}
|
||||
|
||||
lib, err := NewLdapCredentialLibrary(storeId, fmt.Sprintf("ldap/%s/role-%d", staticOrDynamic, i))
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, lib)
|
||||
|
||||
libId, err := newLdapCredentialLibraryId(t.Context())
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, libId)
|
||||
|
||||
lib.PublicId = libId
|
||||
_, err = w.DoTx(t.Context(), db.StdRetryCnt, db.ExpBackoff{},
|
||||
func(_ db.Reader, w db.Writer) error {
|
||||
return w.Create(t.Context(), lib)
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
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.
|
||||
@@ -532,6 +570,10 @@ type testOptions struct {
|
||||
clientKey *ecdsa.PrivateKey
|
||||
vaultVersion string
|
||||
serverCertHostNames []string
|
||||
bindDn string
|
||||
bindPass string
|
||||
ldapUrl string
|
||||
ldapRootDomain string
|
||||
}
|
||||
|
||||
func getDefaultTestOptions(t testing.TB) testOptions {
|
||||
@@ -666,6 +708,40 @@ func WithAllowedExtension(e string) TestOption {
|
||||
}
|
||||
}
|
||||
|
||||
// WithBindDn sets the distinguished name to be used by the Vault LDAP secrets
|
||||
// engine.
|
||||
func WithBindDn(dn string) TestOption {
|
||||
return func(t testing.TB, o *testOptions) {
|
||||
t.Helper()
|
||||
o.bindDn = dn
|
||||
}
|
||||
}
|
||||
|
||||
// WithBindPass sets the password to be used by the Vault LDAP secrets engine.
|
||||
// It is also used to set the admin user password in the LDAP server.
|
||||
func WithBindPass(p string) TestOption {
|
||||
return func(t testing.TB, o *testOptions) {
|
||||
t.Helper()
|
||||
o.bindPass = p
|
||||
}
|
||||
}
|
||||
|
||||
// WithLdapUrl sets the URL to be used by the Vault LDAP secrets engine.
|
||||
func WithLdapUrl(u string) TestOption {
|
||||
return func(t testing.TB, o *testOptions) {
|
||||
t.Helper()
|
||||
o.ldapUrl = u
|
||||
}
|
||||
}
|
||||
|
||||
// WithLdapRootDomain controls the LDAP server's root domain.
|
||||
func WithLdapRootDomain(d string) TestOption {
|
||||
return func(t testing.TB, o *testOptions) {
|
||||
t.Helper()
|
||||
o.ldapRootDomain = d
|
||||
}
|
||||
}
|
||||
|
||||
// TestOrphanToken sets the token orphan option to b.
|
||||
// The orphan option is true by default.
|
||||
func TestOrphanToken(b bool) TestOption {
|
||||
@@ -916,6 +992,81 @@ func (v *TestVaultServer) MountSSH(t testing.TB, opt ...TestOption) *vault.Secre
|
||||
return s
|
||||
}
|
||||
|
||||
// MountLdap mounts the Vault LDAP secrets engine on a Vault instance and
|
||||
// initializes it.
|
||||
//
|
||||
// Supported options include:
|
||||
// - WithTestMountPath: Sets the mount path. Defaults to "ldap".
|
||||
// - WithBindDn: Sets the BindDN to be used by the Vault LDAP secrets engine.
|
||||
// Defaults to "cn=admin,dc=example,dc=org".
|
||||
// - WithBindPass: Sets the password to be used by the Vault LDAP secrets
|
||||
// engine. Defaults to "admin".
|
||||
// - WithLdapUrl: Sets the URL of the LDAP server to be used by the Vault LDAP
|
||||
// secrets engine. Defaults to "ldap://ldap".
|
||||
//
|
||||
// MountLdap also adds a Vault policy named 'ldap' to the standard set of
|
||||
// policies attached to tokens created with v.CreateToken. The policy is defined
|
||||
// as:
|
||||
//
|
||||
// path "<ldapMountPath>/*" {
|
||||
// capabilities = ["create", "read", "update", "delete", "list"]
|
||||
// }
|
||||
func (v *TestVaultServer) MountLdap(t testing.TB, opt ...TestOption) {
|
||||
require := require.New(t)
|
||||
opts := getTestOpts(t, opt...)
|
||||
vc := v.client(t).cl
|
||||
|
||||
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
|
||||
mountInput := &vault.MountInput{
|
||||
Type: "ldap",
|
||||
Description: t.Name(),
|
||||
Config: vault.MountConfigInput{
|
||||
DefaultLeaseTTL: defaultTTL.String(),
|
||||
MaxLeaseTTL: maxTTL.String(),
|
||||
},
|
||||
}
|
||||
mountPath := opts.mountPath
|
||||
if mountPath == "" {
|
||||
mountPath = "ldap/"
|
||||
}
|
||||
require.True(mountPath[len(mountPath)-1] == '/', "mount path must end with a slash")
|
||||
require.NoError(vc.Sys().Mount(mountPath, mountInput))
|
||||
|
||||
policyPath := fmt.Sprintf("%s*", mountPath)
|
||||
pc := pathCapabilities{
|
||||
policyPath: createCapability | readCapability | updateCapability | deleteCapability | listCapability,
|
||||
}
|
||||
v.addPolicy(t, "ldap", pc)
|
||||
|
||||
// Create and write LDAP server config
|
||||
bindDn := opts.bindDn
|
||||
if bindDn == "" {
|
||||
bindDn = "cn=admin,dc=example,dc=org"
|
||||
}
|
||||
bindPass := opts.bindPass
|
||||
if bindPass == "" {
|
||||
bindPass = "admin"
|
||||
}
|
||||
url := opts.ldapUrl
|
||||
if url == "" {
|
||||
url = "ldap://ldap"
|
||||
}
|
||||
ldapConfig := map[string]any{
|
||||
"binddn": bindDn,
|
||||
"bindpass": bindPass,
|
||||
"url": url,
|
||||
}
|
||||
_, err := vc.Logical().Write(path.Join(mountPath, "config"), ldapConfig)
|
||||
require.NoError(err)
|
||||
}
|
||||
|
||||
// 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.
|
||||
@@ -1038,9 +1189,12 @@ type TestVaultServer struct {
|
||||
serverCertBundle *testCertBundle
|
||||
clientCertBundle *testCertBundle
|
||||
|
||||
// Use any here instead of *dockertest.[Pool|Network|Resource] to avoid
|
||||
// problems with dockertest not building for some netbsd architectures.
|
||||
pool any
|
||||
vaultContainer any
|
||||
network any
|
||||
vaultContainer any
|
||||
ldapContainer any
|
||||
postgresContainer any
|
||||
}
|
||||
|
||||
@@ -1115,3 +1269,39 @@ func (d *TestDatabase) ValidateCredential(t testing.TB, s *vault.Secret) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TestLdapServer is returned from MountLdapServer and contains information
|
||||
// about how an LDAP server was setup. It is primarily used to test
|
||||
// Boundary <> Vault <> LDAP Server interactions.
|
||||
type TestLdapServer struct {
|
||||
BindDomain string
|
||||
BindPass string
|
||||
BindDn string
|
||||
Url string
|
||||
}
|
||||
|
||||
// MountLdapServer creates an LDAP server as a Docker container, within a
|
||||
// running Vault's Docker network. Additionally, it mounts and configures the
|
||||
// LDAP secrets engine in the passed in Vault instance.
|
||||
//
|
||||
// Supported options:
|
||||
// - WithBindPass. Controls the LDAP server's admin password. Defaults to
|
||||
// "admin".
|
||||
// - WithLdapRootDomain. Controls the LDAP server's root domain. Defaults to
|
||||
// "example.org".
|
||||
//
|
||||
// Some roles are added to the LDAP server as well as configured within Vault:
|
||||
// - scientists: A group of unique members: cn=scientists,dc=example,dc=org
|
||||
// - einstein: A user belonging to the scientists group: cn=einstein,dc=example,dc=org
|
||||
// - newton: Another user beloging to the scientists group: cn=newton,dc=example,dc=org
|
||||
//
|
||||
// Note that the domain components (dc) will change accordingly if WithDomain is
|
||||
// passed in.
|
||||
//
|
||||
// Dynamic roles are also setup using LDIF templates. These roles trigger Vault
|
||||
// to create users on-demand and manage their lifecycle. All dynamic users are
|
||||
// added to the "scientists" group.
|
||||
func MountLdapServer(t testing.TB, v *TestVaultServer, opt ...TestOption) *TestLdapServer {
|
||||
t.Helper()
|
||||
return mountLdapServer(t, v, opt...)
|
||||
}
|
||||
+162
-39
@@ -60,6 +60,7 @@ const (
|
||||
var (
|
||||
maskManager handlers.MaskManager
|
||||
sshCertMaskManager handlers.MaskManager
|
||||
ldapMaskManager handlers.MaskManager
|
||||
|
||||
// IdActions contains the set of actions that can be performed on
|
||||
// individual resources
|
||||
@@ -107,6 +108,13 @@ func init() {
|
||||
); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if ldapMaskManager, err = handlers.NewMaskManager(
|
||||
context.Background(),
|
||||
handlers.MaskDestination{&store.LdapCredentialLibrary{}},
|
||||
handlers.MaskSource{&pb.CredentialLibrary{}, &pb.VaultLdapCredentialLibraryAttributes{}},
|
||||
); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// TODO: refactor to remove IdActionsMap and CollectionActions package variables
|
||||
action.RegisterResource(resource.CredentialLibrary, IdActions, CollectionActions)
|
||||
@@ -366,19 +374,27 @@ func (s Service) UpdateCredentialLibrary(ctx context.Context, req *pbs.UpdateCre
|
||||
var currentCredentialType globals.CredentialType
|
||||
var mo vault.MappingOverride
|
||||
switch globals.ResourceInfoFromPrefix(req.GetId()).Subtype {
|
||||
case vault.SSHCertificateLibrarySubtype:
|
||||
cur, err := repo.LookupSSHCertificateCredentialLibrary(ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
currentCredentialType = globals.CredentialType(cur.GetCredentialType())
|
||||
default:
|
||||
case vault.GenericLibrarySubtype:
|
||||
cur, err := repo.LookupCredentialLibrary(ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
currentCredentialType = globals.CredentialType(cur.GetCredentialType())
|
||||
mo = cur.MappingOverride
|
||||
case vault.SSHCertificateLibrarySubtype:
|
||||
cur, err := repo.LookupSSHCertificateCredentialLibrary(ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
currentCredentialType = globals.CredentialType(cur.GetCredentialType())
|
||||
case vault.LdapCredentialLibrarySubtype:
|
||||
cur, err := repo.LookupLdapCredentialLibrary(ctx, req.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
currentCredentialType = globals.CredentialType(cur.GetCredentialType())
|
||||
default:
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "unknown vault credential library subtype")
|
||||
}
|
||||
|
||||
if err := validateUpdateRequest(req, currentCredentialType); err != nil {
|
||||
@@ -456,6 +472,15 @@ func (s Service) getFromRepo(ctx context.Context, id string) (credential.Library
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("ssh certificate credential library %q not found", id))
|
||||
}
|
||||
return cs, err
|
||||
case vault.LdapCredentialLibrarySubtype:
|
||||
cs, err := repo.LookupLdapCredentialLibrary(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("ldapcredential library %q not found", id))
|
||||
}
|
||||
return cs, err
|
||||
}
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "unrecognized credential library subtype")
|
||||
}
|
||||
@@ -464,6 +489,23 @@ func (s Service) createInRepo(ctx context.Context, scopeId string, item *pb.Cred
|
||||
const op = "credentiallibraries.(Service).createInRepo"
|
||||
var out credential.Library
|
||||
switch item.GetType() {
|
||||
case vault.GenericLibrarySubtype.String():
|
||||
cl, err := toStorageVaultLibrary(ctx, item.GetCredentialStoreId(), item)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
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
|
||||
case vault.SSHCertificateLibrarySubtype.String():
|
||||
cl, err := toStorageVaultSSHCertificateLibrary(ctx, item.GetCredentialStoreId(), item)
|
||||
if err != nil {
|
||||
@@ -481,8 +523,8 @@ func (s Service) createInRepo(ctx context.Context, scopeId string, item *pb.Cred
|
||||
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(ctx, item.GetCredentialStoreId(), item)
|
||||
case vault.LdapCredentialLibrarySubtype.String():
|
||||
cl, err := toStorageVaultLdapLibrary(ctx, item.GetCredentialStoreId(), item)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
@@ -490,14 +532,16 @@ func (s Service) createInRepo(ctx context.Context, scopeId string, item *pb.Cred
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
rl, err := repo.CreateCredentialLibrary(ctx, scopeId, cl)
|
||||
rl, err := repo.CreateLdapCredentialLibrary(ctx, scopeId, cl)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create credential library"))
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create ldap credential library"))
|
||||
}
|
||||
if rl == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create credential library but no error returned from repository.")
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create ldap credential library but no error returned from repository.")
|
||||
}
|
||||
out = rl
|
||||
default:
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.InvalidArgument, "Unknown vault credential library subtype %q", item.GetType())
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
@@ -534,6 +578,23 @@ func (s Service) updateInRepo(
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
switch globals.ResourceInfoFromPrefix(id).Subtype {
|
||||
case vault.GenericLibrarySubtype:
|
||||
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(ctx, 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)
|
||||
}
|
||||
case vault.SSHCertificateLibrarySubtype:
|
||||
dbMasks = append(dbMasks, sshCertMaskManager.Translate(masks)...)
|
||||
if getMapUpdate(criticalOptionsField, masks) {
|
||||
@@ -557,23 +618,25 @@ func (s Service) updateInRepo(
|
||||
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)...)
|
||||
case vault.LdapCredentialLibrarySubtype:
|
||||
dbMasks = append(dbMasks, ldapMaskManager.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(ctx, item.GetCredentialStoreId(), item)
|
||||
cl, err := toStorageVaultLdapLibrary(ctx, 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)
|
||||
out, rowsUpdated, err = repo.UpdateLdapCredentialLibrary(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:
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.InvalidArgument, "Unknown vault credential library subtype %q", globals.ResourceInfoFromPrefix(id).Subtype)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
@@ -586,10 +649,14 @@ func (s Service) deleteFromRepo(ctx context.Context, scopeId, id string) (bool,
|
||||
}
|
||||
rows := 0
|
||||
switch globals.ResourceInfoFromPrefix(id).Subtype {
|
||||
case vault.GenericLibrarySubtype:
|
||||
rows, err = repo.DeleteCredentialLibrary(ctx, scopeId, id)
|
||||
case vault.SSHCertificateLibrarySubtype:
|
||||
rows, err = repo.DeleteSSHCertificateCredentialLibrary(ctx, scopeId, id)
|
||||
case vault.LdapCredentialLibrarySubtype:
|
||||
rows, err = repo.DeleteLdapCredentialLibrary(ctx, scopeId, id)
|
||||
default:
|
||||
rows, err = repo.DeleteCredentialLibrary(ctx, scopeId, id)
|
||||
return false, handlers.ApiErrorWithCodeAndMessage(codes.InvalidArgument, "Unknown vault credential library subtype %q", globals.ResourceInfoFromPrefix(id).Subtype)
|
||||
}
|
||||
if err != nil {
|
||||
if errors.IsNotFoundError(err) {
|
||||
@@ -640,6 +707,17 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type, isRec
|
||||
return res
|
||||
}
|
||||
parentId = cl.GetStoreId()
|
||||
case vault.LdapCredentialLibrarySubtype:
|
||||
cl, err := repo.LookupLdapCredentialLibrary(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
|
||||
@@ -854,7 +932,21 @@ func toProto(ctx context.Context, in credential.Library, opt ...handlers.Option)
|
||||
VaultSshCertificateCredentialLibraryAttributes: attrs,
|
||||
}
|
||||
}
|
||||
|
||||
case vault.LdapCredentialLibrarySubtype:
|
||||
vaultIn, ok := in.(*vault.LdapCredentialLibrary)
|
||||
if !ok {
|
||||
return nil, errors.New(ctx, errors.Internal, op, "unable to cast to vault ldap credential library")
|
||||
}
|
||||
if outputFields.Has(globals.CredentialTypeField) {
|
||||
out.CredentialType = vaultIn.GetCredentialType()
|
||||
}
|
||||
if outputFields.Has(globals.AttributesField) {
|
||||
out.Attrs = &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
|
||||
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String(vaultIn.GetVaultPath()),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
@@ -992,20 +1084,33 @@ func toStorageVaultSSHCertificateLibrary(ctx context.Context, storeId string, in
|
||||
return cs, err
|
||||
}
|
||||
|
||||
func toStorageVaultLdapLibrary(ctx context.Context, storeId string, in *pb.CredentialLibrary) (*vault.LdapCredentialLibrary, error) {
|
||||
const op = "credentiallibraries.toStorageVaultLdapLibrary"
|
||||
|
||||
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()))
|
||||
}
|
||||
|
||||
attrs := in.GetVaultLdapCredentialLibraryAttributes()
|
||||
cs, err := vault.NewLdapCredentialLibrary(storeId, attrs.GetPath().GetValue(), opts...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to build credential library"))
|
||||
}
|
||||
|
||||
return cs, nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
prefix := ""
|
||||
switch globals.ResourceInfoFromPrefix(req.GetId()).Subtype {
|
||||
case vault.SSHCertificateLibrarySubtype:
|
||||
prefix = globals.VaultSshCertificateCredentialLibraryPrefix
|
||||
default:
|
||||
prefix = globals.VaultCredentialLibraryPrefix
|
||||
}
|
||||
return handlers.ValidateGetRequest(handlers.NoopValidatorFn, req, prefix)
|
||||
return handlers.ValidateGetRequest(handlers.NoopValidatorFn, req, globals.VaultCredentialLibraryPrefix, globals.VaultSshCertificateCredentialLibraryPrefix, globals.VaultLdapCredentialLibraryPrefix)
|
||||
}
|
||||
|
||||
func validateCreateRequest(req *pbs.CreateCredentialLibraryRequest) error {
|
||||
@@ -1048,8 +1153,9 @@ func validateCreateRequest(req *pbs.CreateCredentialLibraryRequest) error {
|
||||
}
|
||||
|
||||
if t != vault.GenericLibrarySubtype.String() &&
|
||||
t != vault.SSHCertificateLibrarySubtype.String() {
|
||||
badFields[globals.CredentialStoreIdField] = fmt.Sprintf("Type must be a vault subtype %q or %q", vault.GenericLibrarySubtype.String(), vault.SSHCertificateLibrarySubtype.String())
|
||||
t != vault.SSHCertificateLibrarySubtype.String() &&
|
||||
t != vault.LdapCredentialLibrarySubtype.String() {
|
||||
badFields[globals.CredentialStoreIdField] = fmt.Sprintf("Type must be a vault subtype %q, %q or %q", vault.GenericLibrarySubtype.String(), vault.SSHCertificateLibrarySubtype.String(), vault.LdapCredentialLibrarySubtype.String())
|
||||
}
|
||||
|
||||
switch req.GetItem().GetType() {
|
||||
@@ -1111,6 +1217,17 @@ func validateCreateRequest(req *pbs.CreateCredentialLibraryRequest) error {
|
||||
badFields[keyTypeField] = "If set, value must be 'ed25519', 'ecdsa', or 'rsa'."
|
||||
}
|
||||
validateKeyBits(badFields, attrs.GetKeyBits().GetValue(), attrs.GetKeyType().GetValue())
|
||||
case vault.LdapCredentialLibrarySubtype.String():
|
||||
if req.GetItem().GetCredentialType() != "" {
|
||||
badFields[globals.CredentialTypeField] = "This field is read only and cannot be set."
|
||||
}
|
||||
attrs := req.GetItem().GetVaultLdapCredentialLibraryAttributes()
|
||||
if attrs == nil {
|
||||
badFields[attributesPathField] = "This is a required field."
|
||||
}
|
||||
if attrs.GetPath().GetValue() == "" {
|
||||
badFields[vaultPathField] = "This is a required field."
|
||||
}
|
||||
}
|
||||
default:
|
||||
badFields[globals.CredentialStoreIdField] = "This field must be a valid credential store id."
|
||||
@@ -1120,17 +1237,9 @@ func validateCreateRequest(req *pbs.CreateCredentialLibraryRequest) error {
|
||||
}
|
||||
|
||||
func validateUpdateRequest(req *pbs.UpdateCredentialLibraryRequest, currentCredentialType globals.CredentialType) error {
|
||||
prefix := ""
|
||||
st := globals.ResourceInfoFromPrefix(req.GetId()).Subtype
|
||||
switch st {
|
||||
case vault.GenericLibrarySubtype:
|
||||
prefix = globals.VaultCredentialLibraryPrefix
|
||||
case vault.SSHCertificateLibrarySubtype:
|
||||
prefix = globals.VaultSshCertificateCredentialLibraryPrefix
|
||||
}
|
||||
return handlers.ValidateUpdateRequest(req, req.GetItem(), func() map[string]string {
|
||||
badFields := map[string]string{}
|
||||
switch st {
|
||||
switch globals.ResourceInfoFromPrefix(req.GetId()).Subtype {
|
||||
case vault.GenericLibrarySubtype:
|
||||
if req.GetItem().GetType() != "" && req.GetItem().GetType() != vault.GenericLibrarySubtype.String() {
|
||||
badFields[globals.TypeField] = "Cannot modify resource type."
|
||||
@@ -1175,13 +1284,27 @@ func validateUpdateRequest(req *pbs.UpdateCredentialLibraryRequest, currentCrede
|
||||
}
|
||||
validateKeyBits(badFields, attrs.GetKeyBits().GetValue(), attrs.GetKeyType().GetValue())
|
||||
}
|
||||
case vault.LdapCredentialLibrarySubtype:
|
||||
if req.GetItem().GetType() != "" && req.GetItem().GetType() != vault.LdapCredentialLibrarySubtype.String() {
|
||||
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().GetVaultLdapCredentialLibraryAttributes()
|
||||
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."
|
||||
}
|
||||
}
|
||||
}
|
||||
return badFields
|
||||
}, prefix)
|
||||
}, globals.VaultCredentialLibraryPrefix, globals.VaultSshCertificateCredentialLibraryPrefix, globals.VaultLdapCredentialLibraryPrefix)
|
||||
}
|
||||
|
||||
func validateDeleteRequest(req *pbs.DeleteCredentialLibraryRequest) error {
|
||||
return handlers.ValidateDeleteRequest(handlers.NoopValidatorFn, req, globals.VaultCredentialLibraryPrefix, globals.VaultSshCertificateCredentialLibraryPrefix)
|
||||
return handlers.ValidateDeleteRequest(handlers.NoopValidatorFn, req, globals.VaultCredentialLibraryPrefix, globals.VaultSshCertificateCredentialLibraryPrefix, globals.VaultLdapCredentialLibraryPrefix)
|
||||
}
|
||||
|
||||
func validateListRequest(ctx context.Context, req *pbs.ListCredentialLibrariesRequest) error {
|
||||
|
||||
+458
-16
@@ -14,6 +14,7 @@ import (
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/auth/password"
|
||||
"github.com/hashicorp/boundary/internal/authtoken"
|
||||
@@ -86,6 +87,25 @@ func sshCredentialLibraryToProto(credLib *vault.SSHCertificateCredentialLibrary,
|
||||
}
|
||||
}
|
||||
|
||||
func ldapCredentialLibraryToProto(credLib *vault.LdapCredentialLibrary, project *iam.Scope) *pb.CredentialLibrary {
|
||||
return &pb.CredentialLibrary{
|
||||
Id: credLib.GetPublicId(),
|
||||
CredentialStoreId: credLib.GetStoreId(),
|
||||
CredentialType: credLib.GetCredentialType(),
|
||||
Scope: &scopepb.ScopeInfo{Id: project.GetPublicId(), Type: scope.Project.String(), ParentScopeId: project.GetParentId()},
|
||||
CreatedTime: credLib.GetCreateTime().GetTimestamp(),
|
||||
UpdatedTime: credLib.GetUpdateTime().GetTimestamp(),
|
||||
Version: credLib.GetVersion(),
|
||||
Type: vault.LdapCredentialLibrarySubtype.String(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Attrs: &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
|
||||
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String(credLib.GetVaultPath()),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
@@ -114,6 +134,9 @@ func TestList(t *testing.T) {
|
||||
for _, l := range vault.TestSSHCertificateCredentialLibraries(t, conn, wrapper, store.GetPublicId(), 10) {
|
||||
wantLibraries = append(wantLibraries, sshCredentialLibraryToProto(l, prj))
|
||||
}
|
||||
for _, l := range vault.TestLdapCredentialLibraries(t, conn, wrapper, store.GetPublicId(), 10) {
|
||||
wantLibraries = append(wantLibraries, ldapCredentialLibraryToProto(l, prj))
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
@@ -131,7 +154,7 @@ func TestList(t *testing.T) {
|
||||
SortBy: "created_time",
|
||||
SortDir: "desc",
|
||||
RemovedIds: nil,
|
||||
EstItemCount: 20,
|
||||
EstItemCount: 30,
|
||||
},
|
||||
anonRes: &pbs.ListCredentialLibrariesResponse{
|
||||
Items: wantLibraries,
|
||||
@@ -139,7 +162,7 @@ func TestList(t *testing.T) {
|
||||
SortBy: "created_time",
|
||||
SortDir: "desc",
|
||||
RemovedIds: nil,
|
||||
EstItemCount: 20,
|
||||
EstItemCount: 30,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -264,16 +287,20 @@ func TestList_Attributes(t *testing.T) {
|
||||
|
||||
_, prj := iam.TestScopes(t, iamRepo)
|
||||
|
||||
ts := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 2)
|
||||
storeGeneric, storeSSHCertificate := ts[0], ts[1]
|
||||
ts := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 3)
|
||||
storeGeneric, storeSSHCertificate, storeLdap := ts[0], ts[1], ts[2]
|
||||
var wantLibrariesGeneric []*pb.CredentialLibrary
|
||||
var wantLibrariesSSHCertificate []*pb.CredentialLibrary
|
||||
var wantLibrariesLdap []*pb.CredentialLibrary
|
||||
for _, l := range vault.TestCredentialLibraries(t, conn, wrapper, storeGeneric.GetPublicId(), globals.UnspecifiedCredentialType, 5) {
|
||||
wantLibrariesGeneric = append(wantLibrariesGeneric, vaultCredentialLibraryToProto(l, prj))
|
||||
}
|
||||
for _, l := range vault.TestSSHCertificateCredentialLibraries(t, conn, wrapper, storeSSHCertificate.GetPublicId(), 10) {
|
||||
wantLibrariesSSHCertificate = append(wantLibrariesSSHCertificate, sshCredentialLibraryToProto(l, prj))
|
||||
}
|
||||
for _, l := range vault.TestLdapCredentialLibraries(t, conn, wrapper, storeLdap.GetPublicId(), 10) {
|
||||
wantLibrariesLdap = append(wantLibrariesLdap, ldapCredentialLibraryToProto(l, prj))
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
@@ -318,6 +345,24 @@ func TestList_Attributes(t *testing.T) {
|
||||
RemovedIds: nil,
|
||||
}, // anonymous user does not have access to attributes
|
||||
},
|
||||
{
|
||||
name: "Filter on Attribute Ldap Library",
|
||||
req: &pbs.ListCredentialLibrariesRequest{CredentialStoreId: storeLdap.GetPublicId(), Filter: fmt.Sprintf(`"/item/attributes/path"==%q`, wantLibrariesLdap[2].GetVaultLdapCredentialLibraryAttributes().GetPath().Value)},
|
||||
res: &pbs.ListCredentialLibrariesResponse{
|
||||
Items: wantLibrariesLdap[2:3],
|
||||
ResponseType: "complete",
|
||||
SortBy: "created_time",
|
||||
SortDir: "desc",
|
||||
RemovedIds: nil,
|
||||
EstItemCount: 1,
|
||||
},
|
||||
anonRes: &pbs.ListCredentialLibrariesResponse{
|
||||
ResponseType: "complete",
|
||||
SortBy: "created_time",
|
||||
SortDir: "desc",
|
||||
RemovedIds: nil,
|
||||
}, // anonymous user does not have access to attributes
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
@@ -586,6 +631,51 @@ func TestCreate(t *testing.T) {
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "No Vault LDAP path",
|
||||
req: &pbs.CreateCredentialLibraryRequest{
|
||||
Item: &pb.CredentialLibrary{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Type: vault.LdapCredentialLibrarySubtype.String(),
|
||||
Attrs: &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
|
||||
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String(""),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "Invalid Vault LDAP credential type",
|
||||
req: &pbs.CreateCredentialLibraryRequest{
|
||||
Item: &pb.CredentialLibrary{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Type: vault.LdapCredentialLibrarySubtype.String(),
|
||||
CredentialType: string(globals.SshPrivateKeyCredentialType),
|
||||
Attrs: &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
|
||||
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String("ldap/invalid/path"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "Invalid credential library type",
|
||||
req: &pbs.CreateCredentialLibraryRequest{
|
||||
Item: &pb.CredentialLibrary{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Type: "bla-invalid",
|
||||
CredentialType: "bla-invalid",
|
||||
},
|
||||
},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "Using POST method",
|
||||
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
||||
@@ -1103,6 +1193,40 @@ func TestCreate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Create a valid Vault LDAP Credential library",
|
||||
req: &pbs.CreateCredentialLibraryRequest{
|
||||
Item: &pb.CredentialLibrary{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Type: vault.LdapCredentialLibrarySubtype.String(),
|
||||
Attrs: &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
|
||||
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String("ldap/creds/hi"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
idPrefix: globals.VaultLdapCredentialLibraryPrefix + "_",
|
||||
res: &pbs.CreateCredentialLibraryResponse{
|
||||
Uri: fmt.Sprintf("credential-libraries/%s_", globals.VaultLdapCredentialLibraryPrefix),
|
||||
Item: &pb.CredentialLibrary{
|
||||
Id: store.GetPublicId(),
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
CreatedTime: store.GetCreateTime().GetTimestamp(),
|
||||
UpdatedTime: store.GetUpdateTime().GetTimestamp(),
|
||||
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: prj.GetType(), ParentScopeId: prj.GetParentId()},
|
||||
Version: 1,
|
||||
Type: vault.LdapCredentialLibrarySubtype.String(),
|
||||
CredentialType: string(globals.UsernamePasswordDomainCredentialType),
|
||||
Attrs: &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
|
||||
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String("ldap/creds/hi"),
|
||||
},
|
||||
},
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
@@ -1217,6 +1341,13 @@ func TestGet(t *testing.T) {
|
||||
sshCertLib, err := repo.CreateSSHCertificateCredentialLibrary(context.Background(), prj.GetPublicId(), lib2)
|
||||
require.NoError(t, err)
|
||||
|
||||
libLdap1, err := vault.NewLdapCredentialLibrary(store.GetPublicId(), "ldap/creds/test")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, libLdap1)
|
||||
libLdap1, err = repo.CreateLdapCredentialLibrary(t.Context(), prj.GetPublicId(), libLdap1)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, libLdap1)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
id string
|
||||
@@ -1367,6 +1498,28 @@ func TestGet(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "success-ldap",
|
||||
id: libLdap1.GetPublicId(),
|
||||
res: &pbs.GetCredentialLibraryResponse{
|
||||
Item: &pb.CredentialLibrary{
|
||||
Id: libLdap1.GetPublicId(),
|
||||
CredentialStoreId: libLdap1.GetStoreId(),
|
||||
CredentialType: libLdap1.GetCredentialType(),
|
||||
Scope: &scopepb.ScopeInfo{Id: store.GetProjectId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
|
||||
Type: vault.LdapCredentialLibrarySubtype.String(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
CreatedTime: libLdap1.CreateTime.GetTimestamp(),
|
||||
UpdatedTime: libLdap1.UpdateTime.GetTimestamp(),
|
||||
Version: 1,
|
||||
Attrs: &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
|
||||
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String(libLdap1.GetVaultPath()),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "not found error",
|
||||
id: fmt.Sprintf("%s_1234567890", globals.VaultCredentialLibraryPrefix),
|
||||
@@ -1429,6 +1582,7 @@ func TestDelete(t *testing.T) {
|
||||
store := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0]
|
||||
vl := vault.TestCredentialLibraries(t, conn, wrapper, store.GetPublicId(), globals.UnspecifiedCredentialType, 1)[0]
|
||||
vl2 := vault.TestSSHCertificateCredentialLibraries(t, conn, wrapper, store.GetPublicId(), 1)[0]
|
||||
vl3 := vault.TestLdapCredentialLibraries(t, conn, wrapper, store.GetPublicId(), 1)[0]
|
||||
s, err := NewService(ctx, iamRepoFn, repoFn, 1000)
|
||||
require.NoError(t, err)
|
||||
cases := []struct {
|
||||
@@ -1445,6 +1599,10 @@ func TestDelete(t *testing.T) {
|
||||
name: "success-ssh-cert",
|
||||
id: vl2.GetPublicId(),
|
||||
},
|
||||
{
|
||||
name: "success-ldap",
|
||||
id: vl3.GetPublicId(),
|
||||
},
|
||||
{
|
||||
name: "not found error",
|
||||
id: fmt.Sprintf("%s_1234567890", globals.VaultCredentialLibraryPrefix),
|
||||
@@ -3244,6 +3402,287 @@ func TestUpdate_SSHCertificateCredentialLibrary(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdate_LdapCredentialLibrary(t *testing.T) {
|
||||
testCtx := context.Background()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
rw := db.New(conn)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
repoFn := func() (*vault.Repository, error) {
|
||||
return vault.NewRepository(testCtx, rw, rw, kms, sche)
|
||||
}
|
||||
|
||||
_, prj := iam.TestScopes(t, iamRepo)
|
||||
ctx := auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId())
|
||||
|
||||
s, err := NewService(testCtx, iamRepoFn, repoFn, 1000)
|
||||
require.NoError(t, err)
|
||||
cs := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)
|
||||
store := cs[0]
|
||||
|
||||
freshLibrary := func(opt ...vault.Option) (*vault.LdapCredentialLibrary, func()) {
|
||||
repo, err := repoFn()
|
||||
require.NoError(t, err)
|
||||
lib, err := vault.NewLdapCredentialLibrary(store.GetPublicId(), "ldap/creds/foo", opt...)
|
||||
require.NoError(t, err)
|
||||
|
||||
vl, err := repo.CreateLdapCredentialLibrary(ctx, prj.GetPublicId(), lib)
|
||||
require.NoError(t, err)
|
||||
clean := func() {
|
||||
_, err := s.DeleteCredentialLibrary(ctx, &pbs.DeleteCredentialLibraryRequest{Id: vl.GetPublicId()})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
return vl, clean
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
req *pbs.UpdateCredentialLibraryRequest
|
||||
res func(*pb.CredentialLibrary) *pb.CredentialLibrary
|
||||
wantErr bool
|
||||
wantErrStatus int
|
||||
wantErrStr string
|
||||
}{
|
||||
{
|
||||
name: "updateNonExistentField",
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
Id: "", // Set at test runtime.
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"doesntExist"}},
|
||||
Item: &pb.CredentialLibrary{
|
||||
Name: wrapperspb.String("updateNonExistentField_test"),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrStatus: runtime.HTTPStatusFromCode(codes.InvalidArgument),
|
||||
wantErrStr: "No valid fields included in the update mask.",
|
||||
},
|
||||
{
|
||||
name: "updateImmutableField",
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
Id: "", // Set at test runtime.
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"storeId"}},
|
||||
Item: &pb.CredentialLibrary{
|
||||
CredentialStoreId: "immutable",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrStatus: runtime.HTTPStatusFromCode(codes.InvalidArgument),
|
||||
wantErrStr: "No valid fields included in the update mask.",
|
||||
},
|
||||
{
|
||||
name: "updateValidFieldWithInvalidData",
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
Id: "", // Set at test runtime.
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"attributes.path"}},
|
||||
Item: &pb.CredentialLibrary{
|
||||
Attrs: &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
|
||||
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String("invalid_path"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrStr: "vault_path_must_have_staticcred_or_creds constraint failed",
|
||||
},
|
||||
{
|
||||
name: "updateImmutableCredentialTypeField",
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
Id: "", // Set at test runtime.
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"credentialType"}},
|
||||
Item: &pb.CredentialLibrary{
|
||||
CredentialType: string(globals.SshPrivateKeyCredentialType),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrStatus: runtime.HTTPStatusFromCode(codes.InvalidArgument),
|
||||
wantErrStr: "Cannot modify credential type.",
|
||||
},
|
||||
{
|
||||
name: "updateImmutableTypeField",
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
Id: "", // Set at test runtime.
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"type"}},
|
||||
Item: &pb.CredentialLibrary{
|
||||
Type: string(globals.SshCertificateCredentialType),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrStatus: runtime.HTTPStatusFromCode(codes.InvalidArgument),
|
||||
wantErrStr: "Cannot modify resource type.",
|
||||
},
|
||||
{
|
||||
name: "unsetVaultPath",
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
Id: "", // Set at test runtime.
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"attributes.path"}},
|
||||
Item: &pb.CredentialLibrary{
|
||||
Attrs: &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
|
||||
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String(""),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrStatus: runtime.HTTPStatusFromCode(codes.InvalidArgument),
|
||||
wantErrStr: "attributes.path\", desc: \"This is a required field and cannot be set to empty.",
|
||||
},
|
||||
{
|
||||
name: "updateName",
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
Id: "", // Set at test runtime
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"name"}},
|
||||
Item: &pb.CredentialLibrary{
|
||||
Name: wrapperspb.String("updated_name"),
|
||||
},
|
||||
},
|
||||
res: func(cl *pb.CredentialLibrary) *pb.CredentialLibrary {
|
||||
cl.Name = wrapperspb.String("updated_name")
|
||||
return cl
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "updateDescription",
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
Id: "", // Set at test runtime
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"description"}},
|
||||
Item: &pb.CredentialLibrary{
|
||||
Description: wrapperspb.String("updated_description"),
|
||||
},
|
||||
},
|
||||
res: func(cl *pb.CredentialLibrary) *pb.CredentialLibrary {
|
||||
cl.Description = wrapperspb.String("updated_description")
|
||||
return cl
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "updateValidVaultPath",
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
Id: "", // Set at test runtime
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"attributes.path"}},
|
||||
Item: &pb.CredentialLibrary{
|
||||
Attrs: &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
|
||||
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String("ldap/static-cred/updated/path"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
res: func(cl *pb.CredentialLibrary) *pb.CredentialLibrary {
|
||||
cl.GetVaultLdapCredentialLibraryAttributes().Path = wrapperspb.String("ldap/static-cred/updated/path")
|
||||
return cl
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "updateMultipleOneNonExistent",
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
Id: "", // Set at test runtime.
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"name", "doesntExist"}},
|
||||
Item: &pb.CredentialLibrary{
|
||||
Name: wrapperspb.String("updated_name"),
|
||||
},
|
||||
},
|
||||
res: func(cl *pb.CredentialLibrary) *pb.CredentialLibrary {
|
||||
cl.Name = wrapperspb.String("updated_name")
|
||||
return cl
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "updateMultipleOneImmutable",
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
Id: "", // Set at test runtime.
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"name", "credentialStoreId"}},
|
||||
Item: &pb.CredentialLibrary{
|
||||
Name: wrapperspb.String("updated_name"),
|
||||
CredentialStoreId: "immutable",
|
||||
},
|
||||
},
|
||||
res: func(cl *pb.CredentialLibrary) *pb.CredentialLibrary {
|
||||
cl.Name = wrapperspb.String("updated_name")
|
||||
return cl
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "updateMultiple",
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
Id: "", // Set at test runtime
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"name", "attributes.path"}},
|
||||
Item: &pb.CredentialLibrary{
|
||||
Name: wrapperspb.String("updated_name"),
|
||||
Attrs: &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
|
||||
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String("ldap/creds/updated/path"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
res: func(cl *pb.CredentialLibrary) *pb.CredentialLibrary {
|
||||
cl.Name = wrapperspb.String("updated_name")
|
||||
cl.GetVaultLdapCredentialLibraryAttributes().Path = wrapperspb.String("ldap/creds/updated/path")
|
||||
return cl
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
st, cleanup := freshLibrary()
|
||||
defer cleanup()
|
||||
|
||||
if tc.req.Item.GetVersion() == 0 {
|
||||
tc.req.Item.Version = 1
|
||||
}
|
||||
if tc.req.GetId() == "" {
|
||||
tc.req.Id = st.GetPublicId()
|
||||
}
|
||||
resToChange, err := s.GetCredentialLibrary(ctx, &pbs.GetCredentialLibraryRequest{Id: st.GetPublicId()})
|
||||
require.NoError(err)
|
||||
|
||||
got, gErr := s.UpdateCredentialLibrary(ctx, tc.req)
|
||||
if tc.wantErr {
|
||||
require.ErrorContains(gErr, tc.wantErrStr)
|
||||
|
||||
gApiErr, ok := gErr.(*handlers.ApiError)
|
||||
if ok {
|
||||
// We don't always return status.
|
||||
require.NotNil(gApiErr)
|
||||
require.EqualValues(tc.wantErrStatus, gApiErr.Status)
|
||||
}
|
||||
require.Nil(got)
|
||||
return
|
||||
}
|
||||
require.NoError(gErr)
|
||||
require.NotNil(got)
|
||||
|
||||
want := &pbs.UpdateCredentialLibraryResponse{Item: tc.res(resToChange.GetItem())}
|
||||
gotUpdateTime := got.GetItem().GetUpdatedTime()
|
||||
created := st.GetCreateTime().GetTimestamp()
|
||||
assert.True(gotUpdateTime.AsTime().After(created.AsTime()), "Should have been updated after it's creation. Was updated %v, which is after %v", gotUpdateTime, created)
|
||||
|
||||
want.Item.UpdatedTime = got.Item.UpdatedTime
|
||||
|
||||
assert.EqualValues(2, got.Item.Version)
|
||||
want.Item.Version = 2
|
||||
|
||||
assert.Empty(cmp.Diff(
|
||||
got,
|
||||
want,
|
||||
protocmp.Transform(),
|
||||
cmpopts.SortSlices(func(a, b string) bool {
|
||||
return a < b
|
||||
}),
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListPagination(t *testing.T) {
|
||||
// Set database read timeout to avoid duplicates in response
|
||||
oldReadTimeout := globals.RefreshReadLookbackDuration
|
||||
@@ -3292,6 +3731,9 @@ func TestListPagination(t *testing.T) {
|
||||
for _, l := range vault.TestSSHCertificateCredentialLibraries(t, conn, wrapper, credStore.GetPublicId(), 5) {
|
||||
allCredentialLibraries = append(allCredentialLibraries, sshCredentialLibraryToProto(l, prj))
|
||||
}
|
||||
for _, l := range vault.TestLdapCredentialLibraries(t, conn, wrapper, credStore.GetPublicId(), 5) {
|
||||
allCredentialLibraries = append(allCredentialLibraries, ldapCredentialLibraryToProto(l, prj))
|
||||
}
|
||||
|
||||
// Reverse as we return items sorted by create_time desceding (newest first)
|
||||
slices.Reverse(allCredentialLibraries)
|
||||
@@ -3345,7 +3787,7 @@ func TestListPagination(t *testing.T) {
|
||||
SortBy: "created_time",
|
||||
SortDir: "desc",
|
||||
RemovedIds: nil,
|
||||
EstItemCount: 10,
|
||||
EstItemCount: 15,
|
||||
},
|
||||
cmpopts.SortSlices(func(a, b string) bool {
|
||||
return a < b
|
||||
@@ -3370,7 +3812,7 @@ func TestListPagination(t *testing.T) {
|
||||
SortBy: "created_time",
|
||||
SortDir: "desc",
|
||||
RemovedIds: nil,
|
||||
EstItemCount: 10,
|
||||
EstItemCount: 15,
|
||||
},
|
||||
cmpopts.SortSlices(func(a, b string) bool {
|
||||
return a < b
|
||||
@@ -3382,10 +3824,10 @@ func TestListPagination(t *testing.T) {
|
||||
|
||||
// Request rest of results
|
||||
req.ListToken = got.ListToken
|
||||
req.PageSize = 10
|
||||
req.PageSize = 15
|
||||
got, err = s.ListCredentialLibraries(ctx, req)
|
||||
require.NoError(err)
|
||||
require.Len(got.GetItems(), 6)
|
||||
require.Len(got.GetItems(), 11)
|
||||
// Compare without comparing the list token
|
||||
assert.Empty(
|
||||
cmp.Diff(
|
||||
@@ -3396,7 +3838,7 @@ func TestListPagination(t *testing.T) {
|
||||
SortBy: "created_time",
|
||||
SortDir: "desc",
|
||||
RemovedIds: nil,
|
||||
EstItemCount: 10,
|
||||
EstItemCount: 15,
|
||||
},
|
||||
cmpopts.SortSlices(func(a, b string) bool {
|
||||
return a < b
|
||||
@@ -3421,14 +3863,14 @@ func TestListPagination(t *testing.T) {
|
||||
// Update one of the other stores
|
||||
allCredentialLibraries[1].Name = wrapperspb.String("new-name")
|
||||
allCredentialLibraries[1].Version = 2
|
||||
updatedLibrary := &vault.SSHCertificateCredentialLibrary{
|
||||
SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{
|
||||
updatedLibrary := &vault.LdapCredentialLibrary{
|
||||
LdapCredentialLibrary: &store.LdapCredentialLibrary{
|
||||
PublicId: allCredentialLibraries[1].GetId(),
|
||||
StoreId: allCredentialLibraries[1].GetCredentialStoreId(),
|
||||
Name: allCredentialLibraries[1].Name.Value,
|
||||
},
|
||||
}
|
||||
cred, _, err := vaultRepo.UpdateSSHCertificateCredentialLibrary(ctx, prj.PublicId, updatedLibrary, 1, []string{"name"})
|
||||
cred, _, err := vaultRepo.UpdateLdapCredentialLibrary(ctx, prj.PublicId, updatedLibrary, 1, []string{"name"})
|
||||
require.NoError(err)
|
||||
allCredentialLibraries[1].UpdatedTime = cred.UpdateTime.GetTimestamp()
|
||||
allCredentialLibraries[1].Version = cred.Version
|
||||
@@ -3462,7 +3904,7 @@ func TestListPagination(t *testing.T) {
|
||||
SortDir: "desc",
|
||||
// Should contain the deleted library
|
||||
RemovedIds: []string{deletedCredLib.Id},
|
||||
EstItemCount: 10,
|
||||
EstItemCount: 15,
|
||||
},
|
||||
cmpopts.SortSlices(func(a, b string) bool {
|
||||
return a < b
|
||||
@@ -3487,7 +3929,7 @@ func TestListPagination(t *testing.T) {
|
||||
SortBy: "updated_time",
|
||||
SortDir: "desc",
|
||||
RemovedIds: nil,
|
||||
EstItemCount: 10,
|
||||
EstItemCount: 15,
|
||||
},
|
||||
cmpopts.SortSlices(func(a, b string) bool {
|
||||
return a < b
|
||||
@@ -3515,7 +3957,7 @@ func TestListPagination(t *testing.T) {
|
||||
SortDir: "desc",
|
||||
// Should be empty again
|
||||
RemovedIds: nil,
|
||||
EstItemCount: 10,
|
||||
EstItemCount: 15,
|
||||
},
|
||||
cmpopts.SortSlices(func(a, b string) bool {
|
||||
return a < b
|
||||
@@ -3538,7 +3980,7 @@ func TestListPagination(t *testing.T) {
|
||||
SortBy: "created_time",
|
||||
SortDir: "desc",
|
||||
RemovedIds: nil,
|
||||
EstItemCount: 10,
|
||||
EstItemCount: 15,
|
||||
},
|
||||
cmpopts.SortSlices(func(a, b string) bool {
|
||||
return a < b
|
||||
|
||||
@@ -2056,6 +2056,7 @@ func validateAddCredentialSourcesRequest(req *pbs.AddTargetCredentialSourcesRequ
|
||||
for _, cl := range req.GetBrokeredCredentialSourceIds() {
|
||||
if !handlers.ValidId(handlers.Id(cl),
|
||||
globals.VaultCredentialLibraryPrefix,
|
||||
globals.VaultLdapCredentialLibraryPrefix,
|
||||
globals.UsernamePasswordCredentialPrefix,
|
||||
globals.UsernamePasswordCredentialPreviousPrefix,
|
||||
globals.UsernamePasswordDomainCredentialPrefix,
|
||||
@@ -2069,6 +2070,7 @@ func validateAddCredentialSourcesRequest(req *pbs.AddTargetCredentialSourcesRequ
|
||||
if !handlers.ValidId(handlers.Id(cl),
|
||||
globals.VaultCredentialLibraryPrefix,
|
||||
globals.VaultSshCertificateCredentialLibraryPrefix,
|
||||
globals.VaultLdapCredentialLibraryPrefix,
|
||||
globals.UsernamePasswordCredentialPrefix,
|
||||
globals.UsernamePasswordCredentialPreviousPrefix,
|
||||
globals.UsernamePasswordDomainCredentialPrefix,
|
||||
@@ -2094,6 +2096,7 @@ func validateSetCredentialSourcesRequest(req *pbs.SetTargetCredentialSourcesRequ
|
||||
for _, cl := range req.GetBrokeredCredentialSourceIds() {
|
||||
if !handlers.ValidId(handlers.Id(cl),
|
||||
globals.VaultCredentialLibraryPrefix,
|
||||
globals.VaultLdapCredentialLibraryPrefix,
|
||||
globals.UsernamePasswordCredentialPrefix,
|
||||
globals.UsernamePasswordCredentialPreviousPrefix,
|
||||
globals.UsernamePasswordDomainCredentialPrefix,
|
||||
@@ -2107,6 +2110,7 @@ func validateSetCredentialSourcesRequest(req *pbs.SetTargetCredentialSourcesRequ
|
||||
if !handlers.ValidId(handlers.Id(cl),
|
||||
globals.VaultCredentialLibraryPrefix,
|
||||
globals.VaultSshCertificateCredentialLibraryPrefix,
|
||||
globals.VaultLdapCredentialLibraryPrefix,
|
||||
globals.UsernamePasswordCredentialPrefix,
|
||||
globals.UsernamePasswordCredentialPreviousPrefix,
|
||||
globals.UsernamePasswordDomainCredentialPrefix,
|
||||
@@ -2136,6 +2140,7 @@ func validateRemoveCredentialSourcesRequest(req *pbs.RemoveTargetCredentialSourc
|
||||
for _, cl := range req.GetBrokeredCredentialSourceIds() {
|
||||
if !handlers.ValidId(handlers.Id(cl),
|
||||
globals.VaultCredentialLibraryPrefix,
|
||||
globals.VaultLdapCredentialLibraryPrefix,
|
||||
globals.UsernamePasswordCredentialPrefix,
|
||||
globals.UsernamePasswordCredentialPreviousPrefix,
|
||||
globals.UsernamePasswordDomainCredentialPrefix,
|
||||
@@ -2149,6 +2154,7 @@ func validateRemoveCredentialSourcesRequest(req *pbs.RemoveTargetCredentialSourc
|
||||
if !handlers.ValidId(handlers.Id(cl),
|
||||
globals.VaultCredentialLibraryPrefix,
|
||||
globals.VaultSshCertificateCredentialLibraryPrefix,
|
||||
globals.VaultLdapCredentialLibraryPrefix,
|
||||
globals.UsernamePasswordCredentialPrefix,
|
||||
globals.UsernamePasswordCredentialPreviousPrefix,
|
||||
globals.UsernamePasswordDomainCredentialPrefix,
|
||||
|
||||
@@ -264,6 +264,7 @@ begin;
|
||||
('GET'),
|
||||
('POST');
|
||||
|
||||
-- Renamed in 99/01_credential_vault_library_refactor.up.sql
|
||||
create table credential_vault_library (
|
||||
public_id wt_public_id primary key,
|
||||
store_id wt_public_id not null
|
||||
@@ -379,6 +380,7 @@ begin;
|
||||
('expired'),
|
||||
('unknown');
|
||||
|
||||
-- Updated in 99/01_credential_vault_library_refactor.up.sql
|
||||
create table credential_vault_credential (
|
||||
public_id wt_public_id primary key,
|
||||
library_id wt_public_id
|
||||
|
||||
+506
@@ -0,0 +1,506 @@
|
||||
-- Copyright (c) HashiCorp, Inc.
|
||||
-- SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
begin;
|
||||
create table credential_vault_ldap_library (
|
||||
public_id wt_public_id primary key,
|
||||
project_id wt_public_id not null,
|
||||
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_have_staticcred_or_creds
|
||||
check(vault_path ~ '^.+\/(static-cred|creds)\/(?:[^\/\\\s]+\/)*[^\/\\\s]+$'),
|
||||
credential_type text not null
|
||||
constraint credential_type_enm_fkey
|
||||
references credential_type_enm (name)
|
||||
on delete restrict
|
||||
on update cascade,
|
||||
constraint credential_vault_ldap_library_store_id_public_id_uq
|
||||
unique(store_id, public_id),
|
||||
constraint credential_vault_ldap_library_store_id_name_uq
|
||||
unique(store_id, name),
|
||||
constraint credential_vault_library_fkey
|
||||
foreign key (project_id, store_id, public_id, credential_type)
|
||||
references credential_vault_library (project_id, store_id, public_id, credential_type)
|
||||
on delete cascade
|
||||
on update cascade
|
||||
);
|
||||
|
||||
comment on table credential_vault_ldap_library is
|
||||
'credential_vault_ldap_library is a credential library that issues credentials from a vault ldap secret backend.';
|
||||
|
||||
create trigger insert_credential_vault_library_subtype before insert on credential_vault_ldap_library
|
||||
for each row execute procedure insert_credential_vault_library_subtype();
|
||||
|
||||
create trigger update_credential_vault_library_table_update_time before update on credential_vault_ldap_library
|
||||
for each row execute procedure update_credential_vault_library_table_update_time();
|
||||
|
||||
create trigger immutable_columns before update on credential_vault_ldap_library
|
||||
for each row execute procedure immutable_columns('public_id', 'store_id', 'project_id', 'credential_type', 'create_time');
|
||||
|
||||
create trigger delete_credential_vault_library_subtype after delete on credential_vault_ldap_library
|
||||
for each row execute procedure delete_credential_vault_library_subtype();
|
||||
|
||||
create trigger update_version_column after update on credential_vault_ldap_library
|
||||
for each row execute procedure update_version_column();
|
||||
|
||||
create trigger update_time_column before update on credential_vault_ldap_library
|
||||
for each row execute procedure update_time_column();
|
||||
|
||||
create trigger default_create_time_column before insert on credential_vault_ldap_library
|
||||
for each row execute procedure default_create_time();
|
||||
|
||||
create trigger before_insert_credential_vault_library before insert on credential_vault_ldap_library
|
||||
for each row execute procedure before_insert_credential_vault_library();
|
||||
|
||||
create function default_vault_ldap_credential_type() returns trigger
|
||||
as $$
|
||||
begin
|
||||
if new.credential_type is distinct from 'username_password_domain' then
|
||||
new.credential_type = 'username_password_domain';
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
comment on function default_vault_ldap_credential_type is
|
||||
'default_vault_ldap_credential_type ensures the credential_type is set to username_password_domain';
|
||||
|
||||
create trigger default_vault_ldap_credential_type before insert on credential_vault_ldap_library
|
||||
for each row execute procedure default_vault_ldap_credential_type();
|
||||
|
||||
-- Replaces view from 99/01_credential_vault_library_refactor.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_generic_library_username_password_mapping_ovrd
|
||||
),
|
||||
ssh_private_key_override (library_id, username_attribute, private_key_attribute, private_key_passphrase_attribute) as (
|
||||
select library_id,
|
||||
nullif(username_attribute, wt_to_sentinel('no override')),
|
||||
nullif(private_key_attribute, wt_to_sentinel('no override')),
|
||||
nullif(private_key_passphrase_attribute, wt_to_sentinel('no override'))
|
||||
from credential_vault_generic_library_ssh_private_key_mapping_ovrd
|
||||
),
|
||||
password_domain_override (library_id, username_attribute, password_attribute, domain_attribute) as (
|
||||
select library_id,
|
||||
nullif(username_attribute, wt_to_sentinel('no override')),
|
||||
nullif(password_attribute, wt_to_sentinel('no override')),
|
||||
nullif(domain_attribute, wt_to_sentinel('no override'))
|
||||
from credential_vault_generic_library_usern_pass_domain_mapping_ovrd
|
||||
)
|
||||
select library.public_id as public_id,
|
||||
library.store_id as store_id,
|
||||
library.name as name,
|
||||
library.description as description,
|
||||
library.create_time as create_time,
|
||||
library.update_time as update_time,
|
||||
library.version as version,
|
||||
library.vault_path as vault_path,
|
||||
library.http_method as http_method,
|
||||
library.http_request_body as http_request_body,
|
||||
library.credential_type as credential_type,
|
||||
null as key_type,
|
||||
null as key_bits,
|
||||
null as username,
|
||||
null as ttl,
|
||||
null as key_id,
|
||||
null as critical_options,
|
||||
null as extensions,
|
||||
store.project_id as project_id,
|
||||
store.vault_address as vault_address,
|
||||
store.namespace as namespace,
|
||||
store.ca_cert as ca_cert,
|
||||
store.tls_server_name as tls_server_name,
|
||||
store.tls_skip_verify as tls_skip_verify,
|
||||
store.worker_filter as worker_filter,
|
||||
store.ct_token as ct_token, -- encrypted
|
||||
store.token_hmac as token_hmac,
|
||||
store.token_status as token_status,
|
||||
store.token_key_id as token_key_id,
|
||||
store.client_cert as client_cert,
|
||||
store.ct_client_key as ct_client_key, -- encrypted
|
||||
store.client_key_id as client_key_id,
|
||||
coalesce(upasso.username_attribute, sshpk.username_attribute, pd.username_attribute)
|
||||
as username_attribute,
|
||||
coalesce(upasso.password_attribute, pd.password_attribute)
|
||||
as password_attribute,
|
||||
pd.domain_attribute as domain_attribute,
|
||||
sshpk.private_key_attribute as private_key_attribute,
|
||||
sshpk.private_key_passphrase_attribute as private_key_passphrase_attribute,
|
||||
'generic' as cred_lib_type, -- used to switch on
|
||||
null as additional_valid_principals
|
||||
from credential_vault_generic_library library
|
||||
join credential_vault_store_client store
|
||||
on library.store_id = store.public_id
|
||||
left join password_override upasso
|
||||
on library.public_id = upasso.library_id
|
||||
left join ssh_private_key_override sshpk
|
||||
on library.public_id = sshpk.library_id
|
||||
left join password_domain_override pd
|
||||
on library.public_id = pd.library_id
|
||||
union
|
||||
select library.public_id as public_id,
|
||||
library.store_id as store_id,
|
||||
library.name as name,
|
||||
library.description as description,
|
||||
library.create_time as create_time,
|
||||
library.update_time as update_time,
|
||||
library.version as version,
|
||||
library.vault_path as vault_path,
|
||||
null as http_method,
|
||||
null as http_request_body,
|
||||
library.credential_type as credential_type,
|
||||
library.key_type as key_type,
|
||||
library.key_bits as key_bits,
|
||||
library.username as username,
|
||||
library.ttl as ttl,
|
||||
library.key_id as key_id,
|
||||
library.critical_options as critical_options,
|
||||
library.extensions as extensions,
|
||||
store.project_id as project_id,
|
||||
store.vault_address as vault_address,
|
||||
store.namespace as namespace,
|
||||
store.ca_cert as ca_cert,
|
||||
store.tls_server_name as tls_server_name,
|
||||
store.tls_skip_verify as tls_skip_verify,
|
||||
store.worker_filter as worker_filter,
|
||||
store.ct_token as ct_token, -- encrypted
|
||||
store.token_hmac as token_hmac,
|
||||
store.token_status as token_status,
|
||||
store.token_key_id as token_key_id,
|
||||
store.client_cert as client_cert,
|
||||
store.ct_client_key as ct_client_key, -- encrypted
|
||||
store.client_key_id as client_key_id,
|
||||
null as username_attribute,
|
||||
null as password_attribute,
|
||||
null as domain_attribute,
|
||||
null as private_key_attribute,
|
||||
null as private_key_passphrase_attribute,
|
||||
'ssh-signed-cert' as cred_lib_type, -- used to switch on
|
||||
additional_valid_principals as additional_valid_principals
|
||||
from credential_vault_ssh_cert_library library
|
||||
join credential_vault_store_client store
|
||||
on library.store_id = store.public_id
|
||||
union
|
||||
select library.public_id as public_id,
|
||||
library.store_id as store_id,
|
||||
library.name as name,
|
||||
library.description as description,
|
||||
library.create_time as create_time,
|
||||
library.update_time as update_time,
|
||||
library.version as version,
|
||||
library.vault_path as vault_path,
|
||||
null as http_method,
|
||||
null as http_request_body,
|
||||
library.credential_type as credential_type,
|
||||
null as key_type,
|
||||
null as key_bits,
|
||||
null as username,
|
||||
null as ttl,
|
||||
null as key_id,
|
||||
null as critical_options,
|
||||
null as extensions,
|
||||
store.project_id as project_id,
|
||||
store.vault_address as vault_address,
|
||||
store.namespace as namespace,
|
||||
store.ca_cert as ca_cert,
|
||||
store.tls_server_name as tls_server_name,
|
||||
store.tls_skip_verify as tls_skip_verify,
|
||||
store.worker_filter as worker_filter,
|
||||
store.ct_token as ct_token, -- encrypted
|
||||
store.token_hmac as token_hmac,
|
||||
store.token_status as token_status,
|
||||
store.token_key_id as token_key_id,
|
||||
store.client_cert as client_cert,
|
||||
store.ct_client_key as ct_client_key, -- encrypted
|
||||
store.client_key_id as client_key_id,
|
||||
pd.username_attribute as username_attribute,
|
||||
pd.password_attribute as password_attribute,
|
||||
pd.domain_attribute as domain_attribute,
|
||||
null as private_key_attribute,
|
||||
null as private_key_passphrase_attribute,
|
||||
'ldap' as cred_lib_type, -- used to switch on
|
||||
null as additional_valid_principals
|
||||
from credential_vault_ldap_library library
|
||||
join credential_vault_store_client store
|
||||
on library.store_id = store.public_id
|
||||
left join password_domain_override pd
|
||||
on library.public_id = pd.library_id;
|
||||
|
||||
comment on view credential_vault_library_issue_credentials is
|
||||
'credential_vault_library_issue_credentials is a view where each row contains a credential library and the credential library''s data needed to connect to Vault. '
|
||||
'This view should only be used when issuing credentials from a Vault credential library. Each row may contain encrypted data. '
|
||||
'This view should not be used to retrieve data which will be returned external to boundary.';
|
||||
|
||||
insert into oplog_ticket (name, version)
|
||||
values
|
||||
('credential_vault_ldap_library', 1);
|
||||
|
||||
create table credential_vault_ldap_library_hst (
|
||||
public_id wt_public_id not null,
|
||||
project_id wt_public_id not null,
|
||||
store_id wt_public_id not null,
|
||||
name wt_name,
|
||||
description wt_description,
|
||||
vault_path text not null,
|
||||
credential_type text not null,
|
||||
history_id wt_url_safe_id default wt_url_safe_id() primary key
|
||||
constraint credential_library_history_base_fkey
|
||||
references credential_library_history_base (history_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
valid_range tstzrange not null default tstzrange(current_timestamp, null),
|
||||
constraint credential_vault_ldap_library_hst_valid_range_excl
|
||||
exclude using gist (public_id with =, valid_range with &&)
|
||||
);
|
||||
comment on table credential_vault_ldap_library_hst is
|
||||
'credential_vault_ldap_library_hst is a history table where each row contains values from a row '
|
||||
'in the credential_vault_ldap_library table during the time range in the valid_range column';
|
||||
|
||||
create trigger insert_credential_library_history_subtype before insert on credential_vault_ldap_library_hst
|
||||
for each row execute function insert_credential_library_history_subtype();
|
||||
|
||||
create trigger delete_credential_library_history_subtype after delete on credential_vault_ldap_library_hst
|
||||
for each row execute function delete_credential_library_history_subtype();
|
||||
|
||||
create trigger hst_on_insert after insert on credential_vault_ldap_library
|
||||
for each row execute function hst_on_insert();
|
||||
|
||||
create trigger hst_on_update after update on credential_vault_ldap_library
|
||||
for each row execute function hst_on_update();
|
||||
|
||||
create trigger hst_on_delete after delete on credential_vault_ldap_library
|
||||
for each row execute function hst_on_delete();
|
||||
|
||||
create view credential_vault_ldap_library_hst_aggregate as
|
||||
select
|
||||
rdc.recording_id,
|
||||
vll.public_id,
|
||||
vll.name,
|
||||
vll.description,
|
||||
vll.vault_path,
|
||||
vll.credential_type,
|
||||
vsh.public_id as store_public_id,
|
||||
vsh.project_id as store_project_id,
|
||||
vsh.name as store_name,
|
||||
vsh.description as store_description,
|
||||
vsh.vault_address as store_vault_address,
|
||||
vsh.namespace as store_namespace,
|
||||
vsh.tls_server_name as store_tls_server_name,
|
||||
vsh.tls_skip_verify as store_tls_skip_verify,
|
||||
vsh.worker_filter as store_worker_filter,
|
||||
string_agg(distinct rdc.credential_purpose, '|') as purposes
|
||||
from credential_vault_ldap_library_hst as vll
|
||||
left join recording_dynamic_credential as rdc on vll.history_id = rdc.credential_library_hst_id
|
||||
join credential_vault_store_hst as vsh on rdc.credential_vault_store_hst_id = vsh.history_id
|
||||
group by vll.history_id, rdc.recording_id, vsh.history_id;
|
||||
comment on view credential_vault_ldap_library_hst_aggregate is
|
||||
'credential_vault_ldap_library_hst_aggregate contains the vault ldap library history data along with its store and purpose data.';
|
||||
|
||||
create table credential_vault_ldap_library_deleted (
|
||||
public_id wt_public_id primary key,
|
||||
delete_time wt_timestamp not null
|
||||
);
|
||||
comment on table credential_vault_ldap_library_deleted is
|
||||
'credential_vault_ldap_library_deleted holds the ID and delete_time '
|
||||
'of every deleted vault ldap credential'
|
||||
'It is automatically trimmed of records older than 30 days by a job.';
|
||||
|
||||
create index credential_vault_ldap_library_deleted_delete_time_idx on credential_vault_ldap_library_deleted (delete_time);
|
||||
|
||||
create trigger insert_deleted_id after delete on credential_vault_ldap_library
|
||||
for each row execute function insert_deleted_id('credential_vault_ldap_library_deleted');
|
||||
|
||||
-- Replaces the function in 99/01_credential_vault_library_refactor.up.sql
|
||||
drop trigger insert_recording_dynamic_credentials on recording_session;
|
||||
drop function insert_recording_dynamic_credentials();
|
||||
create function insert_recording_dynamic_credentials() returns trigger
|
||||
as $$
|
||||
begin
|
||||
with
|
||||
session_recording(session_id, recording_id) as (
|
||||
select session_id, public_id
|
||||
from recording_session
|
||||
where session_id = new.session_id
|
||||
),
|
||||
session_dynamic_creds(library_id, purpose, recording_id) as (
|
||||
select library_id, credential_purpose, recording_id
|
||||
from session_credential_dynamic
|
||||
join session_recording using (session_id)
|
||||
),
|
||||
library_history(public_id, store_id, library_hst_id, valid_range) as (
|
||||
select public_id, store_id, history_id, valid_range
|
||||
from credential_vault_generic_library_hst
|
||||
union
|
||||
select public_id, store_id, history_id, valid_range
|
||||
from credential_vault_ssh_cert_library_hst
|
||||
union
|
||||
select public_id, store_id, history_id, valid_range
|
||||
from credential_vault_ldap_library_hst
|
||||
),
|
||||
final(recording_id, library_id, store_id, library_hst_id, store_hst_id, cred_purpose) as (
|
||||
select sdc.recording_id, lib.public_id, lib.store_id, lib.library_hst_id, store_hst.history_id, sdc.purpose
|
||||
from library_history as lib
|
||||
join credential_vault_store_hst as store_hst on lib.store_id = store_hst.public_id
|
||||
and store_hst.valid_range @> current_timestamp
|
||||
join session_dynamic_creds as sdc on lib.public_id = sdc.library_id
|
||||
where lib.public_id in (select library_id from session_dynamic_creds)
|
||||
and lib.valid_range @> current_timestamp
|
||||
)
|
||||
insert into recording_dynamic_credential
|
||||
(recording_id, credential_vault_store_hst_id, credential_library_hst_id, credential_purpose)
|
||||
select recording_id, store_hst_id, library_hst_id, cred_purpose
|
||||
from final;
|
||||
return new;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
comment on function insert_recording_dynamic_credentials is
|
||||
'insert_recording_dynamic_credentials is an after insert trigger for the recording_session table.';
|
||||
|
||||
create trigger insert_recording_dynamic_credentials after insert on recording_session
|
||||
for each row execute procedure insert_recording_dynamic_credentials();
|
||||
|
||||
-- Replaces view defined in 99/01_credential_vault_library_refactor.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_generic_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
|
||||
),
|
||||
vault_ldap_library as (
|
||||
select vldapcl.public_id as public_id,
|
||||
'vault ldap credential library' as type,
|
||||
coalesce(vldapcl.name, 'None') as name,
|
||||
coalesce(vldapcl.description, 'None') as description,
|
||||
vldapcl.vault_path as vault_path,
|
||||
'Not Applicable' as http_method,
|
||||
'Not Applicable' as http_request_body,
|
||||
'Not Applicable' as username,
|
||||
'Not Applicable' as key_type_and_bits
|
||||
from credential_vault_ldap_library as vldapcl
|
||||
),
|
||||
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, vldapcl.type) as credential_library_type,
|
||||
coalesce(vcl.name, vsccl.name, vldapcl.name) as credential_library_name,
|
||||
coalesce(vcl.description, vsccl.description, vldapcl.description) as credential_library_description,
|
||||
coalesce(vcl.vault_path, vsccl.vault_path, vldapcl.vault_path) as credential_library_vault_path,
|
||||
coalesce(vcl.http_method, vsccl.http_method, vldapcl.http_method) as credential_library_vault_http_method,
|
||||
coalesce(vcl.http_request_body, vsccl.http_request_body, vldapcl.http_request_body) as credential_library_vault_http_request_body,
|
||||
coalesce(vcl.username, vsccl.username, vldapcl.username) as credential_library_username,
|
||||
coalesce(vcl.key_type_and_bits, vsccl.key_type_and_bits, vldapcl.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,
|
||||
case
|
||||
when tt.type = 'tcp' then 'tcp target'
|
||||
when tt.type = 'ssh' then 'ssh target'
|
||||
when tt.type = 'rdp' then 'rdp target'
|
||||
else 'Unknown'
|
||||
end 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 vault_ldap_library as vldapcl on cl.public_id = vldapcl.public_id
|
||||
left join credential_vault_store as vcs on cs.public_id = vcs.public_id
|
||||
left join target_all_subtypes 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;
|
||||
|
||||
commit;
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
begin;
|
||||
|
||||
-- Renamed in 99/01_credential_vault_library_refactor.up.sql
|
||||
create table credential_vault_library_mapping_override (
|
||||
library_id wt_public_id primary key
|
||||
constraint credential_vault_library_fkey
|
||||
@@ -16,6 +17,7 @@ begin;
|
||||
|
||||
-- insert_credential_vault_library_mapping_override_subtype() is a before insert trigger
|
||||
-- function for subtypes of credential_vault_library_mapping_override
|
||||
-- Updated and renamed in 99/01_credential_vault_library_refactor.up.sql
|
||||
create function insert_credential_vault_library_mapping_override_subtype() returns trigger
|
||||
as $$
|
||||
begin
|
||||
@@ -29,6 +31,7 @@ begin;
|
||||
|
||||
-- delete_credential_vault_library_mapping_override_subtype() is an after delete trigger
|
||||
-- function for subtypes of credential_vault_library_mapping_override
|
||||
-- Updated and renamed in 99/01_credential_vault_library_refactor.up.sql
|
||||
create function delete_credential_vault_library_mapping_override_subtype() returns trigger
|
||||
as $$
|
||||
begin
|
||||
|
||||
+2
-1
@@ -3,7 +3,8 @@
|
||||
|
||||
begin;
|
||||
|
||||
-- Updated in 36/01_vault_library_map_username_password.up.sql
|
||||
-- Updated in 36/01_vault_library_map_username_password.up.sql
|
||||
-- Renamed in 99/01_credential_vault_library_refactor.up.sql
|
||||
create table credential_vault_library_user_password_mapping_override (
|
||||
library_id wt_public_id primary key
|
||||
constraint credential_vault_library_fkey
|
||||
|
||||
+2
-1
@@ -3,7 +3,8 @@
|
||||
|
||||
begin;
|
||||
|
||||
-- Renames table from 22/04_vault_library_map_user_password.up.sql
|
||||
-- Renames table from 22/04_vault_library_map_user_password.up.sql
|
||||
-- Renamed again in 99/01_credential_vault_library_refactor.up.sql
|
||||
alter table credential_vault_library_user_password_mapping_override
|
||||
rename to credential_vault_library_username_password_mapping_override;
|
||||
comment on table credential_vault_library_username_password_mapping_override is
|
||||
|
||||
@@ -7,6 +7,7 @@ begin;
|
||||
insert into credential_type_enm (name)
|
||||
values ('ssh_private_key');
|
||||
|
||||
-- Renamed in 99/01_credential_vault_library_refactor.up.sql
|
||||
create table credential_vault_library_ssh_private_key_mapping_override (
|
||||
library_id wt_public_id primary key
|
||||
constraint credential_vault_library_fkey
|
||||
|
||||
+1
@@ -93,6 +93,7 @@ begin;
|
||||
('rsa', 3072),
|
||||
('rsa', 4096);
|
||||
|
||||
-- Updated in 99/01_credential_vault_library_refactor.up.sql
|
||||
create table credential_vault_ssh_cert_library (
|
||||
public_id wt_public_id primary key,
|
||||
store_id wt_public_id not null
|
||||
|
||||
+1
@@ -4,6 +4,7 @@
|
||||
begin;
|
||||
|
||||
-- Replaces view from 56/02_add_data_key_foreign_key_references.up.sql
|
||||
-- Replaced in 78/01_ssh_signed_certs_additional_valid_principals
|
||||
drop view credential_vault_library_issue_credentials;
|
||||
create view credential_vault_library_issue_credentials as
|
||||
with
|
||||
|
||||
@@ -37,6 +37,7 @@ begin;
|
||||
'delete_credential_library_history_subtype() is an after delete trigger '
|
||||
'function for subtypes of credential_library_history_base.';
|
||||
|
||||
-- Renamed in 99/01_credential_vault_library_refactor.up.sql
|
||||
create table credential_vault_library_hst (
|
||||
public_id wt_public_id not null,
|
||||
name wt_name,
|
||||
|
||||
@@ -33,6 +33,7 @@ begin;
|
||||
'along with the values of those entities at the time of the recording. '
|
||||
'These values are also stored in the BSR file.';
|
||||
|
||||
-- Updated in 99/01_credential_vault_library_refactor.up.sql
|
||||
create function insert_recording_dynamic_credentials() returns trigger
|
||||
as $$
|
||||
begin
|
||||
|
||||
@@ -66,7 +66,8 @@ group by sspkc.history_id, rsc.recording_id, css.history_id;
|
||||
comment on view credential_static_ssh_private_key_credential_hst_aggregate is
|
||||
'credential_static_ssh_private_key_credential_hst_aggregate contains the ssh private key credential history data along with its store and purpose data.';
|
||||
|
||||
-- Dynamic Credentials
|
||||
-- Replaced in 99/01_credential_vault_library_refactor.up.sql. Note that this
|
||||
-- view's name has changed to credential_vault_generic_library_hst_aggregate.
|
||||
create view credential_vault_library_hst_aggregate as
|
||||
select
|
||||
rdc.recording_id,
|
||||
|
||||
@@ -118,6 +118,7 @@ begin;
|
||||
'It is automatically trimmed of records older than 30 days by a job.';
|
||||
|
||||
-- Credential libraries
|
||||
-- Renamed in 99/01_credential_vault_library_refactor.up.sql
|
||||
create table credential_vault_library_deleted (
|
||||
public_id wt_public_id primary key,
|
||||
delete_time wt_timestamp not null
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
begin;
|
||||
|
||||
-- Renamed in 99/01_credential_vault_library_refactor.up.sql
|
||||
create table credential_vault_library_username_password_domain_mapping_ovrd (
|
||||
library_id wt_public_id primary key
|
||||
constraint credential_vault_library_fkey
|
||||
@@ -38,6 +39,7 @@ begin;
|
||||
|
||||
|
||||
-- Replaces view from 78/01_ssh_signed_certs_additional_valid_principals.up.sql
|
||||
-- Replaced in 99/01_credential_vault_library_refactor.up.sql
|
||||
drop view credential_vault_library_issue_credentials;
|
||||
create view credential_vault_library_issue_credentials as
|
||||
with
|
||||
@@ -161,6 +163,8 @@ begin;
|
||||
|
||||
|
||||
-- Replaces view created in 49/01_vault_credentials.up.sql
|
||||
-- Replaced in 99/01_credential_vault_library_refactor.up.sql where this
|
||||
-- view's name changed to credential_vault_generic_library_list_lookup.
|
||||
drop view credential_vault_library_list_lookup;
|
||||
create view credential_vault_library_list_lookup as
|
||||
with
|
||||
|
||||
@@ -355,6 +355,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 71/07_targets.up.sql
|
||||
-- Replaced in 99/01_credential_vault_library_refactor.up.sql.
|
||||
create view whx_credential_dimension_source as
|
||||
with vault_generic_library as (
|
||||
select vcl.public_id as public_id,
|
||||
|
||||
+765
@@ -0,0 +1,765 @@
|
||||
-- Copyright (c) HashiCorp, Inc.
|
||||
-- SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
-- For context, check the README.md in this folder.
|
||||
begin;
|
||||
-- Main table.
|
||||
-- Renames the table defined in 10/04_vault_credential.up.sql
|
||||
alter table credential_vault_library
|
||||
rename to credential_vault_generic_library;
|
||||
|
||||
alter table credential_vault_generic_library
|
||||
rename constraint credential_vault_library_pkey to credential_vault_generic_library_pkey;
|
||||
|
||||
alter table credential_vault_generic_library
|
||||
rename constraint credential_vault_library_store_id_name_uq to credential_vault_generic_library_store_id_name_uq;
|
||||
|
||||
alter table credential_vault_generic_library
|
||||
rename constraint credential_vault_library_store_id_public_id_uq to credential_vault_generic_library_store_id_public_id_uq;
|
||||
|
||||
comment on table credential_vault_generic_library is
|
||||
'credential_vault_generic_library is a table where each row is a resource that represents a vault generic credential library. '
|
||||
'It is a credential_vault_library subtype and a child table of credential_vault_store.';
|
||||
|
||||
drop trigger insert_deleted_id on credential_vault_generic_library;
|
||||
create trigger insert_deleted_id after delete on credential_vault_generic_library
|
||||
for each row execute procedure insert_deleted_id('credential_vault_generic_library_deleted');
|
||||
|
||||
insert into oplog_ticket
|
||||
(name, version)
|
||||
values
|
||||
('credential_vault_generic_library', 1)
|
||||
on conflict do nothing;
|
||||
delete from oplog_ticket where name = 'credential_vault_library';
|
||||
|
||||
|
||||
-- History table.
|
||||
-- Renames the table defined in 71/15_dynamic_credential_history.up.sql
|
||||
alter table credential_vault_library_hst
|
||||
rename to credential_vault_generic_library_hst;
|
||||
|
||||
alter table credential_vault_generic_library_hst
|
||||
rename constraint credential_vault_library_hst_pkey to credential_vault_generic_library_hst_pkey;
|
||||
|
||||
alter table credential_vault_generic_library_hst
|
||||
rename constraint credential_vault_library_hst_valid_range_excl to credential_vault_generic_library_hst_valid_range_excl;
|
||||
|
||||
comment on table credential_vault_generic_library_hst is
|
||||
'credential_vault_generic_library_hst is a history table where each row contains the values from '
|
||||
'a row in the credential_vault_library table during the time range in the valid_range column.';
|
||||
|
||||
-- Updates the function defined in 71/16_recording_dynamic_credential.up.sql
|
||||
-- Updated in 100/01_credential_vault_ldap_library.up.sql
|
||||
drop trigger insert_recording_dynamic_credentials on recording_session;
|
||||
drop function insert_recording_dynamic_credentials();
|
||||
create function insert_recording_dynamic_credentials() returns trigger
|
||||
as $$
|
||||
begin
|
||||
with
|
||||
session_recording(session_id, recording_id) as (
|
||||
select session_id, public_id
|
||||
from recording_session
|
||||
where session_id = new.session_id
|
||||
),
|
||||
session_dynamic_creds(library_id, purpose, recording_id) as (
|
||||
select library_id, credential_purpose, recording_id
|
||||
from session_credential_dynamic
|
||||
join session_recording using (session_id)
|
||||
),
|
||||
library_history(public_id, store_id, library_hst_id, valid_range) as (
|
||||
select public_id, store_id, history_id, valid_range
|
||||
from credential_vault_generic_library_hst
|
||||
union
|
||||
select public_id, store_id, history_id, valid_range
|
||||
from credential_vault_ssh_cert_library_hst
|
||||
),
|
||||
final(recording_id, library_id, store_id, library_hst_id, store_hst_id, cred_purpose) as (
|
||||
select sdc.recording_id, lib.public_id, lib.store_id, lib.library_hst_id, store_hst.history_id, sdc.purpose
|
||||
from library_history as lib
|
||||
join credential_vault_store_hst as store_hst on lib.store_id = store_hst.public_id
|
||||
and store_hst.valid_range @> current_timestamp
|
||||
join session_dynamic_creds as sdc on lib.public_id = sdc.library_id
|
||||
where lib.public_id in (select library_id from session_dynamic_creds)
|
||||
and lib.valid_range @> current_timestamp
|
||||
)
|
||||
insert into recording_dynamic_credential
|
||||
(recording_id, credential_vault_store_hst_id, credential_library_hst_id, credential_purpose)
|
||||
select recording_id, store_hst_id, library_hst_id, cred_purpose
|
||||
from final;
|
||||
return new;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
comment on function insert_recording_dynamic_credentials is
|
||||
'insert_recording_dynamic_credentials is an after insert trigger for the recording_session table.';
|
||||
|
||||
create trigger insert_recording_dynamic_credentials after insert on recording_session
|
||||
for each row execute procedure insert_recording_dynamic_credentials();
|
||||
|
||||
|
||||
-- Deleted table.
|
||||
-- Renames table defined in 81/01_deleted_tables_and_triggers.up.sql
|
||||
alter table credential_vault_library_deleted
|
||||
rename to credential_vault_generic_library_deleted;
|
||||
|
||||
alter table credential_vault_generic_library_deleted
|
||||
rename constraint credential_vault_library_deleted_pkey to credential_vault_generic_library_deleted_pkey;
|
||||
|
||||
alter index credential_vault_library_deleted_delete_time_idx
|
||||
rename to credential_vault_generic_library_deleted_delete_time_idx;
|
||||
|
||||
comment on table credential_vault_generic_library_deleted is
|
||||
'credential_vault_generic_library_deleted holds the ID and delete_time of every deleted Vault credential library. '
|
||||
'It is automatically trimmed of records older than 30 days by a job.';
|
||||
|
||||
|
||||
-- Mapping override base table.
|
||||
-- Note that table/constraint/trigger names get very long in the mapping
|
||||
-- overrides section, and Postgres limits us to 64-1 chars by default, so we
|
||||
-- may have to restrict our naming.
|
||||
-- Renames table defined in 22/03_vault_library_map.up.sql
|
||||
alter table credential_vault_library_mapping_override
|
||||
rename to credential_vault_generic_library_mapping_override;
|
||||
|
||||
alter table credential_vault_generic_library_mapping_override
|
||||
rename constraint credential_vault_library_mapping_override_pkey to credential_vault_generic_library_mapping_override_pkey;
|
||||
|
||||
alter table credential_vault_generic_library_mapping_override
|
||||
rename constraint credential_vault_library_fkey to credential_vault_generic_library_fkey;
|
||||
|
||||
comment on table credential_vault_generic_library_mapping_override is
|
||||
'credential_vault_generic_library_mapping_override is a base table for the vault generic library mapping override type. '
|
||||
'Each row is owned by a single vault generic library and maps 1-to-1 to a row in one of the vault generic library mapping override subtype tables.';
|
||||
|
||||
-- These triggers are recreated on each of the tables' specific sections below.
|
||||
-- Renames and updates function defined in 22/03_vault_library_map.up.sql
|
||||
drop trigger insert_credential_vault_library_mapping_override_subtype on credential_vault_library_username_password_mapping_override;
|
||||
drop trigger insert_credential_vault_library_mapping_override_subtype on credential_vault_library_username_password_domain_mapping_ovrd;
|
||||
drop trigger insert_credential_vault_library_mapping_override_subtype on credential_vault_library_ssh_private_key_mapping_override;
|
||||
drop function insert_credential_vault_library_mapping_override_subtype;
|
||||
create function insert_credential_vault_generic_library_mapping_override_subtyp() returns trigger
|
||||
as $$
|
||||
begin
|
||||
insert into credential_vault_generic_library_mapping_override
|
||||
(library_id)
|
||||
values
|
||||
(new.library_id);
|
||||
return new;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
comment on function insert_credential_vault_generic_library_mapping_override_subtyp() is
|
||||
'insert_credential_vault_generic_library_mapping_override_subtyp() is a '
|
||||
'before insert trigger function that inserts rows from subtype tables into '
|
||||
'credential_vault_generic_library_mapping_override. It must be used on all '
|
||||
'Vault generic credential library mapping subtype tables.';
|
||||
|
||||
-- These triggers are recreated on each of the tables' specific sections below.
|
||||
-- Renames and updates function defined in 22/03_vault_library_map.up.sql
|
||||
drop trigger delete_credential_vault_library_mapping_override_subtype on credential_vault_library_username_password_mapping_override;
|
||||
drop trigger delete_credential_vault_library_mapping_override_subtype on credential_vault_library_username_password_domain_mapping_ovrd;
|
||||
drop trigger delete_credential_vault_library_mapping_override_subtype on credential_vault_library_ssh_private_key_mapping_override;
|
||||
drop function delete_credential_vault_library_mapping_override_subtype;
|
||||
create function delete_credential_vault_generic_library_mapping_override_subtyp() returns trigger
|
||||
as $$
|
||||
begin
|
||||
delete from credential_vault_generic_library_mapping_override
|
||||
where library_id = old.library_id;
|
||||
return null; -- result is ignored since this is an after trigger
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
comment on function delete_credential_vault_generic_library_mapping_override_subtyp() is
|
||||
'delete_credential_vault_generic_library_mapping_override_subtyp() is a '
|
||||
'before insert trigger function that deletes rows from '
|
||||
'credential_vault_generic_library_mapping_override when they''re deleted '
|
||||
'on the subtype tables. It must be used on all Vault generic credential '
|
||||
'library mapping subtype tables.';
|
||||
|
||||
|
||||
-- Mapping override: SSH PK table.
|
||||
-- Renames table defined in 39/02_vault_ssh_private_key_override.up.sql
|
||||
alter table credential_vault_library_ssh_private_key_mapping_override
|
||||
rename to credential_vault_generic_library_ssh_private_key_mapping_ovrd;
|
||||
|
||||
alter table credential_vault_generic_library_ssh_private_key_mapping_ovrd
|
||||
rename constraint credential_vault_library_ssh_private_key_mapping_override_pkey to credential_vault_generic_library_ssh_priv_key_mapping_ovrd_pkey;
|
||||
|
||||
alter table credential_vault_generic_library_ssh_private_key_mapping_ovrd
|
||||
rename constraint credential_vault_library_mapping_override_fkey to credential_vault_generic_library_mapping_override_fkey;
|
||||
|
||||
alter table credential_vault_generic_library_ssh_private_key_mapping_ovrd
|
||||
rename constraint credential_vault_library_fkey to credential_vault_generic_library_fkey;
|
||||
|
||||
comment on table credential_vault_generic_library_ssh_private_key_mapping_ovrd is
|
||||
'credential_vault_generic_library_ssh_private_key_mapping_ovrd is a table '
|
||||
'where each row represents a mapping that overrides the default mapping '
|
||||
'from a generic vault secret to a ssh private key credential type '
|
||||
'for a vault generic credential library.';
|
||||
|
||||
create trigger insert_credential_vault_generic_library_mapping_override_subtyp before insert on credential_vault_generic_library_ssh_private_key_mapping_ovrd
|
||||
for each row execute procedure insert_credential_vault_generic_library_mapping_override_subtyp();
|
||||
|
||||
create trigger delete_credential_vault_generic_library_mapping_override_subtyp after delete on credential_vault_generic_library_ssh_private_key_mapping_ovrd
|
||||
for each row execute procedure delete_credential_vault_generic_library_mapping_override_subtyp();
|
||||
|
||||
|
||||
-- Mapping override: Username & Password table.
|
||||
-- Renames table in 36/01_vault_library_map_username_password.up.sql
|
||||
alter table credential_vault_library_username_password_mapping_override
|
||||
rename to credential_vault_generic_library_username_password_mapping_ovrd;
|
||||
|
||||
alter table credential_vault_generic_library_username_password_mapping_ovrd
|
||||
rename constraint credential_vault_library_user_password_mapping_override_pkey to credential_vault_generic_library_usern_pass_mapping_ovrd_pkey;
|
||||
|
||||
alter table credential_vault_generic_library_username_password_mapping_ovrd
|
||||
rename constraint credential_vault_library_fkey to credential_vault_generic_library_fkey;
|
||||
|
||||
alter table credential_vault_generic_library_username_password_mapping_ovrd
|
||||
rename constraint credential_vault_library_mapping_override_fkey to credential_vault_generic_library_mapping_override_fkey;
|
||||
|
||||
comment on table credential_vault_generic_library_username_password_mapping_ovrd is
|
||||
'credential_vault_generic_library_username_password_mapping_ovrd is a table '
|
||||
'where each row represents a mapping that overrides the default mapping '
|
||||
'from a generic vault secret to a username password credential type '
|
||||
'for a vault generic credential library.';
|
||||
|
||||
create trigger insert_credential_vault_generic_library_mapping_override_subtyp before insert on credential_vault_generic_library_username_password_mapping_ovrd
|
||||
for each row execute procedure insert_credential_vault_generic_library_mapping_override_subtyp();
|
||||
|
||||
create trigger delete_credential_vault_generic_library_mapping_override_subtyp after delete on credential_vault_generic_library_username_password_mapping_ovrd
|
||||
for each row execute procedure delete_credential_vault_generic_library_mapping_override_subtyp();
|
||||
|
||||
|
||||
-- Mapping override: Username, Password & Domain table.
|
||||
-- Renames table defined in 98/02_username_password_domain_vault.up.sql
|
||||
alter table credential_vault_library_username_password_domain_mapping_ovrd
|
||||
rename to credential_vault_generic_library_usern_pass_domain_mapping_ovrd;
|
||||
|
||||
alter table credential_vault_generic_library_usern_pass_domain_mapping_ovrd
|
||||
rename constraint credential_vault_library_username_password_domain_mapping__pkey to credential_vault_generic_library_upd_mapping_ovrd_pkey;
|
||||
|
||||
alter table credential_vault_generic_library_usern_pass_domain_mapping_ovrd
|
||||
rename constraint credential_vault_library_fkey to credential_vault_generic_library_fkey;
|
||||
|
||||
alter table credential_vault_generic_library_usern_pass_domain_mapping_ovrd
|
||||
rename constraint credential_vault_library_mapping_override_fkey to credential_vault_generic_library_mapping_override_fkey;
|
||||
|
||||
comment on table credential_vault_generic_library_usern_pass_domain_mapping_ovrd is
|
||||
'credential_vault_generic_library_usern_pass_domain_mapping_ovrd is a table '
|
||||
'where each row represents a mapping that overrides the default mapping '
|
||||
'from a generic vault secret to a user password domain credential type '
|
||||
'for a vault generic credential library.';
|
||||
|
||||
create trigger insert_credential_vault_generic_library_mapping_override_subtyp before insert on credential_vault_generic_library_usern_pass_domain_mapping_ovrd
|
||||
for each row execute procedure insert_credential_vault_generic_library_mapping_override_subtyp();
|
||||
|
||||
create trigger delete_credential_vault_generic_library_mapping_override_subtyp after delete on credential_vault_generic_library_usern_pass_domain_mapping_ovrd
|
||||
for each row execute procedure delete_credential_vault_generic_library_mapping_override_subtyp();
|
||||
|
||||
|
||||
-- Views. Postgres already automatically updated them to reference the renamed
|
||||
-- tables, however these should still be explictly in the migration.
|
||||
-- > whx_credential_dimension_source
|
||||
-- > credential_vault_library_issue_credentials
|
||||
-- > credential_vault_library_list_lookup: Renamed to credential_vault_generic_library_list_lookup.
|
||||
-- > credential_vault_library_hst_aggregate: Renamed to credential_vault_generic_library_hst_aggregate.
|
||||
|
||||
-- Replaces view defined in 98/04_rdp_targets.up.sql
|
||||
-- Replaced in 100/01_credential_vault_ldap_library.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_generic_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,
|
||||
case
|
||||
when tt.type = 'tcp' then 'tcp target'
|
||||
when tt.type = 'ssh' then 'ssh target'
|
||||
when tt.type = 'rdp' then 'rdp target'
|
||||
else 'Unknown'
|
||||
end 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_all_subtypes 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 defined in 98/02_username_password_domain_vault.up.sql
|
||||
-- Replaced in 100/01_credential_vault_ldap_library.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_generic_library_username_password_mapping_ovrd
|
||||
),
|
||||
ssh_private_key_override (library_id, username_attribute, private_key_attribute, private_key_passphrase_attribute) as (
|
||||
select library_id,
|
||||
nullif(username_attribute, wt_to_sentinel('no override')),
|
||||
nullif(private_key_attribute, wt_to_sentinel('no override')),
|
||||
nullif(private_key_passphrase_attribute, wt_to_sentinel('no override'))
|
||||
from credential_vault_generic_library_ssh_private_key_mapping_ovrd
|
||||
),
|
||||
password_domain_override (library_id, username_attribute, password_attribute, domain_attribute) as (
|
||||
select library_id,
|
||||
nullif(username_attribute, wt_to_sentinel('no override')),
|
||||
nullif(password_attribute, wt_to_sentinel('no override')),
|
||||
nullif(domain_attribute, wt_to_sentinel('no override'))
|
||||
from credential_vault_generic_library_usern_pass_domain_mapping_ovrd
|
||||
)
|
||||
select library.public_id as public_id,
|
||||
library.store_id as store_id,
|
||||
library.name as name,
|
||||
library.description as description,
|
||||
library.create_time as create_time,
|
||||
library.update_time as update_time,
|
||||
library.version as version,
|
||||
library.vault_path as vault_path,
|
||||
library.http_method as http_method,
|
||||
library.http_request_body as http_request_body,
|
||||
library.credential_type as credential_type,
|
||||
null as key_type,
|
||||
null as key_bits,
|
||||
null as username,
|
||||
null as ttl,
|
||||
null as key_id,
|
||||
null as critical_options,
|
||||
null as extensions,
|
||||
store.project_id as project_id,
|
||||
store.vault_address as vault_address,
|
||||
store.namespace as namespace,
|
||||
store.ca_cert as ca_cert,
|
||||
store.tls_server_name as tls_server_name,
|
||||
store.tls_skip_verify as tls_skip_verify,
|
||||
store.worker_filter as worker_filter,
|
||||
store.ct_token as ct_token, -- encrypted
|
||||
store.token_hmac as token_hmac,
|
||||
store.token_status as token_status,
|
||||
store.token_key_id as token_key_id,
|
||||
store.client_cert as client_cert,
|
||||
store.ct_client_key as ct_client_key, -- encrypted
|
||||
store.client_key_id as client_key_id,
|
||||
coalesce(upasso.username_attribute, sshpk.username_attribute, pd.username_attribute)
|
||||
as username_attribute,
|
||||
coalesce(upasso.password_attribute, pd.password_attribute)
|
||||
as password_attribute,
|
||||
pd.domain_attribute as domain_attribute,
|
||||
sshpk.private_key_attribute as private_key_attribute,
|
||||
sshpk.private_key_passphrase_attribute as private_key_passphrase_attribute,
|
||||
'generic' as cred_lib_type, -- used to switch on
|
||||
null as additional_valid_principals
|
||||
from credential_vault_generic_library library
|
||||
join credential_vault_store_client store
|
||||
on library.store_id = store.public_id
|
||||
left join password_override upasso
|
||||
on library.public_id = upasso.library_id
|
||||
left join ssh_private_key_override sshpk
|
||||
on library.public_id = sshpk.library_id
|
||||
left join password_domain_override pd
|
||||
on library.public_id = pd.library_id
|
||||
union
|
||||
select library.public_id as public_id,
|
||||
library.store_id as store_id,
|
||||
library.name as name,
|
||||
library.description as description,
|
||||
library.create_time as create_time,
|
||||
library.update_time as update_time,
|
||||
library.version as version,
|
||||
library.vault_path as vault_path,
|
||||
null as http_method,
|
||||
null as http_request_body,
|
||||
library.credential_type as credential_type,
|
||||
library.key_type as key_type,
|
||||
library.key_bits as key_bits,
|
||||
library.username as username,
|
||||
library.ttl as ttl,
|
||||
library.key_id as key_id,
|
||||
library.critical_options as critical_options,
|
||||
library.extensions as extensions,
|
||||
store.project_id as project_id,
|
||||
store.vault_address as vault_address,
|
||||
store.namespace as namespace,
|
||||
store.ca_cert as ca_cert,
|
||||
store.tls_server_name as tls_server_name,
|
||||
store.tls_skip_verify as tls_skip_verify,
|
||||
store.worker_filter as worker_filter,
|
||||
store.ct_token as ct_token, -- encrypted
|
||||
store.token_hmac as token_hmac,
|
||||
store.token_status as token_status,
|
||||
store.token_key_id as token_key_id,
|
||||
store.client_cert as client_cert,
|
||||
store.ct_client_key as ct_client_key, -- encrypted
|
||||
store.client_key_id as client_key_id,
|
||||
null as username_attribute,
|
||||
null as password_attribute,
|
||||
null as domain_attribute,
|
||||
null as private_key_attribute,
|
||||
null as private_key_passphrase_attribute,
|
||||
'ssh-signed-cert' as cred_lib_type, -- used to switch on
|
||||
additional_valid_principals as additional_valid_principals
|
||||
from credential_vault_ssh_cert_library library
|
||||
join credential_vault_store_client store
|
||||
on library.store_id = store.public_id;
|
||||
comment on view credential_vault_library_issue_credentials is
|
||||
'credential_vault_library_issue_credentials is a view where each row contains a credential library and the credential library''s data needed to connect to Vault. '
|
||||
'This view should only be used when issuing credentials from a Vault credential library. Each row may contain encrypted data. '
|
||||
'This view should not be used to retrieve data which will be returned external to boundary.';
|
||||
|
||||
-- Replaces and renames view defined in 98/02_username_password_domain_vault.up.sql.
|
||||
alter view credential_vault_library_list_lookup
|
||||
rename to credential_vault_generic_library_list_lookup;
|
||||
drop view credential_vault_generic_library_list_lookup;
|
||||
create view credential_vault_generic_library_list_lookup 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_generic_library_username_password_mapping_ovrd
|
||||
),
|
||||
ssh_private_key_override (library_id, username_attribute, private_key_attribute, private_key_passphrase_attribute) as (
|
||||
select library_id,
|
||||
nullif(username_attribute, wt_to_sentinel('no override')),
|
||||
nullif(private_key_attribute, wt_to_sentinel('no override')),
|
||||
nullif(private_key_passphrase_attribute, wt_to_sentinel('no override'))
|
||||
from credential_vault_generic_library_ssh_private_key_mapping_ovrd
|
||||
),
|
||||
password_domain_override (library_id, username_attribute, password_attribute, domain_attribute) as (
|
||||
select library_id,
|
||||
nullif(username_attribute, wt_to_sentinel('no override')),
|
||||
nullif(password_attribute, wt_to_sentinel('no override')),
|
||||
nullif(domain_attribute, wt_to_sentinel('no override'))
|
||||
from credential_vault_generic_library_usern_pass_domain_mapping_ovrd
|
||||
)
|
||||
select library.public_id as public_id,
|
||||
library.store_id as store_id,
|
||||
library.name as name,
|
||||
library.description as description,
|
||||
library.create_time as create_time,
|
||||
library.update_time as update_time,
|
||||
library.version as version,
|
||||
library.vault_path as vault_path,
|
||||
library.http_method as http_method,
|
||||
library.http_request_body as http_request_body,
|
||||
library.credential_type as credential_type,
|
||||
coalesce(upasso.username_attribute, sshpk.username_attribute, pd.username_attribute)
|
||||
as username_attribute,
|
||||
coalesce(upasso.password_attribute, pd.password_attribute)
|
||||
as password_attribute,
|
||||
sshpk.private_key_attribute as private_key_attribute,
|
||||
sshpk.private_key_passphrase_attribute as private_key_passphrase_attribute,
|
||||
pd.domain_attribute as domain_attribute
|
||||
from credential_vault_generic_library library
|
||||
left join password_override upasso
|
||||
on library.public_id = upasso.library_id
|
||||
left join ssh_private_key_override sshpk
|
||||
on library.public_id = sshpk.library_id
|
||||
left join password_domain_override pd
|
||||
on library.public_id = pd.library_id;
|
||||
comment on view credential_vault_generic_library_list_lookup is
|
||||
'credential_vault_generic_library_list_lookup is a view where each row contains a Vault generic credential library and any of library''s credential mapping overrides. '
|
||||
'No encrypted data is returned. This view can be used to retrieve data which will be returned external to boundary.';
|
||||
|
||||
-- Replaces and renames view defined in 71/17_credential_history_views.up.sql.
|
||||
alter view credential_vault_library_hst_aggregate
|
||||
rename to credential_vault_generic_library_hst_aggregate;
|
||||
drop view credential_vault_generic_library_hst_aggregate;
|
||||
create view credential_vault_generic_library_hst_aggregate as
|
||||
select
|
||||
rdc.recording_id,
|
||||
vl.public_id,
|
||||
vl.name,
|
||||
vl.description,
|
||||
vl.vault_path,
|
||||
vl.http_method,
|
||||
vl.http_request_body,
|
||||
vl.credential_type,
|
||||
vsh.public_id as store_public_id,
|
||||
vsh.project_id as store_project_id,
|
||||
vsh.name as store_name,
|
||||
vsh.description as store_description,
|
||||
vsh.vault_address as store_vault_address,
|
||||
vsh.namespace as store_namespace,
|
||||
vsh.tls_server_name as store_tls_server_name,
|
||||
vsh.tls_skip_verify as store_tls_skip_verify,
|
||||
vsh.worker_filter as store_worker_filter,
|
||||
string_agg(distinct rdc.credential_purpose, '|') as purposes
|
||||
from credential_vault_generic_library_hst as vl
|
||||
left join recording_dynamic_credential as rdc on vl.history_id = rdc.credential_library_hst_id
|
||||
join credential_vault_store_hst as vsh on rdc.credential_vault_store_hst_id = vsh.history_id
|
||||
group by vl.history_id, rdc.recording_id, vsh.history_id;
|
||||
comment on view credential_vault_generic_library_hst_aggregate is
|
||||
'credential_vault_generic_library_hst_aggregate contains Vault generic credential library history data along with its store and purpose data.';
|
||||
|
||||
|
||||
-- Renaming is complete. We can now redefine credential_vault_library and
|
||||
-- rewire all the Vault subtype tables into this redefined table.
|
||||
|
||||
-- credential_vault_store doesn't have a unique constraint on (project_id,
|
||||
-- public_id) fields. We'll need this to attach a FK to them later. There
|
||||
-- should be no side-effects from this given that:
|
||||
-- > public_id is a PK (already globally unique) and,
|
||||
-- > credential_vault_store is a subtype table of credential_store, which
|
||||
-- does have a (project_id, public_id) uniqueness constraint.
|
||||
alter table credential_vault_store
|
||||
add constraint credential_vault_store_project_id_public_id_uq
|
||||
unique(project_id, public_id);
|
||||
|
||||
-- This statement defines the basic fields and pulls all the data from
|
||||
-- credential_library. We can do this as a blanket select * statement because
|
||||
-- only Vault-subtype credential libraries exist at this point.
|
||||
--
|
||||
-- We want to insert the data before any constraints are set because
|
||||
-- credential_library has had a few revisions and while we have also updated
|
||||
-- the data alongside those table revisions, success on an insert with
|
||||
-- historical data isn't guaranteed. Additionally, we also want to do this
|
||||
-- before any triggers are put in place.
|
||||
create table credential_vault_library as
|
||||
select * from credential_library;
|
||||
|
||||
alter table credential_vault_library
|
||||
add primary key (public_id),
|
||||
alter column store_id set not null,
|
||||
alter column credential_type set not null,
|
||||
alter column credential_type set default 'unspecified',
|
||||
add constraint credential_type_enm_fkey
|
||||
foreign key (credential_type)
|
||||
references credential_type_enm (name)
|
||||
on delete restrict
|
||||
on update cascade,
|
||||
alter column project_id set not null,
|
||||
alter column create_time set not null,
|
||||
alter column update_time set not null,
|
||||
add constraint credential_library_fkey
|
||||
foreign key (public_id, store_id, credential_type, project_id)
|
||||
references credential_library (public_id, store_id, credential_type, project_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
add constraint credential_vault_store_fkey
|
||||
foreign key (project_id, store_id)
|
||||
references credential_vault_store (project_id, public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
add constraint credential_vault_library_project_id_public_id_uq
|
||||
unique(project_id, public_id),
|
||||
add constraint credential_vault_library_project_store_public_ids_credtype_uq
|
||||
unique(project_id, store_id, public_id, credential_type);
|
||||
comment on table credential_vault_library is
|
||||
'credential_vault_library is a base table for Vault credential library '
|
||||
'subtypes and a child table of credential_vault_store. Each row maps 1-to-1 '
|
||||
'to a row in one of the Vault credential library subtype tables.';
|
||||
|
||||
create trigger immutable_columns before update on credential_vault_library
|
||||
for each row execute function immutable_columns('public_id', 'store_id', 'project_id', 'credential_type');
|
||||
create trigger insert_credential_library_subtype before insert on credential_vault_library
|
||||
for each row execute procedure insert_credential_library_subtype();
|
||||
create trigger delete_credential_library_subtype after delete on credential_vault_library
|
||||
for each row execute procedure delete_credential_library_subtype();
|
||||
create trigger update_credential_library_table_update_time before update on credential_vault_library
|
||||
for each row execute procedure update_credential_library_table_update_time();
|
||||
|
||||
create index credential_vault_library_create_time_public_id_idx
|
||||
on credential_vault_library (create_time desc, public_id desc);
|
||||
create index credential_vault_library_update_time_public_id_idx
|
||||
on credential_vault_library (update_time desc, public_id desc);
|
||||
|
||||
create or replace function insert_credential_vault_library_subtype() returns trigger
|
||||
as $$
|
||||
begin
|
||||
select project_id into new.project_id
|
||||
from credential_store
|
||||
where credential_store.public_id = new.store_id;
|
||||
|
||||
insert into credential_vault_library
|
||||
(public_id, store_id, project_id, credential_type, create_time)
|
||||
values
|
||||
(new.public_id, new.store_id, new.project_id, new.credential_type, new.create_time);
|
||||
return new;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
comment on function insert_credential_vault_library_subtype() is
|
||||
'insert_credential_vault_library_subtype is a before insert trigger '
|
||||
'function that inserts rows from subtype tables into '
|
||||
'credential_vault_library. It must be used on all Vault credential library '
|
||||
'subtype tables.';
|
||||
|
||||
create function delete_credential_vault_library_subtype() returns trigger
|
||||
as $$
|
||||
begin
|
||||
delete from credential_vault_library
|
||||
where public_id = old.public_id;
|
||||
return null; -- result is ignored since this is an after trigger
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
comment on function delete_credential_vault_library_subtype() is
|
||||
'delete_credential_vault_library_subtype is an after delete trigger '
|
||||
'function that deletes rows from credential_vault_library when they''re '
|
||||
'deleted on the subtype tables. It must be used on all Vault credential '
|
||||
'library subtype tables.';
|
||||
|
||||
create function update_credential_vault_library_table_update_time() returns trigger
|
||||
as $$
|
||||
begin
|
||||
update credential_vault_library set update_time = now() where public_id = new.public_id;
|
||||
return new;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
comment on function update_credential_vault_library_table_update_time() is
|
||||
'update_credential_vault_library_table_update_time is a before update '
|
||||
'trigger function that updates the update_time column on '
|
||||
'credential_vault_library. It must be used on all Vault credential library '
|
||||
'subtype tables.';
|
||||
|
||||
|
||||
-- We still have some entities on the Vault subtype tables that reference
|
||||
-- credential_library when they should now reference credential_vault_library.
|
||||
alter table credential_vault_generic_library
|
||||
drop constraint credential_library_fkey,
|
||||
add constraint credential_vault_library_fkey
|
||||
foreign key (project_id, store_id, public_id, credential_type)
|
||||
references credential_vault_library (project_id, store_id, public_id, credential_type)
|
||||
on delete cascade
|
||||
on update cascade;
|
||||
|
||||
-- Updates table defined in 63/01_credential_vault_ssh_cert_library.up.sql
|
||||
alter table credential_vault_ssh_cert_library
|
||||
drop constraint credential_library_fkey,
|
||||
add constraint credential_vault_library_fkey
|
||||
foreign key (project_id, store_id, public_id, credential_type)
|
||||
references credential_vault_library (project_id, store_id, public_id, credential_type)
|
||||
on delete cascade
|
||||
on update cascade;
|
||||
|
||||
drop trigger insert_credential_library_subtype on credential_vault_generic_library;
|
||||
create trigger insert_credential_vault_library_subtype before insert on credential_vault_generic_library
|
||||
for each row execute procedure insert_credential_vault_library_subtype();
|
||||
|
||||
drop trigger delete_credential_library_subtype on credential_vault_generic_library;
|
||||
create trigger delete_credential_vault_library_subtype after delete on credential_vault_generic_library
|
||||
for each row execute procedure delete_credential_vault_library_subtype();
|
||||
|
||||
drop trigger update_credential_library_table_update_time on credential_vault_generic_library;
|
||||
create trigger update_credential_vault_library_table_update_time before update on credential_vault_generic_library
|
||||
for each row execute procedure update_credential_vault_library_table_update_time();
|
||||
|
||||
drop trigger insert_credential_library_subtype on credential_vault_ssh_cert_library;
|
||||
create trigger insert_credential_vault_library_subtype before insert on credential_vault_ssh_cert_library
|
||||
for each row execute procedure insert_credential_vault_library_subtype();
|
||||
|
||||
drop trigger delete_credential_library_subtype on credential_vault_ssh_cert_library;
|
||||
create trigger delete_credential_vault_library_subtype after delete on credential_vault_ssh_cert_library
|
||||
for each row execute procedure delete_credential_vault_library_subtype();
|
||||
|
||||
drop trigger update_credential_library_table_update_time on credential_vault_ssh_cert_library;
|
||||
create trigger update_credential_vault_library_table_update_time before update on credential_vault_ssh_cert_library
|
||||
for each row execute procedure update_credential_vault_library_table_update_time();
|
||||
|
||||
-- Finally, we can correct the restriction in credential_vault_credential.
|
||||
-- credential_vault_library is now a superset of
|
||||
-- credential_vault_generic_library, so this shouldn't have side-effects.
|
||||
-- Updates the table defined in 10/04_vault_credential.up.sql
|
||||
alter table credential_vault_credential
|
||||
drop constraint credential_vault_library_fkey,
|
||||
add constraint credential_vault_library_fkey
|
||||
foreign key (library_id)
|
||||
references credential_vault_library (public_id)
|
||||
on delete set null
|
||||
on update cascade;
|
||||
commit;
|
||||
@@ -0,0 +1,50 @@
|
||||
# Vault Generic Credential Library DB Refactor
|
||||
Historically, the "Vault generic" credential library was named just "Vault"
|
||||
credential library. Over the course of Boundary's development, it was renamed to
|
||||
"Vault generic" to better match its abilities and to conceptually separate it
|
||||
from other Vault-specific credential libraries, however this nomenclature change
|
||||
was not applied to database (or domain) entities. This migration applies this
|
||||
nomenclature change at the database level, renaming all related entities to
|
||||
match the new "Vault generic" name.
|
||||
|
||||
Currently, these database entities are either named or prefixed with
|
||||
`credential_vault_library`. This migration changes them to be named or prefixed
|
||||
with `credential_vault_generic_library`.
|
||||
|
||||
Additionally, `credential_vault_library` takes on a new meaning and is defined
|
||||
as a new base table that holds information about all *Vault-specific* credential
|
||||
libraries. `credential_vault_library` is a subtype table of
|
||||
`credential_library`:
|
||||
|
||||
```
|
||||
credential_library |> credential_vault_library |> credential_vault_generic_library
|
||||
| |> credential_vault_ssh_cred_library
|
||||
| |> any future Vault-specific credential libraries
|
||||
|
|
||||
In the future, we could also have:
|
||||
|> base table for a non-Vault system's credlibs |> credential library subtype tables
|
||||
```
|
||||
|
||||
While the history table for Vault generic credential libraries is renamed to
|
||||
`credential_vault_generic_library_hst`, the overall history design remains
|
||||
unchanged.
|
||||
|
||||
This pattern is being established due to the need to differentiate between the
|
||||
systems that Boundary's credential libraries integrate with, for database
|
||||
integrity purposes:
|
||||
|
||||
`credential_dynamic` is a base table that represents all renewable/revokable
|
||||
dynamic credentials. `credential_vault_credential` is a subtype table that
|
||||
represents renewable/revokable dynamic credentials (Vault leases). Given the
|
||||
specificity of this design, these leases must be obtained by a Vault credential
|
||||
library.
|
||||
|
||||
To enforce this, the `library_id` field in `credential_vault_credential` has a
|
||||
foreign key constraint on `credential_vault_library(public_id)`, meaning only
|
||||
Vault leases obtained by a Vault generic credential library can ever be inserted
|
||||
into this table and therefore managed by Boundary.
|
||||
|
||||
With the future introduction of new Vault credential libraries that can also
|
||||
issue Vault leases, this constraint becomes too restrictive and the design needs
|
||||
to be extended. By redefining `credential_vault_library` as described above,
|
||||
this refactor provides the backing for this to be done.
|
||||
File diff suppressed because it is too large.
Load diff
@@ -479,7 +479,7 @@ begin;
|
||||
('p____rcolors', 'cvs__rcolors', 'red vault store', 'Some', 'https://red.vault.color', 'red'),
|
||||
('p____gcolors', 'cvs__gcolors', 'green vault store', 'Maybe', 'https://green.vault.color', 'green');
|
||||
|
||||
insert into credential_vault_library
|
||||
insert into credential_vault_generic_library
|
||||
(store_id, public_id, name, description, vault_path, http_method)
|
||||
values
|
||||
('cvs__bcolors', 'cvl_______b1', 'blue vault library', 'None', '/secrets', 'GET'),
|
||||
@@ -493,6 +493,13 @@ begin;
|
||||
('cvs__rcolors', 'cvl__ssh__r1', 'red vault ssh library', '/ssh/issue/red', 'webdev', 'ecdsa', 521),
|
||||
('cvs__gcolors', 'cvl__ssh__g1', 'green vault ssh library', '/ssh/issue/green', 'dba', 'rsa', 4096);
|
||||
|
||||
insert into credential_vault_ldap_library
|
||||
(store_id, public_id, name, vault_path, credential_type)
|
||||
values
|
||||
('cvs__bcolors', 'cvl__ldap_b1', 'blue vault ldap library', '/ldap/static-cred/blue', 'username_password_domain'),
|
||||
('cvs__rcolors', 'cvl__ldap_r1', 'red vault ldap library', '/ldap/creds/red', 'username_password_domain'),
|
||||
('cvs__gcolors', 'cvl__ldap_g1', 'green vault ldap library', '/ldap/static-cred/org-colors-r-us/group-green-color/green','username_password_domain');
|
||||
|
||||
insert into credential_static_store
|
||||
(project_id, public_id, name, description)
|
||||
values
|
||||
|
||||
@@ -388,7 +388,7 @@ begin;
|
||||
values
|
||||
('vs_______wvs', 'kdkv___widget', 'current', 'hmac-value', 'token-value', now(), now() + interval '1 hour');
|
||||
|
||||
insert into credential_vault_library
|
||||
insert into credential_vault_generic_library
|
||||
(store_id, public_id, name, description, vault_path, http_method, credential_type)
|
||||
values
|
||||
('vs_______wvs', 'vl______wvl1', 'widget vault library', 'None', '/secrets', 'GET', 'unspecified'),
|
||||
@@ -413,82 +413,82 @@ begin;
|
||||
('vs_______wvs', 'vl______wvl20', 'widget vault kv eighteen', 'None', '/secrets/kv/eighteen', 'GET', 'username_password_domain'),
|
||||
('vs_______wvs', 'vl______wvl21', 'widget vault kv nineteen', 'None', '/secrets/kv/nineteen', 'GET', 'username_password_domain');
|
||||
|
||||
insert into credential_vault_library_username_password_mapping_override
|
||||
insert into credential_vault_generic_library_username_password_mapping_ovrd
|
||||
(library_id)
|
||||
values
|
||||
('vl______wvl4');
|
||||
|
||||
insert into credential_vault_library_username_password_mapping_override
|
||||
insert into credential_vault_generic_library_username_password_mapping_ovrd
|
||||
(library_id, username_attribute)
|
||||
values
|
||||
('vl______wvl5', 'my_username');
|
||||
|
||||
insert into credential_vault_library_username_password_mapping_override
|
||||
insert into credential_vault_generic_library_username_password_mapping_ovrd
|
||||
(library_id, password_attribute)
|
||||
values
|
||||
('vl______wvl6', 'my_password');
|
||||
|
||||
insert into credential_vault_library_username_password_mapping_override
|
||||
insert into credential_vault_generic_library_username_password_mapping_ovrd
|
||||
(library_id, username_attribute, password_attribute)
|
||||
values
|
||||
('vl______wvl7', 'my_username', 'my_password');
|
||||
|
||||
insert into credential_vault_library_ssh_private_key_mapping_override
|
||||
insert into credential_vault_generic_library_ssh_private_key_mapping_ovrd
|
||||
(library_id)
|
||||
values
|
||||
('vl______wvl9');
|
||||
|
||||
insert into credential_vault_library_ssh_private_key_mapping_override
|
||||
insert into credential_vault_generic_library_ssh_private_key_mapping_ovrd
|
||||
(library_id, username_attribute)
|
||||
values
|
||||
('vl______wvl10', 'my_username');
|
||||
|
||||
insert into credential_vault_library_ssh_private_key_mapping_override
|
||||
insert into credential_vault_generic_library_ssh_private_key_mapping_ovrd
|
||||
(library_id, private_key_attribute)
|
||||
values
|
||||
('vl______wvl11', 'my_private_key');
|
||||
|
||||
insert into credential_vault_library_ssh_private_key_mapping_override
|
||||
insert into credential_vault_generic_library_ssh_private_key_mapping_ovrd
|
||||
(library_id, username_attribute, private_key_attribute, private_key_passphrase_attribute)
|
||||
values
|
||||
('vl______wvl12', 'my_username', 'my_private_key', 'my_passphrase');
|
||||
|
||||
insert into credential_vault_library_username_password_domain_mapping_ovrd
|
||||
insert into credential_vault_generic_library_usern_pass_domain_mapping_ovrd
|
||||
(library_id)
|
||||
values
|
||||
('vl______wvl13');
|
||||
|
||||
insert into credential_vault_library_username_password_domain_mapping_ovrd
|
||||
insert into credential_vault_generic_library_usern_pass_domain_mapping_ovrd
|
||||
(library_id, username_attribute, password_attribute, domain_attribute)
|
||||
values
|
||||
('vl______wvl14', 'my_username', 'my_password', 'my_domain');
|
||||
|
||||
insert into credential_vault_library_username_password_domain_mapping_ovrd
|
||||
insert into credential_vault_generic_library_usern_pass_domain_mapping_ovrd
|
||||
(library_id, username_attribute, domain_attribute)
|
||||
values
|
||||
('vl______wvl15', 'my_username', 'my_domain');
|
||||
|
||||
insert into credential_vault_library_username_password_domain_mapping_ovrd
|
||||
insert into credential_vault_generic_library_usern_pass_domain_mapping_ovrd
|
||||
(library_id, password_attribute, domain_attribute)
|
||||
values
|
||||
('vl______wvl16', 'my_password', 'my_domain');
|
||||
|
||||
insert into credential_vault_library_username_password_domain_mapping_ovrd
|
||||
insert into credential_vault_generic_library_usern_pass_domain_mapping_ovrd
|
||||
(library_id, username_attribute, password_attribute)
|
||||
values
|
||||
('vl______wvl17', 'my_username', 'my_password');
|
||||
|
||||
insert into credential_vault_library_username_password_domain_mapping_ovrd
|
||||
insert into credential_vault_generic_library_usern_pass_domain_mapping_ovrd
|
||||
(library_id, username_attribute)
|
||||
values
|
||||
('vl______wvl18', 'my_username');
|
||||
|
||||
insert into credential_vault_library_username_password_domain_mapping_ovrd
|
||||
insert into credential_vault_generic_library_usern_pass_domain_mapping_ovrd
|
||||
(library_id, password_attribute)
|
||||
values
|
||||
('vl______wvl19', 'my_password');
|
||||
|
||||
insert into credential_vault_library_username_password_domain_mapping_ovrd
|
||||
insert into credential_vault_generic_library_usern_pass_domain_mapping_ovrd
|
||||
(library_id, domain_attribute)
|
||||
values
|
||||
('vl______wvl20', 'my_domain');
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
-- Copyright (c) HashiCorp, Inc.
|
||||
-- SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
begin;
|
||||
select plan(4);
|
||||
select wtt_load('widgets', 'iam', 'kms', 'auth', 'hosts', 'targets', 'credentials');
|
||||
|
||||
select lives_ok(
|
||||
$$
|
||||
insert into credential_vault_ldap_library
|
||||
(store_id, public_id, vault_path, credential_type)
|
||||
values
|
||||
('vs_______wvs', 'vl______vlsc1', '/ldap/static-cred/foo', 'username_password_domain'),
|
||||
('vs_______wvs', 'vl______vlsc2', '/ldap/static-cred/foo/bar', 'username_password_domain'),
|
||||
('vs_______wvs', 'vl______vlsc3', '/ldap/static-cred/foo/bar/baz', 'username_password_domain'),
|
||||
('vs_______wvs', 'vl______vlsc4', '/ldap/static-cred/foo/bar/baz/more/paths', 'username_password_domain'),
|
||||
('vs_______wvs', 'vl______vldyn5', '/ldap/creds/foo', 'username_password_domain'),
|
||||
('vs_______wvs', 'vl______vldyn6', '/ldap/creds/foo/bar/baz/more/paths', 'username_password_domain'),
|
||||
('vs_______wvs', 'vl______vldyn7', '/ldap/creds/foo/bar/baz', 'something_that_doesnt_exist');
|
||||
$$
|
||||
);
|
||||
|
||||
select results_eq(
|
||||
$$
|
||||
select public_id, project_id, store_id, name, description, vault_path, credential_type
|
||||
from credential_vault_ldap_library
|
||||
where public_id like 'vl______vlsc%'
|
||||
or public_id like 'vl______vldyn%'
|
||||
order by public_id;
|
||||
$$,
|
||||
$$
|
||||
values
|
||||
('vl______vldyn5'::wt_public_id, 'p____bwidget'::wt_public_id, 'vs_______wvs'::wt_public_id, null::wt_name, null::wt_description, '/ldap/creds/foo', 'username_password_domain'),
|
||||
('vl______vldyn6'::wt_public_id, 'p____bwidget'::wt_public_id, 'vs_______wvs'::wt_public_id, null::wt_name, null::wt_description, '/ldap/creds/foo/bar/baz/more/paths', 'username_password_domain'),
|
||||
('vl______vldyn7'::wt_public_id, 'p____bwidget'::wt_public_id, 'vs_______wvs'::wt_public_id, null::wt_name, null::wt_description, '/ldap/creds/foo/bar/baz', 'username_password_domain'),
|
||||
('vl______vlsc1'::wt_public_id, 'p____bwidget'::wt_public_id, 'vs_______wvs'::wt_public_id, null::wt_name, null::wt_description, '/ldap/static-cred/foo', 'username_password_domain'),
|
||||
('vl______vlsc2'::wt_public_id, 'p____bwidget'::wt_public_id, 'vs_______wvs'::wt_public_id, null::wt_name, null::wt_description, '/ldap/static-cred/foo/bar', 'username_password_domain'),
|
||||
('vl______vlsc3'::wt_public_id, 'p____bwidget'::wt_public_id, 'vs_______wvs'::wt_public_id, null::wt_name, null::wt_description, '/ldap/static-cred/foo/bar/baz', 'username_password_domain'),
|
||||
('vl______vlsc4'::wt_public_id, 'p____bwidget'::wt_public_id, 'vs_______wvs'::wt_public_id, null::wt_name, null::wt_description, '/ldap/static-cred/foo/bar/baz/more/paths', 'username_password_domain');
|
||||
$$
|
||||
);
|
||||
|
||||
select row_eq(
|
||||
$$
|
||||
select count(*)
|
||||
from credential_vault_library
|
||||
where public_id like 'vl______vlsc%'
|
||||
or public_id like 'vl______vldyn%';
|
||||
$$,
|
||||
row(7::bigint)
|
||||
);
|
||||
|
||||
select row_eq(
|
||||
$$
|
||||
select count(*)
|
||||
from credential_library
|
||||
where public_id like 'vl______vlsc%'
|
||||
or public_id like 'vl______vldyn%';
|
||||
$$,
|
||||
row(7::bigint)
|
||||
);
|
||||
|
||||
select * from finish();
|
||||
rollback;
|
||||
@@ -11,31 +11,31 @@ begin;
|
||||
select wtt_load('widgets', 'iam', 'kms', 'auth', 'hosts', 'targets', 'credentials');
|
||||
|
||||
-- validate the setup data
|
||||
select is(count(*), 1::bigint) from credential_vault_library where public_id = 'vl______wvl1' and credential_type = 'unspecified';
|
||||
select is(count(*), 1::bigint) from credential_vault_generic_library where public_id = 'vl______wvl1' and credential_type = 'unspecified';
|
||||
select is(count(*), 1::bigint) from credential_library where public_id = 'vl______wvl1' and credential_type = 'unspecified';
|
||||
|
||||
select is(count(*), 1::bigint) from credential_vault_library where public_id = 'vl______wvl3' and credential_type = 'username_password';
|
||||
select is(count(*), 1::bigint) from credential_vault_generic_library where public_id = 'vl______wvl3' and credential_type = 'username_password';
|
||||
select is(count(*), 1::bigint) from credential_library where public_id = 'vl______wvl3' and credential_type = 'username_password';
|
||||
|
||||
-- validate the insert triggers
|
||||
prepare insert_vault_library as
|
||||
insert into credential_vault_library
|
||||
insert into credential_vault_generic_library
|
||||
(store_id, public_id, vault_path, http_method, credential_type)
|
||||
values
|
||||
('vs_______wvs', 'vl_______tt1', '/secrets/kv', 'GET', 'username_password');
|
||||
select lives_ok('insert_vault_library');
|
||||
|
||||
select is(count(*), 1::bigint) from credential_vault_library where public_id = 'vl_______tt1' and credential_type = 'username_password';
|
||||
select is(count(*), 1::bigint) from credential_vault_generic_library where public_id = 'vl_______tt1' and credential_type = 'username_password';
|
||||
select is(count(*), 1::bigint) from credential_library where public_id = 'vl_______tt1' and credential_type = 'username_password';
|
||||
|
||||
-- validate the delete triggers
|
||||
prepare delete_vault_library as
|
||||
delete
|
||||
from credential_vault_library
|
||||
from credential_vault_generic_library
|
||||
where public_id = 'vl_______tt1';
|
||||
select lives_ok('delete_vault_library');
|
||||
|
||||
select is(count(*), 0::bigint) from credential_vault_library where public_id = 'vl_______tt1';
|
||||
select is(count(*), 0::bigint) from credential_vault_generic_library where public_id = 'vl_______tt1';
|
||||
select is(count(*), 0::bigint) from credential_library where public_id = 'vl_______tt1';
|
||||
|
||||
select * from finish();
|
||||
|
||||
+4
-4
@@ -13,19 +13,19 @@ begin;
|
||||
select is(count(*), 1::bigint) from credential_vault_store where public_id = 'vs_______wvs';
|
||||
|
||||
select is(count(*), 4::bigint)
|
||||
from credential_vault_library_ssh_private_key_mapping_override
|
||||
from credential_vault_generic_library_ssh_private_key_mapping_ovrd
|
||||
where library_id in ('vl______wvl9', 'vl______wvl10', 'vl______wvl11', 'vl______wvl12');
|
||||
|
||||
select is(count(*), 4::bigint)
|
||||
from credential_vault_library_username_password_mapping_override
|
||||
from credential_vault_generic_library_username_password_mapping_ovrd
|
||||
where library_id in ('vl______wvl4', 'vl______wvl5', 'vl______wvl6', 'vl______wvl7');
|
||||
|
||||
select is(count(*), 8::bigint)
|
||||
from credential_vault_library_username_password_domain_mapping_ovrd
|
||||
from credential_vault_generic_library_usern_pass_domain_mapping_ovrd
|
||||
where library_id in ('vl______wvl13', 'vl______wvl14', 'vl______wvl15', 'vl______wvl16', 'vl______wvl17', 'vl______wvl18', 'vl______wvl19', 'vl______wvl20');
|
||||
|
||||
select is(count(*), 8::bigint)
|
||||
from credential_vault_library_mapping_override
|
||||
from credential_vault_generic_library_mapping_override
|
||||
where library_id in ('vl______wvl4', 'vl______wvl5', 'vl______wvl6', 'vl______wvl7', 'vl______wvl9', 'vl______wvl10', 'vl______wvl11', 'vl______wvl12');
|
||||
|
||||
-- create test vault tokens
|
||||
|
||||
@@ -0,0 +1,602 @@
|
||||
-- Copyright (c) HashiCorp, Inc.
|
||||
-- SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
-- Tests the refactor around credential_vault_library (and other associated
|
||||
-- tables/views). It tests that the new structure is setup as expected with all
|
||||
-- the associated triggers, FKs, constraints, etc. It does not test the refactor
|
||||
-- where state is present in the database beforehand. For this, see the Go SQL
|
||||
-- tests in migrations/oss/postgres_99_01_test.go.
|
||||
--
|
||||
-- For extra context, see migration folder 99/README.md.
|
||||
|
||||
begin;
|
||||
select plan(97);
|
||||
|
||||
-- Redefined credential_vault_library.
|
||||
select has_table( 'credential_vault_library');
|
||||
select has_column( 'credential_vault_library', 'public_id');
|
||||
select col_is_pk( 'credential_vault_library', 'public_id');
|
||||
select col_not_null('credential_vault_library', 'public_id');
|
||||
|
||||
select has_column( 'credential_vault_library', 'store_id');
|
||||
select col_not_null('credential_vault_library', 'store_id');
|
||||
|
||||
select has_column( 'credential_vault_library', 'credential_type');
|
||||
select col_not_null( 'credential_vault_library', 'credential_type');
|
||||
select col_default_is('credential_vault_library', 'credential_type', 'unspecified');
|
||||
|
||||
select has_column( 'credential_vault_library', 'project_id');
|
||||
select col_not_null('credential_vault_library', 'project_id');
|
||||
|
||||
select has_column( 'credential_vault_library', 'create_time');
|
||||
select col_not_null('credential_vault_library', 'create_time');
|
||||
|
||||
select has_column( 'credential_vault_library', 'update_time');
|
||||
select col_not_null('credential_vault_library', 'update_time');
|
||||
|
||||
select fk_ok('credential_vault_library', ARRAY['public_id', 'store_id', 'credential_type', 'project_id'], 'credential_library', ARRAY['public_id', 'store_id', 'credential_type', 'project_id']);
|
||||
select fk_ok('credential_vault_library', ARRAY['project_id', 'store_id'], 'credential_vault_store', ARRAY['project_id', 'public_id']);
|
||||
|
||||
select has_trigger('credential_vault_library', 'immutable_columns');
|
||||
select has_trigger('credential_vault_library', 'insert_credential_library_subtype');
|
||||
select has_trigger('credential_vault_library', 'delete_credential_library_subtype');
|
||||
select has_trigger('credential_vault_library', 'update_credential_library_table_update_time');
|
||||
|
||||
-- Vault generic credential library table.
|
||||
select has_table('credential_vault_generic_library');
|
||||
select has_column( 'credential_vault_generic_library', 'public_id');
|
||||
select col_is_pk('credential_vault_generic_library', 'public_id');
|
||||
select has_column( 'credential_vault_generic_library', 'store_id');
|
||||
select has_column( 'credential_vault_generic_library', 'name');
|
||||
select has_column( 'credential_vault_generic_library', 'description');
|
||||
select has_column( 'credential_vault_generic_library', 'create_time');
|
||||
select has_column( 'credential_vault_generic_library', 'update_time');
|
||||
select has_column( 'credential_vault_generic_library', 'version');
|
||||
select has_column( 'credential_vault_generic_library', 'vault_path');
|
||||
select has_column( 'credential_vault_generic_library', 'http_method');
|
||||
select has_column( 'credential_vault_generic_library', 'http_request_body');
|
||||
select has_column( 'credential_vault_generic_library', 'credential_type');
|
||||
select has_column( 'credential_vault_generic_library', 'project_id');
|
||||
|
||||
select fk_ok('credential_vault_generic_library', ARRAY['project_id', 'store_id', 'public_id', 'credential_type'], 'credential_vault_library', ARRAY['project_id', 'store_id', 'public_id', 'credential_type']);
|
||||
|
||||
select has_trigger('credential_vault_generic_library', 'immutable_columns');
|
||||
select has_trigger('credential_vault_generic_library', 'insert_deleted_id');
|
||||
select has_trigger('credential_vault_generic_library', 'insert_credential_vault_library_subtype');
|
||||
select has_trigger('credential_vault_generic_library', 'update_credential_vault_library_table_update_time');
|
||||
select has_trigger('credential_vault_generic_library', 'delete_credential_vault_library_subtype');
|
||||
select has_trigger('credential_vault_generic_library', 'before_insert_credential_vault_library');
|
||||
select has_trigger('credential_vault_generic_library', 'hst_on_insert');
|
||||
select has_trigger('credential_vault_generic_library', 'hst_on_update');
|
||||
select has_trigger('credential_vault_generic_library', 'hst_on_delete');
|
||||
|
||||
-- Vault generic deleted table.
|
||||
select hasnt_table('credential_vault_library_deleted');
|
||||
select has_table( 'credential_vault_generic_library_deleted');
|
||||
|
||||
-- Vault generic history table.
|
||||
select hasnt_table('credential_vault_library_hst');
|
||||
select has_table( 'credential_vault_generic_library_hst');
|
||||
|
||||
-- Mapping overrides: Base table, UP, UPD and SSH PK.
|
||||
select hasnt_table('credential_vault_library_mapping_override');
|
||||
select has_table('credential_vault_generic_library_mapping_override');
|
||||
|
||||
select hasnt_table('credential_vault_library_username_password_mapping_override');
|
||||
select has_table('credential_vault_generic_library_username_password_mapping_ovrd');
|
||||
|
||||
select hasnt_table('credential_vault_library_username_password_domain_mapping_ovrd');
|
||||
select has_table('credential_vault_generic_library_usern_pass_domain_mapping_ovrd');
|
||||
|
||||
select hasnt_table('credential_vault_library_ssh_private_key_mapping_override');
|
||||
select has_table('credential_vault_generic_library_ssh_private_key_mapping_ovrd');
|
||||
|
||||
-- Views.
|
||||
select has_view('credential_vault_library_issue_credentials');
|
||||
select has_view('whx_credential_dimension_source');
|
||||
|
||||
select hasnt_view('credential_vault_generic_hst_aggregate');
|
||||
select has_view('credential_vault_generic_library_hst_aggregate');
|
||||
|
||||
select hasnt_view('credential_vault_library_list_lookup');
|
||||
select has_view('credential_vault_generic_library_list_lookup');
|
||||
|
||||
-- Vault dynamic credentials table.
|
||||
select fk_ok('credential_vault_credential', 'library_id', 'credential_vault_library', 'public_id');
|
||||
|
||||
-- We can be reasonably sure the structure is as intended. Now we insert data
|
||||
-- to actually test it.
|
||||
select lives_ok(
|
||||
$$
|
||||
insert into iam_scope
|
||||
(parent_id, type, public_id, name)
|
||||
values
|
||||
('global', 'org', 'o_LaIu234Pg6', 'Test Org Scope');
|
||||
|
||||
insert into iam_scope
|
||||
(parent_id, type, public_id, name)
|
||||
values
|
||||
('o_LaIu234Pg6', 'project', 'p_31ouN5ldA2', 'Test Project Scope');
|
||||
|
||||
insert into credential_vault_store
|
||||
(public_id, project_id, name, vault_address)
|
||||
values
|
||||
('csvlt_5n3A2kJo', 'p_31ouN5ldA2', 'Test Vault Credential Store', 'https://my.vault.instance.local');
|
||||
|
||||
insert into credential_vault_generic_library
|
||||
(public_id, store_id, project_id, name, description, vault_path, http_method, http_request_body)
|
||||
values
|
||||
('clvlt_V134iOjA', 'csvlt_5n3A2kJo', 'p_31ouN5ldA2', 'vault_generic1', 'Test vault generic credential library', '/my/vault/path', 'POST', 'my_http_request_body');
|
||||
|
||||
insert into credential_vault_generic_library
|
||||
(public_id, store_id, project_id, credential_type, name, description, vault_path, http_method)
|
||||
values
|
||||
('clvlt_j5oK17vG', 'csvlt_5n3A2kJo', 'p_31ouN5ldA2', 'username_password', 'vault_generic2', 'Test vault generic credential library 2', '/my/vault2/path', 'GET');
|
||||
|
||||
insert into credential_vault_ssh_cert_library
|
||||
(public_id, store_id, project_id, name, vault_path, username, key_type, key_bits)
|
||||
values
|
||||
('clvsclt_35iWGH12', 'csvlt_5n3A2kJo', 'p_31ouN5ldA2', 'vault_ssh_cert1', '/ssh/issue/mycert', 'myuser', 'ed25519', 0);
|
||||
|
||||
insert into credential_vault_generic_library_usern_pass_domain_mapping_ovrd
|
||||
values ('clvlt_V134iOjA', 'custom_username_attr', 'custom_pw_attr', 'custom_domain_attr');
|
||||
|
||||
insert into credential_vault_generic_library_username_password_mapping_ovrd
|
||||
values ('clvlt_j5oK17vG', 'custom_username_attr', 'custom_pw_attr');
|
||||
$$
|
||||
);
|
||||
|
||||
-- Assert the data was inserted correctly into the subtype tables.
|
||||
select row_eq(
|
||||
$$ -- Query:
|
||||
select public_id,
|
||||
store_id,
|
||||
name,
|
||||
description,
|
||||
version,
|
||||
vault_path,
|
||||
http_method,
|
||||
http_request_body,
|
||||
credential_type,
|
||||
project_id
|
||||
from credential_vault_generic_library
|
||||
where public_id = 'clvlt_V134iOjA';
|
||||
$$,
|
||||
row( -- Expect:
|
||||
'clvlt_V134iOjA'::wt_public_id,
|
||||
'csvlt_5n3A2kJo'::wt_public_id,
|
||||
'vault_generic1'::wt_name,
|
||||
'Test vault generic credential library'::wt_description,
|
||||
1::wt_version,
|
||||
'/my/vault/path'::text,
|
||||
'POST'::text,
|
||||
'my_http_request_body'::bytea,
|
||||
'unspecified'::text,
|
||||
'p_31ouN5ldA2'::wt_public_id
|
||||
)
|
||||
);
|
||||
|
||||
select row_eq(
|
||||
$$ -- Query:
|
||||
select public_id,
|
||||
store_id,
|
||||
name,
|
||||
description,
|
||||
version,
|
||||
vault_path,
|
||||
http_method,
|
||||
http_request_body,
|
||||
credential_type,
|
||||
project_id
|
||||
from credential_vault_generic_library
|
||||
where public_id = 'clvlt_j5oK17vG';
|
||||
$$,
|
||||
row( -- Expect:
|
||||
'clvlt_j5oK17vG'::wt_public_id,
|
||||
'csvlt_5n3A2kJo'::wt_public_id,
|
||||
'vault_generic2'::wt_name,
|
||||
'Test vault generic credential library 2'::wt_description,
|
||||
1::wt_version,
|
||||
'/my/vault2/path'::text,
|
||||
'GET'::text,
|
||||
null::bytea,
|
||||
'username_password'::text,
|
||||
'p_31ouN5ldA2'::wt_public_id
|
||||
)
|
||||
);
|
||||
|
||||
select row_eq(
|
||||
$$ -- Query:
|
||||
select public_id,
|
||||
store_id,
|
||||
name,
|
||||
version,
|
||||
vault_path,
|
||||
username,
|
||||
key_type,
|
||||
key_bits,
|
||||
ttl,
|
||||
key_id,
|
||||
critical_options,
|
||||
extensions,
|
||||
credential_type,
|
||||
project_id,
|
||||
additional_valid_principals
|
||||
from credential_vault_ssh_cert_library
|
||||
where public_id = 'clvsclt_35iWGH12';
|
||||
$$,
|
||||
row( -- Expect:
|
||||
'clvsclt_35iWGH12'::wt_public_id,
|
||||
'csvlt_5n3A2kJo'::wt_public_id,
|
||||
'vault_ssh_cert1'::wt_name,
|
||||
1::wt_version,
|
||||
'/ssh/issue/mycert'::text,
|
||||
'myuser'::text,
|
||||
'ed25519'::text,
|
||||
0::integer,
|
||||
null::text,
|
||||
null::text,
|
||||
null::bytea,
|
||||
null::bytea,
|
||||
'ssh_certificate'::text,
|
||||
'p_31ouN5ldA2'::wt_public_id,
|
||||
null::text
|
||||
)
|
||||
);
|
||||
|
||||
-- Assert that the data was inserted correctly into the base tables
|
||||
-- (credential_vault_library and credential_library).
|
||||
select row_eq(
|
||||
$$ -- Query:
|
||||
select public_id,
|
||||
store_id,
|
||||
credential_type,
|
||||
project_id
|
||||
from credential_vault_library
|
||||
where public_id = 'clvlt_V134iOjA';
|
||||
$$,
|
||||
row( -- Expect:
|
||||
'clvlt_V134iOjA'::wt_public_id,
|
||||
'csvlt_5n3A2kJo'::wt_public_id,
|
||||
'unspecified'::text,
|
||||
'p_31ouN5ldA2'::wt_public_id
|
||||
)
|
||||
);
|
||||
select row_eq(
|
||||
$$ -- Query:
|
||||
select public_id,
|
||||
store_id,
|
||||
credential_type,
|
||||
project_id
|
||||
from credential_library
|
||||
where public_id = 'clvlt_V134iOjA';
|
||||
$$,
|
||||
row( -- Expect:
|
||||
'clvlt_V134iOjA'::wt_public_id,
|
||||
'csvlt_5n3A2kJo'::wt_public_id,
|
||||
'unspecified'::text,
|
||||
'p_31ouN5ldA2'::wt_public_id
|
||||
)
|
||||
);
|
||||
|
||||
select row_eq(
|
||||
$$ -- Query:
|
||||
select public_id,
|
||||
store_id,
|
||||
credential_type,
|
||||
project_id
|
||||
from credential_vault_library
|
||||
where public_id = 'clvlt_j5oK17vG';
|
||||
$$,
|
||||
row( -- Expect:
|
||||
'clvlt_j5oK17vG'::wt_public_id,
|
||||
'csvlt_5n3A2kJo'::wt_public_id,
|
||||
'username_password'::text,
|
||||
'p_31ouN5ldA2'::wt_public_id
|
||||
)
|
||||
);
|
||||
select row_eq(
|
||||
$$ -- Query:
|
||||
select public_id,
|
||||
store_id,
|
||||
credential_type,
|
||||
project_id
|
||||
from credential_library
|
||||
where public_id = 'clvlt_j5oK17vG';
|
||||
$$,
|
||||
row( -- Expect:
|
||||
'clvlt_j5oK17vG'::wt_public_id,
|
||||
'csvlt_5n3A2kJo'::wt_public_id,
|
||||
'username_password'::text,
|
||||
'p_31ouN5ldA2'::wt_public_id
|
||||
)
|
||||
);
|
||||
|
||||
select row_eq(
|
||||
$$ -- Query:
|
||||
select public_id,
|
||||
store_id,
|
||||
credential_type,
|
||||
project_id
|
||||
from credential_vault_library
|
||||
where public_id = 'clvsclt_35iWGH12';
|
||||
$$,
|
||||
row( -- Expect:
|
||||
'clvsclt_35iWGH12'::wt_public_id,
|
||||
'csvlt_5n3A2kJo'::wt_public_id,
|
||||
'ssh_certificate'::text,
|
||||
'p_31ouN5ldA2'::wt_public_id
|
||||
)
|
||||
);
|
||||
select row_eq(
|
||||
$$ -- Query:
|
||||
select public_id,
|
||||
store_id,
|
||||
credential_type,
|
||||
project_id
|
||||
from credential_library
|
||||
where public_id = 'clvsclt_35iWGH12';
|
||||
$$,
|
||||
row( -- Expect:
|
||||
'clvsclt_35iWGH12'::wt_public_id,
|
||||
'csvlt_5n3A2kJo'::wt_public_id,
|
||||
'ssh_certificate'::text,
|
||||
'p_31ouN5ldA2'::wt_public_id
|
||||
)
|
||||
);
|
||||
|
||||
-- Assert that the data was inserted correctly into the history tables.
|
||||
select row_eq(
|
||||
$$
|
||||
select count(*)
|
||||
from credential_vault_generic_library_hst
|
||||
where public_id = 'clvlt_V134iOjA';
|
||||
$$,
|
||||
row(1::bigint)
|
||||
);
|
||||
select row_eq(
|
||||
$$
|
||||
select count(*)
|
||||
from credential_library_history_base as base
|
||||
join credential_vault_generic_library_hst as vglh on base.history_id = vglh.history_id
|
||||
where vglh.public_id = 'clvlt_V134iOjA';
|
||||
$$,
|
||||
row(1::bigint)
|
||||
);
|
||||
|
||||
select row_eq(
|
||||
$$
|
||||
select count(*)
|
||||
from credential_vault_generic_library_hst
|
||||
where public_id = 'clvlt_j5oK17vG';
|
||||
$$,
|
||||
row(1::bigint)
|
||||
);
|
||||
select row_eq(
|
||||
$$
|
||||
select count(*)
|
||||
from credential_library_history_base as base
|
||||
join credential_vault_generic_library_hst as vglh on base.history_id = vglh.history_id
|
||||
where vglh.public_id = 'clvlt_j5oK17vG';
|
||||
$$,
|
||||
row(1::bigint)
|
||||
);
|
||||
|
||||
select row_eq(
|
||||
$$
|
||||
select count(*)
|
||||
from credential_vault_ssh_cert_library_hst
|
||||
where public_id = 'clvsclt_35iWGH12';
|
||||
$$,
|
||||
row(1::bigint)
|
||||
);
|
||||
select row_eq(
|
||||
$$
|
||||
select count(*)
|
||||
from credential_library_history_base as base
|
||||
join credential_vault_ssh_cert_library_hst as vsclh on base.history_id = vsclh.history_id
|
||||
where vsclh.public_id = 'clvsclt_35iWGH12';
|
||||
$$,
|
||||
row(1::bigint)
|
||||
);
|
||||
|
||||
-- Assert that the data was inserted correctly into the mapping override
|
||||
-- tables.
|
||||
select row_eq(
|
||||
$$
|
||||
select count(*)
|
||||
from credential_vault_generic_library_mapping_override;
|
||||
$$,
|
||||
row(2::bigint)
|
||||
);
|
||||
select row_eq(
|
||||
$$
|
||||
select count(*)
|
||||
from credential_vault_generic_library_usern_pass_domain_mapping_ovrd
|
||||
where library_id = 'clvlt_V134iOjA';
|
||||
$$,
|
||||
row(1::bigint)
|
||||
);
|
||||
select row_eq(
|
||||
$$
|
||||
select count(*)
|
||||
from credential_vault_generic_library_username_password_mapping_ovrd
|
||||
where library_id = 'clvlt_j5oK17vG';
|
||||
$$,
|
||||
row(1::bigint)
|
||||
);
|
||||
|
||||
-- Update credential library. Verify that:
|
||||
-- - The update works.
|
||||
-- - Version column is incremented.
|
||||
-- - A new history entry is created both in the base table and subtype table.
|
||||
select lives_ok(
|
||||
$$
|
||||
update credential_vault_generic_library
|
||||
set
|
||||
name = 'vault_generic1_updated',
|
||||
vault_path = '/my/updated/vault/path'
|
||||
where public_id = 'clvlt_V134iOjA';
|
||||
$$
|
||||
);
|
||||
|
||||
select row_eq(
|
||||
$$ -- Query:
|
||||
select public_id,
|
||||
name,
|
||||
version,
|
||||
vault_path
|
||||
from credential_vault_generic_library
|
||||
where public_id = 'clvlt_V134iOjA';
|
||||
$$,
|
||||
row( -- Expect:
|
||||
'clvlt_V134iOjA'::wt_public_id,
|
||||
'vault_generic1_updated'::wt_name,
|
||||
2::wt_version,
|
||||
'/my/updated/vault/path'::text
|
||||
)
|
||||
);
|
||||
|
||||
select row_eq(
|
||||
$$
|
||||
select count(*)
|
||||
from credential_vault_generic_library_hst
|
||||
where public_id = 'clvlt_V134iOjA';
|
||||
$$,
|
||||
row(2::bigint)
|
||||
);
|
||||
select row_eq(
|
||||
$$
|
||||
select count(*)
|
||||
from credential_library_history_base as base
|
||||
join credential_vault_generic_library_hst as vglh on base.history_id = vglh.history_id
|
||||
where vglh.public_id = 'clvlt_V134iOjA';
|
||||
$$,
|
||||
row(2::bigint)
|
||||
);
|
||||
|
||||
-- Delete credential library. Verify that:
|
||||
-- - The delete works.
|
||||
-- - The deleted credential library id is inserted into the deleted table.
|
||||
-- - The deleted credential library row is deleted from the base tables.
|
||||
-- - Mapping overrides that reference this credential library are
|
||||
-- automatically deleted.
|
||||
select lives_ok(
|
||||
$$
|
||||
delete from credential_vault_generic_library
|
||||
where public_id = 'clvlt_j5oK17vG';
|
||||
$$
|
||||
);
|
||||
|
||||
select row_eq(
|
||||
$$
|
||||
select count(*)
|
||||
from credential_vault_generic_library_deleted
|
||||
where public_id = 'clvlt_j5oK17vG';
|
||||
$$,
|
||||
row(1::bigint)
|
||||
);
|
||||
|
||||
select row_eq(
|
||||
$$
|
||||
select count(*)
|
||||
from credential_vault_library
|
||||
where public_id = 'clvlt_j5oK17vG';
|
||||
$$,
|
||||
row(0::bigint)
|
||||
);
|
||||
select row_eq(
|
||||
$$
|
||||
select count(*)
|
||||
from credential_library
|
||||
where public_id = 'clvlt_j5oK17vG';
|
||||
$$,
|
||||
row(0::bigint)
|
||||
);
|
||||
|
||||
select row_eq(
|
||||
$$
|
||||
select count(*)
|
||||
from credential_vault_generic_library_username_password_mapping_ovrd
|
||||
where library_id = 'clvlt_j5oK17vG';
|
||||
$$,
|
||||
row(0::bigint)
|
||||
);
|
||||
select row_eq(
|
||||
$$
|
||||
select count(*)
|
||||
from credential_vault_generic_library_mapping_override
|
||||
where library_id = 'clvlt_j5oK17vG';
|
||||
$$,
|
||||
row(0::bigint)
|
||||
);
|
||||
|
||||
-- Check data in views.
|
||||
select results_eq(
|
||||
$$
|
||||
select credential_library_id,
|
||||
credential_library_name
|
||||
from whx_credential_dimension_source
|
||||
order by credential_library_name;
|
||||
$$,
|
||||
$$
|
||||
values
|
||||
('cvl_______g1'::wt_public_id, 'green vault library'),
|
||||
('cvl__ssh__g1'::wt_public_id, 'green vault ssh library')
|
||||
$$
|
||||
);
|
||||
|
||||
select results_eq(
|
||||
$$
|
||||
select public_id,
|
||||
name
|
||||
from credential_vault_library_issue_credentials
|
||||
order by name;
|
||||
$$,
|
||||
$$
|
||||
values
|
||||
( 'cvl__ldap_b1'::wt_public_id, 'blue vault ldap library'::wt_name),
|
||||
( 'cvl_______b1'::wt_public_id, 'blue vault library'::wt_name),
|
||||
( 'cvl__ssh__b1'::wt_public_id, 'blue vault ssh library'::wt_name),
|
||||
( 'cvl__ldap_g1'::wt_public_id, 'green vault ldap library'::wt_name),
|
||||
( 'cvl_______g1'::wt_public_id, 'green vault library'::wt_name),
|
||||
( 'cvl__ssh__g1'::wt_public_id, 'green vault ssh library'::wt_name),
|
||||
( 'cvl__ldap_r1'::wt_public_id, 'red vault ldap library'::wt_name),
|
||||
( 'cvl_______r1'::wt_public_id, 'red vault library'::wt_name),
|
||||
( 'cvl__ssh__r1'::wt_public_id, 'red vault ssh library'::wt_name),
|
||||
( 'clvlt_V134iOjA'::wt_public_id, 'vault_generic1_updated'::wt_name),
|
||||
('clvsclt_35iWGH12'::wt_public_id, 'vault_ssh_cert1'::wt_name);
|
||||
$$
|
||||
);
|
||||
|
||||
select results_eq(
|
||||
$$
|
||||
select public_id,
|
||||
name
|
||||
from credential_vault_generic_library_list_lookup
|
||||
order by name;
|
||||
$$,
|
||||
$$
|
||||
values
|
||||
( 'cvl_______b1'::wt_public_id, 'blue vault library'::wt_name),
|
||||
( 'cvl_______g1'::wt_public_id, 'green vault library'::wt_name),
|
||||
( 'cvl_______r1'::wt_public_id, 'red vault library'::wt_name),
|
||||
( 'clvlt_V134iOjA'::wt_public_id, 'vault_generic1_updated'::wt_name);
|
||||
$$
|
||||
);
|
||||
|
||||
select results_eq(
|
||||
$$
|
||||
select public_id,
|
||||
name
|
||||
from credential_vault_generic_library_hst_aggregate
|
||||
order by name;
|
||||
$$,
|
||||
$$
|
||||
values
|
||||
('cvl_______g1'::wt_public_id, 'green vault library'::wt_name)
|
||||
$$
|
||||
);
|
||||
|
||||
select * from finish();
|
||||
rollback;
|
||||
+14
-14
@@ -3,10 +3,10 @@
|
||||
|
||||
-- credential_vault_library_ssh_private_key_mapping_override tests:
|
||||
-- the following triggers
|
||||
-- insert_credential_vault_library_mapping_override_subtype
|
||||
-- delete_credential_vault_library_mapping_override_subtype
|
||||
-- insert_credential_vault_generic_library_mapping_override_subtyp
|
||||
-- delete_credential_vault_generic_library_mapping_override_subtyp
|
||||
-- and the following view
|
||||
-- credential_vault_library_list_lookup
|
||||
-- credential_vault_generic_library_list_lookup
|
||||
|
||||
begin;
|
||||
select plan(11);
|
||||
@@ -14,16 +14,16 @@ begin;
|
||||
|
||||
-- validate the setup data
|
||||
select is(count(*), 4::bigint)
|
||||
from credential_vault_library_ssh_private_key_mapping_override
|
||||
from credential_vault_generic_library_ssh_private_key_mapping_ovrd
|
||||
where library_id in ('vl______wvl9', 'vl______wvl10', 'vl______wvl11', 'vl______wvl12');
|
||||
|
||||
select is(count(*), 4::bigint)
|
||||
from credential_vault_library_mapping_override
|
||||
from credential_vault_generic_library_mapping_override
|
||||
where library_id in ('vl______wvl9', 'vl______wvl10', 'vl______wvl11', 'vl______wvl12');
|
||||
|
||||
prepare select_libraries as
|
||||
select public_id::text, credential_type::text, username_attribute::text, private_key_attribute::text, private_key_passphrase_attribute::text
|
||||
from credential_vault_library_list_lookup
|
||||
from credential_vault_generic_library_list_lookup
|
||||
where public_id in ('vl______wvl2', 'vl______wvl8', 'vl______wvl9', 'vl______wvl10', 'vl______wvl11', 'vl______wvl12')
|
||||
order by public_id;
|
||||
|
||||
@@ -39,28 +39,28 @@ begin;
|
||||
);
|
||||
|
||||
-- validate the insert triggers
|
||||
select is(count(*), 0::bigint) from credential_vault_library_ssh_private_key_mapping_override where library_id = 'vl______wvl8';
|
||||
select is(count(*), 0::bigint) from credential_vault_library_mapping_override where library_id = 'vl______wvl8';
|
||||
select is(count(*), 0::bigint) from credential_vault_generic_library_ssh_private_key_mapping_ovrd where library_id = 'vl______wvl8';
|
||||
select is(count(*), 0::bigint) from credential_vault_generic_library_mapping_override where library_id = 'vl______wvl8';
|
||||
|
||||
prepare insert_cvl_ssh_private_key_mapping_override as
|
||||
insert into credential_vault_library_ssh_private_key_mapping_override
|
||||
insert into credential_vault_generic_library_ssh_private_key_mapping_ovrd
|
||||
(library_id, username_attribute, private_key_attribute)
|
||||
values
|
||||
('vl______wvl8', 'my_username', 'my_private_key');
|
||||
select lives_ok('insert_cvl_ssh_private_key_mapping_override');
|
||||
|
||||
select is(count(*), 1::bigint) from credential_vault_library_ssh_private_key_mapping_override where library_id = 'vl______wvl8';
|
||||
select is(count(*), 1::bigint) from credential_vault_library_mapping_override where library_id = 'vl______wvl8';
|
||||
select is(count(*), 1::bigint) from credential_vault_generic_library_ssh_private_key_mapping_ovrd where library_id = 'vl______wvl8';
|
||||
select is(count(*), 1::bigint) from credential_vault_generic_library_mapping_override where library_id = 'vl______wvl8';
|
||||
|
||||
-- validate the delete triggers
|
||||
prepare delete_cvl_ssh_private_key_mapping_override as
|
||||
delete
|
||||
from credential_vault_library_ssh_private_key_mapping_override
|
||||
from credential_vault_generic_library_ssh_private_key_mapping_ovrd
|
||||
where library_id = 'vl______wvl8';
|
||||
select lives_ok('delete_cvl_ssh_private_key_mapping_override');
|
||||
|
||||
select is(count(*), 0::bigint) from credential_vault_library_ssh_private_key_mapping_override where library_id = 'vl______wvl8';
|
||||
select is(count(*), 0::bigint) from credential_vault_library_mapping_override where library_id = 'vl______wvl8';
|
||||
select is(count(*), 0::bigint) from credential_vault_generic_library_ssh_private_key_mapping_ovrd where library_id = 'vl______wvl8';
|
||||
select is(count(*), 0::bigint) from credential_vault_generic_library_mapping_override where library_id = 'vl______wvl8';
|
||||
|
||||
select * from finish();
|
||||
rollback;
|
||||
+14
-14
@@ -3,10 +3,10 @@
|
||||
|
||||
-- credential_vault_library_username_password_domain_mapping_ovrd tests:
|
||||
-- the following triggers
|
||||
-- insert_credential_vault_library_mapping_override_subtype
|
||||
-- delete_credential_vault_library_mapping_override_subtype
|
||||
-- insert_credential_vault_generic_library_mapping_override_subtyp
|
||||
-- delete_credential_vault_generic_library_mapping_override_subtyp
|
||||
-- and the following view
|
||||
-- credential_vault_library_list_lookup
|
||||
-- credential_vault_generic_library_list_lookup
|
||||
|
||||
begin;
|
||||
|
||||
@@ -15,16 +15,16 @@ begin;
|
||||
|
||||
|
||||
select is(count(*), 8::bigint)
|
||||
from credential_vault_library_username_password_domain_mapping_ovrd
|
||||
from credential_vault_generic_library_usern_pass_domain_mapping_ovrd
|
||||
where library_id in ('vl______wvl13', 'vl______wvl14', 'vl______wvl15', 'vl______wvl16', 'vl______wvl17', 'vl______wvl18', 'vl______wvl19', 'vl______wvl20');
|
||||
|
||||
select is(count(*), 8::bigint)
|
||||
from credential_vault_library_mapping_override
|
||||
from credential_vault_generic_library_mapping_override
|
||||
where library_id in ('vl______wvl13', 'vl______wvl14', 'vl______wvl15', 'vl______wvl16', 'vl______wvl17', 'vl______wvl18', 'vl______wvl19', 'vl______wvl20');
|
||||
|
||||
prepare select_libraries as
|
||||
select public_id::text, credential_type::text, username_attribute::text, password_attribute::text, domain_attribute::text
|
||||
from credential_vault_library_list_lookup
|
||||
from credential_vault_generic_library_list_lookup
|
||||
where public_id in ('vl______wvl2', 'vl______wvl21', 'vl______wvl13', 'vl______wvl14', 'vl______wvl15', 'vl______wvl16', 'vl______wvl17', 'vl______wvl18', 'vl______wvl19', 'vl______wvl20')
|
||||
order by public_id;
|
||||
|
||||
@@ -45,30 +45,30 @@ begin;
|
||||
|
||||
|
||||
-- validate insert triggers
|
||||
select is(count(*), 0::bigint) from credential_vault_library_username_password_domain_mapping_ovrd where library_id = 'vl______wvl21';
|
||||
select is(count(*), 0::bigint) from credential_vault_library_mapping_override where library_id = 'vl______wvl21';
|
||||
select is(count(*), 0::bigint) from credential_vault_generic_library_usern_pass_domain_mapping_ovrd where library_id = 'vl______wvl21';
|
||||
select is(count(*), 0::bigint) from credential_vault_generic_library_mapping_override where library_id = 'vl______wvl21';
|
||||
|
||||
prepare insert_cvl_username_password_mapping_override as
|
||||
insert into credential_vault_library_username_password_domain_mapping_ovrd
|
||||
insert into credential_vault_generic_library_usern_pass_domain_mapping_ovrd
|
||||
(library_id,username_attribute, password_attribute, domain_attribute)
|
||||
values
|
||||
('vl______wvl21', 'my_username', 'my_password', 'my_domain');
|
||||
select lives_ok('insert_cvl_username_password_mapping_override');
|
||||
|
||||
|
||||
select is(count(*), 1::bigint) from credential_vault_library_username_password_domain_mapping_ovrd where library_id = 'vl______wvl21';
|
||||
select is(count(*), 1::bigint) from credential_vault_library_mapping_override where library_id = 'vl______wvl21';
|
||||
select is(count(*), 1::bigint) from credential_vault_generic_library_usern_pass_domain_mapping_ovrd where library_id = 'vl______wvl21';
|
||||
select is(count(*), 1::bigint) from credential_vault_generic_library_mapping_override where library_id = 'vl______wvl21';
|
||||
|
||||
-- validate delete triggers
|
||||
prepare delete_cvl_username_password_domain_mapping_ovrd as
|
||||
delete
|
||||
from credential_vault_library_username_password_domain_mapping_ovrd
|
||||
from credential_vault_generic_library_usern_pass_domain_mapping_ovrd
|
||||
where library_id = 'vl______wvl21';
|
||||
select lives_ok('delete_cvl_username_password_domain_mapping_ovrd');
|
||||
|
||||
|
||||
select is(count(*), 0::bigint) from credential_vault_library_username_password_domain_mapping_ovrd where library_id = 'vl______wvl21';
|
||||
select is(count(*), 0::bigint) from credential_vault_library_mapping_override where library_id = 'vl______wvl21';
|
||||
select is(count(*), 0::bigint) from credential_vault_generic_library_usern_pass_domain_mapping_ovrd where library_id = 'vl______wvl21';
|
||||
select is(count(*), 0::bigint) from credential_vault_generic_library_mapping_override where library_id = 'vl______wvl21';
|
||||
|
||||
select * from finish();
|
||||
|
||||
|
||||
+14
-14
@@ -3,10 +3,10 @@
|
||||
|
||||
-- credential_vault_library_username_password_mapping_override tests:
|
||||
-- the following triggers
|
||||
-- insert_credential_vault_library_mapping_override_subtype
|
||||
-- delete_credential_vault_library_mapping_override_subtype
|
||||
-- insert_credential_vault_generic_library_mapping_override_subtyp
|
||||
-- delete_credential_vault_generic_library_mapping_override_subtyp
|
||||
-- and the following view
|
||||
-- credential_vault_library_list_lookup
|
||||
-- credential_vault_generic_library_list_lookup
|
||||
|
||||
begin;
|
||||
select plan(11);
|
||||
@@ -14,16 +14,16 @@ begin;
|
||||
|
||||
-- validate the setup data
|
||||
select is(count(*), 4::bigint)
|
||||
from credential_vault_library_username_password_mapping_override
|
||||
from credential_vault_generic_library_username_password_mapping_ovrd
|
||||
where library_id in ('vl______wvl4', 'vl______wvl5', 'vl______wvl6', 'vl______wvl7');
|
||||
|
||||
select is(count(*), 4::bigint)
|
||||
from credential_vault_library_mapping_override
|
||||
from credential_vault_generic_library_mapping_override
|
||||
where library_id in ('vl______wvl4', 'vl______wvl5', 'vl______wvl6', 'vl______wvl7');
|
||||
|
||||
prepare select_libraries as
|
||||
select public_id::text, credential_type::text, username_attribute::text, password_attribute::text
|
||||
from credential_vault_library_list_lookup
|
||||
from credential_vault_generic_library_list_lookup
|
||||
where public_id in ('vl______wvl2', 'vl______wvl3', 'vl______wvl4', 'vl______wvl5', 'vl______wvl6', 'vl______wvl7')
|
||||
order by public_id;
|
||||
|
||||
@@ -39,28 +39,28 @@ begin;
|
||||
);
|
||||
|
||||
-- validate the insert triggers
|
||||
select is(count(*), 0::bigint) from credential_vault_library_username_password_mapping_override where library_id = 'vl______wvl3';
|
||||
select is(count(*), 0::bigint) from credential_vault_library_mapping_override where library_id = 'vl______wvl3';
|
||||
select is(count(*), 0::bigint) from credential_vault_generic_library_username_password_mapping_ovrd where library_id = 'vl______wvl3';
|
||||
select is(count(*), 0::bigint) from credential_vault_generic_library_mapping_override where library_id = 'vl______wvl3';
|
||||
|
||||
prepare insert_cvl_username_password_mapping_override as
|
||||
insert into credential_vault_library_username_password_mapping_override
|
||||
insert into credential_vault_generic_library_username_password_mapping_ovrd
|
||||
(library_id, username_attribute, password_attribute)
|
||||
values
|
||||
('vl______wvl3', 'my_username', 'my_password');
|
||||
select lives_ok('insert_cvl_username_password_mapping_override');
|
||||
|
||||
select is(count(*), 1::bigint) from credential_vault_library_username_password_mapping_override where library_id = 'vl______wvl3';
|
||||
select is(count(*), 1::bigint) from credential_vault_library_mapping_override where library_id = 'vl______wvl3';
|
||||
select is(count(*), 1::bigint) from credential_vault_generic_library_username_password_mapping_ovrd where library_id = 'vl______wvl3';
|
||||
select is(count(*), 1::bigint) from credential_vault_generic_library_mapping_override where library_id = 'vl______wvl3';
|
||||
|
||||
-- validate the delete triggers
|
||||
prepare delete_cvl_username_password_mapping_override as
|
||||
delete
|
||||
from credential_vault_library_username_password_mapping_override
|
||||
from credential_vault_generic_library_username_password_mapping_ovrd
|
||||
where library_id = 'vl______wvl3';
|
||||
select lives_ok('delete_cvl_username_password_mapping_override');
|
||||
|
||||
select is(count(*), 0::bigint) from credential_vault_library_username_password_mapping_override where library_id = 'vl______wvl3';
|
||||
select is(count(*), 0::bigint) from credential_vault_library_mapping_override where library_id = 'vl______wvl3';
|
||||
select is(count(*), 0::bigint) from credential_vault_generic_library_username_password_mapping_ovrd where library_id = 'vl______wvl3';
|
||||
select is(count(*), 0::bigint) from credential_vault_generic_library_mapping_override where library_id = 'vl______wvl3';
|
||||
|
||||
select * from finish();
|
||||
rollback;
|
||||
@@ -2,7 +2,7 @@
|
||||
-- SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
begin;
|
||||
select plan(13);
|
||||
select plan(16);
|
||||
|
||||
-- Verify the trigger functions exist and are declared properly
|
||||
select has_function('insert_credential_library_history_subtype');
|
||||
@@ -13,18 +13,23 @@ begin;
|
||||
select volatility_is('delete_credential_library_history_subtype', 'volatile');
|
||||
select isnt_strict('delete_credential_library_history_subtype');
|
||||
|
||||
select has_trigger('credential_vault_library_hst', 'insert_credential_library_history_subtype');
|
||||
select has_trigger('credential_vault_library_hst', 'delete_credential_library_history_subtype');
|
||||
select fk_ok('credential_vault_library_hst', 'history_id', 'credential_library_history_base' , 'history_id');
|
||||
select has_trigger('credential_vault_generic_library_hst', 'insert_credential_library_history_subtype');
|
||||
select has_trigger('credential_vault_generic_library_hst', 'delete_credential_library_history_subtype');
|
||||
select fk_ok('credential_vault_generic_library_hst', 'history_id', 'credential_library_history_base' , 'history_id');
|
||||
|
||||
select has_trigger('credential_vault_ssh_cert_library_hst', 'insert_credential_library_history_subtype');
|
||||
select has_trigger('credential_vault_ssh_cert_library_hst', 'delete_credential_library_history_subtype');
|
||||
select fk_ok('credential_vault_ssh_cert_library_hst', 'history_id', 'credential_library_history_base' , 'history_id');
|
||||
|
||||
select has_trigger('credential_vault_ldap_library_hst', 'insert_credential_library_history_subtype');
|
||||
select has_trigger('credential_vault_ldap_library_hst', 'delete_credential_library_history_subtype');
|
||||
select fk_ok('credential_vault_ldap_library_hst', 'history_id', 'credential_library_history_base' , 'history_id');
|
||||
|
||||
select results_eq(
|
||||
'select '
|
||||
'(select count(*) from credential_vault_library_hst) + '
|
||||
'(select count(*) from credential_vault_ssh_cert_library_hst)',
|
||||
'(select count(*) from credential_vault_generic_library_hst) + '
|
||||
'(select count(*) from credential_vault_ssh_cert_library_hst) + '
|
||||
'(select count(*) from credential_vault_ldap_library_hst)',
|
||||
'select count(*) from credential_library_history_base'
|
||||
);
|
||||
|
||||
|
||||
@@ -2,14 +2,18 @@
|
||||
-- SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
begin;
|
||||
select plan(9);
|
||||
select plan(12);
|
||||
|
||||
-- Verify the trigger functions exist and are declared properly
|
||||
select has_function('update_credential_library_table_update_time');
|
||||
select volatility_is('update_credential_library_table_update_time', 'volatile');
|
||||
select isnt_strict('update_credential_library_table_update_time');
|
||||
select has_trigger('credential_vault_library', 'update_credential_library_table_update_time');
|
||||
select has_trigger('credential_vault_ssh_cert_library', 'update_credential_library_table_update_time');
|
||||
|
||||
select has_function('update_credential_vault_library_table_update_time');
|
||||
select volatility_is('update_credential_vault_library_table_update_time', 'volatile');
|
||||
select isnt_strict('update_credential_vault_library_table_update_time');
|
||||
select has_trigger('credential_vault_generic_library', 'update_credential_vault_library_table_update_time');
|
||||
select has_trigger('credential_vault_ssh_cert_library', 'update_credential_vault_library_table_update_time');
|
||||
select has_index('credential_library', 'credential_library_create_time_public_id_idx', array['create_time', 'public_id']);
|
||||
select has_index('credential_library', 'credential_library_update_time_public_id_idx', array['update_time', 'public_id']);
|
||||
|
||||
@@ -17,14 +21,14 @@ begin;
|
||||
-- when an insert into a subtype table happens works, we check that the
|
||||
-- create time of the entries in both tables match
|
||||
prepare credential_library_create_time as select create_time from credential_library where public_id='cvl_______b1';
|
||||
prepare credential_vault_library_create_time as select create_time from credential_vault_library where public_id='cvl_______b1';
|
||||
prepare credential_vault_library_create_time as select create_time from credential_vault_generic_library where public_id='cvl_______b1';
|
||||
select results_eq('credential_library_create_time','credential_vault_library_create_time');
|
||||
-- To test the trigger that updates the update_time of the base table,
|
||||
-- we update one of the existing creds and check that the base table
|
||||
-- entry has the same update_time as the subtype one.
|
||||
update credential_vault_library set name='blue black vault library' where public_id='cvl_______b1';
|
||||
update credential_vault_generic_library set name='blue black vault library' where public_id='cvl_______b1';
|
||||
prepare credential_library_update_time as select update_time from credential_library where public_id='cvl_______b1';
|
||||
prepare credential_vault_library_update_time as select update_time from credential_vault_library where public_id='cvl_______b1';
|
||||
prepare credential_vault_library_update_time as select update_time from credential_vault_generic_library where public_id='cvl_______b1';
|
||||
select results_eq('credential_library_update_time','credential_vault_library_update_time');
|
||||
|
||||
select * from finish();
|
||||
|
||||
@@ -5,7 +5,7 @@ begin;
|
||||
select plan(31);
|
||||
|
||||
select has_table('recording_dynamic_credential');
|
||||
select has_view('credential_vault_library_hst_aggregate', 'view for aggregate vault library history info does not exist');
|
||||
select has_view('credential_vault_generic_library_hst_aggregate', 'view for aggregate vault generic library history info does not exist');
|
||||
select has_view('credential_vault_ssh_cert_library_hst_aggregate', 'view for aggregate vault ssh cert library history info does not exist');
|
||||
|
||||
-- tests a fk column referencing a history table
|
||||
@@ -55,7 +55,7 @@ begin;
|
||||
|
||||
prepare select_recording_vault_libraries as
|
||||
select recording_id::text, public_id::text, store_public_id::text, purposes::text
|
||||
from credential_vault_library_hst_aggregate
|
||||
from credential_vault_generic_library_hst_aggregate
|
||||
where recording_id = 'sr1_____cora'
|
||||
order by public_id;
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ begin;
|
||||
select is(count(*), 1::bigint) from wh_credential_dimension where organization_id = 'o_____widget';
|
||||
|
||||
-- change the crediential for the target
|
||||
update credential_vault_library set vault_path = '/secrets/tcp/admin' where public_id = 'vl______wvl1';
|
||||
update credential_vault_generic_library set vault_path = '/secrets/tcp/admin' where public_id = 'vl______wvl1';
|
||||
|
||||
-- start another session, should result in a new credential dimension
|
||||
insert into session
|
||||
@@ -80,7 +80,7 @@ begin;
|
||||
select is(count(*), 3::bigint) from wh_credential_dimension where organization_id = 'o_____widget';
|
||||
|
||||
-- change the crediential again for the target
|
||||
update credential_vault_library set vault_path = '/secrets/tcp/user' where vault_path = '/secrets/tcp/admin';
|
||||
update credential_vault_generic_library set vault_path = '/secrets/tcp/user' where vault_path = '/secrets/tcp/admin';
|
||||
|
||||
-- start another session, should result in a one new credential dimensions since one changed
|
||||
insert into session
|
||||
@@ -101,7 +101,7 @@ begin;
|
||||
|
||||
-- remove all credentials from the target
|
||||
-- then test creating a session
|
||||
delete from credential_vault_library;
|
||||
delete from credential_vault_generic_library;
|
||||
insert into session
|
||||
( project_id, target_id, user_id, auth_token_id, certificate, endpoint, public_id)
|
||||
values
|
||||
|
||||
@@ -69,7 +69,7 @@ begin;
|
||||
join wh_session_accumulating_fact on wh_session_accumulating_fact.session_id = wh_session_connection_accumulating_fact.session_id
|
||||
where wh_session_connection_accumulating_fact.session_id = 's1____walter';
|
||||
|
||||
update credential_vault_library set vault_path = '/secrets/tcp/user' where public_id = 'vl______wvl2';
|
||||
update credential_vault_generic_library set vault_path = '/secrets/tcp/user' where public_id = 'vl______wvl2';
|
||||
|
||||
insert into session
|
||||
( project_id, target_id, user_id, auth_token_id, certificate, endpoint, public_id)
|
||||
|
||||
+17
@@ -77,6 +77,11 @@ message CredentialLibrary {
|
||||
(custom_options.v1.generate_sdk_option) = true,
|
||||
(custom_options.v1.subtype) = "vault-generic"
|
||||
];
|
||||
VaultLdapCredentialLibraryAttributes vault_ldap_credential_library_attributes = 104 [
|
||||
(google.api.field_visibility).restriction = "INTERNAL",
|
||||
(custom_options.v1.generate_sdk_option) = true,
|
||||
(custom_options.v1.subtype) = "vault-ldap"
|
||||
];
|
||||
}
|
||||
|
||||
// Output only. The available actions on this resource for this user.
|
||||
@@ -210,3 +215,15 @@ message VaultSSHCertificateCredentialLibraryAttributes {
|
||||
}
|
||||
]; // @gotags: `class:"public"`
|
||||
}
|
||||
|
||||
// The attributes of a vault LDAP Credential Library.
|
||||
message VaultLdapCredentialLibraryAttributes {
|
||||
// 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"`
|
||||
}
|
||||
@@ -535,3 +535,52 @@ message SshPrivateKeyOverride {
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string private_key_passphrase_attribute = 4;
|
||||
}
|
||||
|
||||
message LdapCredentialLibrary {
|
||||
// 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"
|
||||
}];
|
||||
|
||||
// credential_type is always ssh_certificate
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string credential_type = 9;
|
||||
}
|
||||
@@ -18,6 +18,17 @@ function create_vault_generic_library() {
|
||||
create vault-generic $@
|
||||
}
|
||||
|
||||
function create_vault_ldap_library() {
|
||||
boundary credential-libraries \
|
||||
create vault-ldap $@
|
||||
}
|
||||
|
||||
function update_vault_ldap_library() {
|
||||
boundary credential-libraries \
|
||||
update vault-ldap $@
|
||||
}
|
||||
|
||||
|
||||
function create_vault_library() {
|
||||
boundary credential-libraries \
|
||||
create vault $@
|
||||
|
||||
@@ -52,7 +52,7 @@ export NEW_VAULT_LIB="test_vault"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "boundary/credential-libraries: can not create already created $NEW_VAULT_LIB vault-generic libary" {
|
||||
@test "boundary/credential-libraries: can not create already created $NEW_VAULT_LIB vault-generic library" {
|
||||
skip_if_no_vault
|
||||
|
||||
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
|
||||
@@ -106,7 +106,7 @@ export NEW_VAULT_LIB="test_vault"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "boundary/credential-libraries: can not create already created $NEW_VAULT_LIB vault-ssh-certificate libary" {
|
||||
@test "boundary/credential-libraries: can not create already created $NEW_VAULT_LIB vault-ssh-certificate library" {
|
||||
skip_if_no_vault
|
||||
|
||||
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
|
||||
@@ -587,6 +587,69 @@ export NEW_VAULT_LIB="test_vault"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "boundary/credential-libraries: can create $NEW_VAULT_LIB vault-ldap library in credential store $NEW_STORE" {
|
||||
skip_if_no_vault
|
||||
|
||||
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
|
||||
run create_vault_ldap_library \
|
||||
-name $NEW_VAULT_LIB -credential-store-id $csid \
|
||||
-vault-path /ldap/static-cred/einstein \
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
|
||||
@test "boundary/credential-libraries: can not create already created $NEW_VAULT_LIB vault-ldap library" {
|
||||
skip_if_no_vault
|
||||
|
||||
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
|
||||
run create_vault_ldap_library \
|
||||
-name $NEW_VAULT_LIB -credential-store-id $csid \
|
||||
-vault-path /ldap/static-cred/einstein \
|
||||
echo "$output"
|
||||
[ "$status" -eq 1 ]
|
||||
}
|
||||
|
||||
@test "boundary/credential-libraries: can update $NEW_VAULT_LIB vault-ldap library description" {
|
||||
skip_if_no_vault
|
||||
|
||||
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
|
||||
local clid=$(credential_library_id $NEW_VAULT_LIB $csid)
|
||||
run update_vault_ldap_library -id $clid -description hellothere
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
run read_credential_library $clid
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
got=$(echo "$output")
|
||||
|
||||
run field_eq "$got" ".item.attributes.description" "null"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "boundary/credential-libraries: can read $NEW_VAULT_LIB vault-ldap 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-ldaplibrary" {
|
||||
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
|
||||
|
||||
@@ -58,6 +58,7 @@ type CredentialLibrary struct {
|
||||
// *CredentialLibrary_VaultCredentialLibraryAttributes
|
||||
// *CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes
|
||||
// *CredentialLibrary_VaultGenericCredentialLibraryAttributes
|
||||
// *CredentialLibrary_VaultLdapCredentialLibraryAttributes
|
||||
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"`
|
||||
@@ -205,6 +206,15 @@ func (x *CredentialLibrary) GetVaultGenericCredentialLibraryAttributes() *VaultC
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *CredentialLibrary) GetVaultLdapCredentialLibraryAttributes() *VaultLdapCredentialLibraryAttributes {
|
||||
if x != nil {
|
||||
if x, ok := x.Attrs.(*CredentialLibrary_VaultLdapCredentialLibraryAttributes); ok {
|
||||
return x.VaultLdapCredentialLibraryAttributes
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *CredentialLibrary) GetAuthorizedActions() []string {
|
||||
if x != nil {
|
||||
return x.AuthorizedActions
|
||||
@@ -247,6 +257,10 @@ type CredentialLibrary_VaultGenericCredentialLibraryAttributes struct {
|
||||
VaultGenericCredentialLibraryAttributes *VaultCredentialLibraryAttributes `protobuf:"bytes,103,opt,name=vault_generic_credential_library_attributes,json=vaultGenericCredentialLibraryAttributes,proto3,oneof"`
|
||||
}
|
||||
|
||||
type CredentialLibrary_VaultLdapCredentialLibraryAttributes struct {
|
||||
VaultLdapCredentialLibraryAttributes *VaultLdapCredentialLibraryAttributes `protobuf:"bytes,104,opt,name=vault_ldap_credential_library_attributes,json=vaultLdapCredentialLibraryAttributes,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*CredentialLibrary_Attributes) isCredentialLibrary_Attrs() {}
|
||||
|
||||
func (*CredentialLibrary_VaultCredentialLibraryAttributes) isCredentialLibrary_Attrs() {}
|
||||
@@ -256,6 +270,8 @@ func (*CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes) isCrede
|
||||
|
||||
func (*CredentialLibrary_VaultGenericCredentialLibraryAttributes) isCredentialLibrary_Attrs() {}
|
||||
|
||||
func (*CredentialLibrary_VaultLdapCredentialLibraryAttributes) isCredentialLibrary_Attrs() {}
|
||||
|
||||
// The attributes of a vault typed Credential Library.
|
||||
type VaultCredentialLibraryAttributes struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
@@ -438,11 +454,57 @@ func (x *VaultSSHCertificateCredentialLibraryAttributes) GetAdditionalValidPrinc
|
||||
return nil
|
||||
}
|
||||
|
||||
// The attributes of a vault LDAP Credential Library.
|
||||
type VaultLdapCredentialLibraryAttributes struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// 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"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *VaultLdapCredentialLibraryAttributes) Reset() {
|
||||
*x = VaultLdapCredentialLibraryAttributes{}
|
||||
mi := &file_controller_api_resources_credentiallibraries_v1_credential_library_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *VaultLdapCredentialLibraryAttributes) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*VaultLdapCredentialLibraryAttributes) ProtoMessage() {}
|
||||
|
||||
func (x *VaultLdapCredentialLibraryAttributes) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_api_resources_credentiallibraries_v1_credential_library_proto_msgTypes[3]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use VaultLdapCredentialLibraryAttributes.ProtoReflect.Descriptor instead.
|
||||
func (*VaultLdapCredentialLibraryAttributes) Descriptor() ([]byte, []int) {
|
||||
return file_controller_api_resources_credentiallibraries_v1_credential_library_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *VaultLdapCredentialLibraryAttributes) GetPath() *wrapperspb.StringValue {
|
||||
if x != nil {
|
||||
return x.Path
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_controller_api_resources_credentiallibraries_v1_credential_library_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_controller_api_resources_credentiallibraries_v1_credential_library_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"Hcontroller/api/resources/credentiallibraries/v1/credential_library.proto\x12/controller.api.resources.credentiallibraries.v1\x1a.controller/api/resources/scopes/v1/scope.proto\x1a*controller/custom_options/v1/options.proto\x1a\x1bgoogle/api/visibility.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1egoogle/protobuf/wrappers.proto\"\xae\v\n" +
|
||||
"Hcontroller/api/resources/credentiallibraries/v1/credential_library.proto\x12/controller.api.resources.credentiallibraries.v1\x1a.controller/api/resources/scopes/v1/scope.proto\x1a*controller/custom_options/v1/options.proto\x1a\x1bgoogle/api/visibility.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1egoogle/protobuf/wrappers.proto\"\x84\r\n" +
|
||||
"\x11CredentialLibrary\x12\x0e\n" +
|
||||
"\x02id\x18\n" +
|
||||
" \x01(\tR\x02id\x120\n" +
|
||||
@@ -464,7 +526,10 @@ const file_controller_api_resources_credentiallibraries_v1_credential_library_pr
|
||||
"3vault_ssh_certificate_credential_library_attributes\x18f \x01(\v2_.controller.api.resources.credentiallibraries.v1.VaultSSHCertificateCredentialLibraryAttributesB-\xa0\xda)\x01\x9a\xe3)\x15vault-ssh-certificate\xfa\xd2\xe4\x93\x02\n" +
|
||||
"\x12\bINTERNALH\x00R.vaultSshCertificateCredentialLibraryAttributes\x12\xd8\x01\n" +
|
||||
"+vault_generic_credential_library_attributes\x18g \x01(\v2Q.controller.api.resources.credentiallibraries.v1.VaultCredentialLibraryAttributesB%\xa0\xda)\x01\x9a\xe3)\rvault-generic\xfa\xd2\xe4\x93\x02\n" +
|
||||
"\x12\bINTERNALH\x00R'vaultGenericCredentialLibraryAttributes\x12/\n" +
|
||||
"\x12\bINTERNALH\x00R'vaultGenericCredentialLibraryAttributes\x12\xd3\x01\n" +
|
||||
"(vault_ldap_credential_library_attributes\x18h \x01(\v2U.controller.api.resources.credentiallibraries.v1.VaultLdapCredentialLibraryAttributesB\"\xa0\xda)\x01\x9a\xe3)\n" +
|
||||
"vault-ldap\xfa\xd2\xe4\x93\x02\n" +
|
||||
"\x12\bINTERNALH\x00R$vaultLdapCredentialLibraryAttributes\x12/\n" +
|
||||
"\x12authorized_actions\x18\xac\x02 \x03(\tR\x12authorized_actions\x12/\n" +
|
||||
"\x0fcredential_type\x18\xb6\x02 \x01(\tB\x04\xa0\xda)\x01R\x0fcredential_type\x12b\n" +
|
||||
"\x1ccredential_mapping_overrides\x18\xc0\x02 \x01(\v2\x17.google.protobuf.StructB\x04\xa0\xda)\x01R\x1ccredential_mapping_overridesB\a\n" +
|
||||
@@ -507,7 +572,11 @@ const file_controller_api_resources_credentiallibraries_v1_credential_library_pr
|
||||
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\x1a=\n" +
|
||||
"\x0fExtensionsEntry\x12\x10\n" +
|
||||
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
|
||||
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01BhZfgithub.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentiallibraries;credentiallibrariesb\x06proto3"
|
||||
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"~\n" +
|
||||
"$VaultLdapCredentialLibraryAttributes\x12V\n" +
|
||||
"\x04path\x18\n" +
|
||||
" \x01(\v2\x1c.google.protobuf.StringValueB$\xa0\xda)\x01\xc2\xdd)\x1c\n" +
|
||||
"\x0fattributes.path\x12\tVaultPathR\x04pathBhZfgithub.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentiallibraries;credentiallibrariesb\x06proto3"
|
||||
|
||||
var (
|
||||
file_controller_api_resources_credentiallibraries_v1_credential_library_proto_rawDescOnce sync.Once
|
||||
@@ -521,47 +590,50 @@ 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, 5)
|
||||
var file_controller_api_resources_credentiallibraries_v1_credential_library_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
|
||||
var file_controller_api_resources_credentiallibraries_v1_credential_library_proto_goTypes = []any{
|
||||
(*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
|
||||
(*VaultLdapCredentialLibraryAttributes)(nil), // 3: controller.api.resources.credentiallibraries.v1.VaultLdapCredentialLibraryAttributes
|
||||
nil, // 4: controller.api.resources.credentiallibraries.v1.VaultSSHCertificateCredentialLibraryAttributes.CriticalOptionsEntry
|
||||
nil, // 5: controller.api.resources.credentiallibraries.v1.VaultSSHCertificateCredentialLibraryAttributes.ExtensionsEntry
|
||||
(*scopes.ScopeInfo)(nil), // 6: controller.api.resources.scopes.v1.ScopeInfo
|
||||
(*wrapperspb.StringValue)(nil), // 7: google.protobuf.StringValue
|
||||
(*timestamppb.Timestamp)(nil), // 8: google.protobuf.Timestamp
|
||||
(*structpb.Struct)(nil), // 9: google.protobuf.Struct
|
||||
(*wrapperspb.UInt32Value)(nil), // 10: google.protobuf.UInt32Value
|
||||
}
|
||||
var file_controller_api_resources_credentiallibraries_v1_credential_library_proto_depIdxs = []int32{
|
||||
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
|
||||
6, // 0: controller.api.resources.credentiallibraries.v1.CredentialLibrary.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
|
||||
7, // 1: controller.api.resources.credentiallibraries.v1.CredentialLibrary.name:type_name -> google.protobuf.StringValue
|
||||
7, // 2: controller.api.resources.credentiallibraries.v1.CredentialLibrary.description:type_name -> google.protobuf.StringValue
|
||||
8, // 3: controller.api.resources.credentiallibraries.v1.CredentialLibrary.created_time:type_name -> google.protobuf.Timestamp
|
||||
8, // 4: controller.api.resources.credentiallibraries.v1.CredentialLibrary.updated_time:type_name -> google.protobuf.Timestamp
|
||||
9, // 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
|
||||
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
|
||||
6, // 21: controller.api.resources.credentiallibraries.v1.VaultSSHCertificateCredentialLibraryAttributes.additional_valid_principals:type_name -> google.protobuf.StringValue
|
||||
22, // [22:22] is the sub-list for method output_type
|
||||
22, // [22:22] is the sub-list for method input_type
|
||||
22, // [22:22] is the sub-list for extension type_name
|
||||
22, // [22:22] is the sub-list for extension extendee
|
||||
0, // [0:22] is the sub-list for field type_name
|
||||
3, // 9: controller.api.resources.credentiallibraries.v1.CredentialLibrary.vault_ldap_credential_library_attributes:type_name -> controller.api.resources.credentiallibraries.v1.VaultLdapCredentialLibraryAttributes
|
||||
9, // 10: controller.api.resources.credentiallibraries.v1.CredentialLibrary.credential_mapping_overrides:type_name -> google.protobuf.Struct
|
||||
7, // 11: controller.api.resources.credentiallibraries.v1.VaultCredentialLibraryAttributes.path:type_name -> google.protobuf.StringValue
|
||||
7, // 12: controller.api.resources.credentiallibraries.v1.VaultCredentialLibraryAttributes.http_method:type_name -> google.protobuf.StringValue
|
||||
7, // 13: controller.api.resources.credentiallibraries.v1.VaultCredentialLibraryAttributes.http_request_body:type_name -> google.protobuf.StringValue
|
||||
7, // 14: controller.api.resources.credentiallibraries.v1.VaultSSHCertificateCredentialLibraryAttributes.path:type_name -> google.protobuf.StringValue
|
||||
7, // 15: controller.api.resources.credentiallibraries.v1.VaultSSHCertificateCredentialLibraryAttributes.username:type_name -> google.protobuf.StringValue
|
||||
7, // 16: controller.api.resources.credentiallibraries.v1.VaultSSHCertificateCredentialLibraryAttributes.key_type:type_name -> google.protobuf.StringValue
|
||||
10, // 17: controller.api.resources.credentiallibraries.v1.VaultSSHCertificateCredentialLibraryAttributes.key_bits:type_name -> google.protobuf.UInt32Value
|
||||
7, // 18: controller.api.resources.credentiallibraries.v1.VaultSSHCertificateCredentialLibraryAttributes.ttl:type_name -> google.protobuf.StringValue
|
||||
7, // 19: controller.api.resources.credentiallibraries.v1.VaultSSHCertificateCredentialLibraryAttributes.key_id:type_name -> google.protobuf.StringValue
|
||||
4, // 20: controller.api.resources.credentiallibraries.v1.VaultSSHCertificateCredentialLibraryAttributes.critical_options:type_name -> controller.api.resources.credentiallibraries.v1.VaultSSHCertificateCredentialLibraryAttributes.CriticalOptionsEntry
|
||||
5, // 21: controller.api.resources.credentiallibraries.v1.VaultSSHCertificateCredentialLibraryAttributes.extensions:type_name -> controller.api.resources.credentiallibraries.v1.VaultSSHCertificateCredentialLibraryAttributes.ExtensionsEntry
|
||||
7, // 22: controller.api.resources.credentiallibraries.v1.VaultSSHCertificateCredentialLibraryAttributes.additional_valid_principals:type_name -> google.protobuf.StringValue
|
||||
7, // 23: controller.api.resources.credentiallibraries.v1.VaultLdapCredentialLibraryAttributes.path:type_name -> google.protobuf.StringValue
|
||||
24, // [24:24] is the sub-list for method output_type
|
||||
24, // [24:24] is the sub-list for method input_type
|
||||
24, // [24:24] is the sub-list for extension type_name
|
||||
24, // [24:24] is the sub-list for extension extendee
|
||||
0, // [0:24] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_controller_api_resources_credentiallibraries_v1_credential_library_proto_init() }
|
||||
@@ -574,6 +646,7 @@ func file_controller_api_resources_credentiallibraries_v1_credential_library_pro
|
||||
(*CredentialLibrary_VaultCredentialLibraryAttributes)(nil),
|
||||
(*CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes)(nil),
|
||||
(*CredentialLibrary_VaultGenericCredentialLibraryAttributes)(nil),
|
||||
(*CredentialLibrary_VaultLdapCredentialLibraryAttributes)(nil),
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
@@ -581,7 +654,7 @@ func file_controller_api_resources_credentiallibraries_v1_credential_library_pro
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_controller_api_resources_credentiallibraries_v1_credential_library_proto_rawDesc), len(file_controller_api_resources_credentiallibraries_v1_credential_library_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 5,
|
||||
NumMessages: 6,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
||||
@@ -43,6 +43,7 @@ database-up:
|
||||
-e POSTGRES_PASSWORD=boundary \
|
||||
-e POSTGRES_USER=boundary \
|
||||
-e POSTGRES_DB=boundary \
|
||||
-e TEST_DATABASE_UP_WITH_OP=$(TEST_DATABASE_UP_WITH_OP) \
|
||||
-e PGDATA=/pgdata \
|
||||
--mount type=tmpfs,destination=/pgdata \
|
||||
-v "$(CWD)/../../../internal/db/schema/migrations":/migrations \
|
||||
@@ -54,6 +55,7 @@ database-up:
|
||||
@echo "Test database available at: 127.0.0.1:$(TEST_DB_PORT)"
|
||||
@echo "For database logs run:"
|
||||
@echo " docker logs $(TEST_CONTAINER_NAME)"
|
||||
|
||||
clean:
|
||||
docker stop $(TEST_CONTAINER_NAME) || true
|
||||
docker rm -v $(TEST_CONTAINER_NAME) || true
|
||||
|
||||
@@ -62,3 +62,11 @@ done
|
||||
psql -v "ON_ERROR_STOP=1" --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" -q <<EOSQL
|
||||
update pg_database set datistemplate = true, datallowconn = false where datname = 'boundary_template';
|
||||
EOSQL
|
||||
|
||||
# Create database from template
|
||||
if [ -n "$TEST_DATABASE_UP_WITH_OP" ]; then
|
||||
psql -v "ON_ERROR_STOP=1" --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" -q <<EOSQL
|
||||
create database op template boundary_template owner ${POSTGRES_USER};
|
||||
EOSQL
|
||||
echo "Created operational database 'op' based on boundary_template"
|
||||
fi
|
||||
@@ -6,12 +6,14 @@ package boundary
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/boundary/api/authmethods"
|
||||
"github.com/hashicorp/boundary/api/targets"
|
||||
)
|
||||
|
||||
// ConnectCliOutput parses the json response from running `boundary connect`
|
||||
type ConnectCliOutput struct {
|
||||
Port int `json:"port"`
|
||||
Address string `json:"address"`
|
||||
Port int `json:"port"`
|
||||
Address string `json:"address"`
|
||||
Credentials []*targets.SessionCredential `json:"credentials"`
|
||||
}
|
||||
|
||||
// AuthenticateCliOutput parses the json response from running `boundary authenticate`
|
||||
|
||||
@@ -333,6 +333,54 @@ func CreateStaticCredentialPasswordApi(t testing.TB, ctx context.Context, client
|
||||
return credentialId, nil
|
||||
}
|
||||
|
||||
func CreateVaultLdapCredentialLibraryApi(t testing.TB, ctx context.Context, client *api.Client, credentialStoreId string, vaultPath string) (string, error) {
|
||||
name, err := base62.Random(16)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
res, err := credentiallibraries.NewClient(client).Create(ctx, "vault-ldap", credentialStoreId,
|
||||
credentiallibraries.WithName(fmt.Sprintf("e2e vault credential library %s", name)),
|
||||
credentiallibraries.WithVaultLdapCredentialLibraryPath(vaultPath),
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return res.GetItem().Id, nil
|
||||
}
|
||||
|
||||
func CreateVaultLdapCredentialLibraryCli(t testing.TB, ctx context.Context, credentialStoreId string, vaultPath string) (string, error) {
|
||||
name, err := base62.Random(16)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
output := e2e.RunCommand(ctx, "boundary",
|
||||
e2e.WithArgs(
|
||||
"credential-libraries", "create", "vault-ldap",
|
||||
"-credential-store-id", credentialStoreId,
|
||||
"-vault-path", vaultPath,
|
||||
"-name", fmt.Sprintf("e2e vault credential library %s", name),
|
||||
"-description", "e2e",
|
||||
"-format", "json",
|
||||
),
|
||||
)
|
||||
if output.Err != nil {
|
||||
return "", fmt.Errorf("%w: %s", output.Err, string(output.Stderr))
|
||||
}
|
||||
|
||||
var createCredentialLibraryResult credentiallibraries.CredentialLibraryCreateResult
|
||||
err = json.Unmarshal(output.Stdout, &createCredentialLibraryResult)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
credentialLibraryId := createCredentialLibraryResult.Item.Id
|
||||
t.Logf("Created Vault LDAP Credential Library: %s", credentialLibraryId)
|
||||
return credentialLibraryId, nil
|
||||
}
|
||||
|
||||
// CreateStaticCredentialPasswordDomainApi uses the API to create a new username-password-domain credential in the
|
||||
// provided static credential store.
|
||||
// Returns the id of the new credential
|
||||
|
||||
@@ -0,0 +1,368 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package base_with_vault_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/api/scopes"
|
||||
"github.com/hashicorp/boundary/api/targets"
|
||||
"github.com/hashicorp/boundary/testing/internal/e2e"
|
||||
"github.com/hashicorp/boundary/testing/internal/e2e/boundary"
|
||||
"github.com/hashicorp/boundary/testing/internal/e2e/vault"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestApiVaultLdapCredentialLibrary uses the Boundary API to test CRUDL on a
|
||||
// Vault LDAP credential library. Additionally, it tests assigning it as a
|
||||
// brokered credential source on a TCP target and connecting.
|
||||
func TestApiVaultLdapCredentialLibrary(t *testing.T) {
|
||||
e2e.MaybeSkipTest(t)
|
||||
c, err := loadTestConfig()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, c)
|
||||
|
||||
client, err := boundary.NewApiClient()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, client)
|
||||
|
||||
orgId, err := boundary.CreateOrgApi(t, t.Context(), client)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
_, err := scopes.NewClient(client).Delete(context.Background(), orgId)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
projectId, err := boundary.CreateProjectApi(t, t.Context(), client, orgId)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create TCP target.
|
||||
targetPort, err := strconv.ParseUint(c.TargetPort, 10, 32)
|
||||
require.NoError(t, err)
|
||||
targetId, err := boundary.CreateTargetApi(t, t.Context(), client, projectId, "tcp",
|
||||
targets.WithTcpTargetDefaultPort(uint32(targetPort)),
|
||||
targets.WithTcpTargetDefaultClientPort(uint32(targetPort)),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create host catalog, host set and host; Add host set to the target as a
|
||||
// host source.
|
||||
hostCatalogId, err := boundary.CreateHostCatalogApi(t, t.Context(), client, projectId)
|
||||
require.NoError(t, err)
|
||||
hostSetId, err := boundary.CreateHostSetApi(t, t.Context(), client, hostCatalogId)
|
||||
require.NoError(t, err)
|
||||
hostId, err := boundary.CreateHostApi(t, t.Context(), client, hostCatalogId, c.TargetAddress)
|
||||
require.NoError(t, err)
|
||||
err = boundary.AddHostToHostSetApi(t, t.Context(), client, hostSetId, hostId)
|
||||
require.NoError(t, err)
|
||||
err = boundary.AddHostSourceToTargetApi(t, t.Context(), client, targetId, hostSetId)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Configure Vault for LDAP.
|
||||
boundaryPolicyName := vault.SetupForBoundaryController(t, "testdata/boundary-controller-policy.hcl")
|
||||
t.Cleanup(func() {
|
||||
output := e2e.RunCommand(context.Background(), "vault",
|
||||
e2e.WithArgs("policy", "delete", boundaryPolicyName),
|
||||
)
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
})
|
||||
|
||||
ldapPolicyName := vault.SetupLdap(t, c.VaultLdapPath,
|
||||
c.LdapAddress, c.LdapAdminDn, c.LdapAdminPassword,
|
||||
c.LdapDomainDn, c.LdapUserName, c.LdapGroupName,
|
||||
)
|
||||
t.Cleanup(func() {
|
||||
// Destroy LDAP secrets engine and LDAP policy.
|
||||
output := e2e.RunCommand(context.Background(), "vault",
|
||||
e2e.WithArgs("secrets", "disable", c.VaultLdapPath),
|
||||
)
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
|
||||
output = e2e.RunCommand(context.Background(), "vault",
|
||||
e2e.WithArgs("policy", "delete", ldapPolicyName),
|
||||
)
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
})
|
||||
|
||||
// Create Vault token for Boundary.
|
||||
output := e2e.RunCommand(t.Context(), "vault",
|
||||
e2e.WithArgs(
|
||||
"token", "create",
|
||||
"-no-default-policy=true",
|
||||
fmt.Sprintf("-policy=%s", boundaryPolicyName),
|
||||
fmt.Sprintf("-policy=%s", ldapPolicyName),
|
||||
"-orphan=true",
|
||||
"-period=20m",
|
||||
"-renewable=true",
|
||||
"-format=json",
|
||||
),
|
||||
)
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
var tokenCreateResult vault.CreateTokenResponse
|
||||
err = json.Unmarshal(output.Stdout, &tokenCreateResult)
|
||||
require.NoError(t, err)
|
||||
vaultToken := tokenCreateResult.Auth.Client_Token
|
||||
|
||||
// Create two Vault LDAP credential libraries: One with static, one with
|
||||
// dynamic creds.
|
||||
storeId, err := boundary.CreateCredentialStoreVaultApi(t, t.Context(), client, projectId, c.VaultAddr, vaultToken)
|
||||
require.NoError(t, err)
|
||||
staticCredVclId, err := boundary.CreateVaultLdapCredentialLibraryApi(t, t.Context(), client, storeId, fmt.Sprintf("%s/static-cred/%s", c.VaultLdapPath, c.LdapUserName))
|
||||
require.NoError(t, err)
|
||||
dynamicCredVclId, err := boundary.CreateVaultLdapCredentialLibraryApi(t, t.Context(), client, storeId, fmt.Sprintf("%s/creds/%s", c.VaultLdapPath, c.LdapGroupName))
|
||||
require.NoError(t, err)
|
||||
err = boundary.AddBrokeredCredentialSourceToTargetApi(t, t.Context(), client, targetId, staticCredVclId)
|
||||
require.NoError(t, err)
|
||||
err = boundary.AddBrokeredCredentialSourceToTargetApi(t, t.Context(), client, targetId, dynamicCredVclId)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Authorize session and assert that session credentials exist.
|
||||
sar, err := targets.NewClient(client).AuthorizeSession(t.Context(), targetId)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, sar)
|
||||
|
||||
sa, err := sar.GetSessionAuthorization()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, sa)
|
||||
|
||||
require.Len(t, sa.Credentials, 2) // One for static, one for dynamic role.
|
||||
slices.SortFunc(sa.Credentials, func(a *targets.SessionCredential, b *targets.SessionCredential) int {
|
||||
// Sort by static role credential library first.
|
||||
if strings.EqualFold(staticCredVclId, a.CredentialSource.Id) {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
})
|
||||
|
||||
// Determine the LDAP server's domain using LDAP dn. LdapDomainDn is a
|
||||
// string like "dc=subdomain,dc=domain,dc=tld".
|
||||
expDomain := strings.ReplaceAll(
|
||||
strings.ReplaceAll(c.LdapDomainDn, "dc=", ""), // Transforms into "subdomain,domain,tld".
|
||||
",", ".", // Then replace commas with periods to get "subdomain.domain.tld".
|
||||
)
|
||||
|
||||
// Static credential.
|
||||
require.NotEmpty(t, sa.Credentials[0].Credential)
|
||||
require.Contains(t, sa.Credentials[0].Credential, "username")
|
||||
require.Contains(t, sa.Credentials[0].Credential, "password")
|
||||
require.Contains(t, sa.Credentials[0].Credential, "domain")
|
||||
require.EqualValues(t, c.LdapUserName, sa.Credentials[0].Credential["username"])
|
||||
require.NotEmpty(t, sa.Credentials[0].Credential["password"])
|
||||
require.EqualValues(t, expDomain, sa.Credentials[0].Credential["domain"])
|
||||
|
||||
require.NotNil(t, sa.Credentials[0].CredentialSource)
|
||||
require.EqualValues(t, staticCredVclId, sa.Credentials[0].CredentialSource.Id)
|
||||
require.Contains(t, sa.Credentials[0].CredentialSource.Name, "e2e vault credential library")
|
||||
require.EqualValues(t, "vault-ldap", sa.Credentials[0].CredentialSource.Type)
|
||||
require.EqualValues(t, storeId, sa.Credentials[0].CredentialSource.CredentialStoreId)
|
||||
require.EqualValues(t, "username_password_domain", sa.Credentials[0].CredentialSource.CredentialType)
|
||||
|
||||
require.NotNil(t, sa.Credentials[0].Secret)
|
||||
require.NotEmpty(t, sa.Credentials[0].Secret.Decoded)
|
||||
require.Contains(t, sa.Credentials[0].Secret.Decoded, "username")
|
||||
require.Contains(t, sa.Credentials[0].Secret.Decoded, "password")
|
||||
require.Contains(t, sa.Credentials[0].Secret.Decoded, "dn")
|
||||
require.EqualValues(t, c.LdapUserName, sa.Credentials[0].Secret.Decoded["username"])
|
||||
require.EqualValues(t, sa.Credentials[0].Credential["password"], sa.Credentials[0].Secret.Decoded["password"])
|
||||
require.EqualValues(t, fmt.Sprintf("cn=%s,%s", c.LdapUserName, c.LdapDomainDn), sa.Credentials[0].Secret.Decoded["dn"])
|
||||
|
||||
// Dynamic credential.
|
||||
require.NotEmpty(t, sa.Credentials[1].Credential)
|
||||
require.Contains(t, sa.Credentials[1].Credential, "username")
|
||||
require.Contains(t, sa.Credentials[1].Credential, "password")
|
||||
require.Contains(t, sa.Credentials[1].Credential, "domain")
|
||||
require.Contains(t, sa.Credentials[1].Credential["username"], fmt.Sprintf("b_token_%s_", c.LdapGroupName))
|
||||
require.NotEmpty(t, sa.Credentials[1].Credential["password"])
|
||||
require.EqualValues(t, expDomain, sa.Credentials[1].Credential["domain"])
|
||||
|
||||
require.NotNil(t, sa.Credentials[1].CredentialSource)
|
||||
require.EqualValues(t, dynamicCredVclId, sa.Credentials[1].CredentialSource.Id)
|
||||
require.Contains(t, sa.Credentials[1].CredentialSource.Name, "e2e vault credential library")
|
||||
require.EqualValues(t, "vault-ldap", sa.Credentials[1].CredentialSource.Type)
|
||||
require.EqualValues(t, storeId, sa.Credentials[1].CredentialSource.CredentialStoreId)
|
||||
require.EqualValues(t, "username_password_domain", sa.Credentials[1].CredentialSource.CredentialType)
|
||||
|
||||
require.NotNil(t, sa.Credentials[1].Secret)
|
||||
require.NotEmpty(t, sa.Credentials[1].Secret.Decoded)
|
||||
require.Contains(t, sa.Credentials[1].Secret.Decoded, "username")
|
||||
require.Contains(t, sa.Credentials[1].Secret.Decoded, "password")
|
||||
require.Contains(t, sa.Credentials[1].Secret.Decoded, "distinguished_names")
|
||||
require.EqualValues(t, sa.Credentials[1].Credential["username"], sa.Credentials[1].Secret.Decoded["username"])
|
||||
require.EqualValues(t, sa.Credentials[1].Credential["password"], sa.Credentials[1].Secret.Decoded["password"])
|
||||
distinguishedNames, ok := sa.Credentials[1].Secret.Decoded["distinguished_names"].([]any)
|
||||
require.True(t, ok)
|
||||
require.NotEmpty(t, distinguishedNames)
|
||||
require.Contains(t, distinguishedNames, fmt.Sprintf("cn=%s,%s", sa.Credentials[1].Secret.Decoded["username"], c.LdapDomainDn))
|
||||
require.Contains(t, distinguishedNames, fmt.Sprintf("cn=%s,%s", c.LdapGroupName, c.LdapDomainDn))
|
||||
}
|
||||
|
||||
// TestCliVaultLdapCredentialLibrary uses the Boundary CLI to test CRUDL on a
|
||||
// Vault LDAP credential library. Additionally, it tests assigning it as a
|
||||
// brokered credential source on a TCP target and connecting.
|
||||
func TestCliVaultLdapCredentialLibrary(t *testing.T) {
|
||||
e2e.MaybeSkipTest(t)
|
||||
c, err := loadTestConfig()
|
||||
require.NoError(t, err)
|
||||
|
||||
boundary.AuthenticateAdminCli(t, t.Context())
|
||||
orgId, err := boundary.CreateOrgCli(t, t.Context())
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
boundary.AuthenticateAdminCli(t, context.Background())
|
||||
output := e2e.RunCommand(context.Background(), "boundary", e2e.WithArgs("scopes", "delete", "-id", orgId))
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
})
|
||||
|
||||
projectId, err := boundary.CreateProjectCli(t, t.Context(), orgId)
|
||||
require.NoError(t, err)
|
||||
hostCatalogId, err := boundary.CreateHostCatalogCli(t, t.Context(), projectId)
|
||||
require.NoError(t, err)
|
||||
hostSetId, err := boundary.CreateHostSetCli(t, t.Context(), hostCatalogId)
|
||||
require.NoError(t, err)
|
||||
hostId, err := boundary.CreateHostCli(t, t.Context(), hostCatalogId, c.TargetAddress)
|
||||
require.NoError(t, err)
|
||||
err = boundary.AddHostToHostSetCli(t, t.Context(), hostSetId, hostId)
|
||||
require.NoError(t, err)
|
||||
targetId, err := boundary.CreateTargetCli(t, t.Context(), projectId, c.TargetPort)
|
||||
require.NoError(t, err)
|
||||
err = boundary.AddHostSourceToTargetCli(t, t.Context(), targetId, hostSetId)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Configure Vault for LDAP.
|
||||
boundaryPolicyName := vault.SetupForBoundaryController(t, "testdata/boundary-controller-policy.hcl")
|
||||
t.Cleanup(func() {
|
||||
output := e2e.RunCommand(context.Background(), "vault",
|
||||
e2e.WithArgs("policy", "delete", boundaryPolicyName),
|
||||
)
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
})
|
||||
|
||||
ldapPolicyName := vault.SetupLdap(t, c.VaultLdapPath,
|
||||
c.LdapAddress, c.LdapAdminDn, c.LdapAdminPassword,
|
||||
c.LdapDomainDn, c.LdapUserName, c.LdapGroupName,
|
||||
)
|
||||
t.Cleanup(func() {
|
||||
// Destroy LDAP secrets engine and LDAP policy.
|
||||
output := e2e.RunCommand(context.Background(), "vault",
|
||||
e2e.WithArgs("secrets", "disable", c.VaultLdapPath),
|
||||
)
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
|
||||
output = e2e.RunCommand(context.Background(), "vault",
|
||||
e2e.WithArgs("policy", "delete", ldapPolicyName),
|
||||
)
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
})
|
||||
|
||||
// Create Vault token for Boundary.
|
||||
output := e2e.RunCommand(t.Context(), "vault",
|
||||
e2e.WithArgs(
|
||||
"token", "create",
|
||||
"-no-default-policy=true",
|
||||
fmt.Sprintf("-policy=%s", boundaryPolicyName),
|
||||
fmt.Sprintf("-policy=%s", ldapPolicyName),
|
||||
"-orphan=true",
|
||||
"-period=20m",
|
||||
"-renewable=true",
|
||||
"-format=json",
|
||||
),
|
||||
)
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
var tokenCreateResult vault.CreateTokenResponse
|
||||
err = json.Unmarshal(output.Stdout, &tokenCreateResult)
|
||||
require.NoError(t, err)
|
||||
vaultToken := tokenCreateResult.Auth.Client_Token
|
||||
|
||||
// Create two Vault LDAP credential libraries: One with static, one with
|
||||
// dynamic creds.
|
||||
storeId, err := boundary.CreateCredentialStoreVaultCli(t, t.Context(), projectId, c.VaultAddr, vaultToken)
|
||||
require.NoError(t, err)
|
||||
staticCredVclId, err := boundary.CreateVaultLdapCredentialLibraryCli(t, t.Context(), storeId, fmt.Sprintf("%s/static-cred/%s", c.VaultLdapPath, c.LdapUserName))
|
||||
require.NoError(t, err)
|
||||
dynamicCredVclId, err := boundary.CreateVaultLdapCredentialLibraryCli(t, t.Context(), storeId, fmt.Sprintf("%s/creds/%s", c.VaultLdapPath, c.LdapGroupName))
|
||||
require.NoError(t, err)
|
||||
err = boundary.AddBrokeredCredentialSourceToTargetCli(t, t.Context(), targetId, staticCredVclId)
|
||||
require.NoError(t, err)
|
||||
err = boundary.AddBrokeredCredentialSourceToTargetCli(t, t.Context(), targetId, dynamicCredVclId)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Connect and assert that session credentials exist.
|
||||
out := boundary.ConnectCli(t, t.Context(), targetId)
|
||||
require.NotEmpty(t, out)
|
||||
|
||||
require.Len(t, out.Credentials, 2) // One for static, one for dynamic role.
|
||||
slices.SortFunc(out.Credentials, func(a *targets.SessionCredential, b *targets.SessionCredential) int {
|
||||
// Sort by static role credential library first.
|
||||
if strings.EqualFold(staticCredVclId, a.CredentialSource.Id) {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
})
|
||||
|
||||
// Determine the LDAP server's domain using LDAP dn. LdapDomainDn is a
|
||||
// string like "dc=subdomain,dc=domain,dc=tld".
|
||||
expDomain := strings.ReplaceAll(
|
||||
strings.ReplaceAll(c.LdapDomainDn, "dc=", ""), // Transforms into "subdomain,domain,tld".
|
||||
",", ".", // Then replace commas with periods to get "subdomain.domain.tld".
|
||||
)
|
||||
|
||||
// Static credential.
|
||||
require.NotEmpty(t, out.Credentials[0].Credential)
|
||||
require.Contains(t, out.Credentials[0].Credential, "username")
|
||||
require.Contains(t, out.Credentials[0].Credential, "password")
|
||||
require.Contains(t, out.Credentials[0].Credential, "domain")
|
||||
require.EqualValues(t, c.LdapUserName, out.Credentials[0].Credential["username"])
|
||||
require.NotEmpty(t, out.Credentials[0].Credential["password"])
|
||||
require.EqualValues(t, expDomain, out.Credentials[0].Credential["domain"])
|
||||
|
||||
require.NotNil(t, out.Credentials[0].CredentialSource)
|
||||
require.EqualValues(t, staticCredVclId, out.Credentials[0].CredentialSource.Id)
|
||||
require.Contains(t, out.Credentials[0].CredentialSource.Name, "e2e vault credential library")
|
||||
require.EqualValues(t, "vault-ldap", out.Credentials[0].CredentialSource.Type)
|
||||
require.EqualValues(t, storeId, out.Credentials[0].CredentialSource.CredentialStoreId)
|
||||
require.EqualValues(t, "username_password_domain", out.Credentials[0].CredentialSource.CredentialType)
|
||||
|
||||
require.NotNil(t, out.Credentials[0].Secret)
|
||||
require.NotEmpty(t, out.Credentials[0].Secret.Decoded)
|
||||
require.Contains(t, out.Credentials[0].Secret.Decoded, "username")
|
||||
require.Contains(t, out.Credentials[0].Secret.Decoded, "password")
|
||||
require.Contains(t, out.Credentials[0].Secret.Decoded, "dn")
|
||||
require.EqualValues(t, c.LdapUserName, out.Credentials[0].Secret.Decoded["username"])
|
||||
require.EqualValues(t, out.Credentials[0].Credential["password"], out.Credentials[0].Secret.Decoded["password"])
|
||||
require.EqualValues(t, fmt.Sprintf("cn=%s,%s", c.LdapUserName, c.LdapDomainDn), out.Credentials[0].Secret.Decoded["dn"])
|
||||
|
||||
// Dynamic credential.
|
||||
require.NotEmpty(t, out.Credentials[1].Credential)
|
||||
require.Contains(t, out.Credentials[1].Credential, "username")
|
||||
require.Contains(t, out.Credentials[1].Credential, "password")
|
||||
require.Contains(t, out.Credentials[1].Credential, "domain")
|
||||
require.Contains(t, out.Credentials[1].Credential["username"], fmt.Sprintf("b_token_%s_", c.LdapGroupName))
|
||||
require.NotEmpty(t, out.Credentials[1].Credential["password"])
|
||||
require.EqualValues(t, expDomain, out.Credentials[1].Credential["domain"])
|
||||
|
||||
require.NotNil(t, out.Credentials[1].CredentialSource)
|
||||
require.EqualValues(t, dynamicCredVclId, out.Credentials[1].CredentialSource.Id)
|
||||
require.Contains(t, out.Credentials[1].CredentialSource.Name, "e2e vault credential library")
|
||||
require.EqualValues(t, "vault-ldap", out.Credentials[1].CredentialSource.Type)
|
||||
require.EqualValues(t, storeId, out.Credentials[1].CredentialSource.CredentialStoreId)
|
||||
require.EqualValues(t, "username_password_domain", out.Credentials[1].CredentialSource.CredentialType)
|
||||
|
||||
require.NotNil(t, out.Credentials[1].Secret)
|
||||
require.NotEmpty(t, out.Credentials[1].Secret.Decoded)
|
||||
require.Contains(t, out.Credentials[1].Secret.Decoded, "username")
|
||||
require.Contains(t, out.Credentials[1].Secret.Decoded, "password")
|
||||
require.Contains(t, out.Credentials[1].Secret.Decoded, "distinguished_names")
|
||||
require.EqualValues(t, out.Credentials[1].Credential["username"], out.Credentials[1].Secret.Decoded["username"])
|
||||
require.EqualValues(t, out.Credentials[1].Credential["password"], out.Credentials[1].Secret.Decoded["password"])
|
||||
distinguishedNames, ok := out.Credentials[1].Secret.Decoded["distinguished_names"].([]any)
|
||||
require.True(t, ok)
|
||||
require.NotEmpty(t, distinguishedNames)
|
||||
require.Contains(t, distinguishedNames, fmt.Sprintf("cn=%s,%s", out.Credentials[1].Secret.Decoded["username"], c.LdapDomainDn))
|
||||
require.Contains(t, distinguishedNames, fmt.Sprintf("cn=%s,%s", c.LdapGroupName, c.LdapDomainDn))
|
||||
}
|
||||
@@ -55,7 +55,7 @@ func TestCliVaultCredentialStore(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Configure vault
|
||||
boundaryPolicyName, kvPolicyFilePath := vault.Setup(t, "testdata/boundary-controller-policy.hcl")
|
||||
boundaryPolicyName := vault.SetupForBoundaryController(t, "testdata/boundary-controller-policy.hcl")
|
||||
t.Cleanup(func() {
|
||||
output := e2e.RunCommand(ctx, "vault",
|
||||
e2e.WithArgs("policy", "delete", boundaryPolicyName),
|
||||
@@ -75,13 +75,26 @@ func TestCliVaultCredentialStore(t *testing.T) {
|
||||
})
|
||||
|
||||
// Create credentials in vault
|
||||
privateKeySecretName := vault.CreateKvPrivateKeyCredential(t, c.VaultSecretPath, c.TargetSshUser, c.TargetSshKeyPath, kvPolicyFilePath)
|
||||
passwordSecretName, password := vault.CreateKvPasswordCredential(t, c.VaultSecretPath, c.TargetSshUser, kvPolicyFilePath)
|
||||
domainSecretName, domainPassword := vault.CreateKvPasswordDomainCredential(t, c.VaultSecretPath, c.TargetSshUser, "domain.com", kvPolicyFilePath)
|
||||
kvPolicyName := vault.WritePolicy(t, ctx, kvPolicyFilePath)
|
||||
privateKeySecretName, privateKeyPolicyName := vault.CreateKvPrivateKeyCredential(t, c.VaultSecretPath, c.TargetSshUser, c.TargetSshKeyPath)
|
||||
t.Cleanup(func() {
|
||||
output := e2e.RunCommand(ctx, "vault",
|
||||
e2e.WithArgs("policy", "delete", kvPolicyName),
|
||||
e2e.WithArgs("policy", "delete", privateKeyPolicyName),
|
||||
)
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
})
|
||||
|
||||
passwordSecretName, passwordPolicyName, password := vault.CreateKvPasswordCredential(t, c.VaultSecretPath, c.TargetSshUser)
|
||||
t.Cleanup(func() {
|
||||
output := e2e.RunCommand(ctx, "vault",
|
||||
e2e.WithArgs("policy", "delete", passwordPolicyName),
|
||||
)
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
})
|
||||
|
||||
domainSecretName, domainPolicyName, domainPassword := vault.CreateKvPasswordDomainCredential(t, c.VaultSecretPath, c.TargetSshUser, "domain.com")
|
||||
t.Cleanup(func() {
|
||||
output := e2e.RunCommand(ctx, "vault",
|
||||
e2e.WithArgs("policy", "delete", domainPolicyName),
|
||||
)
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
})
|
||||
@@ -93,7 +106,9 @@ func TestCliVaultCredentialStore(t *testing.T) {
|
||||
"token", "create",
|
||||
"-no-default-policy=true",
|
||||
"-policy="+boundaryPolicyName,
|
||||
"-policy="+kvPolicyName,
|
||||
"-policy="+privateKeyPolicyName,
|
||||
"-policy="+passwordPolicyName,
|
||||
"-policy="+domainPolicyName,
|
||||
"-orphan=true",
|
||||
"-period=20m",
|
||||
"-renewable=true",
|
||||
@@ -243,7 +258,7 @@ func TestApiVaultCredentialStore(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Configure vault
|
||||
boundaryPolicyName, kvPolicyFilePath := vault.Setup(t, "testdata/boundary-controller-policy.hcl")
|
||||
boundaryPolicyName := vault.SetupForBoundaryController(t, "testdata/boundary-controller-policy.hcl")
|
||||
output := e2e.RunCommand(ctx, "vault",
|
||||
e2e.WithArgs("secrets", "enable", "-path="+c.VaultSecretPath, "kv-v2"),
|
||||
)
|
||||
@@ -255,11 +270,30 @@ func TestApiVaultCredentialStore(t *testing.T) {
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
})
|
||||
|
||||
// Create credential in vault
|
||||
privateKeySecretName := vault.CreateKvPrivateKeyCredential(t, c.VaultSecretPath, c.TargetSshUser, c.TargetSshKeyPath, kvPolicyFilePath)
|
||||
passwordSecretName, password := vault.CreateKvPasswordCredential(t, c.VaultSecretPath, c.TargetSshUser, kvPolicyFilePath)
|
||||
domainSecretName, domainPassword := vault.CreateKvPasswordDomainCredential(t, c.VaultSecretPath, c.TargetSshUser, "domain.com", kvPolicyFilePath)
|
||||
kvPolicyName := vault.WritePolicy(t, ctx, kvPolicyFilePath)
|
||||
// Create credentials in vault
|
||||
privateKeySecretName, privateKeyPolicyName := vault.CreateKvPrivateKeyCredential(t, c.VaultSecretPath, c.TargetSshUser, c.TargetSshKeyPath)
|
||||
t.Cleanup(func() {
|
||||
output := e2e.RunCommand(ctx, "vault",
|
||||
e2e.WithArgs("policy", "delete", privateKeyPolicyName),
|
||||
)
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
})
|
||||
|
||||
passwordSecretName, passwordPolicyName, password := vault.CreateKvPasswordCredential(t, c.VaultSecretPath, c.TargetSshUser)
|
||||
t.Cleanup(func() {
|
||||
output := e2e.RunCommand(ctx, "vault",
|
||||
e2e.WithArgs("policy", "delete", passwordPolicyName),
|
||||
)
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
})
|
||||
|
||||
domainSecretName, domainPolicyName, domainPassword := vault.CreateKvPasswordDomainCredential(t, c.VaultSecretPath, c.TargetSshUser, "domain.com")
|
||||
t.Cleanup(func() {
|
||||
output := e2e.RunCommand(ctx, "vault",
|
||||
e2e.WithArgs("policy", "delete", domainPolicyName),
|
||||
)
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
})
|
||||
t.Log("Created Vault Credentials")
|
||||
|
||||
// Create vault token for boundary
|
||||
@@ -268,7 +302,9 @@ func TestApiVaultCredentialStore(t *testing.T) {
|
||||
"token", "create",
|
||||
"-no-default-policy=true",
|
||||
"-policy="+boundaryPolicyName,
|
||||
"-policy="+kvPolicyName,
|
||||
"-policy="+privateKeyPolicyName,
|
||||
"-policy="+passwordPolicyName,
|
||||
"-policy="+domainPolicyName,
|
||||
"-orphan=true",
|
||||
"-period=20m",
|
||||
"-renewable=true",
|
||||
|
||||
@@ -16,7 +16,16 @@ type config struct {
|
||||
VaultAddr string `envconfig:"E2E_VAULT_ADDR_PUBLIC" required:"true"` // e.g. "http://127.0.0.1:8200"
|
||||
VaultAddrPrivate string `envconfig:"E2E_VAULT_ADDR_PRIVATE" required:"true"` // e.g. "http://10.10.10.10:8200"
|
||||
VaultSecretPath string `envconfig:"E2E_VAULT_SECRET_PATH" default:"e2e_secrets"`
|
||||
VaultLdapPath string `envconfig:"E2E_VAULT_LDAP_PATH" default:"e2e_ldap"`
|
||||
MaxPageSize int `envconfig:"E2E_MAX_PAGE_SIZE" default:"1000"`
|
||||
// Ldap configuration.
|
||||
LdapAddress string `envconfig:"E2E_LDAP_ADDR" required:"true"` // e.g. ldap://ldap
|
||||
LdapDomainDn string `envconfig:"E2E_LDAP_DOMAIN_DN" required:"true"` // e.g. dc=example,dc=org
|
||||
LdapAdminDn string `envconfig:"E2E_LDAP_ADMIN_DN" required:"true"` // e.g. cn=admin,dc=example,dc=org
|
||||
LdapAdminPassword string `envconfig:"E2E_LDAP_ADMIN_PASSWORD" required:"true"`
|
||||
LdapUserName string `envconfig:"E2E_LDAP_USER_NAME" required:"true"`
|
||||
LdapUserPassword string `envconfig:"E2E_LDAP_USER_PASSWORD" required:"true"`
|
||||
LdapGroupName string `envconfig:"E2E_LDAP_GROUP_NAME" required:"true"`
|
||||
}
|
||||
|
||||
func loadTestConfig() (*config, error) {
|
||||
|
||||
@@ -41,7 +41,7 @@ func TestCliPaginateCredentialLibraries(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Configure vault
|
||||
boundaryPolicyName, kvPolicyFilePath := vault.Setup(t, "testdata/boundary-controller-policy.hcl")
|
||||
boundaryPolicyName := vault.SetupForBoundaryController(t, "testdata/boundary-controller-policy.hcl")
|
||||
t.Cleanup(func() {
|
||||
output := e2e.RunCommand(ctx, "vault",
|
||||
e2e.WithArgs("policy", "delete", boundaryPolicyName),
|
||||
@@ -61,11 +61,10 @@ func TestCliPaginateCredentialLibraries(t *testing.T) {
|
||||
})
|
||||
|
||||
// Create credentials in vault
|
||||
privateKeySecretName := vault.CreateKvPrivateKeyCredential(t, c.VaultSecretPath, c.TargetSshUser, c.TargetSshKeyPath, kvPolicyFilePath)
|
||||
kvPolicyName := vault.WritePolicy(t, ctx, kvPolicyFilePath)
|
||||
privateKeySecretName, privateKeyPolicyName := vault.CreateKvPrivateKeyCredential(t, c.VaultSecretPath, c.TargetSshUser, c.TargetSshKeyPath)
|
||||
t.Cleanup(func() {
|
||||
output := e2e.RunCommand(ctx, "vault",
|
||||
e2e.WithArgs("policy", "delete", kvPolicyName),
|
||||
e2e.WithArgs("policy", "delete", privateKeyPolicyName),
|
||||
)
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
})
|
||||
@@ -76,7 +75,7 @@ func TestCliPaginateCredentialLibraries(t *testing.T) {
|
||||
"token", "create",
|
||||
"-no-default-policy=true",
|
||||
"-policy="+boundaryPolicyName,
|
||||
"-policy="+kvPolicyName,
|
||||
"-policy="+privateKeyPolicyName,
|
||||
"-orphan=true",
|
||||
"-period=20m",
|
||||
"-renewable=true",
|
||||
@@ -194,7 +193,7 @@ func TestApiPaginateCredentialLibraries(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Configure vault
|
||||
boundaryPolicyName, kvPolicyFilePath := vault.Setup(t, "testdata/boundary-controller-policy.hcl")
|
||||
boundaryPolicyName := vault.SetupForBoundaryController(t, "testdata/boundary-controller-policy.hcl")
|
||||
t.Cleanup(func() {
|
||||
output := e2e.RunCommand(ctx, "vault",
|
||||
e2e.WithArgs("policy", "delete", boundaryPolicyName),
|
||||
@@ -214,11 +213,10 @@ func TestApiPaginateCredentialLibraries(t *testing.T) {
|
||||
})
|
||||
|
||||
// Create credentials in vault
|
||||
privateKeySecretName := vault.CreateKvPrivateKeyCredential(t, c.VaultSecretPath, c.TargetSshUser, c.TargetSshKeyPath, kvPolicyFilePath)
|
||||
kvPolicyName := vault.WritePolicy(t, ctx, kvPolicyFilePath)
|
||||
privateKeySecretName, privateKeyPolicyName := vault.CreateKvPrivateKeyCredential(t, c.VaultSecretPath, c.TargetSshUser, c.TargetSshKeyPath)
|
||||
t.Cleanup(func() {
|
||||
output := e2e.RunCommand(ctx, "vault",
|
||||
e2e.WithArgs("policy", "delete", kvPolicyName),
|
||||
e2e.WithArgs("policy", "delete", privateKeyPolicyName),
|
||||
)
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
})
|
||||
@@ -229,7 +227,7 @@ func TestApiPaginateCredentialLibraries(t *testing.T) {
|
||||
"token", "create",
|
||||
"-no-default-policy=true",
|
||||
"-policy="+boundaryPolicyName,
|
||||
"-policy="+kvPolicyName,
|
||||
"-policy="+privateKeyPolicyName,
|
||||
"-orphan=true",
|
||||
"-period=20m",
|
||||
"-renewable=true",
|
||||
|
||||
+4
-5
@@ -53,7 +53,7 @@ func TestCliTcpTargetVaultGenericConnectTargetWithAuthzToken(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Configure vault
|
||||
boundaryPolicyName, kvPolicyFilePath := vault.Setup(t, "testdata/boundary-controller-policy.hcl")
|
||||
boundaryPolicyName := vault.SetupForBoundaryController(t, "testdata/boundary-controller-policy.hcl")
|
||||
t.Cleanup(func() {
|
||||
output := e2e.RunCommand(ctx, "vault",
|
||||
e2e.WithArgs("policy", "delete", boundaryPolicyName),
|
||||
@@ -73,11 +73,10 @@ func TestCliTcpTargetVaultGenericConnectTargetWithAuthzToken(t *testing.T) {
|
||||
})
|
||||
|
||||
// Create credential in vault
|
||||
privateKeySecretName := vault.CreateKvPrivateKeyCredential(t, c.VaultSecretPath, c.TargetSshUser, c.TargetSshKeyPath, kvPolicyFilePath)
|
||||
kvPolicyName := vault.WritePolicy(t, ctx, kvPolicyFilePath)
|
||||
privateKeySecretName, privateKeyPolicyName := vault.CreateKvPrivateKeyCredential(t, c.VaultSecretPath, c.TargetSshUser, c.TargetSshKeyPath)
|
||||
t.Cleanup(func() {
|
||||
output := e2e.RunCommand(ctx, "vault",
|
||||
e2e.WithArgs("policy", "delete", kvPolicyName),
|
||||
e2e.WithArgs("policy", "delete", privateKeyPolicyName),
|
||||
)
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
})
|
||||
@@ -89,7 +88,7 @@ func TestCliTcpTargetVaultGenericConnectTargetWithAuthzToken(t *testing.T) {
|
||||
"token", "create",
|
||||
"-no-default-policy=true",
|
||||
fmt.Sprintf("-policy=%s", boundaryPolicyName),
|
||||
fmt.Sprintf("-policy=%s", kvPolicyName),
|
||||
fmt.Sprintf("-policy=%s", privateKeyPolicyName),
|
||||
"-orphan=true",
|
||||
"-period=20m",
|
||||
"-renewable=true",
|
||||
|
||||
+4
-5
@@ -53,7 +53,7 @@ func TestCliTcpTargetVaultGenericConnectTargetWithSsh(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Configure vault
|
||||
boundaryPolicyName, kvPolicyFilePath := vault.Setup(t, "testdata/boundary-controller-policy.hcl")
|
||||
boundaryPolicyName := vault.SetupForBoundaryController(t, "testdata/boundary-controller-policy.hcl")
|
||||
t.Cleanup(func() {
|
||||
output := e2e.RunCommand(ctx, "vault",
|
||||
e2e.WithArgs("policy", "delete", boundaryPolicyName),
|
||||
@@ -73,11 +73,10 @@ func TestCliTcpTargetVaultGenericConnectTargetWithSsh(t *testing.T) {
|
||||
})
|
||||
|
||||
// Create credential in vault
|
||||
privateKeySecretName := vault.CreateKvPrivateKeyCredential(t, c.VaultSecretPath, c.TargetSshUser, c.TargetSshKeyPath, kvPolicyFilePath)
|
||||
kvPolicyName := vault.WritePolicy(t, ctx, kvPolicyFilePath)
|
||||
privateKeySecretName, privateKeyPolicyName := vault.CreateKvPrivateKeyCredential(t, c.VaultSecretPath, c.TargetSshUser, c.TargetSshKeyPath)
|
||||
t.Cleanup(func() {
|
||||
output := e2e.RunCommand(ctx, "vault",
|
||||
e2e.WithArgs("policy", "delete", kvPolicyName),
|
||||
e2e.WithArgs("policy", "delete", privateKeyPolicyName),
|
||||
)
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
})
|
||||
@@ -89,7 +88,7 @@ func TestCliTcpTargetVaultGenericConnectTargetWithSsh(t *testing.T) {
|
||||
"token", "create",
|
||||
"-no-default-policy=true",
|
||||
fmt.Sprintf("-policy=%s", boundaryPolicyName),
|
||||
fmt.Sprintf("-policy=%s", kvPolicyName),
|
||||
fmt.Sprintf("-policy=%s", privateKeyPolicyName),
|
||||
"-orphan=true",
|
||||
"-period=20m",
|
||||
"-renewable=true",
|
||||
|
||||
@@ -54,7 +54,7 @@ func TestCliTcpTargetVaultGenericConnectTarget(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Configure vault
|
||||
boundaryPolicyName, kvPolicyFilePath := vault.Setup(t, "testdata/boundary-controller-policy.hcl")
|
||||
boundaryPolicyName := vault.SetupForBoundaryController(t, "testdata/boundary-controller-policy.hcl")
|
||||
t.Cleanup(func() {
|
||||
output := e2e.RunCommand(ctx, "vault",
|
||||
e2e.WithArgs("policy", "delete", boundaryPolicyName),
|
||||
@@ -74,11 +74,10 @@ func TestCliTcpTargetVaultGenericConnectTarget(t *testing.T) {
|
||||
})
|
||||
|
||||
// Create credential in vault
|
||||
privateKeySecretName := vault.CreateKvPrivateKeyCredential(t, c.VaultSecretPath, c.TargetSshUser, c.TargetSshKeyPath, kvPolicyFilePath)
|
||||
kvPolicyName := vault.WritePolicy(t, ctx, kvPolicyFilePath)
|
||||
privateKeySecretName, privateKeyPolicyName := vault.CreateKvPrivateKeyCredential(t, c.VaultSecretPath, c.TargetSshUser, c.TargetSshKeyPath)
|
||||
t.Cleanup(func() {
|
||||
output := e2e.RunCommand(ctx, "vault",
|
||||
e2e.WithArgs("policy", "delete", kvPolicyName),
|
||||
e2e.WithArgs("policy", "delete", privateKeyPolicyName),
|
||||
)
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
})
|
||||
@@ -90,7 +89,7 @@ func TestCliTcpTargetVaultGenericConnectTarget(t *testing.T) {
|
||||
"token", "create",
|
||||
"-no-default-policy=true",
|
||||
fmt.Sprintf("-policy=%s", boundaryPolicyName),
|
||||
fmt.Sprintf("-policy=%s", kvPolicyName),
|
||||
fmt.Sprintf("-policy=%s", privateKeyPolicyName),
|
||||
"-orphan=true",
|
||||
"-period=20m",
|
||||
"-renewable=true",
|
||||
|
||||
@@ -49,7 +49,7 @@ func TestCliTcpTargetWorkerConnectTarget(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Configure vault
|
||||
boundaryPolicyName, kvPolicyFilePath := vault.Setup(t, "testdata/boundary-controller-policy.hcl")
|
||||
boundaryPolicyName := vault.SetupForBoundaryController(t, "testdata/boundary-controller-policy.hcl")
|
||||
t.Cleanup(func() {
|
||||
output := e2e.RunCommand(ctx, "vault",
|
||||
e2e.WithArgs("policy", "delete", boundaryPolicyName),
|
||||
@@ -69,11 +69,10 @@ func TestCliTcpTargetWorkerConnectTarget(t *testing.T) {
|
||||
})
|
||||
|
||||
// Create credential in vault
|
||||
privateKeySecretName := vault.CreateKvPrivateKeyCredential(t, c.VaultSecretPath, c.TargetSshUser, c.TargetSshKeyPath, kvPolicyFilePath)
|
||||
kvPolicyName := vault.WritePolicy(t, ctx, kvPolicyFilePath)
|
||||
privateKeySecretName, privateKeyPolicyName := vault.CreateKvPrivateKeyCredential(t, c.VaultSecretPath, c.TargetSshUser, c.TargetSshKeyPath)
|
||||
t.Cleanup(func() {
|
||||
output := e2e.RunCommand(ctx, "vault",
|
||||
e2e.WithArgs("policy", "delete", kvPolicyName),
|
||||
e2e.WithArgs("policy", "delete", privateKeyPolicyName),
|
||||
)
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
})
|
||||
@@ -85,7 +84,7 @@ func TestCliTcpTargetWorkerConnectTarget(t *testing.T) {
|
||||
"token", "create",
|
||||
"-no-default-policy=true",
|
||||
fmt.Sprintf("-policy=%s", boundaryPolicyName),
|
||||
fmt.Sprintf("-policy=%s", kvPolicyName),
|
||||
fmt.Sprintf("-policy=%s", privateKeyPolicyName),
|
||||
"-orphan=true",
|
||||
"-period=20m",
|
||||
"-renewable=true",
|
||||
|
||||
@@ -278,22 +278,36 @@ func populateBoundaryDatabase(t testing.TB, ctx context.Context, c *config, te T
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create vault credentials
|
||||
boundaryPolicyName, kvPolicyFilePath := vault.Setup(t, "testdata/boundary-controller-policy.hcl")
|
||||
boundaryPolicyName := vault.SetupForBoundaryController(t, "testdata/boundary-controller-policy.hcl")
|
||||
output := e2e.RunCommand(ctx, "vault",
|
||||
e2e.WithArgs("secrets", "enable", "-path="+c.VaultSecretPath, "kv-v2"),
|
||||
)
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
|
||||
privateKeySecretName := vault.CreateKvPrivateKeyCredential(t, c.VaultSecretPath, c.TargetSshUser, c.TargetSshKeyPath, kvPolicyFilePath)
|
||||
passwordSecretName, _ := vault.CreateKvPasswordCredential(t, c.VaultSecretPath, c.TargetSshUser, kvPolicyFilePath)
|
||||
kvPolicyName := vault.WritePolicy(t, ctx, kvPolicyFilePath)
|
||||
privateKeySecretName, privateKeyPolicyName := vault.CreateKvPrivateKeyCredential(t, c.VaultSecretPath, c.TargetSshUser, c.TargetSshKeyPath)
|
||||
t.Cleanup(func() {
|
||||
output := e2e.RunCommand(ctx, "vault",
|
||||
e2e.WithArgs("policy", "delete", privateKeyPolicyName),
|
||||
)
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
})
|
||||
|
||||
passwordSecretName, passwordPolicyName, _ := vault.CreateKvPasswordCredential(t, c.VaultSecretPath, c.TargetSshUser)
|
||||
t.Cleanup(func() {
|
||||
output := e2e.RunCommand(ctx, "vault",
|
||||
e2e.WithArgs("policy", "delete", passwordPolicyName),
|
||||
)
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
})
|
||||
t.Log("Created Vault Credential")
|
||||
|
||||
output = e2e.RunCommand(ctx, "vault",
|
||||
e2e.WithArgs(
|
||||
"token", "create",
|
||||
"-no-default-policy=true",
|
||||
"-policy="+boundaryPolicyName,
|
||||
"-policy="+kvPolicyName,
|
||||
"-policy="+privateKeyPolicyName,
|
||||
"-policy="+passwordPolicyName,
|
||||
"-orphan=true",
|
||||
"-period=20m",
|
||||
"-renewable=true",
|
||||
|
||||
@@ -8,7 +8,9 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/testing/internal/e2e"
|
||||
@@ -23,37 +25,143 @@ type CreateTokenResponse struct {
|
||||
}
|
||||
}
|
||||
|
||||
// Setup verifies if appropriate credentials are set and adds the boundary controller
|
||||
// policy to vault. Returns the vault address.
|
||||
func Setup(t testing.TB, boundaryControllerFilePath string) (boundaryPolicyName string, kvPolicyFilePath string) {
|
||||
// SetupForBoundaryController verifies if appropriate credentials are set and
|
||||
// adds the boundary controller policy to vault. Returns the policy name.
|
||||
func SetupForBoundaryController(t testing.TB, boundaryControllerFilePath string) (boundaryPolicyName string) {
|
||||
// Set up boundary policy
|
||||
boundaryPolicyFilePath, err := filepath.Abs(boundaryControllerFilePath)
|
||||
require.NoError(t, err)
|
||||
boundaryPolicyName = WritePolicy(t, context.Background(), boundaryPolicyFilePath)
|
||||
boundaryPolicyName = WritePolicy(t, t.Context(), boundaryPolicyFilePath)
|
||||
|
||||
// Create kv policy
|
||||
kvPolicyFilePath = fmt.Sprintf("%s/%s", t.TempDir(), "kv-policy.hcl")
|
||||
_, err = os.Create(kvPolicyFilePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
return boundaryPolicyName, kvPolicyFilePath
|
||||
return boundaryPolicyName
|
||||
}
|
||||
|
||||
// CreateKvPrivateKeyCredential creates a private key credential in vault and creates a vault policy
|
||||
// to be able to read that credential. Returns the name of the policy.
|
||||
func CreateKvPrivateKeyCredential(t testing.TB, secretPath string, user string, keyPath string, kvPolicyFilePath string) string {
|
||||
// SetupLdap sets a Vault server up for LDAP against an OpenLDAP server. It
|
||||
// enables the LDAP secrets engine, configures it and creates a static user
|
||||
// according to what is in OpenLDAP. Additionally, it sets up Vault's ability to
|
||||
// manage LDAP users dynamically. Note that this function does not put any
|
||||
// clean-up in place to run after a test is complete. When applicable, callers
|
||||
// should destroy the Vault LDAP policy and LDAP secrets engine this function
|
||||
// creates.
|
||||
func SetupLdap(t testing.TB, vaultLdapMountPath, ldapAddr, ldapAdminDn, ldapAdminPw, ldapDn, ldapUser, ldapGroup string) string {
|
||||
// Enable LDAP secrets engine.
|
||||
output := e2e.RunCommand(t.Context(), "vault",
|
||||
e2e.WithArgs("secrets", "enable", fmt.Sprintf("-path=%s", vaultLdapMountPath), "ldap"),
|
||||
)
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
|
||||
// Define and write LDAP access policy to Vault.
|
||||
vaultLdapPolicyFilePath := path.Join(t.TempDir(), "ldap-policy.hcl")
|
||||
f, err := os.Create(vaultLdapPolicyFilePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = fmt.Fprintf(f, `
|
||||
path "%[1]s/static-cred/%[2]s" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
path "%[1]s/static-role/%[2]s" {
|
||||
capabilities = ["create", "read", "update", "patch", "delete", "list"]
|
||||
}
|
||||
|
||||
path "%[1]s/creds/%[3]s" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
path "%[1]s/role/%[3]s" {
|
||||
capabilities = ["create", "read", "update", "patch", "delete", "list"]
|
||||
}
|
||||
`, vaultLdapMountPath, ldapUser, ldapGroup)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.Sync())
|
||||
_ = f.Close()
|
||||
|
||||
policyName := WritePolicy(t, t.Context(), vaultLdapPolicyFilePath)
|
||||
|
||||
// Configure LDAP secrets engine to point to existing OpenLDAP server.
|
||||
output = e2e.RunCommand(t.Context(), "vault",
|
||||
e2e.WithArgs(
|
||||
"write",
|
||||
fmt.Sprintf("%s/config", vaultLdapMountPath),
|
||||
fmt.Sprintf("url=%s", ldapAddr),
|
||||
fmt.Sprintf("binddn=%s", ldapAdminDn),
|
||||
fmt.Sprintf("bindpass=%s", ldapAdminPw),
|
||||
),
|
||||
)
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
|
||||
// Create static LDAP user in Vault (already defined in OpenLDAP server).
|
||||
output = e2e.RunCommand(t.Context(), "vault",
|
||||
e2e.WithArgs(
|
||||
"write",
|
||||
fmt.Sprintf("%s/static-role/%s", vaultLdapMountPath, ldapUser),
|
||||
fmt.Sprintf("dn=cn=%s,%s", ldapUser, ldapDn),
|
||||
fmt.Sprintf("username=%s", ldapUser),
|
||||
"rotation_period=24h",
|
||||
),
|
||||
)
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
|
||||
// Create Vault dynamic role for LDAP group.
|
||||
createLdif := fmt.Sprintf(`
|
||||
dn: cn={{.Username}},%[1]s
|
||||
changetype: add
|
||||
objectClass: inetOrgPerson
|
||||
cn: {{.Username}}
|
||||
sn: {{.Username}}
|
||||
uid: {{.Username}}
|
||||
userPassword:{{.Password}}
|
||||
|
||||
dn: cn=%s,%[1]s
|
||||
changetype: modify
|
||||
add: uniqueMember
|
||||
uniqueMember: cn={{.Username}},%[1]s
|
||||
-
|
||||
`, ldapDn, ldapGroup)
|
||||
createLdif = strings.ReplaceAll(createLdif, "\t", "")
|
||||
|
||||
deleteLdif := fmt.Sprintf(`
|
||||
dn: cn={{.Username}},%s
|
||||
changetype: delete
|
||||
`, ldapDn)
|
||||
deleteLdif = strings.ReplaceAll(deleteLdif, "\t", "")
|
||||
|
||||
output = e2e.RunCommand(t.Context(), "vault",
|
||||
e2e.WithArgs(
|
||||
"write",
|
||||
fmt.Sprintf("%s/role/%s", vaultLdapMountPath, ldapGroup),
|
||||
"username_template=b_{{.DisplayName}}_{{.RoleName}}_{{random 10}}_{{unix_time}}",
|
||||
fmt.Sprintf("creation_ldif=%s", createLdif),
|
||||
fmt.Sprintf("rollback_ldif=%s", deleteLdif),
|
||||
fmt.Sprintf("deletion_ldif=%s", deleteLdif),
|
||||
"default_ttl=1h",
|
||||
"max_ttxl=24h",
|
||||
),
|
||||
)
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
|
||||
return policyName
|
||||
}
|
||||
|
||||
// CreateKvPrivateKeyCredential creates a private key credential in vault and
|
||||
// creates a vault policy to be able to read that credential. Returns the secret
|
||||
// and policy names. Note that this function does not put any clean-up in place
|
||||
// to run after a test is complete. When applicable, callers should destroy the
|
||||
// policy and secret this function creates.
|
||||
func CreateKvPrivateKeyCredential(t testing.TB, secretPath string, user string, keyPath string) (secretName string, policyName string) {
|
||||
secretName, err := base62.Random(16)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Update policy file
|
||||
f, err := os.OpenFile(kvPolicyFilePath, os.O_APPEND|os.O_WRONLY, 0o644)
|
||||
policyFilePath := path.Join(t.TempDir(), fmt.Sprintf("kv-pk-%s-policy.hcl", secretName))
|
||||
f, err := os.Create(policyFilePath)
|
||||
require.NoError(t, err)
|
||||
_, err = f.WriteString(fmt.Sprintf("path \"%s/data/%s\" { capabilities = [\"read\"] }\n",
|
||||
|
||||
_, err = fmt.Fprintf(f, "path \"%s/data/%s\" { capabilities = [\"read\"] }\n",
|
||||
secretPath,
|
||||
secretName,
|
||||
))
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
policyName = WritePolicy(t, t.Context(), policyFilePath)
|
||||
|
||||
// Create secret
|
||||
output := e2e.RunCommand(context.Background(), "vault",
|
||||
e2e.WithArgs(
|
||||
@@ -66,24 +174,31 @@ func CreateKvPrivateKeyCredential(t testing.TB, secretPath string, user string,
|
||||
)
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
|
||||
return secretName
|
||||
return secretName, policyName
|
||||
}
|
||||
|
||||
// CreateKvPasswordCredential creates a username/password credential in vault and creates a vault
|
||||
// policy to be able to read that credential. Returns the name of the policy
|
||||
func CreateKvPasswordCredential(t testing.TB, secretPath string, user string, kvPolicyFilePath string) (secretName string, password string) {
|
||||
// CreateKvPasswordCredential creates a username/password credential in vault
|
||||
// and creates a vault policy to be able to read that credential. Returns the
|
||||
// secret and policy names as well as the password for the secret. Note that
|
||||
// this function does not put any clean-up in place to run after a test is
|
||||
// complete. When applicable, callers should destroy the policy and secret this
|
||||
// function creates.
|
||||
func CreateKvPasswordCredential(t testing.TB, secretPath string, user string) (secretName string, policyName string, password string) {
|
||||
secretName, err := base62.Random(16)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Update policy file
|
||||
f, err := os.OpenFile(kvPolicyFilePath, os.O_APPEND|os.O_WRONLY, 0o644)
|
||||
policyFilePath := path.Join(t.TempDir(), fmt.Sprintf("kv-up-%s-policy.hcl", secretName))
|
||||
f, err := os.Create(policyFilePath)
|
||||
require.NoError(t, err)
|
||||
_, err = f.WriteString(fmt.Sprintf("path \"%s/data/%s\" { capabilities = [\"read\"] }\n",
|
||||
|
||||
_, err = fmt.Fprintf(f, "path \"%s/data/%s\" { capabilities = [\"read\"] }\n",
|
||||
secretPath,
|
||||
secretName,
|
||||
))
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
policyName = WritePolicy(t, t.Context(), policyFilePath)
|
||||
|
||||
// Create secret
|
||||
password, err = base62.Random(16)
|
||||
require.NoError(t, err)
|
||||
@@ -98,24 +213,30 @@ func CreateKvPasswordCredential(t testing.TB, secretPath string, user string, kv
|
||||
)
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
|
||||
return secretName, password
|
||||
return secretName, policyName, password
|
||||
}
|
||||
|
||||
// CreateKvPasswordDomainCredential creates a username/password/domain credential in vault and creates a vault
|
||||
// policy to be able to read that credential. Returns the name of the policy
|
||||
func CreateKvPasswordDomainCredential(t testing.TB, secretPath string, user string, domain string, kvPolicyFilePath string) (secretName string, password string) {
|
||||
// CreateKvPasswordDomainCredential creates a username/password/domain
|
||||
// credential in vault and creates a vault policy to be able to read that
|
||||
// credential. Returns the name of the policy. Note that this function does not
|
||||
// put any clean-up in place to run after a test is complete. When applicable,
|
||||
// callers should destroy the policy and secret this function creates.
|
||||
func CreateKvPasswordDomainCredential(t testing.TB, secretPath string, user string, domain string) (secretName string, policyName string, password string) {
|
||||
secretName, err := base62.Random(16)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Update policy file
|
||||
f, err := os.OpenFile(kvPolicyFilePath, os.O_APPEND|os.O_WRONLY, 0o644)
|
||||
policyFilePath := path.Join(t.TempDir(), fmt.Sprintf("kv-upd-%s-policy.hcl", secretName))
|
||||
f, err := os.Create(policyFilePath)
|
||||
require.NoError(t, err)
|
||||
_, err = f.WriteString(fmt.Sprintf("path \"%s/data/%s\" { capabilities = [\"read\"] }\n",
|
||||
|
||||
_, err = fmt.Fprintf(f, "path \"%s/data/%s\" { capabilities = [\"read\"] }\n",
|
||||
secretPath,
|
||||
secretName,
|
||||
))
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
policyName = WritePolicy(t, t.Context(), policyFilePath)
|
||||
|
||||
// Create secret
|
||||
password, err = base62.Random(16)
|
||||
require.NoError(t, err)
|
||||
@@ -131,7 +252,7 @@ func CreateKvPasswordDomainCredential(t testing.TB, secretPath string, user stri
|
||||
)
|
||||
require.NoError(t, output.Err, string(output.Stderr))
|
||||
|
||||
return secretName, password
|
||||
return secretName, policyName, password
|
||||
}
|
||||
|
||||
// WritePolicy adds a policy to vault. Provide a name for the policy that you want to create as well
|
||||
|
||||
Reference in new issue
Block a user