Merge pull request #2049 from hashicorp/llb-listing-speedups
Listing Session and Targets Performance Improvements Improve response time for `GET` requests to: - `/v1/targets` - `/v1/sessions` These endpoints now perform authorization checks prior to retrieving the full resources from the database. This also includes a bug fix for retrieving sessions that could result in incomplete results when there is a large number (10k+) of sessions.
This commit is contained in:
63 files changed
+1332
-335
No files matched your search
@@ -6,6 +6,8 @@ Canonical reference for changes, improvements, and bugfixes for Boundary.
|
||||
|
||||
### New and Improved
|
||||
* controller: new health endpoint ([PR](https://github.com/hashicorp/boundary/pull/1882)).
|
||||
* Improve response time for listing sessions and targets.
|
||||
[PR](https://github.com/hashicorp/boundary/pull/2049)
|
||||
|
||||
### Bug Fixes
|
||||
* worker: create new error to prevent `event.newError: missing error: invalid parameter` and handle session cancel
|
||||
@@ -13,6 +15,9 @@ Canonical reference for changes, improvements, and bugfixes for Boundary.
|
||||
[PR](https://github.com/hashicorp/boundary/pull/1929))
|
||||
* controller: Reconcile DEKs with existing scopes ([Issue](https://github.com/hashicorp/boundary/issues/1856),
|
||||
[PR](https://github.com/hashicorp/boundary/pull/1976))
|
||||
* Fix for retrieving sessions that could result in incomplete results when
|
||||
there is a large number (10k+) of sessions.
|
||||
[PR](https://github.com/hashicorp/boundary/pull/2049)
|
||||
|
||||
## 0.7.6 (2022/03/15)
|
||||
|
||||
|
||||
@@ -214,6 +214,10 @@ test-database-up:
|
||||
test-database-down:
|
||||
make -C testing/dbtest/docker clean
|
||||
|
||||
.PHONY: generate-database-dumps
|
||||
generate-database-dumps:
|
||||
@$(MAKE) -C testing/dbtest/docker generate-database-dumps
|
||||
|
||||
.PHONY: test-ci
|
||||
test-ci: export CI_BUILD=1
|
||||
test-ci:
|
||||
@@ -239,6 +243,19 @@ test-api:
|
||||
.PHONY: test-all
|
||||
test-all: test-sdk test-api test
|
||||
|
||||
BENCH_TIME?=1s
|
||||
BENCH_COUNT?=1s
|
||||
.PHONY: benchmark
|
||||
benchmark:
|
||||
@go test \
|
||||
-timeout 60m \
|
||||
./internal/servers/controller/handlers/sessions/ \
|
||||
-v \
|
||||
-bench '^BenchmarkSessionList$$' \
|
||||
-benchtime $(BENCH_TIME) \
|
||||
-count $(BENCH_COUNT) \
|
||||
-run '^$$'
|
||||
|
||||
.PHONY: install-go
|
||||
install-go:
|
||||
./ci/goinstall.sh
|
||||
|
||||
@@ -85,7 +85,10 @@ require (
|
||||
|
||||
require github.com/hashicorp/go-dbw v0.0.0-20211215222256-2ff0d37184ff // this is a branch and should be updated before merging
|
||||
|
||||
require github.com/prometheus/client_golang v1.12.1
|
||||
require (
|
||||
github.com/prometheus/client_golang v1.12.1
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
|
||||
@@ -177,7 +180,6 @@ require (
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
|
||||
@@ -366,7 +366,7 @@ func Test_MakeInactive_MakePrivate_MakePublic(t *testing.T) {
|
||||
}(),
|
||||
version: 1,
|
||||
wantErrMatch: errors.T(errors.InvalidParameter),
|
||||
wantErrContains: "certificate signed by unknown authority",
|
||||
wantErrContains: "certificate",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ const TestFakeManagedGroupFilter = `"/foo" == "bar"`
|
||||
// WithMaxAge, WithApiUrl, WithIssuer, WithCertificates, WithAudClaims, and
|
||||
// WithSigningAlgs options are supported.
|
||||
func TestAuthMethod(
|
||||
t *testing.T,
|
||||
t testing.TB,
|
||||
conn *db.DB,
|
||||
databaseWrapper wrapping.Wrapper,
|
||||
scopeId string,
|
||||
@@ -131,7 +131,7 @@ func TestAuthMethod(
|
||||
// TestSortAuthMethods will sort the provided auth methods by public id and it
|
||||
// will sort each auth method's embedded value objects (algs, auds, certs,
|
||||
// callbacks)
|
||||
func TestSortAuthMethods(t *testing.T, methods []*AuthMethod) {
|
||||
func TestSortAuthMethods(t testing.TB, methods []*AuthMethod) {
|
||||
// sort them by public id first...
|
||||
sort.Slice(methods, func(a, b int) bool {
|
||||
return methods[a].PublicId < methods[b].PublicId
|
||||
@@ -158,7 +158,7 @@ func TestSortAuthMethods(t *testing.T, methods []*AuthMethod) {
|
||||
}
|
||||
|
||||
// TestAccount creates a test oidc auth account.
|
||||
func TestAccount(t *testing.T, conn *db.DB, am *AuthMethod, subject string, opt ...Option) *Account {
|
||||
func TestAccount(t testing.TB, conn *db.DB, am *AuthMethod, subject string, opt ...Option) *Account {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
@@ -179,7 +179,7 @@ func TestAccount(t *testing.T, conn *db.DB, am *AuthMethod, subject string, opt
|
||||
}
|
||||
|
||||
// TestManagedGroup creates a test oidc managed group.
|
||||
func TestManagedGroup(t *testing.T, conn *db.DB, am *AuthMethod, filter string, opt ...Option) *ManagedGroup {
|
||||
func TestManagedGroup(t testing.TB, conn *db.DB, am *AuthMethod, filter string, opt ...Option) *ManagedGroup {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
@@ -197,7 +197,7 @@ func TestManagedGroup(t *testing.T, conn *db.DB, am *AuthMethod, filter string,
|
||||
}
|
||||
|
||||
// TestManagedGroupMember adds given account IDs to a managed group
|
||||
func TestManagedGroupMember(t *testing.T, conn *db.DB, managedGroupId, memberId string, opt ...Option) *ManagedGroupMemberAccount {
|
||||
func TestManagedGroupMember(t testing.TB, conn *db.DB, managedGroupId, memberId string, opt ...Option) *ManagedGroupMemberAccount {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
@@ -212,7 +212,7 @@ func TestManagedGroupMember(t *testing.T, conn *db.DB, managedGroupId, memberId
|
||||
|
||||
// TestConvertToUrls will convert URL string representations to a slice of
|
||||
// *url.URL
|
||||
func TestConvertToUrls(t *testing.T, urls ...string) []*url.URL {
|
||||
func TestConvertToUrls(t testing.TB, urls ...string) []*url.URL {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
require.NotEmpty(urls)
|
||||
@@ -228,7 +228,7 @@ func TestConvertToUrls(t *testing.T, urls ...string) []*url.URL {
|
||||
|
||||
// testGenerateCA will generate a test x509 CA cert, along with it encoded in a
|
||||
// PEM format.
|
||||
func testGenerateCA(t *testing.T, hosts ...string) (*x509.Certificate, string) {
|
||||
func testGenerateCA(t testing.TB, hosts ...string) (*x509.Certificate, string) {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
|
||||
@@ -280,7 +280,7 @@ func testGenerateCA(t *testing.T, hosts ...string) (*x509.Certificate, string) {
|
||||
return c, string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}))
|
||||
}
|
||||
|
||||
func testProvider(t *testing.T, clientId, clientSecret, allowedRedirectURL string, tp *oidc.TestProvider) *oidc.Provider {
|
||||
func testProvider(t testing.TB, clientId, clientSecret, allowedRedirectURL string, tp *oidc.TestProvider) *oidc.Provider {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
require.NotEmptyf(clientId, "%s: client id is empty")
|
||||
@@ -307,7 +307,7 @@ func testProvider(t *testing.T, clientId, clientSecret, allowedRedirectURL strin
|
||||
// testState will make a request.State and encrypt/encode within a request.Wrapper.
|
||||
// the returned string can be used as a parameter for functions like: oidc.Callback
|
||||
func testState(
|
||||
t *testing.T,
|
||||
t testing.TB,
|
||||
am *AuthMethod,
|
||||
kms *kms.Kms,
|
||||
tokenRequestId string,
|
||||
@@ -344,7 +344,7 @@ func testState(
|
||||
// TestTokenRequestId will make a request.Token and encrypt/encode within a request.Wrapper.
|
||||
// the returned string can be used as a parameter for functions like: oidc.TokenRequest
|
||||
func TestTokenRequestId(
|
||||
t *testing.T,
|
||||
t testing.TB,
|
||||
am *AuthMethod,
|
||||
kms *kms.Kms,
|
||||
expIn time.Duration,
|
||||
@@ -371,7 +371,7 @@ func TestTokenRequestId(
|
||||
|
||||
// TestPendingToken will create a pending auth token for the tokenRequestId (aka public id)
|
||||
func TestPendingToken(
|
||||
t *testing.T,
|
||||
t testing.TB,
|
||||
tokenRepo *authtoken.Repository,
|
||||
user *iam.User,
|
||||
acct *Account,
|
||||
@@ -401,11 +401,11 @@ type testControllerSrv struct {
|
||||
iamRepoFn IamRepoFactory
|
||||
atRepoFn AuthTokenRepoFactory
|
||||
httpServer *httptest.Server
|
||||
t *testing.T
|
||||
t testing.TB
|
||||
}
|
||||
|
||||
// startTestControllerSrv returns a running testControllerSrv
|
||||
func startTestControllerSrv(t *testing.T, oidcRepoFn OidcRepoFactory, iamRepoFn IamRepoFactory, atRepoFn AuthTokenRepoFactory) *testControllerSrv {
|
||||
func startTestControllerSrv(t testing.TB, oidcRepoFn OidcRepoFactory, iamRepoFn IamRepoFactory, atRepoFn AuthTokenRepoFactory) *testControllerSrv {
|
||||
require := require.New(t)
|
||||
require.NotNil(t)
|
||||
t.Helper()
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
// TestAuthMethod creates a password auth methods to the provided DB with the
|
||||
// provided scope id. If any errors are encountered during the creation of the
|
||||
// auth methods, the test will fail.
|
||||
func TestAuthMethod(t *testing.T, conn *db.DB, scopeId string, opt ...Option) *AuthMethod {
|
||||
func TestAuthMethod(t testing.TB, conn *db.DB, scopeId string, opt ...Option) *AuthMethod {
|
||||
t.Helper()
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
w := db.New(conn)
|
||||
@@ -47,7 +47,7 @@ func TestAuthMethod(t *testing.T, conn *db.DB, scopeId string, opt ...Option) *A
|
||||
// TestAuthMethods creates count number of password auth methods to the provided DB
|
||||
// with the provided scope id. If any errors are encountered during the creation of
|
||||
// the auth methods, the test will fail.
|
||||
func TestAuthMethods(t *testing.T, conn *db.DB, scopeId string, count int) []*AuthMethod {
|
||||
func TestAuthMethods(t testing.TB, conn *db.DB, scopeId string, count int) []*AuthMethod {
|
||||
t.Helper()
|
||||
var auts []*AuthMethod
|
||||
for i := 0; i < count; i++ {
|
||||
@@ -59,7 +59,7 @@ func TestAuthMethods(t *testing.T, conn *db.DB, scopeId string, count int) []*Au
|
||||
// TestMultipleAccounts creates count number of password account to the provided DB
|
||||
// with the provided auth method id. The auth method must have been created previously.
|
||||
// If any errors are encountered during the creation of the account, the test will fail.
|
||||
func TestMultipleAccounts(t *testing.T, conn *db.DB, authMethodId string, count int) []*Account {
|
||||
func TestMultipleAccounts(t testing.TB, conn *db.DB, authMethodId string, count int) []*Account {
|
||||
t.Helper()
|
||||
var auts []*Account
|
||||
for i := 0; i < count; i++ {
|
||||
@@ -72,7 +72,7 @@ func TestMultipleAccounts(t *testing.T, conn *db.DB, authMethodId string, count
|
||||
// auth method id and loginName. The auth method must have been created
|
||||
// previously. See password.NewAccount(...) for a list of supported options.
|
||||
// If any errors are encountered during the creation of the account, the test will fail.
|
||||
func TestAccount(t *testing.T, conn *db.DB, authMethodId, loginName string, opt ...Option) *Account {
|
||||
func TestAccount(t testing.TB, conn *db.DB, authMethodId, loginName string, opt ...Option) *Account {
|
||||
t.Helper()
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
require.NotEmpty(loginName)
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAuthToken(t *testing.T, conn *db.DB, kms *kms.Kms, scopeId string, opt ...Option) *AuthToken {
|
||||
func TestAuthToken(t testing.TB, conn *db.DB, kms *kms.Kms, scopeId string, opt ...Option) *AuthToken {
|
||||
t.Helper()
|
||||
authMethod := password.TestAuthMethods(t, conn, scopeId, 1)[0]
|
||||
// auth account is only used to join auth method to user.
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
// define the Boundary domain.
|
||||
package boundary
|
||||
|
||||
import "github.com/hashicorp/boundary/internal/db/timestamp"
|
||||
import (
|
||||
"github.com/hashicorp/boundary/internal/db/timestamp"
|
||||
)
|
||||
|
||||
// An Entity is an object distinguished by its identity, rather than its
|
||||
// attributes. It can contain value objects and other entities.
|
||||
@@ -25,3 +27,12 @@ type Resource interface {
|
||||
GetName() string
|
||||
GetDescription() string
|
||||
}
|
||||
|
||||
// AuthzProtectedEntity is used by some functions (primarily
|
||||
// scopeids.AuthzProtectedEntityProvider-conforming implementations) to deliver
|
||||
// some common information necessary for calculating authz.
|
||||
type AuthzProtectedEntity interface {
|
||||
Entity
|
||||
GetScopeId() string
|
||||
GetUserId() string
|
||||
}
|
||||
@@ -3,16 +3,16 @@ package vault
|
||||
import "testing"
|
||||
|
||||
var (
|
||||
newVaultServer func(t *testing.T, opt ...TestOption) *TestVaultServer = skipNewServer
|
||||
mountDatabase func(t *testing.T, v *TestVaultServer, opt ...TestOption) *TestDatabase = skipMountDatabase
|
||||
newVaultServer func(t testing.TB, opt ...TestOption) *TestVaultServer = skipNewServer
|
||||
mountDatabase func(t testing.TB, v *TestVaultServer, opt ...TestOption) *TestDatabase = skipMountDatabase
|
||||
)
|
||||
|
||||
func skipNewServer(t *testing.T, opt ...TestOption) *TestVaultServer {
|
||||
func skipNewServer(t testing.TB, opt ...TestOption) *TestVaultServer {
|
||||
t.Skip("docker not available")
|
||||
return nil
|
||||
}
|
||||
|
||||
func skipMountDatabase(t *testing.T, v *TestVaultServer, opt ...TestOption) *TestDatabase {
|
||||
func skipMountDatabase(t testing.TB, v *TestVaultServer, opt ...TestOption) *TestDatabase {
|
||||
t.Skip("docker not available")
|
||||
return nil
|
||||
}
|
||||
@@ -30,9 +30,9 @@ func init() {
|
||||
mountDatabase = gotMountDatabase
|
||||
}
|
||||
|
||||
func gotDocker(t *testing.T) {}
|
||||
func gotDocker(t testing.TB) {}
|
||||
|
||||
func gotNewServer(t *testing.T, opt ...TestOption) *TestVaultServer {
|
||||
func gotNewServer(t testing.TB, opt ...TestOption) *TestVaultServer {
|
||||
const (
|
||||
serverTlsTemplate = `{
|
||||
"listener": [
|
||||
@@ -193,7 +193,7 @@ func gotNewServer(t *testing.T, opt ...TestOption) *TestVaultServer {
|
||||
return server
|
||||
}
|
||||
|
||||
func gotMountDatabase(t *testing.T, v *TestVaultServer, opt ...TestOption) *TestDatabase {
|
||||
func gotMountDatabase(t testing.TB, v *TestVaultServer, opt ...TestOption) *TestDatabase {
|
||||
require := require.New(t)
|
||||
require.Nil(v.postgresContainer, "postgres container exists")
|
||||
require.NotNil(v.network, "Vault server must be created with docker network")
|
||||
@@ -265,8 +265,10 @@ func gotMountDatabase(t *testing.T, v *TestVaultServer, opt ...TestOption) *Test
|
||||
|
||||
// Mount Database
|
||||
maxTTL := 24 * time.Hour
|
||||
if deadline, ok := t.Deadline(); ok {
|
||||
maxTTL = time.Until(deadline) * 2
|
||||
if t, ok := t.(*testing.T); ok {
|
||||
if deadline, ok := t.Deadline(); ok {
|
||||
maxTTL = time.Until(deadline) * 2
|
||||
}
|
||||
}
|
||||
|
||||
defaultTTL := maxTTL / 2
|
||||
@@ -348,7 +350,7 @@ grant closed_role to "{{name}}";
|
||||
}
|
||||
}
|
||||
|
||||
func cleanupResource(t *testing.T, pool *dockertest.Pool, resource *dockertest.Resource) {
|
||||
func cleanupResource(t testing.TB, pool *dockertest.Pool, resource *dockertest.Resource) {
|
||||
t.Helper()
|
||||
var err error
|
||||
for i := 0; i < 10; i++ {
|
||||
|
||||
@@ -33,7 +33,7 @@ import (
|
||||
// the provided scope, vault address, token, and accessor and any values passed
|
||||
// in through the Options vargs. If any errors are encountered during the
|
||||
// creation of the store, the test will fail.
|
||||
func TestCredentialStore(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, scopeId, vaultAddr, vaultToken, accessor string, opts ...Option) *CredentialStore {
|
||||
func TestCredentialStore(t testing.TB, conn *db.DB, wrapper wrapping.Wrapper, scopeId, vaultAddr, vaultToken, accessor string, opts ...Option) *CredentialStore {
|
||||
t.Helper()
|
||||
ctx := context.Background()
|
||||
kmsCache := kms.TestKms(t, conn, wrapper)
|
||||
@@ -74,7 +74,7 @@ func TestCredentialStore(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, sc
|
||||
// the provided DB with the provided scope id. If any errors are
|
||||
// encountered during the creation of the credential stores, the test will
|
||||
// fail.
|
||||
func TestCredentialStores(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, scopeId string, count int) []*CredentialStore {
|
||||
func TestCredentialStores(t testing.TB, conn *db.DB, wrapper wrapping.Wrapper, scopeId string, count int) []*CredentialStore {
|
||||
t.Helper()
|
||||
ctx := context.Background()
|
||||
kmsCache := kms.TestKms(t, conn, wrapper)
|
||||
@@ -112,7 +112,7 @@ func TestCredentialStores(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, s
|
||||
// libraries in the provided DB with the provided store id. If any errors
|
||||
// are encountered during the creation of the credential libraries, the
|
||||
// test will fail.
|
||||
func TestCredentialLibraries(t *testing.T, conn *db.DB, _ wrapping.Wrapper, storeId string, count int) []*CredentialLibrary {
|
||||
func TestCredentialLibraries(t testing.TB, conn *db.DB, _ wrapping.Wrapper, storeId string, count int) []*CredentialLibrary {
|
||||
t.Helper()
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
w := db.New(conn)
|
||||
@@ -143,7 +143,7 @@ func TestCredentialLibraries(t *testing.T, conn *db.DB, _ wrapping.Wrapper, stor
|
||||
// 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.
|
||||
func TestCredentials(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, libraryId, sessionId string, count int) []*Credential {
|
||||
func TestCredentials(t testing.TB, conn *db.DB, wrapper wrapping.Wrapper, libraryId, sessionId string, count int) []*Credential {
|
||||
t.Helper()
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
rw := db.New(conn)
|
||||
@@ -187,7 +187,7 @@ func TestCredentials(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, librar
|
||||
return credentials
|
||||
}
|
||||
|
||||
func createTestToken(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, scopeId, storeId, token, accessor string) *Token {
|
||||
func createTestToken(t testing.TB, conn *db.DB, wrapper wrapping.Wrapper, scopeId, storeId, token, accessor string) *Token {
|
||||
t.Helper()
|
||||
w := db.New(conn)
|
||||
ctx := context.Background()
|
||||
@@ -209,7 +209,7 @@ func createTestToken(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, scopeI
|
||||
return inToken
|
||||
}
|
||||
|
||||
func testTokens(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, scopeId, storeId string, count int) []*Token {
|
||||
func testTokens(t testing.TB, conn *db.DB, wrapper wrapping.Wrapper, scopeId, storeId string, count int) []*Token {
|
||||
t.Helper()
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
w := db.New(conn)
|
||||
@@ -267,7 +267,7 @@ type testCert struct {
|
||||
priv *ecdsa.PrivateKey
|
||||
}
|
||||
|
||||
func (tc *testCert) ClientCertificate(t *testing.T) *ClientCertificate {
|
||||
func (tc *testCert) ClientCertificate(t testing.TB) *ClientCertificate {
|
||||
t.Helper()
|
||||
c, err := NewClientCertificate(tc.Cert, tc.Key)
|
||||
require.NoError(t, err)
|
||||
@@ -280,7 +280,7 @@ type testCertBundle struct {
|
||||
}
|
||||
|
||||
// testCaCert will generate a test x509 CA cert.
|
||||
func testCaCert(t *testing.T) *testCert {
|
||||
func testCaCert(t testing.TB) *testCert {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
|
||||
@@ -292,8 +292,10 @@ func testCaCert(t *testing.T) *testCert {
|
||||
keyUsage := x509.KeyUsageDigitalSignature
|
||||
|
||||
notBefore, notAfter := time.Now(), time.Now().Add(24*time.Hour)
|
||||
if deadLine, ok := t.Deadline(); ok {
|
||||
notAfter = deadLine
|
||||
if t, ok := t.(*testing.T); ok {
|
||||
if deadline, ok := t.Deadline(); ok {
|
||||
notAfter = deadline
|
||||
}
|
||||
}
|
||||
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
@@ -331,7 +333,7 @@ func testCaCert(t *testing.T) *testCert {
|
||||
}
|
||||
|
||||
// testServerCert will generate a test x509 server cert.
|
||||
func testServerCert(t *testing.T, ca *testCert, hosts ...string) *testCertBundle {
|
||||
func testServerCert(t testing.TB, ca *testCert, hosts ...string) *testCertBundle {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
|
||||
@@ -343,8 +345,10 @@ func testServerCert(t *testing.T, ca *testCert, hosts ...string) *testCertBundle
|
||||
keyUsage := x509.KeyUsageDigitalSignature
|
||||
|
||||
notBefore, notAfter := time.Now(), time.Now().Add(24*time.Hour)
|
||||
if deadLine, ok := t.Deadline(); ok {
|
||||
notAfter = deadLine
|
||||
if t, ok := t.(*testing.T); ok {
|
||||
if deadline, ok := t.Deadline(); ok {
|
||||
notAfter = deadline
|
||||
}
|
||||
}
|
||||
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
@@ -396,7 +400,7 @@ func testServerCert(t *testing.T, ca *testCert, hosts ...string) *testCertBundle
|
||||
}
|
||||
|
||||
// testClientCert will generate a test x509 client cert.
|
||||
func testClientCert(t *testing.T, ca *testCert, opt ...TestOption) *testCertBundle {
|
||||
func testClientCert(t testing.TB, ca *testCert, opt ...TestOption) *testCertBundle {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
|
||||
@@ -413,8 +417,10 @@ func testClientCert(t *testing.T, ca *testCert, opt ...TestOption) *testCertBund
|
||||
keyUsage := x509.KeyUsageDigitalSignature
|
||||
|
||||
notBefore, notAfter := time.Now(), time.Now().Add(24*time.Hour)
|
||||
if deadLine, ok := t.Deadline(); ok {
|
||||
notAfter = deadLine
|
||||
if t, ok := t.(*testing.T); ok {
|
||||
if deadline, ok := t.Deadline(); ok {
|
||||
notAfter = deadline
|
||||
}
|
||||
}
|
||||
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
@@ -457,7 +463,7 @@ func testClientCert(t *testing.T, ca *testCert, opt ...TestOption) *testCertBund
|
||||
}
|
||||
}
|
||||
|
||||
func getTestOpts(t *testing.T, opt ...TestOption) testOptions {
|
||||
func getTestOpts(t testing.TB, opt ...TestOption) testOptions {
|
||||
t.Helper()
|
||||
opts := getDefaultTestOptions(t)
|
||||
for _, o := range opt {
|
||||
@@ -467,7 +473,7 @@ func getTestOpts(t *testing.T, opt ...TestOption) testOptions {
|
||||
}
|
||||
|
||||
// TestOption - how Options are passed as arguments.
|
||||
type TestOption func(*testing.T, *testOptions)
|
||||
type TestOption func(testing.TB, *testOptions)
|
||||
|
||||
// options = how options are represented
|
||||
type testOptions struct {
|
||||
@@ -484,11 +490,13 @@ type testOptions struct {
|
||||
clientKey *ecdsa.PrivateKey
|
||||
}
|
||||
|
||||
func getDefaultTestOptions(t *testing.T) testOptions {
|
||||
func getDefaultTestOptions(t testing.TB) testOptions {
|
||||
t.Helper()
|
||||
defaultPeriod := 24 * time.Hour
|
||||
if deadline, ok := t.Deadline(); ok {
|
||||
defaultPeriod = time.Until(deadline)
|
||||
if t, ok := t.(*testing.T); ok {
|
||||
if deadline, ok := t.Deadline(); ok {
|
||||
defaultPeriod = time.Until(deadline)
|
||||
}
|
||||
}
|
||||
|
||||
return testOptions{
|
||||
@@ -509,7 +517,7 @@ func getDefaultTestOptions(t *testing.T) testOptions {
|
||||
// is the value of t.Deadline() or 24 hours if
|
||||
// t.Deadline() is nil.
|
||||
func WithTokenPeriod(d time.Duration) TestOption {
|
||||
return func(t *testing.T, o *testOptions) {
|
||||
return func(t testing.TB, o *testOptions) {
|
||||
t.Helper()
|
||||
o.tokenPeriod = d
|
||||
}
|
||||
@@ -518,7 +526,7 @@ func WithTokenPeriod(d time.Duration) TestOption {
|
||||
// WithPolicies sets the polices to attach to a token. The default policy
|
||||
// attached to tokens is 'default'.
|
||||
func WithPolicies(p []string) TestOption {
|
||||
return func(t *testing.T, o *testOptions) {
|
||||
return func(t testing.TB, o *testOptions) {
|
||||
t.Helper()
|
||||
o.policies = p
|
||||
}
|
||||
@@ -527,7 +535,7 @@ func WithPolicies(p []string) TestOption {
|
||||
// WithDockerNetwork sets the option to create docker network when creating
|
||||
// a Vault test server. The default is to not create a docker network.
|
||||
func WithDockerNetwork(b bool) TestOption {
|
||||
return func(t *testing.T, o *testOptions) {
|
||||
return func(t testing.TB, o *testOptions) {
|
||||
t.Helper()
|
||||
o.dockerNetwork = b
|
||||
}
|
||||
@@ -536,7 +544,7 @@ func WithDockerNetwork(b bool) TestOption {
|
||||
// WithTestVaultTLS sets the Vault TLS option.
|
||||
// TestNoTLS is the default TLS option.
|
||||
func WithTestVaultTLS(s TestVaultTLS) TestOption {
|
||||
return func(t *testing.T, o *testOptions) {
|
||||
return func(t testing.TB, o *testOptions) {
|
||||
t.Helper()
|
||||
o.vaultTLS = s
|
||||
}
|
||||
@@ -545,7 +553,7 @@ func WithTestVaultTLS(s TestVaultTLS) TestOption {
|
||||
// WithClientKey sets the private key that will be used to generate the
|
||||
// client certificate. The option is only valid when used together with TestClientTLS.
|
||||
func WithClientKey(k *ecdsa.PrivateKey) TestOption {
|
||||
return func(t *testing.T, o *testOptions) {
|
||||
return func(t testing.TB, o *testOptions) {
|
||||
t.Helper()
|
||||
o.clientKey = k
|
||||
}
|
||||
@@ -553,7 +561,7 @@ func WithClientKey(k *ecdsa.PrivateKey) TestOption {
|
||||
|
||||
// WithTestMountPath sets the mount path option to p.
|
||||
func WithTestMountPath(p string) TestOption {
|
||||
return func(t *testing.T, o *testOptions) {
|
||||
return func(t testing.TB, o *testOptions) {
|
||||
t.Helper()
|
||||
p = strings.TrimSpace(p)
|
||||
if p != "" {
|
||||
@@ -566,7 +574,7 @@ func WithTestMountPath(p string) TestOption {
|
||||
// WithTestRoleName sets the roleName name to n.
|
||||
// The default role name is boundary.
|
||||
func WithTestRoleName(n string) TestOption {
|
||||
return func(t *testing.T, o *testOptions) {
|
||||
return func(t testing.TB, o *testOptions) {
|
||||
t.Helper()
|
||||
n = strings.TrimSpace(n)
|
||||
if n == "" {
|
||||
@@ -579,7 +587,7 @@ func WithTestRoleName(n string) TestOption {
|
||||
// WithDontCleanUp causes the resource created to not
|
||||
// be automaticaly cleaned up at the end of the test run.
|
||||
func WithDontCleanUp() TestOption {
|
||||
return func(t *testing.T, o *testOptions) {
|
||||
return func(t testing.TB, o *testOptions) {
|
||||
t.Helper()
|
||||
o.skipCleanup = true
|
||||
}
|
||||
@@ -588,7 +596,7 @@ func WithDontCleanUp() TestOption {
|
||||
// TestOrphanToken sets the token orphan option to b.
|
||||
// The orphan option is true by default.
|
||||
func TestOrphanToken(b bool) TestOption {
|
||||
return func(t *testing.T, o *testOptions) {
|
||||
return func(t testing.TB, o *testOptions) {
|
||||
t.Helper()
|
||||
o.orphan = b
|
||||
}
|
||||
@@ -597,7 +605,7 @@ func TestOrphanToken(b bool) TestOption {
|
||||
// TestPeriodicToken sets the token periodic option to b.
|
||||
// The periodic option is true by default.
|
||||
func TestPeriodicToken(b bool) TestOption {
|
||||
return func(t *testing.T, o *testOptions) {
|
||||
return func(t testing.TB, o *testOptions) {
|
||||
t.Helper()
|
||||
o.periodic = b
|
||||
}
|
||||
@@ -606,18 +614,18 @@ func TestPeriodicToken(b bool) TestOption {
|
||||
// TestRenewableToken sets the token renewable option to b.
|
||||
// The renewable option is true by default.
|
||||
func TestRenewableToken(b bool) TestOption {
|
||||
return func(t *testing.T, o *testOptions) {
|
||||
return func(t testing.TB, o *testOptions) {
|
||||
t.Helper()
|
||||
o.renewable = b
|
||||
}
|
||||
}
|
||||
|
||||
func (v *TestVaultServer) client(t *testing.T) *client {
|
||||
func (v *TestVaultServer) client(t testing.TB) *client {
|
||||
t.Helper()
|
||||
return v.clientUsingToken(t, v.RootToken)
|
||||
}
|
||||
|
||||
func (v *TestVaultServer) clientUsingToken(t *testing.T, token string) *client {
|
||||
func (v *TestVaultServer) clientUsingToken(t testing.TB, token string) *client {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
conf := &clientConfig{
|
||||
@@ -639,7 +647,7 @@ func (v *TestVaultServer) clientUsingToken(t *testing.T, token string) *client {
|
||||
// using v.RootToken. It returns the vault secret containing the token and
|
||||
// the token itself. See
|
||||
// https://www.vaultproject.io/api-docs/auth/token#create-token.
|
||||
func (v *TestVaultServer) CreateToken(t *testing.T, opt ...TestOption) (*vault.Secret, string) {
|
||||
func (v *TestVaultServer) CreateToken(t testing.TB, opt ...TestOption) (*vault.Secret, string) {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
opts := getTestOpts(t, opt...)
|
||||
@@ -668,7 +676,7 @@ func (v *TestVaultServer) CreateToken(t *testing.T, opt ...TestOption) (*vault.S
|
||||
|
||||
// LookupToken calls /auth/token/lookup on v for the token. See
|
||||
// https://www.vaultproject.io/api-docs/auth/token#lookup-a-token.
|
||||
func (v *TestVaultServer) LookupToken(t *testing.T, token string) *vault.Secret {
|
||||
func (v *TestVaultServer) LookupToken(t testing.TB, token string) *vault.Secret {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
vc := v.client(t).cl
|
||||
@@ -681,7 +689,7 @@ func (v *TestVaultServer) LookupToken(t *testing.T, token string) *vault.Secret
|
||||
// VerifyTokenInvalid calls /auth/token/lookup on v for the token. It expects the lookup to
|
||||
// fail with a StatusForbidden. See
|
||||
// https://www.vaultproject.io/api-docs/auth/token#lookup-a-token.
|
||||
func (v *TestVaultServer) VerifyTokenInvalid(t *testing.T, token string) {
|
||||
func (v *TestVaultServer) VerifyTokenInvalid(t testing.TB, token string) {
|
||||
t.Helper()
|
||||
require, assert := require.New(t), assert.New(t)
|
||||
vc := v.client(t).cl
|
||||
@@ -698,7 +706,7 @@ func (v *TestVaultServer) VerifyTokenInvalid(t *testing.T, token string) {
|
||||
// LookupLease calls the /sys/leases/lookup Vault endpoint and returns
|
||||
// the vault.Secret response. See
|
||||
// https://www.vaultproject.io/api-docs/system/leases#read-lease.
|
||||
func (v *TestVaultServer) LookupLease(t *testing.T, leaseId string) *vault.Secret {
|
||||
func (v *TestVaultServer) LookupLease(t testing.TB, leaseId string) *vault.Secret {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
vc := v.client(t).cl
|
||||
@@ -709,7 +717,7 @@ func (v *TestVaultServer) LookupLease(t *testing.T, leaseId string) *vault.Secre
|
||||
return secret
|
||||
}
|
||||
|
||||
func (v *TestVaultServer) addPolicy(t *testing.T, name string, pc pathCapabilities) {
|
||||
func (v *TestVaultServer) addPolicy(t testing.TB, name string, pc pathCapabilities) {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
policy := pc.vaultPolicy()
|
||||
@@ -733,7 +741,7 @@ func (v *TestVaultServer) addPolicy(t *testing.T, name string, pc pathCapabiliti
|
||||
// path "mountPath/*" {
|
||||
// capabilities = ["create", "read", "update", "delete", "list"]
|
||||
// }
|
||||
func (v *TestVaultServer) MountPKI(t *testing.T, opt ...TestOption) *vault.Secret {
|
||||
func (v *TestVaultServer) MountPKI(t testing.TB, opt ...TestOption) *vault.Secret {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
opts := getTestOpts(t, opt...)
|
||||
@@ -741,8 +749,10 @@ func (v *TestVaultServer) MountPKI(t *testing.T, opt ...TestOption) *vault.Secre
|
||||
|
||||
// Mount PKI
|
||||
maxTTL := 24 * time.Hour
|
||||
if deadline, ok := t.Deadline(); ok {
|
||||
maxTTL = time.Until(deadline) * 2
|
||||
if t, ok := t.(*testing.T); ok {
|
||||
if deadline, ok := t.Deadline(); ok {
|
||||
maxTTL = time.Until(deadline) * 2
|
||||
}
|
||||
}
|
||||
|
||||
defaultTTL := maxTTL / 2
|
||||
@@ -797,7 +807,7 @@ func (v *TestVaultServer) MountPKI(t *testing.T, opt ...TestOption) *vault.Secre
|
||||
// }
|
||||
//
|
||||
// All options are ignored.
|
||||
func (v *TestVaultServer) AddKVPolicy(t *testing.T, _ ...TestOption) {
|
||||
func (v *TestVaultServer) AddKVPolicy(t testing.TB, _ ...TestOption) {
|
||||
t.Helper()
|
||||
|
||||
pc := pathCapabilities{
|
||||
@@ -811,7 +821,7 @@ func (v *TestVaultServer) AddKVPolicy(t *testing.T, _ ...TestOption) {
|
||||
// `{"data": {"key": "value", "key2": "value2"}}`
|
||||
// See
|
||||
// https://www.vaultproject.io/api-docs/secret/kv/kv-v2#create-update-secret
|
||||
func (v *TestVaultServer) CreateKVSecret(t *testing.T, p string, data []byte) *vault.Secret {
|
||||
func (v *TestVaultServer) CreateKVSecret(t testing.TB, p string, data []byte) *vault.Secret {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
|
||||
@@ -849,7 +859,7 @@ type TestVaultServer struct {
|
||||
// WithTestVaultTLS and WithDockerNetwork are the only valid options.
|
||||
// Setting the WithDockerNetwork option can significantly increase the
|
||||
// amount of time required for a test to run.
|
||||
func NewTestVaultServer(t *testing.T, opt ...TestOption) *TestVaultServer {
|
||||
func NewTestVaultServer(t testing.TB, opt ...TestOption) *TestVaultServer {
|
||||
t.Helper()
|
||||
return newVaultServer(t, opt...)
|
||||
}
|
||||
@@ -868,7 +878,7 @@ func NewTestVaultServer(t *testing.T, opt ...TestOption) *TestVaultServer {
|
||||
//
|
||||
// MountDatabase returns a TestDatabase for testing credentials from the
|
||||
// mount.
|
||||
func (v *TestVaultServer) MountDatabase(t *testing.T, opt ...TestOption) *TestDatabase {
|
||||
func (v *TestVaultServer) MountDatabase(t testing.TB, opt ...TestOption) *TestDatabase {
|
||||
t.Helper()
|
||||
return mountDatabase(t, v, opt...)
|
||||
}
|
||||
@@ -878,7 +888,7 @@ func (v *TestVaultServer) MountDatabase(t *testing.T, opt ...TestOption) *TestDa
|
||||
type TestDatabaseURL string
|
||||
|
||||
// Encode encodes the username and password credentials from s into u.
|
||||
func (u TestDatabaseURL) Encode(t *testing.T, s *vault.Secret) string {
|
||||
func (u TestDatabaseURL) Encode(t testing.TB, s *vault.Secret) string {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
require.NotNil(s, "vault.Secret")
|
||||
@@ -897,7 +907,7 @@ type TestDatabase struct {
|
||||
|
||||
// ValidateCredential tests the credentials in s against d. An error is
|
||||
// returned if the credentials are not valid.
|
||||
func (d *TestDatabase) ValidateCredential(t *testing.T, s *vault.Secret) error {
|
||||
func (d *TestDatabase) ValidateCredential(t testing.TB, s *vault.Secret) error {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
require.NotNil(s)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
begin;
|
||||
-- Session list queries always use an order by on create_time.
|
||||
-- This index can aide in performing this order by.
|
||||
create index
|
||||
session_create_time
|
||||
on
|
||||
session (create_time);
|
||||
|
||||
analyze session;
|
||||
|
||||
commit;
|
||||
+10
-8
@@ -25,7 +25,8 @@ import (
|
||||
// setup the tests (initialize the database one-time and intialized
|
||||
// testDatabaseURL). Do not close the returned db. Supported options:
|
||||
// WithTestLogLevel, WithTestDatabaseUrl
|
||||
func TestSetup(t *testing.T, dialect string, opt ...TestOption) (*DB, string) {
|
||||
func TestSetup(t testing.TB, dialect string, opt ...TestOption) (*DB, string) {
|
||||
t.Helper()
|
||||
var cleanup func() error
|
||||
var url string
|
||||
var err error
|
||||
@@ -73,7 +74,8 @@ func TestSetup(t *testing.T, dialect string, opt ...TestOption) (*DB, string) {
|
||||
}
|
||||
|
||||
// TestWrapper initializes an AEAD wrapping.Wrapper for testing the oplog
|
||||
func TestWrapper(t *testing.T) wrapping.Wrapper {
|
||||
func TestWrapper(t testing.TB) wrapping.Wrapper {
|
||||
t.Helper()
|
||||
rootKey := make([]byte, 32)
|
||||
n, err := rand.Read(rootKey)
|
||||
if err != nil {
|
||||
@@ -95,7 +97,7 @@ func TestWrapper(t *testing.T) wrapping.Wrapper {
|
||||
|
||||
// AssertPublicId is a test helper that asserts that the provided id is in
|
||||
// the format of a public id.
|
||||
func AssertPublicId(t *testing.T, prefix, actual string) {
|
||||
func AssertPublicId(t testing.TB, prefix, actual string) {
|
||||
t.Helper()
|
||||
assert.NotEmpty(t, actual)
|
||||
parts := strings.Split(actual, "_")
|
||||
@@ -105,7 +107,7 @@ func AssertPublicId(t *testing.T, prefix, actual string) {
|
||||
|
||||
// TestDeleteWhere allows you to easily delete resources for testing purposes
|
||||
// including all the current resources.
|
||||
func TestDeleteWhere(t *testing.T, conn *DB, i interface{}, whereClause string, args ...interface{}) {
|
||||
func TestDeleteWhere(t testing.TB, conn *DB, i interface{}, whereClause string, args ...interface{}) {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
ctx := context.Background()
|
||||
@@ -122,7 +124,7 @@ func TestDeleteWhere(t *testing.T, conn *DB, i interface{}, whereClause string,
|
||||
// WithResourcePrivateId option is provided, the lookup will use the `resource-private-id` tag.
|
||||
// An error is returned if the entry or it's metadata is not found. Returning an error
|
||||
// allows clients to test if an entry was not written, which is a valid use case.
|
||||
func TestVerifyOplog(t *testing.T, r Reader, resourceId string, opt ...TestOption) error {
|
||||
func TestVerifyOplog(t testing.TB, r Reader, resourceId string, opt ...TestOption) error {
|
||||
t.Helper()
|
||||
|
||||
// sql where clauses
|
||||
@@ -264,14 +266,14 @@ const (
|
||||
|
||||
// WithTestLogLevel provides a way to specify a test log level for the
|
||||
// underlying database package (if applicable).
|
||||
func WithTestLogLevel(_ *testing.T, l TestLogLevel) TestOption {
|
||||
func WithTestLogLevel(_ testing.TB, l TestLogLevel) TestOption {
|
||||
return func(o *testOptions) {
|
||||
o.withLogLevel = l
|
||||
}
|
||||
}
|
||||
|
||||
// TestCreateTables will create the test tables for the db pkg
|
||||
func TestCreateTables(t *testing.T, conn *DB) {
|
||||
func TestCreateTables(t testing.TB, conn *DB) {
|
||||
t.Helper()
|
||||
t.Cleanup(func() { testDropTables(t, conn) })
|
||||
require := require.New(t)
|
||||
@@ -283,7 +285,7 @@ func TestCreateTables(t *testing.T, conn *DB) {
|
||||
require.NoError(err)
|
||||
}
|
||||
|
||||
func testDropTables(t *testing.T, conn *DB) {
|
||||
func testDropTables(t testing.TB, conn *DB) {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
testCtx := context.Background()
|
||||
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
// TestCatalogs creates count number of static host catalogs to the provided DB
|
||||
// with the provided scope id. If any errors are encountered during the creation of
|
||||
// the host catalog, the test will fail.
|
||||
func TestCatalogs(t *testing.T, conn *db.DB, scopeId, pluginId string, count int) []*HostCatalog {
|
||||
func TestCatalogs(t testing.TB, conn *db.DB, scopeId, pluginId string, count int) []*HostCatalog {
|
||||
t.Helper()
|
||||
var cats []*HostCatalog
|
||||
for i := 0; i < count; i++ {
|
||||
@@ -36,7 +36,7 @@ func TestCatalogs(t *testing.T, conn *db.DB, scopeId, pluginId string, count int
|
||||
// TestCatalog creates a plugin host catalogs to the provided DB
|
||||
// with the provided scope id. If any errors are encountered during the creation of
|
||||
// the host catalog, the test will fail.
|
||||
func TestCatalog(t *testing.T, conn *db.DB, scopeId, pluginId string, opt ...Option) *HostCatalog {
|
||||
func TestCatalog(t testing.TB, conn *db.DB, scopeId, pluginId string, opt ...Option) *HostCatalog {
|
||||
t.Helper()
|
||||
ctx := context.Background()
|
||||
w := db.New(conn)
|
||||
@@ -61,7 +61,7 @@ func TestCatalog(t *testing.T, conn *db.DB, scopeId, pluginId string, opt ...Opt
|
||||
// TestSet creates a plugin host sets in the provided DB
|
||||
// with the provided catalog id. The catalog must have been created
|
||||
// previously. The test will fail if any errors are encountered.
|
||||
func TestSet(t *testing.T, conn *db.DB, kmsCache *kms.Kms, sched *scheduler.Scheduler, hc *HostCatalog, plgm map[string]plgpb.HostPluginServiceClient, opt ...Option) *HostSet {
|
||||
func TestSet(t testing.TB, conn *db.DB, kmsCache *kms.Kms, sched *scheduler.Scheduler, hc *HostCatalog, plgm map[string]plgpb.HostPluginServiceClient, opt ...Option) *HostSet {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
ctx := context.Background()
|
||||
@@ -91,7 +91,7 @@ func TestSet(t *testing.T, conn *db.DB, kmsCache *kms.Kms, sched *scheduler.Sche
|
||||
// TestSetMembers adds hosts to the specified setId in the provided DB.
|
||||
// The set and hosts must have been created previously and belong to the
|
||||
// same catalog. The test will fail if any errors are encountered.
|
||||
func TestSetMembers(t *testing.T, conn *db.DB, setId string, hosts []*Host) []*HostSetMember {
|
||||
func TestSetMembers(t testing.TB, conn *db.DB, setId string, hosts []*Host) []*HostSetMember {
|
||||
t.Helper()
|
||||
assert := assert.New(t)
|
||||
|
||||
@@ -112,7 +112,7 @@ func TestSetMembers(t *testing.T, conn *db.DB, setId string, hosts []*Host) []*H
|
||||
// TestHost creates a plugin host in the provided DB in the catalog with the
|
||||
// provided catalog id. The catalog must have been created previously.
|
||||
// The test will fail if any errors are encountered.
|
||||
func TestHost(t *testing.T, conn *db.DB, catId, externId string, opt ...Option) *Host {
|
||||
func TestHost(t testing.TB, conn *db.DB, catId, externId string, opt ...Option) *Host {
|
||||
t.Helper()
|
||||
w := db.New(conn)
|
||||
ctx := context.Background()
|
||||
@@ -148,7 +148,7 @@ func TestHost(t *testing.T, conn *db.DB, catId, externId string, opt ...Option)
|
||||
return host1
|
||||
}
|
||||
|
||||
func TestExternalHosts(t *testing.T, catalog *HostCatalog, setIds []string, count int) ([]*plgpb.ListHostsResponseHost, []*Host) {
|
||||
func TestExternalHosts(t testing.TB, catalog *HostCatalog, setIds []string, count int) ([]*plgpb.ListHostsResponseHost, []*Host) {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
retRH := make([]*plgpb.ListHostsResponseHost, 0, count)
|
||||
@@ -198,7 +198,7 @@ func TestExternalHosts(t *testing.T, catalog *HostCatalog, setIds []string, coun
|
||||
}
|
||||
|
||||
// TestRunSetSync runs the set sync job a single time.
|
||||
func TestRunSetSync(t *testing.T, conn *db.DB, kmsCache *kms.Kms, plgm map[string]plgpb.HostPluginServiceClient) {
|
||||
func TestRunSetSync(t testing.TB, conn *db.DB, kmsCache *kms.Kms, plgm map[string]plgpb.HostPluginServiceClient) {
|
||||
t.Helper()
|
||||
rw := db.New(conn)
|
||||
ctx := context.Background()
|
||||
@@ -208,13 +208,13 @@ func TestRunSetSync(t *testing.T, conn *db.DB, kmsCache *kms.Kms, plgm map[strin
|
||||
require.NoError(t, j.Run(ctx))
|
||||
}
|
||||
|
||||
func testGetDnsName(t *testing.T) string {
|
||||
func testGetDnsName(t testing.TB) string {
|
||||
dnsName, err := base62.Random(10)
|
||||
require.NoError(t, err)
|
||||
return fmt.Sprintf("%s.example.com", dnsName)
|
||||
}
|
||||
|
||||
func testGetIpAddress(t *testing.T) string {
|
||||
func testGetIpAddress(t testing.TB) string {
|
||||
ipBytes := make([]byte, 4)
|
||||
for {
|
||||
lr := io.LimitReader(rand.Reader, 4)
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
// TestCatalogs creates count number of static host catalogs to the provided DB
|
||||
// with the provided scope id. If any errors are encountered during the creation of
|
||||
// the host catalog, the test will fail.
|
||||
func TestCatalogs(t *testing.T, conn *db.DB, scopeId string, count int) []*HostCatalog {
|
||||
func TestCatalogs(t testing.TB, conn *db.DB, scopeId string, count int) []*HostCatalog {
|
||||
t.Helper()
|
||||
assert := assert.New(t)
|
||||
var cats []*HostCatalog
|
||||
@@ -36,7 +36,7 @@ func TestCatalogs(t *testing.T, conn *db.DB, scopeId string, count int) []*HostC
|
||||
// TestHosts creates count number of static hosts to the provided DB
|
||||
// with the provided catalog id. The catalog must have been created previously.
|
||||
// If any errors are encountered during the creation of the host, the test will fail.
|
||||
func TestHosts(t *testing.T, conn *db.DB, catalogId string, count int) []*Host {
|
||||
func TestHosts(t testing.TB, conn *db.DB, catalogId string, count int) []*Host {
|
||||
t.Helper()
|
||||
assert := assert.New(t)
|
||||
var hosts []*Host
|
||||
@@ -62,7 +62,7 @@ func TestHosts(t *testing.T, conn *db.DB, catalogId string, count int) []*Host {
|
||||
// TestSets creates count number of static host sets in the provided DB
|
||||
// with the provided catalog id. The catalog must have been created
|
||||
// previously. The test will fail if any errors are encountered.
|
||||
func TestSets(t *testing.T, conn *db.DB, catalogId string, count int) []*HostSet {
|
||||
func TestSets(t testing.TB, conn *db.DB, catalogId string, count int) []*HostSet {
|
||||
t.Helper()
|
||||
assert := assert.New(t)
|
||||
var sets []*HostSet
|
||||
@@ -87,7 +87,7 @@ func TestSets(t *testing.T, conn *db.DB, catalogId string, count int) []*HostSet
|
||||
// TestSetMembers adds hosts to the specified setId in the provided DB.
|
||||
// The set and hosts must have been created previously and belong to the
|
||||
// same catalog. The test will fail if any errors are encountered.
|
||||
func TestSetMembers(t *testing.T, conn *db.DB, setId string, hosts []*Host) []*HostSetMember {
|
||||
func TestSetMembers(t testing.TB, conn *db.DB, setId string, hosts []*Host) []*HostSetMember {
|
||||
t.Helper()
|
||||
assert := assert.New(t)
|
||||
|
||||
|
||||
+20
-19
@@ -17,7 +17,8 @@ import (
|
||||
|
||||
// TestRepo creates a repo that can be used for various purposes. Crucially, it
|
||||
// ensures that the global scope contains a valid root key.
|
||||
func TestRepo(t *testing.T, conn *db.DB, rootWrapper wrapping.Wrapper, opt ...Option) *Repository {
|
||||
func TestRepo(t testing.TB, conn *db.DB, rootWrapper wrapping.Wrapper, opt ...Option) *Repository {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
kmsCache := kms.TestKms(t, conn, rootWrapper)
|
||||
@@ -39,7 +40,7 @@ func TestRepo(t *testing.T, conn *db.DB, rootWrapper wrapping.Wrapper, opt ...Op
|
||||
}
|
||||
|
||||
// TestSetPrimaryAuthMethod will set the PrimaryAuthMethodId for a scope.
|
||||
func TestSetPrimaryAuthMethod(t *testing.T, repo *Repository, s *Scope, authMethodId string) {
|
||||
func TestSetPrimaryAuthMethod(t testing.TB, repo *Repository, s *Scope, authMethodId string) {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
require.NotEmpty(s)
|
||||
@@ -54,7 +55,7 @@ func TestSetPrimaryAuthMethod(t *testing.T, repo *Repository, s *Scope, authMeth
|
||||
}
|
||||
|
||||
// TestScopes creates an org and project suitable for testing.
|
||||
func TestScopes(t *testing.T, repo *Repository, opt ...Option) (org *Scope, prj *Scope) {
|
||||
func TestScopes(t testing.TB, repo *Repository, opt ...Option) (org *Scope, prj *Scope) {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
|
||||
@@ -77,7 +78,7 @@ func TestScopes(t *testing.T, repo *Repository, opt ...Option) (org *Scope, prj
|
||||
return
|
||||
}
|
||||
|
||||
func TestOrg(t *testing.T, repo *Repository, opt ...Option) *Scope {
|
||||
func TestOrg(t testing.TB, repo *Repository, opt ...Option) *Scope {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
|
||||
@@ -93,7 +94,7 @@ func TestOrg(t *testing.T, repo *Repository, opt ...Option) *Scope {
|
||||
return org
|
||||
}
|
||||
|
||||
func TestProject(t *testing.T, repo *Repository, orgId string, opt ...Option) *Scope {
|
||||
func TestProject(t testing.TB, repo *Repository, orgId string, opt ...Option) *Scope {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
|
||||
@@ -109,7 +110,7 @@ func TestProject(t *testing.T, repo *Repository, orgId string, opt ...Option) *S
|
||||
return proj
|
||||
}
|
||||
|
||||
func testOrg(t *testing.T, repo *Repository, name, description string) (org *Scope) {
|
||||
func testOrg(t testing.TB, repo *Repository, name, description string) (org *Scope) {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
|
||||
@@ -123,7 +124,7 @@ func testOrg(t *testing.T, repo *Repository, name, description string) (org *Sco
|
||||
return o
|
||||
}
|
||||
|
||||
func testProject(t *testing.T, repo *Repository, orgId string, opt ...Option) *Scope {
|
||||
func testProject(t testing.TB, repo *Repository, orgId string, opt ...Option) *Scope {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
|
||||
@@ -137,14 +138,14 @@ func testProject(t *testing.T, repo *Repository, orgId string, opt ...Option) *S
|
||||
return p
|
||||
}
|
||||
|
||||
func testId(t *testing.T) string {
|
||||
func testId(t testing.TB) string {
|
||||
t.Helper()
|
||||
id, err := uuid.GenerateUUID()
|
||||
require.NoError(t, err)
|
||||
return id
|
||||
}
|
||||
|
||||
func testPublicId(t *testing.T, prefix string) string {
|
||||
func testPublicId(t testing.TB, prefix string) string {
|
||||
t.Helper()
|
||||
publicId, err := db.NewPublicId(prefix)
|
||||
require.NoError(t, err)
|
||||
@@ -153,7 +154,7 @@ func testPublicId(t *testing.T, prefix string) string {
|
||||
|
||||
// TestUser creates a user suitable for testing. Supports the options:
|
||||
// WithName, WithDescription and WithAccountIds.
|
||||
func TestUser(t *testing.T, repo *Repository, scopeId string, opt ...Option) *User {
|
||||
func TestUser(t testing.TB, repo *Repository, scopeId string, opt ...Option) *User {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
|
||||
@@ -171,7 +172,7 @@ func TestUser(t *testing.T, repo *Repository, scopeId string, opt ...Option) *Us
|
||||
}
|
||||
|
||||
// TestRole creates a role suitable for testing.
|
||||
func TestRole(t *testing.T, conn *db.DB, scopeId string, opt ...Option) *Role {
|
||||
func TestRole(t testing.TB, conn *db.DB, scopeId string, opt ...Option) *Role {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
@@ -191,7 +192,7 @@ func TestRole(t *testing.T, conn *db.DB, scopeId string, opt ...Option) *Role {
|
||||
return role
|
||||
}
|
||||
|
||||
func TestRoleGrant(t *testing.T, conn *db.DB, roleId, grant string, opt ...Option) *RoleGrant {
|
||||
func TestRoleGrant(t testing.TB, conn *db.DB, roleId, grant string, opt ...Option) *RoleGrant {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
@@ -204,7 +205,7 @@ func TestRoleGrant(t *testing.T, conn *db.DB, roleId, grant string, opt ...Optio
|
||||
}
|
||||
|
||||
// TestGroup creates a group suitable for testing.
|
||||
func TestGroup(t *testing.T, conn *db.DB, scopeId string, opt ...Option) *Group {
|
||||
func TestGroup(t testing.TB, conn *db.DB, scopeId string, opt ...Option) *Group {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
@@ -220,7 +221,7 @@ func TestGroup(t *testing.T, conn *db.DB, scopeId string, opt ...Option) *Group
|
||||
return grp
|
||||
}
|
||||
|
||||
func TestGroupMember(t *testing.T, conn *db.DB, groupId, userId string, opt ...Option) *GroupMemberUser {
|
||||
func TestGroupMember(t testing.TB, conn *db.DB, groupId, userId string, opt ...Option) *GroupMemberUser {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
@@ -233,7 +234,7 @@ func TestGroupMember(t *testing.T, conn *db.DB, groupId, userId string, opt ...O
|
||||
return gm
|
||||
}
|
||||
|
||||
func TestUserRole(t *testing.T, conn *db.DB, roleId, userId string, opt ...Option) *UserRole {
|
||||
func TestUserRole(t testing.TB, conn *db.DB, roleId, userId string, opt ...Option) *UserRole {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
@@ -245,7 +246,7 @@ func TestUserRole(t *testing.T, conn *db.DB, roleId, userId string, opt ...Optio
|
||||
return r
|
||||
}
|
||||
|
||||
func TestGroupRole(t *testing.T, conn *db.DB, roleId, grpId string, opt ...Option) *GroupRole {
|
||||
func TestGroupRole(t testing.TB, conn *db.DB, roleId, grpId string, opt ...Option) *GroupRole {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
@@ -257,7 +258,7 @@ func TestGroupRole(t *testing.T, conn *db.DB, roleId, grpId string, opt ...Optio
|
||||
return r
|
||||
}
|
||||
|
||||
func TestManagedGroupRole(t *testing.T, conn *db.DB, roleId, managedGrpId string, opt ...Option) *ManagedGroupRole {
|
||||
func TestManagedGroupRole(t testing.TB, conn *db.DB, roleId, managedGrpId string, opt ...Option) *ManagedGroupRole {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
@@ -272,7 +273,7 @@ func TestManagedGroupRole(t *testing.T, conn *db.DB, roleId, managedGrpId string
|
||||
// testAccount is a temporary test function. TODO - replace with an auth
|
||||
// subsystem testAccount function. If userId is zero value, then an auth
|
||||
// account will be created with a null IamUserId
|
||||
func testAccount(t *testing.T, conn *db.DB, scopeId, authMethodId, userId string) *authAccount {
|
||||
func testAccount(t testing.TB, conn *db.DB, scopeId, authMethodId, userId string) *authAccount {
|
||||
const (
|
||||
accountPrefix = "aa_"
|
||||
)
|
||||
@@ -326,7 +327,7 @@ func testAccount(t *testing.T, conn *db.DB, scopeId, authMethodId, userId string
|
||||
|
||||
// testAuthMethod is a temporary test function. TODO - replace with an auth
|
||||
// subsystem testAuthMethod function.
|
||||
func testAuthMethod(t *testing.T, conn *db.DB, scopeId string) string {
|
||||
func testAuthMethod(t testing.TB, conn *db.DB, scopeId string) string {
|
||||
const (
|
||||
authMethodPrefix = "am_"
|
||||
)
|
||||
|
||||
+15
-15
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRootKey(t *testing.T, conn *db.DB, scopeId string) *RootKey {
|
||||
func TestRootKey(t testing.TB, conn *db.DB, scopeId string) *RootKey {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
db.TestDeleteWhere(t, conn, func() interface{} { i := AllocRootKey(); return &i }(), "scope_id = ?", scopeId)
|
||||
@@ -25,7 +25,7 @@ func TestRootKey(t *testing.T, conn *db.DB, scopeId string) *RootKey {
|
||||
return k
|
||||
}
|
||||
|
||||
func TestRootKeyVersion(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, rootId string) (kv *RootKeyVersion, kvWrapper wrapping.Wrapper) {
|
||||
func TestRootKeyVersion(t testing.TB, conn *db.DB, wrapper wrapping.Wrapper, rootId string) (kv *RootKeyVersion, kvWrapper wrapping.Wrapper) {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
@@ -46,7 +46,7 @@ func TestRootKeyVersion(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, roo
|
||||
return k, rootKeyVersionWrapper
|
||||
}
|
||||
|
||||
func TestKms(t *testing.T, conn *db.DB, rootWrapper wrapping.Wrapper) *Kms {
|
||||
func TestKms(t testing.TB, conn *db.DB, rootWrapper wrapping.Wrapper) *Kms {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
@@ -59,7 +59,7 @@ func TestKms(t *testing.T, conn *db.DB, rootWrapper wrapping.Wrapper) *Kms {
|
||||
return kms
|
||||
}
|
||||
|
||||
func TestDatabaseKey(t *testing.T, conn *db.DB, rootKeyId string) *DatabaseKey {
|
||||
func TestDatabaseKey(t testing.TB, conn *db.DB, rootKeyId string) *DatabaseKey {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
@@ -75,7 +75,7 @@ func TestDatabaseKey(t *testing.T, conn *db.DB, rootKeyId string) *DatabaseKey {
|
||||
return k
|
||||
}
|
||||
|
||||
func TestDatabaseKeyVersion(t *testing.T, conn *db.DB, rootKeyVersionWrapper wrapping.Wrapper, databaseKeyId string, key []byte) *DatabaseKeyVersion {
|
||||
func TestDatabaseKeyVersion(t testing.TB, conn *db.DB, rootKeyVersionWrapper wrapping.Wrapper, databaseKeyId string, key []byte) *DatabaseKeyVersion {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
@@ -95,7 +95,7 @@ func TestDatabaseKeyVersion(t *testing.T, conn *db.DB, rootKeyVersionWrapper wra
|
||||
return k
|
||||
}
|
||||
|
||||
func TestOplogKey(t *testing.T, conn *db.DB, rootKeyId string) *OplogKey {
|
||||
func TestOplogKey(t testing.TB, conn *db.DB, rootKeyId string) *OplogKey {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
@@ -111,7 +111,7 @@ func TestOplogKey(t *testing.T, conn *db.DB, rootKeyId string) *OplogKey {
|
||||
return k
|
||||
}
|
||||
|
||||
func TestOplogKeyVersion(t *testing.T, conn *db.DB, rootKeyVersionWrapper wrapping.Wrapper, oplogKeyId string, key []byte) *OplogKeyVersion {
|
||||
func TestOplogKeyVersion(t testing.TB, conn *db.DB, rootKeyVersionWrapper wrapping.Wrapper, oplogKeyId string, key []byte) *OplogKeyVersion {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
@@ -131,7 +131,7 @@ func TestOplogKeyVersion(t *testing.T, conn *db.DB, rootKeyVersionWrapper wrappi
|
||||
return k
|
||||
}
|
||||
|
||||
func TestTokenKey(t *testing.T, conn *db.DB, rootKeyId string) *TokenKey {
|
||||
func TestTokenKey(t testing.TB, conn *db.DB, rootKeyId string) *TokenKey {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
db.TestDeleteWhere(t, conn, func() interface{} { i := AllocTokenKey(); return &i }(), "root_key_id = ?", rootKeyId)
|
||||
@@ -147,7 +147,7 @@ func TestTokenKey(t *testing.T, conn *db.DB, rootKeyId string) *TokenKey {
|
||||
return k
|
||||
}
|
||||
|
||||
func TestTokenKeyVersion(t *testing.T, conn *db.DB, rootKeyVersionWrapper wrapping.Wrapper, tokenKeyId string, key []byte) *TokenKeyVersion {
|
||||
func TestTokenKeyVersion(t testing.TB, conn *db.DB, rootKeyVersionWrapper wrapping.Wrapper, tokenKeyId string, key []byte) *TokenKeyVersion {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
@@ -167,7 +167,7 @@ func TestTokenKeyVersion(t *testing.T, conn *db.DB, rootKeyVersionWrapper wrappi
|
||||
return k
|
||||
}
|
||||
|
||||
func TestSessionKey(t *testing.T, conn *db.DB, rootKeyId string) *SessionKey {
|
||||
func TestSessionKey(t testing.TB, conn *db.DB, rootKeyId string) *SessionKey {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
db.TestDeleteWhere(t, conn, func() interface{} { i := AllocSessionKey(); return &i }(), "root_key_id = ?", rootKeyId)
|
||||
@@ -183,7 +183,7 @@ func TestSessionKey(t *testing.T, conn *db.DB, rootKeyId string) *SessionKey {
|
||||
return k
|
||||
}
|
||||
|
||||
func TestSessionKeyVersion(t *testing.T, conn *db.DB, rootKeyVersionWrapper wrapping.Wrapper, sessionKeyId string, key []byte) *SessionKeyVersion {
|
||||
func TestSessionKeyVersion(t testing.TB, conn *db.DB, rootKeyVersionWrapper wrapping.Wrapper, sessionKeyId string, key []byte) *SessionKeyVersion {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
@@ -203,7 +203,7 @@ func TestSessionKeyVersion(t *testing.T, conn *db.DB, rootKeyVersionWrapper wrap
|
||||
return k
|
||||
}
|
||||
|
||||
func TestOidcKey(t *testing.T, conn *db.DB, rootKeyId string) *OidcKey {
|
||||
func TestOidcKey(t testing.TB, conn *db.DB, rootKeyId string) *OidcKey {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
db.TestDeleteWhere(t, conn, func() interface{} { i := AllocOidcKey(); return &i }(), "root_key_id = ?", rootKeyId)
|
||||
@@ -219,7 +219,7 @@ func TestOidcKey(t *testing.T, conn *db.DB, rootKeyId string) *OidcKey {
|
||||
return k
|
||||
}
|
||||
|
||||
func TestOidcKeyVersion(t *testing.T, conn *db.DB, rootKeyVersionWrapper wrapping.Wrapper, oidcKeyId string, key []byte) *OidcKeyVersion {
|
||||
func TestOidcKeyVersion(t testing.TB, conn *db.DB, rootKeyVersionWrapper wrapping.Wrapper, oidcKeyId string, key []byte) *OidcKeyVersion {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
@@ -239,7 +239,7 @@ func TestOidcKeyVersion(t *testing.T, conn *db.DB, rootKeyVersionWrapper wrappin
|
||||
return k
|
||||
}
|
||||
|
||||
func TestAuditKey(t *testing.T, conn *db.DB, rootKeyId string) *AuditKey {
|
||||
func TestAuditKey(t testing.TB, conn *db.DB, rootKeyId string) *AuditKey {
|
||||
t.Helper()
|
||||
ctx := context.Background()
|
||||
require := require.New(t)
|
||||
@@ -256,7 +256,7 @@ func TestAuditKey(t *testing.T, conn *db.DB, rootKeyId string) *AuditKey {
|
||||
return k
|
||||
}
|
||||
|
||||
func TestAuditKeyVersion(t *testing.T, conn *db.DB, rootKeyVersionWrapper wrapping.Wrapper, auditKeyId string, key []byte) *AuditKeyVersion {
|
||||
func TestAuditKeyVersion(t testing.TB, conn *db.DB, rootKeyVersionWrapper wrapping.Wrapper, auditKeyId string, key []byte) *AuditKeyVersion {
|
||||
t.Helper()
|
||||
ctx := context.Background()
|
||||
require := require.New(t)
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
// TestWrapper initializes an AEAD wrapping.Wrapper for testing
|
||||
func TestWrapper(t *testing.T) wrapping.Wrapper {
|
||||
func TestWrapper(t testing.TB) wrapping.Wrapper {
|
||||
rootKey := make([]byte, 32)
|
||||
n, err := rand.Read(rootKey)
|
||||
if err != nil {
|
||||
|
||||
@@ -15,13 +15,13 @@ import (
|
||||
)
|
||||
|
||||
// TestGetEventerConfig is a test accessor for the eventer's config
|
||||
func TestGetEventerConfig(t *testing.T, e *Eventer) EventerConfig {
|
||||
func TestGetEventerConfig(t testing.TB, e *Eventer) EventerConfig {
|
||||
t.Helper()
|
||||
return e.conf
|
||||
}
|
||||
|
||||
// TestResetSysEventer will reset event.syseventer to an uninitialized state.
|
||||
func TestResetSystEventer(t *testing.T) {
|
||||
func TestResetSystEventer(t testing.TB) {
|
||||
t.Helper()
|
||||
sysEventerLock.Lock()
|
||||
defer sysEventerLock.Unlock()
|
||||
@@ -38,7 +38,7 @@ type TestConfig struct {
|
||||
|
||||
// TestEventerConfig creates a test config and registers a cleanup func for its
|
||||
// test tmp files.
|
||||
func TestEventerConfig(t *testing.T, testName string, opt ...Option) TestConfig {
|
||||
func TestEventerConfig(t testing.TB, testName string, opt ...Option) TestConfig {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
tmpAllFile, err := ioutil.TempFile("./", "tmp-all-events-"+testName)
|
||||
@@ -153,7 +153,7 @@ func TestEventerConfig(t *testing.T, testName string, opt ...Option) TestConfig
|
||||
}
|
||||
|
||||
// TestRequestInfo provides a test RequestInfo
|
||||
func TestRequestInfo(t *testing.T) *RequestInfo {
|
||||
func TestRequestInfo(t testing.TB) *RequestInfo {
|
||||
t.Helper()
|
||||
return &RequestInfo{
|
||||
EventId: "test-event-id",
|
||||
@@ -164,7 +164,7 @@ func TestRequestInfo(t *testing.T) *RequestInfo {
|
||||
}
|
||||
}
|
||||
|
||||
func testAuth(t *testing.T) *Auth {
|
||||
func testAuth(t testing.TB) *Auth {
|
||||
t.Helper()
|
||||
return &Auth{
|
||||
UserEmail: "test-auth@example.com",
|
||||
@@ -172,7 +172,7 @@ func testAuth(t *testing.T) *Auth {
|
||||
}
|
||||
}
|
||||
|
||||
func testRequest(t *testing.T) *Request {
|
||||
func testRequest(t testing.TB) *Request {
|
||||
t.Helper()
|
||||
return &Request{
|
||||
Operation: "op",
|
||||
@@ -183,7 +183,7 @@ func testRequest(t *testing.T) *Request {
|
||||
}
|
||||
}
|
||||
|
||||
func testResponse(t *testing.T) *Response {
|
||||
func testResponse(t testing.TB) *Response {
|
||||
t.Helper()
|
||||
return &Response{
|
||||
StatusCode: 200,
|
||||
@@ -200,7 +200,7 @@ func testResponse(t *testing.T) *Response {
|
||||
}
|
||||
|
||||
// TestWithBroker is an unexported and a test option for passing in an optional broker
|
||||
func TestWithBroker(t *testing.T, b broker) Option {
|
||||
func TestWithBroker(t testing.TB, b broker) Option {
|
||||
t.Helper()
|
||||
return func(o *options) {
|
||||
o.withBroker = b
|
||||
@@ -208,7 +208,7 @@ func TestWithBroker(t *testing.T, b broker) Option {
|
||||
}
|
||||
|
||||
// TestWithObservationSink is a test option
|
||||
func TestWithObservationSink(t *testing.T) Option {
|
||||
func TestWithObservationSink(t testing.TB) Option {
|
||||
t.Helper()
|
||||
return func(o *options) {
|
||||
o.withObservationSink = true
|
||||
@@ -216,7 +216,7 @@ func TestWithObservationSink(t *testing.T) Option {
|
||||
}
|
||||
|
||||
// TestWithAuditSink is a test option
|
||||
func TestWithAuditSink(t *testing.T) Option {
|
||||
func TestWithAuditSink(t testing.TB) Option {
|
||||
t.Helper()
|
||||
return func(o *options) {
|
||||
o.withAuditSink = true
|
||||
@@ -224,7 +224,7 @@ func TestWithAuditSink(t *testing.T) Option {
|
||||
}
|
||||
|
||||
// TestWithSysSink is a test option
|
||||
func TestWithSysSink(t *testing.T) Option {
|
||||
func TestWithSysSink(t testing.TB) Option {
|
||||
t.Helper()
|
||||
return func(o *options) {
|
||||
o.withSysSink = true
|
||||
@@ -232,7 +232,7 @@ func TestWithSysSink(t *testing.T) Option {
|
||||
}
|
||||
|
||||
// testWithSinkFormat is an unexported and a test option
|
||||
func testWithSinkFormat(t *testing.T, fmt SinkFormat) Option {
|
||||
func testWithSinkFormat(t testing.TB, fmt SinkFormat) Option {
|
||||
t.Helper()
|
||||
return func(o *options) {
|
||||
o.withSinkFormat = fmt
|
||||
|
||||
@@ -16,7 +16,7 @@ const (
|
||||
)
|
||||
|
||||
// Init will use gorm migrations to init tables for test models
|
||||
func Init(t *testing.T, db *dbw.DB) {
|
||||
func Init(t testing.TB, db *dbw.DB) {
|
||||
const testQueryCreateTables = `
|
||||
begin;
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
// setup the tests (initialize the database one-time and intialized testDatabaseURL)
|
||||
func setup(t *testing.T) *dbw.DB {
|
||||
func setup(t testing.TB) *dbw.DB {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
cleanup, url, err := testInitDbInDocker(t)
|
||||
@@ -33,7 +33,7 @@ func setup(t *testing.T) *dbw.DB {
|
||||
return db
|
||||
}
|
||||
|
||||
func testOpen(t *testing.T, dbType string, connectionUrl string) *dbw.DB {
|
||||
func testOpen(t testing.TB, dbType string, connectionUrl string) *dbw.DB {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
var dialect dbw.Dialector
|
||||
@@ -52,7 +52,7 @@ func testOpen(t *testing.T, dbType string, connectionUrl string) *dbw.DB {
|
||||
return db
|
||||
}
|
||||
|
||||
func testUser(t *testing.T, db *dbw.DB, name, phoneNumber, email string) *oplog_test.TestUser {
|
||||
func testUser(t testing.TB, db *dbw.DB, name, phoneNumber, email string) *oplog_test.TestUser {
|
||||
t.Helper()
|
||||
u := &oplog_test.TestUser{
|
||||
Name: name,
|
||||
@@ -63,21 +63,21 @@ func testUser(t *testing.T, db *dbw.DB, name, phoneNumber, email string) *oplog_
|
||||
return u
|
||||
}
|
||||
|
||||
func testFindUser(t *testing.T, db *dbw.DB, userId uint32) *oplog_test.TestUser {
|
||||
func testFindUser(t testing.TB, db *dbw.DB, userId uint32) *oplog_test.TestUser {
|
||||
t.Helper()
|
||||
var foundUser oplog_test.TestUser
|
||||
require.NoError(t, dbw.New(db).LookupWhere(context.Background(), &foundUser, "id = ?", []interface{}{userId}))
|
||||
return &foundUser
|
||||
}
|
||||
|
||||
func testId(t *testing.T) string {
|
||||
func testId(t testing.TB) string {
|
||||
t.Helper()
|
||||
id, err := dbw.NewId("i")
|
||||
require.NoError(t, err)
|
||||
return id
|
||||
}
|
||||
|
||||
func testInitDbInDocker(t *testing.T) (cleanup func() error, retURL string, err error) {
|
||||
func testInitDbInDocker(t testing.TB) (cleanup func() error, retURL string, err error) {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
cleanup, retURL, _, err = dbtest.StartUsingTemplate(dbtest.Postgres)
|
||||
@@ -87,7 +87,7 @@ func testInitDbInDocker(t *testing.T) (cleanup func() error, retURL string, err
|
||||
}
|
||||
|
||||
// testWrapper initializes an AEAD wrapping.Wrapper for testing the oplog
|
||||
func testWrapper(t *testing.T) wrapping.Wrapper {
|
||||
func testWrapper(t testing.TB) wrapping.Wrapper {
|
||||
t.Helper()
|
||||
rootKey := make([]byte, 32)
|
||||
n, err := rand.Read(rootKey)
|
||||
@@ -100,7 +100,7 @@ func testWrapper(t *testing.T) wrapping.Wrapper {
|
||||
}
|
||||
|
||||
// testInitStore will execute the migrations needed to initialize the store for tests
|
||||
func testInitStore(t *testing.T, cleanup func() error, url string) {
|
||||
func testInitStore(t testing.TB, cleanup func() error, url string) {
|
||||
t.Helper()
|
||||
ctx := context.Background()
|
||||
dialect := "postgres"
|
||||
@@ -117,7 +117,7 @@ type constraintResults struct {
|
||||
TableName string
|
||||
}
|
||||
|
||||
func testListConstraints(t *testing.T, db *dbw.DB, tableName string) []constraintResults {
|
||||
func testListConstraints(t testing.TB, db *dbw.DB, tableName string) []constraintResults {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
testCtx := context.Background()
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPlugin(t *testing.T, conn *db.DB, name string) *Plugin {
|
||||
func TestPlugin(t testing.TB, conn *db.DB, name string) *Plugin {
|
||||
t.Helper()
|
||||
p := NewPlugin(WithName(name))
|
||||
id, err := newPluginId()
|
||||
|
||||
@@ -41,7 +41,7 @@ func (c *plugin) SetTableName(n string) {
|
||||
c.tableName = n
|
||||
}
|
||||
|
||||
func testPlugin(t *testing.T, conn *db.DB, name string) *plugin {
|
||||
func testPlugin(t testing.TB, conn *db.DB, name string) *plugin {
|
||||
t.Helper()
|
||||
p := newPlugin(name)
|
||||
id, err := db.NewPublicId("plg")
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func testJob(t *testing.T, conn *db.DB, name, description string, wrapper wrapping.Wrapper, opt ...Option) *Job {
|
||||
func testJob(t testing.TB, conn *db.DB, name, description string, wrapper wrapping.Wrapper, opt ...Option) *Job {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
@@ -88,7 +88,7 @@ func testRunWithUpdateTime(conn *db.DB, pluginId, name, cId string, updateTime t
|
||||
return run, nil
|
||||
}
|
||||
|
||||
func testController(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper) *servers.Server {
|
||||
func testController(t testing.TB, conn *db.DB, wrapper wrapping.Wrapper) *servers.Server {
|
||||
t.Helper()
|
||||
rw := db.New(conn)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
//
|
||||
// WithRunJobsLimit, WithRunJobsInterval, WithMonitorInterval and WithInterruptThreshold are
|
||||
// the only valid options.
|
||||
func TestScheduler(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...Option) *Scheduler {
|
||||
func TestScheduler(t testing.TB, conn *db.DB, wrapper wrapping.Wrapper, opt ...Option) *Scheduler {
|
||||
t.Helper()
|
||||
|
||||
rw := db.New(conn)
|
||||
|
||||
@@ -196,7 +196,7 @@ func Verify(ctx context.Context, opt ...Option) (ret VerifyResults) {
|
||||
return
|
||||
}
|
||||
if scp == nil {
|
||||
ret.Error = errors.New(ctx, errors.InvalidParameter, op, fmt.Sprint("non-existent scope $q", ret.Scope.Id))
|
||||
ret.Error = errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("non-existent scope %q", ret.Scope.Id))
|
||||
return
|
||||
}
|
||||
ret.Scope = &scopes.ScopeInfo{
|
||||
|
||||
@@ -2,7 +2,9 @@ package scopeids
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/boundary"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
@@ -15,70 +17,127 @@ import (
|
||||
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
|
||||
)
|
||||
|
||||
// GetListingScopeIds, given common parameters for List calls, returns the set of scope
|
||||
// IDs in which to search for resources. It also returns a memoized map of the
|
||||
// scopes to their info for populating returned values.
|
||||
//
|
||||
// Note: This was originally pulled out 1:1 from the role service. It and other
|
||||
// tests in the other service handlers test this function extensively as it
|
||||
// forms the basis for all recursive listing tests; see those tests for list
|
||||
// functionality in the various service handlers.
|
||||
func GetListingScopeIds(
|
||||
type authzProtectedEntityProvider interface {
|
||||
// Fetches basic resource info for the given scopes. Note that this is a
|
||||
// "where clause" style of argument: if the set of scopes is populated these
|
||||
// are the scopes to limit to (e.g. to put in a where clause). An empty set
|
||||
// of scopes means to look in *all* scopes, not none!
|
||||
FetchAuthzProtectedEntitiesByScope(ctx context.Context, scopeIds []string) (map[string][]boundary.AuthzProtectedEntity, error)
|
||||
}
|
||||
|
||||
// ResourceInfo contains information about a particular resource
|
||||
type ResourceInfo struct {
|
||||
AuthorizedActions action.ActionSet
|
||||
}
|
||||
|
||||
// ScopeInfoWithResourceIds contains information about a scope and the resources
|
||||
// found within it
|
||||
type ScopeInfoWithResourceIds struct {
|
||||
*scopes.ScopeInfo
|
||||
Resources map[string]ResourceInfo
|
||||
}
|
||||
|
||||
// GetListingResourceInformationInput contains input parameters to the function
|
||||
type GetListingResourceInformationInput struct {
|
||||
// An IAM repo function to use for a scope listing call
|
||||
IamRepoFn common.IamRepoFactory
|
||||
|
||||
// The original auth results from the list command
|
||||
AuthResults auth.VerifyResults
|
||||
|
||||
// The scope ID to use, or the starting point for a recursive search
|
||||
RootScopeId string
|
||||
|
||||
// The type of resource being listed
|
||||
Type resource.Type
|
||||
|
||||
// Whether the search is recursive
|
||||
Recursive bool
|
||||
|
||||
// A repo to fetch resources
|
||||
AuthzProtectedEntityProvider authzProtectedEntityProvider
|
||||
|
||||
// The available actions for the resource type
|
||||
ActionSet action.ActionSet
|
||||
}
|
||||
|
||||
// GetListingResourceInformationOutput contains results from the function
|
||||
type GetListingResourceInformationOutput struct {
|
||||
// The calculated list of relevant scope IDs
|
||||
ScopeIds []string
|
||||
|
||||
// The specific resource IDs calculated to be authorized for listing
|
||||
ResourceIds []string
|
||||
|
||||
// A map of scope ID to scope information and a map of resource IDs in that
|
||||
// scope and specific information about that resource, such as available
|
||||
// actions
|
||||
ScopeResourceMap map[string]*ScopeInfoWithResourceIds
|
||||
}
|
||||
|
||||
// GetListingResourceInformation, given common parameters for List calls,
|
||||
// returns useful information: the set of scope IDs in which to search for
|
||||
// resources; the IDs of the resources known to be authorized for that user; and
|
||||
// a memoized map of the scopes to their info for populating returned values.
|
||||
func GetListingResourceInformation(
|
||||
// The context to use when listing in the DB, if required
|
||||
ctx context.Context,
|
||||
// An IAM repo function to use for a listing call, if required
|
||||
repoFn common.IamRepoFactory,
|
||||
// The original auth results from the list command
|
||||
authResults auth.VerifyResults,
|
||||
// The scope ID to use, or to use as the starting point for a recursive
|
||||
// search
|
||||
rootScopeId string,
|
||||
// The type of resource we are listing
|
||||
typ resource.Type,
|
||||
// Whether or not the search should be recursive
|
||||
recursive bool,
|
||||
// Whether to only return scopes with exact permissions, or whether parent
|
||||
// scopes with appropriate permissions are sufficient
|
||||
directOnly bool,
|
||||
) ([]string, map[string]*scopes.ScopeInfo, error) {
|
||||
const op = "GetListingScopeIds"
|
||||
// The input struct
|
||||
input GetListingResourceInformationInput,
|
||||
) (*GetListingResourceInformationOutput, error) {
|
||||
const op = "scopeids.GetListingResourceInformation"
|
||||
|
||||
output := new(GetListingResourceInformationOutput)
|
||||
|
||||
// Validation
|
||||
switch {
|
||||
case typ == resource.Unknown:
|
||||
return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "unknown resource")
|
||||
case repoFn == nil:
|
||||
return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "nil iam repo")
|
||||
case rootScopeId == "":
|
||||
return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing root scope id")
|
||||
case authResults.Scope == nil:
|
||||
return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "nil scope in auth results")
|
||||
}
|
||||
|
||||
// Base case: if not recursive, return the scope we were given and the
|
||||
// already-looked-up info
|
||||
if !recursive {
|
||||
return []string{authResults.Scope.Id}, map[string]*scopes.ScopeInfo{authResults.Scope.Id: authResults.Scope}, nil
|
||||
case input.Type == resource.Unknown:
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "unknown resource")
|
||||
case input.IamRepoFn == nil:
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "nil iam repo")
|
||||
case input.RootScopeId == "":
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing root scope id")
|
||||
case input.AuthResults.Scope == nil:
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "nil scope in auth results")
|
||||
case !input.Recursive && input.AuthResults.Scope.Id != input.RootScopeId:
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "non-recursive search but auth results scope does not match root scope")
|
||||
}
|
||||
|
||||
// This will be used to memoize scope info so we can put the right scope
|
||||
// info for each returned value
|
||||
scopeInfoMap := map[string]*scopes.ScopeInfo{}
|
||||
output.ScopeResourceMap = map[string]*ScopeInfoWithResourceIds{}
|
||||
|
||||
repo, err := repoFn()
|
||||
// Base case: if not recursive, return the scope we were given and the
|
||||
// already-looked-up info
|
||||
if !input.Recursive {
|
||||
output.ScopeResourceMap[input.AuthResults.Scope.Id] = &ScopeInfoWithResourceIds{ScopeInfo: input.AuthResults.Scope}
|
||||
// If we don't have information do to the resource lookup ourselves,
|
||||
// return what we have
|
||||
if input.AuthzProtectedEntityProvider == nil {
|
||||
output.ScopeIds = []string{input.AuthResults.Scope.Id}
|
||||
return output, nil
|
||||
}
|
||||
// Otherwise filter on this one scope and return
|
||||
if err := filterAuthorizedResourceIds(ctx, input, output); err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error filtering to only authorized resources"))
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
|
||||
repo, err := input.IamRepoFn()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
// Get all scopes recursively. Start at global because we need to take into
|
||||
// account permissions in parent scopes even if they want to scale back the
|
||||
// returned information to a child scope and its children.
|
||||
scps, err := repo.ListScopesRecursively(ctx, scope.Global.String())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := perms.Resource{
|
||||
Type: typ,
|
||||
Type: input.Type,
|
||||
}
|
||||
// For each scope, see if we have permission to list that type in that
|
||||
// scope
|
||||
@@ -88,7 +147,7 @@ func GetListingScopeIds(
|
||||
for _, scp := range scps {
|
||||
scpId := scp.GetPublicId()
|
||||
res.ScopeId = scpId
|
||||
aSet := authResults.FetchActionSetForType(ctx,
|
||||
aSet := input.AuthResults.FetchActionSetForType(ctx,
|
||||
// This is overridden by WithResource
|
||||
resource.Unknown,
|
||||
action.ActionSet{action.List},
|
||||
@@ -97,16 +156,14 @@ func GetListingScopeIds(
|
||||
switch len(aSet) {
|
||||
case 0:
|
||||
// Defer until we've read all scopes. We do this because if the
|
||||
// ordering coming back isn't in parent-first ording our map
|
||||
// ordering coming back isn't in parent-first ordering our map
|
||||
// lookup might fail.
|
||||
if !directOnly {
|
||||
deferredScopes = append(deferredScopes, scp)
|
||||
}
|
||||
deferredScopes = append(deferredScopes, scp)
|
||||
case 1:
|
||||
if aSet[0] != action.List {
|
||||
return nil, nil, errors.New(ctx, errors.Internal, op, "unexpected action in set")
|
||||
return nil, errors.New(ctx, errors.Internal, op, "unexpected action in set")
|
||||
}
|
||||
if scopeInfoMap[scpId] == nil {
|
||||
if output.ScopeResourceMap[scpId] == nil {
|
||||
scopeInfo := &scopes.ScopeInfo{
|
||||
Id: scp.GetPublicId(),
|
||||
Type: scp.GetType(),
|
||||
@@ -114,13 +171,13 @@ func GetListingScopeIds(
|
||||
Description: scp.GetDescription(),
|
||||
ParentScopeId: scp.GetParentId(),
|
||||
}
|
||||
scopeInfoMap[scpId] = scopeInfo
|
||||
output.ScopeResourceMap[scpId] = &ScopeInfoWithResourceIds{ScopeInfo: scopeInfo}
|
||||
}
|
||||
if scpId == scope.Global.String() {
|
||||
globalHasList = true
|
||||
}
|
||||
default:
|
||||
return nil, nil, errors.New(ctx, errors.Internal, op, "unexpected number of actions back in set")
|
||||
return nil, errors.New(ctx, errors.Internal, op, "unexpected number of actions back in set")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,9 +186,9 @@ func GetListingScopeIds(
|
||||
// If they had list on global scope anything else is automatically
|
||||
// included; otherwise if they had list on the parent scope, this
|
||||
// scope is included in the map and is sufficient here.
|
||||
if globalHasList || scopeInfoMap[scp.GetParentId()] != nil {
|
||||
if globalHasList || output.ScopeResourceMap[scp.GetParentId()] != nil {
|
||||
scpId := scp.GetPublicId()
|
||||
if scopeInfoMap[scpId] == nil {
|
||||
if output.ScopeResourceMap[scpId] == nil {
|
||||
scopeInfo := &scopes.ScopeInfo{
|
||||
Id: scp.GetPublicId(),
|
||||
Type: scp.GetType(),
|
||||
@@ -139,21 +196,15 @@ func GetListingScopeIds(
|
||||
Description: scp.GetDescription(),
|
||||
ParentScopeId: scp.GetParentId(),
|
||||
}
|
||||
scopeInfoMap[scpId] = scopeInfo
|
||||
output.ScopeResourceMap[scpId] = &ScopeInfoWithResourceIds{ScopeInfo: scopeInfo}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we have nothing in scopeInfoMap at this point, we aren't authorized
|
||||
// anywhere so return 403.
|
||||
if len(scopeInfoMap) == 0 {
|
||||
return nil, nil, handlers.ForbiddenError()
|
||||
}
|
||||
|
||||
// Now elide out any that aren't under the root scope ID
|
||||
elideScopes := make([]string, 0, len(scopeInfoMap))
|
||||
for scpId, scp := range scopeInfoMap {
|
||||
switch rootScopeId {
|
||||
elideScopes := make([]string, 0, len(output.ScopeResourceMap))
|
||||
for scpId, scp := range output.ScopeResourceMap {
|
||||
switch input.RootScopeId {
|
||||
// If the root is global, it matches
|
||||
case scope.Global.String():
|
||||
// If the current scope matches the root, it matches
|
||||
@@ -168,13 +219,137 @@ func GetListingScopeIds(
|
||||
}
|
||||
|
||||
for _, scpId := range elideScopes {
|
||||
delete(scopeInfoMap, scpId)
|
||||
delete(output.ScopeResourceMap, scpId)
|
||||
}
|
||||
|
||||
scopeIds := make([]string, 0, len(scopeInfoMap))
|
||||
for k := range scopeInfoMap {
|
||||
scopeIds = append(scopeIds, k)
|
||||
// If we have nothing in scopeInfoMap at this point, we aren't authorized
|
||||
// anywhere so return 403.
|
||||
if len(output.ScopeResourceMap) == 0 {
|
||||
return nil, handlers.ForbiddenError()
|
||||
}
|
||||
|
||||
return scopeIds, scopeInfoMap, nil
|
||||
if input.AuthzProtectedEntityProvider == nil {
|
||||
output.populateScopeIdsFromScopeResourceMap()
|
||||
return output, nil
|
||||
}
|
||||
|
||||
if err := filterAuthorizedResourceIds(ctx, input, output); err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error filtering to only authorized resources"))
|
||||
}
|
||||
|
||||
return output, nil
|
||||
}
|
||||
|
||||
// filterAuthorizedResourceIds calls the passed in function to get IDs for
|
||||
// resources in the given scopes and then figures out which ones are actually
|
||||
// authorized for listing by the user.
|
||||
//
|
||||
// It also populates the scope IDs in the output
|
||||
func filterAuthorizedResourceIds(
|
||||
// The context to use when listing in the DB, if required
|
||||
ctx context.Context,
|
||||
// The input struct
|
||||
input GetListingResourceInformationInput,
|
||||
// The scope information to fill out
|
||||
output *GetListingResourceInformationOutput,
|
||||
) error {
|
||||
const op = "scopeids.filterAuthorizedResources"
|
||||
|
||||
// Populate scopeIds and determine if we found global
|
||||
output.populateScopeIdsFromScopeResourceMap()
|
||||
|
||||
// The calling function is giving us a complete set with any recursive
|
||||
// lookup already performed (that's the point of the function).
|
||||
scopedResourceInfo, err := input.AuthzProtectedEntityProvider.FetchAuthzProtectedEntitiesByScope(ctx, output.ScopeIds)
|
||||
if err != nil {
|
||||
return errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
res := perms.Resource{
|
||||
Type: input.Type,
|
||||
}
|
||||
|
||||
// Now run authorization checks against each so we know if there is a point
|
||||
// in fetching the full resource, and cache the authorized actions
|
||||
for scopeId, resourceInfos := range scopedResourceInfo {
|
||||
for _, resourceInfo := range resourceInfos {
|
||||
res.Id = resourceInfo.GetPublicId()
|
||||
res.ScopeId = scopeId
|
||||
authorizedActions := input.AuthResults.FetchActionSetForId(ctx, resourceInfo.GetPublicId(), input.ActionSet, auth.WithResource(&res))
|
||||
if len(authorizedActions) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if resourceInfo.GetUserId() != "" {
|
||||
if authorizedActions.OnlySelf() && resourceInfo.GetUserId() != input.AuthResults.UserId {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if output.ScopeResourceMap[scopeId] == nil {
|
||||
return errors.New(ctx, errors.Internal, op, fmt.Sprintf("scope id %s returned from fetching authz protected entities not found in scope resource map", scopeId))
|
||||
}
|
||||
if output.ScopeResourceMap[scopeId].Resources == nil {
|
||||
output.ScopeResourceMap[scopeId].Resources = make(map[string]ResourceInfo)
|
||||
}
|
||||
|
||||
output.ScopeResourceMap[scopeId].Resources[resourceInfo.GetPublicId()] = ResourceInfo{AuthorizedActions: authorizedActions}
|
||||
output.ResourceIds = append(output.ResourceIds, resourceInfo.GetPublicId())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// populateScopeIdsFromScopeResourceMap populates the ScopeIds field and returns
|
||||
// whether global scope was found
|
||||
func (i *GetListingResourceInformationOutput) populateScopeIdsFromScopeResourceMap() {
|
||||
for k := range i.ScopeResourceMap {
|
||||
i.ScopeIds = append(i.ScopeIds, k)
|
||||
}
|
||||
}
|
||||
|
||||
// GetListingScopeIds is provided for backwards compatibility with existing
|
||||
// services; services should eventually migrate to
|
||||
// GetListingResourceInformation.
|
||||
func GetListingScopeIds(
|
||||
// The context to use when listing in the DB, if required
|
||||
ctx context.Context,
|
||||
// An IAM repo function to use for a listing call, if required
|
||||
repoFn common.IamRepoFactory,
|
||||
// The original auth results from the list command
|
||||
authResults auth.VerifyResults,
|
||||
// The scope ID to use, or to use as the starting point for a recursive
|
||||
// search
|
||||
rootScopeId string,
|
||||
// The type of resource we are listing
|
||||
typ resource.Type,
|
||||
// Whether or not the search should be recursive
|
||||
recursive bool,
|
||||
) ([]string, map[string]*scopes.ScopeInfo, error) {
|
||||
const op = "scopeids.GetListingScopeIds"
|
||||
scopeResourceInfo, err := GetListingResourceInformation(ctx,
|
||||
GetListingResourceInformationInput{
|
||||
IamRepoFn: repoFn,
|
||||
AuthResults: authResults,
|
||||
RootScopeId: rootScopeId,
|
||||
Type: typ,
|
||||
Recursive: recursive,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if err == handlers.ForbiddenError() {
|
||||
return nil, nil, err
|
||||
}
|
||||
return nil, nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
if len(scopeResourceInfo.ScopeIds) == 0 {
|
||||
// This should have already happened in the other function, but...
|
||||
return nil, nil, handlers.ForbiddenError()
|
||||
}
|
||||
scopeMap := make(map[string]*scopes.ScopeInfo, len(scopeResourceInfo.ScopeResourceMap))
|
||||
for k, v := range scopeResourceInfo.ScopeResourceMap {
|
||||
scopeMap[k] = v.ScopeInfo
|
||||
}
|
||||
return scopeResourceInfo.ScopeIds, scopeMap, nil
|
||||
}
|
||||
@@ -130,7 +130,7 @@ func (s Service) ListAuthMethods(ctx context.Context, req *pbs.ListAuthMethodsRe
|
||||
}
|
||||
|
||||
scopeIds, scopeInfoMap, err := scopeids.GetListingScopeIds(
|
||||
ctx, s.iamRepoFn, authResults, req.GetScopeId(), resource.AuthMethod, req.GetRecursive(), false)
|
||||
ctx, s.iamRepoFn, authResults, req.GetScopeId(), resource.AuthMethod, req.GetRecursive())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ func (s Service) ListAuthTokens(ctx context.Context, req *pbs.ListAuthTokensRequ
|
||||
}
|
||||
|
||||
scopeIds, scopeInfoMap, err := scopeids.GetListingScopeIds(
|
||||
ctx, s.iamRepoFn, authResults, req.GetScopeId(), resource.AuthToken, req.GetRecursive(), false)
|
||||
ctx, s.iamRepoFn, authResults, req.GetScopeId(), resource.AuthToken, req.GetRecursive())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ func (s Service) ListCredentialStores(ctx context.Context, req *pbs.ListCredenti
|
||||
}
|
||||
|
||||
scopeIds, scopeInfoMap, err := scopeids.GetListingScopeIds(
|
||||
ctx, s.iamRepoFn, authResults, req.GetScopeId(), resource.CredentialStore, req.GetRecursive(), false)
|
||||
ctx, s.iamRepoFn, authResults, req.GetScopeId(), resource.CredentialStore, req.GetRecursive())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ func (s Service) ListGroups(ctx context.Context, req *pbs.ListGroupsRequest) (*p
|
||||
}
|
||||
|
||||
scopeIds, scopeInfoMap, err := scopeids.GetListingScopeIds(
|
||||
ctx, s.repoFn, authResults, req.GetScopeId(), resource.Group, req.GetRecursive(), false)
|
||||
ctx, s.repoFn, authResults, req.GetScopeId(), resource.Group, req.GetRecursive())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ func (s Service) ListHostCatalogs(ctx context.Context, req *pbs.ListHostCatalogs
|
||||
}
|
||||
|
||||
scopeIds, scopeInfoMap, err := scopeids.GetListingScopeIds(
|
||||
ctx, s.iamRepoFn, authResults, req.GetScopeId(), resource.HostCatalog, req.GetRecursive(), false)
|
||||
ctx, s.iamRepoFn, authResults, req.GetScopeId(), resource.HostCatalog, req.GetRecursive())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ func (s Service) ListRoles(ctx context.Context, req *pbs.ListRolesRequest) (*pbs
|
||||
}
|
||||
|
||||
scopeIds, scopeInfoMap, err := scopeids.GetListingScopeIds(
|
||||
ctx, s.repoFn, authResults, req.GetScopeId(), resource.Role, req.GetRecursive(), false)
|
||||
ctx, s.repoFn, authResults, req.GetScopeId(), resource.Role, req.GetRecursive())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ func (s Service) ListScopes(ctx context.Context, req *pbs.ListScopesRequest) (*p
|
||||
}
|
||||
|
||||
scopeIds, scopeInfoMap, err := scopeids.GetListingScopeIds(
|
||||
ctx, s.repoFn, authResults, req.GetScopeId(), resource.Scope, req.GetRecursive(), false)
|
||||
ctx, s.repoFn, authResults, req.GetScopeId(), resource.Scope, req.GetRecursive())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
package sessions_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/auth/password"
|
||||
"github.com/hashicorp/boundary/internal/authtoken"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
||||
authpb "github.com/hashicorp/boundary/internal/gen/controller/auth"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/servers"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/auth"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers/sessions"
|
||||
"github.com/hashicorp/boundary/internal/session"
|
||||
"github.com/hashicorp/boundary/internal/types/scope"
|
||||
"github.com/hashicorp/boundary/testing/dbtest"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type template struct {
|
||||
name string
|
||||
sessions int
|
||||
connsPerSession int
|
||||
users int
|
||||
}
|
||||
|
||||
func BenchmarkSessionList(b *testing.B) {
|
||||
// See the explanation in testing/dbtest/session_list_benchmarks_dump_generation_test.go for
|
||||
// an overview of the assumptions made when creating these template scenarios.
|
||||
for _, template := range []template{
|
||||
{
|
||||
name: dbtest.Boundary1000Sessions10ConnsPerSession10Template,
|
||||
sessions: 1000,
|
||||
connsPerSession: 10,
|
||||
users: 10,
|
||||
},
|
||||
{
|
||||
name: dbtest.Boundary1000Sessions10ConnsPerSession25Template,
|
||||
sessions: 1000,
|
||||
connsPerSession: 10,
|
||||
users: 25,
|
||||
},
|
||||
{
|
||||
name: dbtest.Boundary1000Sessions10ConnsPerSession50Template,
|
||||
sessions: 1000,
|
||||
connsPerSession: 10,
|
||||
users: 50,
|
||||
},
|
||||
{
|
||||
name: dbtest.Boundary1000Sessions10ConnsPerSession75Template,
|
||||
sessions: 1000,
|
||||
connsPerSession: 10,
|
||||
users: 75,
|
||||
},
|
||||
{
|
||||
name: dbtest.Boundary1000Sessions10ConnsPerSession100Template,
|
||||
sessions: 1000,
|
||||
connsPerSession: 10,
|
||||
users: 100,
|
||||
},
|
||||
{
|
||||
name: dbtest.Boundary1000Sessions10ConnsPerSession500Template,
|
||||
sessions: 1000,
|
||||
connsPerSession: 10,
|
||||
users: 500,
|
||||
},
|
||||
} {
|
||||
b.Run(fmt.Sprintf("%d_sessions_%d_conns_per_session_%d_users", template.sessions, template.connsPerSession, template.users), func(b *testing.B) {
|
||||
ctx := context.Background()
|
||||
conn, _ := db.TestSetup(b, "postgres", db.WithTemplate(template.name))
|
||||
rw := db.New(conn)
|
||||
kmsRepo, err := kms.NewRepository(rw, rw)
|
||||
require.NoError(b, err)
|
||||
kmsThing, err := kms.NewKms(kmsRepo)
|
||||
require.NoError(b, err)
|
||||
wrap, err := dbtest.GetBoundaryBenchmarksRootKeyWrapper(ctx)
|
||||
require.NoError(b, err)
|
||||
err = kmsThing.AddExternalWrappers(ctx, kms.WithRootWrapper(wrap))
|
||||
require.NoError(b, err)
|
||||
|
||||
iamRepo, err := iam.NewRepository(rw, rw, kmsThing)
|
||||
require.NoError(b, err)
|
||||
|
||||
sessRepo, err := session.NewRepository(rw, rw, kmsThing)
|
||||
require.NoError(b, err)
|
||||
|
||||
authTokenRepo, err := authtoken.NewRepository(rw, rw, kmsThing)
|
||||
require.NoError(b, err)
|
||||
|
||||
pwRepo, err := password.NewRepository(rw, rw, kmsThing)
|
||||
require.NoError(b, err)
|
||||
|
||||
serversRepo, err := servers.NewRepository(rw, rw, kmsThing)
|
||||
require.NoError(b, err)
|
||||
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
sessRepoFn := func() (*session.Repository, error) {
|
||||
return sessRepo, nil
|
||||
}
|
||||
authTokenRepoFn := func() (*authtoken.Repository, error) {
|
||||
return authTokenRepo, nil
|
||||
}
|
||||
serversRepoFn := func() (*servers.Repository, error) {
|
||||
return serversRepo, nil
|
||||
}
|
||||
|
||||
s, err := sessions.NewService(sessRepoFn, iamRepoFn)
|
||||
require.NoError(b, err)
|
||||
|
||||
var users []*userWithToken
|
||||
rows, err := rw.Query(ctx, "select public_id from iam_user where name like 'user%'", nil)
|
||||
require.NoError(b, err)
|
||||
var userId string
|
||||
for rows.Next() {
|
||||
err = rows.Scan(&userId)
|
||||
require.NoError(b, err)
|
||||
require.NotEmpty(b, userId)
|
||||
u, accountIds, err := iamRepo.LookupUser(ctx, userId)
|
||||
require.NoError(b, err)
|
||||
require.NotEmpty(b, accountIds)
|
||||
account, err := pwRepo.LookupAccount(ctx, accountIds[0])
|
||||
require.NoError(b, err)
|
||||
require.NotNil(b, account)
|
||||
acct, err := pwRepo.Authenticate(ctx, u.ScopeId, account.AuthMethodId, u.Name, dbtest.BoundaryBenchmarksUserPassword)
|
||||
require.NoError(b, err)
|
||||
require.NotNil(b, acct)
|
||||
tok, err := authTokenRepo.CreateAuthToken(ctx, u, acct.GetPublicId())
|
||||
require.NoError(b, err)
|
||||
require.NotEmpty(b, tok)
|
||||
tokString, err := authtoken.EncryptToken(ctx, kmsThing, u.ScopeId, tok.PublicId, tok.Token)
|
||||
require.NoError(b, err)
|
||||
req := &pbs.ListSessionsRequest{
|
||||
ScopeId: scope.Global.String(),
|
||||
Recursive: true,
|
||||
Filter: fmt.Sprintf(`"/item/user_id"==%q`, u.PublicId),
|
||||
}
|
||||
ctx := auth.NewVerifierContext(
|
||||
ctx,
|
||||
iamRepoFn,
|
||||
authTokenRepoFn,
|
||||
serversRepoFn,
|
||||
kmsThing,
|
||||
&authpb.RequestInfo{
|
||||
PublicId: tok.PublicId,
|
||||
EncryptedToken: tokString,
|
||||
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
||||
})
|
||||
users = append(users, &userWithToken{
|
||||
User: u,
|
||||
req: req,
|
||||
ctx: ctx,
|
||||
})
|
||||
}
|
||||
require.NoError(b, rows.Close())
|
||||
require.NoError(b, rows.Err())
|
||||
require.Len(b, users, template.users)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
eg := &errgroup.Group{}
|
||||
for i := range users {
|
||||
user := users[i]
|
||||
eg.Go(func() error {
|
||||
_, err := s.ListSessions(user.ctx, user.req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("list failed: %s", err.Error())
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
require.NoError(b, eg.Wait())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type userWithToken struct {
|
||||
*iam.User
|
||||
req *pbs.ListSessionsRequest
|
||||
ctx context.Context
|
||||
}
|
||||
@@ -119,6 +119,8 @@ func (s Service) GetSession(ctx context.Context, req *pbs.GetSessionRequest) (*p
|
||||
|
||||
// ListSessions implements the interface pbs.SessionServiceServer.
|
||||
func (s Service) ListSessions(ctx context.Context, req *pbs.ListSessionsRequest) (*pbs.ListSessionsResponse, error) {
|
||||
const op = "session.(Service).ListSessions"
|
||||
|
||||
if err := validateListRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -137,17 +139,34 @@ func (s Service) ListSessions(ctx context.Context, req *pbs.ListSessionsRequest)
|
||||
}
|
||||
}
|
||||
|
||||
scopeIds, scopeInfoMap, err := scopeids.GetListingScopeIds(ctx,
|
||||
s.iamRepoFn, authResults, req.GetScopeId(), resource.Session, req.GetRecursive(), false)
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
scopeResourceInfo, err := scopeids.GetListingResourceInformation(
|
||||
ctx,
|
||||
scopeids.GetListingResourceInformationInput{
|
||||
IamRepoFn: s.iamRepoFn,
|
||||
AuthResults: authResults,
|
||||
RootScopeId: req.GetScopeId(),
|
||||
Type: resource.Session,
|
||||
Recursive: req.GetRecursive(),
|
||||
AuthzProtectedEntityProvider: repo,
|
||||
ActionSet: IdActions,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// If no scopes match, return an empty response
|
||||
if len(scopeIds) == 0 {
|
||||
// If no scopes match or we match scopes but there are no resources in them
|
||||
// that we are authorized to see, return an empty response
|
||||
if len(scopeResourceInfo.ScopeIds) == 0 ||
|
||||
len(scopeResourceInfo.ResourceIds) == 0 {
|
||||
return &pbs.ListSessionsResponse{}, nil
|
||||
}
|
||||
|
||||
sesList, err := s.listFromRepo(ctx, scopeIds)
|
||||
sesList, err := s.listFromRepo(ctx, scopeResourceInfo.ResourceIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -164,25 +183,14 @@ func (s Service) ListSessions(ctx context.Context, req *pbs.ListSessionsRequest)
|
||||
Type: resource.Session,
|
||||
}
|
||||
for _, item := range sesList {
|
||||
res.Id = item.GetPublicId()
|
||||
res.ScopeId = item.ScopeId
|
||||
authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions, auth.WithResource(&res))
|
||||
if len(authorizedActions) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if authorizedActions.OnlySelf() && item.UserId != authResults.UserId {
|
||||
continue
|
||||
}
|
||||
|
||||
outputFields := authResults.FetchOutputFields(res, action.List).SelfOrDefaults(authResults.UserId)
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(scopeInfoMap[item.ScopeId]))
|
||||
outputOpts = append(outputOpts, handlers.WithScope(scopeResourceInfo.ScopeResourceMap[item.ScopeId].ScopeInfo))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions.Strings()))
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(scopeResourceInfo.ScopeResourceMap[item.ScopeId].Resources[item.PublicId].AuthorizedActions.Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, item, outputOpts...)
|
||||
@@ -287,12 +295,12 @@ func (s Service) getFromRepo(ctx context.Context, id string) (*session.Session,
|
||||
return sess, nil
|
||||
}
|
||||
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*session.Session, error) {
|
||||
func (s Service) listFromRepo(ctx context.Context, sessionIds []string) ([]*session.Session, error) {
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sesList, err := repo.ListSessions(ctx, session.WithScopeIds(scopeIds))
|
||||
sesList, err := repo.ListSessions(ctx, session.WithSessionIds(sessionIds...))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -146,6 +146,8 @@ var _ pbs.TargetServiceServer = Service{}
|
||||
|
||||
// ListTargets implements the interface pbs.TargetServiceServer.
|
||||
func (s Service) ListTargets(ctx context.Context, req *pbs.ListTargetsRequest) (*pbs.ListTargetsResponse, error) {
|
||||
const op = "targets.(Service).ListSessions"
|
||||
|
||||
if err := validateListRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -163,17 +165,34 @@ func (s Service) ListTargets(ctx context.Context, req *pbs.ListTargetsRequest) (
|
||||
}
|
||||
}
|
||||
|
||||
scopeIds, scopeInfoMap, err := scopeids.GetListingScopeIds(
|
||||
ctx, s.iamRepoFn, authResults, req.GetScopeId(), resource.Target, req.GetRecursive(), false)
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
scopeResourceInfo, err := scopeids.GetListingResourceInformation(
|
||||
ctx,
|
||||
scopeids.GetListingResourceInformationInput{
|
||||
IamRepoFn: s.iamRepoFn,
|
||||
AuthResults: authResults,
|
||||
RootScopeId: req.GetScopeId(),
|
||||
Type: resource.Target,
|
||||
Recursive: req.GetRecursive(),
|
||||
AuthzProtectedEntityProvider: repo,
|
||||
ActionSet: IdActions,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If no scopes match, return an empty response
|
||||
if len(scopeIds) == 0 {
|
||||
if len(scopeResourceInfo.ScopeIds) == 0 ||
|
||||
len(scopeResourceInfo.ResourceIds) == 0 {
|
||||
return &pbs.ListTargetsResponse{}, nil
|
||||
}
|
||||
|
||||
tl, err := s.listFromRepo(ctx, scopeIds)
|
||||
tl, err := s.listFromRepo(ctx, scopeResourceInfo.ResourceIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -190,21 +209,14 @@ func (s Service) ListTargets(ctx context.Context, req *pbs.ListTargetsRequest) (
|
||||
Type: resource.Target,
|
||||
}
|
||||
for _, item := range tl {
|
||||
res.Id = item.GetPublicId()
|
||||
res.ScopeId = item.GetScopeId()
|
||||
authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions, auth.WithResource(&res)).Strings()
|
||||
if len(authorizedActions) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
outputFields := authResults.FetchOutputFields(res, action.List).SelfOrDefaults(authResults.UserId)
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(scopeInfoMap[item.GetScopeId()]))
|
||||
outputOpts = append(outputOpts, handlers.WithScope(scopeResourceInfo.ScopeResourceMap[item.GetScopeId()].ScopeInfo))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions))
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(scopeResourceInfo.ScopeResourceMap[item.GetScopeId()].Resources[item.GetPublicId()].AuthorizedActions.Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, item, nil, nil, outputOpts...)
|
||||
@@ -1346,12 +1358,12 @@ func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) {
|
||||
return rows > 0, nil
|
||||
}
|
||||
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]target.Target, error) {
|
||||
func (s Service) listFromRepo(ctx context.Context, targetIds []string) ([]target.Target, error) {
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ul, err := repo.ListTargets(ctx, target.WithScopeIds(scopeIds))
|
||||
ul, err := repo.ListTargets(ctx, target.WithTargetIds(targetIds))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ func (s Service) ListUsers(ctx context.Context, req *pbs.ListUsersRequest) (*pbs
|
||||
}
|
||||
|
||||
scopeIds, scopeInfoMap, err := scopeids.GetListingScopeIds(
|
||||
ctx, s.repoFn, authResults, req.GetScopeId(), resource.User, req.GetRecursive(), false)
|
||||
ctx, s.repoFn, authResults, req.GetScopeId(), resource.User, req.GetRecursive())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ const (
|
||||
type TestController struct {
|
||||
b *base.Server
|
||||
c *Controller
|
||||
t *testing.T
|
||||
t testing.TB
|
||||
apiAddrs []string // The address the Controller API is listening on
|
||||
clusterAddrs []string
|
||||
client *api.Client
|
||||
@@ -418,7 +418,7 @@ type TestControllerOpts struct {
|
||||
SchedulerRunJobInterval time.Duration
|
||||
}
|
||||
|
||||
func NewTestController(t *testing.T, opts *TestControllerOpts) *TestController {
|
||||
func NewTestController(t testing.TB, opts *TestControllerOpts) *TestController {
|
||||
const op = "controller.NewTestController"
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
@@ -455,7 +455,7 @@ func NewTestController(t *testing.T, opts *TestControllerOpts) *TestController {
|
||||
|
||||
// TestControllerConfig provides a way to create a config for a TestController.
|
||||
// The tc passed as a parameter will be modified by this func.
|
||||
func TestControllerConfig(t *testing.T, ctx context.Context, tc *TestController, opts *TestControllerOpts) *Config {
|
||||
func TestControllerConfig(t testing.TB, ctx context.Context, tc *TestController, opts *TestControllerOpts) *Config {
|
||||
const op = "controller.TestControllerConfig"
|
||||
if opts == nil {
|
||||
opts = new(TestControllerOpts)
|
||||
@@ -667,7 +667,7 @@ func TestControllerConfig(t *testing.T, ctx context.Context, tc *TestController,
|
||||
}
|
||||
}
|
||||
|
||||
func (tc *TestController) AddClusterControllerMember(t *testing.T, opts *TestControllerOpts) *TestController {
|
||||
func (tc *TestController) AddClusterControllerMember(t testing.TB, opts *TestControllerOpts) *TestController {
|
||||
const op = "controller.(TestController).AddClusterControllerMember"
|
||||
if opts == nil {
|
||||
opts = new(TestControllerOpts)
|
||||
@@ -778,7 +778,7 @@ func (tc *TestController) WaitForNextWorkerStatusUpdate(workerId string) error {
|
||||
// test greeter service wrapped with the specified interceptors and return an
|
||||
// initialized client for the service. The test service will be stopped/cleaned
|
||||
// up after the test (or subtests) have completed.
|
||||
func startTestGreeterService(t *testing.T, greeter interceptor.GreeterServiceServer, interceptors ...grpc.UnaryServerInterceptor) interceptor.GreeterServiceClient {
|
||||
func startTestGreeterService(t testing.TB, greeter interceptor.GreeterServiceServer, interceptors ...grpc.UnaryServerInterceptor) interceptor.GreeterServiceClient {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
dialCtx := context.Background()
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
// created during Boundary connect. The proxyConn returned should be used as the connection
|
||||
// passed into the worker proxy handler, while the clientConn can be used to simulate
|
||||
// the local end user connection.
|
||||
func TestWsConn(t *testing.T, ctx context.Context) (clientConn, proxyConn *websocket.Conn) {
|
||||
func TestWsConn(t testing.TB, ctx context.Context) (clientConn, proxyConn *websocket.Conn) {
|
||||
t.Helper()
|
||||
require, assert := require.New(t), assert.New(t)
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
type TestWorker struct {
|
||||
b *base.Server
|
||||
w *Worker
|
||||
t *testing.T
|
||||
t testing.TB
|
||||
addrs []string // The address the worker proxies are listening on
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
@@ -193,7 +193,7 @@ type TestWorkerOpts struct {
|
||||
NonceFn randFn
|
||||
}
|
||||
|
||||
func NewTestWorker(t *testing.T, opts *TestWorkerOpts) *TestWorker {
|
||||
func NewTestWorker(t testing.TB, opts *TestWorkerOpts) *TestWorker {
|
||||
const op = "worker.NewTestWorker"
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
@@ -302,7 +302,7 @@ func NewTestWorker(t *testing.T, opts *TestWorkerOpts) *TestWorker {
|
||||
return tw
|
||||
}
|
||||
|
||||
func (tw *TestWorker) AddClusterWorkerMember(t *testing.T, opts *TestWorkerOpts) *TestWorker {
|
||||
func (tw *TestWorker) AddClusterWorkerMember(t testing.TB, opts *TestWorkerOpts) *TestWorker {
|
||||
const op = "worker.(TestWorker).AddClusterWorkerMember"
|
||||
if opts == nil {
|
||||
opts = new(TestWorkerOpts)
|
||||
|
||||
@@ -118,14 +118,30 @@ select expiration_time, connection_limit, current_connection_count
|
||||
from
|
||||
session_connection_limit, session_connection_count;
|
||||
`
|
||||
|
||||
sessionPublicIdList = `
|
||||
select public_id, scope_id, user_id from session
|
||||
%s
|
||||
;
|
||||
`
|
||||
|
||||
sessionList = `
|
||||
select *
|
||||
from
|
||||
(select public_id from session %s) s,
|
||||
session_list ss
|
||||
where
|
||||
s.public_id = ss.public_id
|
||||
with
|
||||
session_ids as (
|
||||
select public_id
|
||||
from session as s
|
||||
-- where clause is constructed
|
||||
%s
|
||||
-- order by clause is constructed
|
||||
%s
|
||||
-- limit is constructed
|
||||
%s
|
||||
)
|
||||
select *
|
||||
from session_list
|
||||
where
|
||||
session_list.public_id in (select * from session_ids)
|
||||
-- order by clause again since order from cte is not guaranteed to be preserved
|
||||
%s
|
||||
;
|
||||
`
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/boundary"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/db/timestamp"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
@@ -205,6 +206,51 @@ func (r *Repository) LookupSession(ctx context.Context, sessionId string, _ ...O
|
||||
return &session, authzSummary, nil
|
||||
}
|
||||
|
||||
// FetchAuthzProtectedEntitiesByScope implements boundary.AuthzProtectedEntityProvider
|
||||
func (r *Repository) FetchAuthzProtectedEntitiesByScope(ctx context.Context, scopeIds []string) (map[string][]boundary.AuthzProtectedEntity, error) {
|
||||
const op = "session.(Repository).FetchAuthzProtectedEntityInfo"
|
||||
|
||||
var where string
|
||||
var args []interface{}
|
||||
|
||||
inClauseCnt := 0
|
||||
|
||||
switch len(scopeIds) {
|
||||
case 0:
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "no scopes given")
|
||||
case 1:
|
||||
inClauseCnt += 1
|
||||
where, args = fmt.Sprintf("where scope_id = @%d", inClauseCnt), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), scopeIds[0]))
|
||||
default:
|
||||
idsInClause := make([]string, 0, len(scopeIds))
|
||||
for _, id := range scopeIds {
|
||||
inClauseCnt += 1
|
||||
idsInClause, args = append(idsInClause, fmt.Sprintf("@%d", inClauseCnt)), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), id))
|
||||
}
|
||||
where = fmt.Sprintf("where scope_id in (%s)", strings.Join(idsInClause, ","))
|
||||
}
|
||||
|
||||
q := sessionPublicIdList
|
||||
query := fmt.Sprintf(q, where)
|
||||
|
||||
rows, err := r.reader.Query(ctx, query, args)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
sessionsMap := map[string][]boundary.AuthzProtectedEntity{}
|
||||
for rows.Next() {
|
||||
var ses Session
|
||||
if err := r.reader.ScanRows(ctx, rows, &ses); err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("scan row failed"))
|
||||
}
|
||||
sessionsMap[ses.GetScopeId()] = append(sessionsMap[ses.GetScopeId()], ses)
|
||||
}
|
||||
|
||||
return sessionsMap, nil
|
||||
}
|
||||
|
||||
// ListSessions will sessions. Supports the WithLimit, WithScopeId, WithSessionIds, and WithServerId options.
|
||||
func (r *Repository) ListSessions(ctx context.Context, opt ...Option) ([]*Session, error) {
|
||||
const op = "session.(Repository).ListSessions"
|
||||
@@ -213,25 +259,31 @@ func (r *Repository) ListSessions(ctx context.Context, opt ...Option) ([]*Sessio
|
||||
var args []interface{}
|
||||
|
||||
inClauseCnt := 0
|
||||
if len(opts.withScopeIds) != 0 {
|
||||
switch len(opts.withScopeIds) {
|
||||
case 1:
|
||||
switch len(opts.withScopeIds) {
|
||||
case 0:
|
||||
case 1:
|
||||
inClauseCnt += 1
|
||||
where, args = append(where, fmt.Sprintf("scope_id = @%d", inClauseCnt)), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), opts.withScopeIds[0]))
|
||||
default:
|
||||
idsInClause := make([]string, 0, len(opts.withScopeIds))
|
||||
for _, id := range opts.withScopeIds {
|
||||
inClauseCnt += 1
|
||||
where, args = append(where, fmt.Sprintf("scope_id = @%d", inClauseCnt)), append(args, sql.Named("1", opts.withScopeIds[0]))
|
||||
default:
|
||||
idsInClause := make([]string, 0, len(opts.withScopeIds))
|
||||
for _, id := range opts.withScopeIds {
|
||||
inClauseCnt += 1
|
||||
idsInClause, args = append(idsInClause, fmt.Sprintf("@%d", inClauseCnt)), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), id))
|
||||
}
|
||||
where = append(where, fmt.Sprintf("scope_id in (%s)", strings.Join(idsInClause, ",")))
|
||||
idsInClause, args = append(idsInClause, fmt.Sprintf("@%d", inClauseCnt)), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), id))
|
||||
}
|
||||
where = append(where, fmt.Sprintf("scope_id in (%s)", strings.Join(idsInClause, ",")))
|
||||
}
|
||||
|
||||
if opts.withUserId != "" {
|
||||
inClauseCnt += 1
|
||||
where, args = append(where, fmt.Sprintf("user_id = @%d", inClauseCnt)), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), opts.withUserId))
|
||||
}
|
||||
if len(opts.withSessionIds) > 0 {
|
||||
|
||||
switch len(opts.withSessionIds) {
|
||||
case 0:
|
||||
case 1:
|
||||
inClauseCnt += 1
|
||||
where, args = append(where, fmt.Sprintf("s.public_id = @%d", inClauseCnt)), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), opts.withSessionIds[0]))
|
||||
default:
|
||||
idsInClause := make([]string, 0, len(opts.withSessionIds))
|
||||
for _, id := range opts.withSessionIds {
|
||||
inClauseCnt += 1
|
||||
@@ -239,6 +291,7 @@ func (r *Repository) ListSessions(ctx context.Context, opt ...Option) ([]*Sessio
|
||||
}
|
||||
where = append(where, fmt.Sprintf("s.public_id in (%s)", strings.Join(idsInClause, ",")))
|
||||
}
|
||||
|
||||
if opts.withServerId != "" {
|
||||
inClauseCnt += 1
|
||||
where, args = append(where, fmt.Sprintf("server_id = @%d", inClauseCnt)), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), opts.withServerId))
|
||||
@@ -266,10 +319,10 @@ func (r *Repository) ListSessions(ctx context.Context, opt ...Option) ([]*Sessio
|
||||
|
||||
var whereClause string
|
||||
if len(where) > 0 {
|
||||
whereClause = " and " + strings.Join(where, " and ")
|
||||
whereClause = " where " + strings.Join(where, " and ")
|
||||
}
|
||||
q := sessionList
|
||||
query := fmt.Sprintf(q, limit, whereClause, withOrder)
|
||||
query := fmt.Sprintf(q, whereClause, withOrder, limit, withOrder)
|
||||
|
||||
rows, err := r.reader.Query(ctx, query, args)
|
||||
if err != nil {
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/boundary"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/db/timestamp"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
@@ -119,13 +120,22 @@ type Session struct {
|
||||
tableName string `gorm:"-"`
|
||||
}
|
||||
|
||||
func (s *Session) GetPublicId() string {
|
||||
func (s Session) GetPublicId() string {
|
||||
return s.PublicId
|
||||
}
|
||||
|
||||
func (s Session) GetScopeId() string {
|
||||
return s.ScopeId
|
||||
}
|
||||
|
||||
func (s Session) GetUserId() string {
|
||||
return s.UserId
|
||||
}
|
||||
|
||||
var (
|
||||
_ Cloneable = (*Session)(nil)
|
||||
_ db.VetForWriter = (*Session)(nil)
|
||||
_ Cloneable = (*Session)(nil)
|
||||
_ db.VetForWriter = (*Session)(nil)
|
||||
_ boundary.AuthzProtectedEntity = (*Session)(nil)
|
||||
)
|
||||
|
||||
// New creates a new in memory session.
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/authtoken"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/db/timestamp"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/hashicorp/boundary/internal/host/static"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
@@ -24,7 +25,7 @@ import (
|
||||
)
|
||||
|
||||
// TestConnection creates a test connection for the sessionId in the repository.
|
||||
func TestConnection(t *testing.T, conn *db.DB, sessionId, clientTcpAddr string, clientTcpPort uint32, endpointTcpAddr string, endpointTcpPort uint32, userClientIp string) *Connection {
|
||||
func TestConnection(t testing.TB, conn *db.DB, sessionId, clientTcpAddr string, clientTcpPort uint32, endpointTcpAddr string, endpointTcpPort uint32, userClientIp string) *Connection {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
@@ -44,7 +45,7 @@ func TestConnection(t *testing.T, conn *db.DB, sessionId, clientTcpAddr string,
|
||||
}
|
||||
|
||||
// TestConnectionState creates a test connection state for the connectionId in the repository.
|
||||
func TestConnectionState(t *testing.T, conn *db.DB, connectionId string, state ConnectionStatus) *ConnectionState {
|
||||
func TestConnectionState(t testing.TB, conn *db.DB, connectionId string, state ConnectionStatus) *ConnectionState {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
@@ -56,7 +57,7 @@ func TestConnectionState(t *testing.T, conn *db.DB, connectionId string, state C
|
||||
}
|
||||
|
||||
// TestState creates a test state for the sessionId in the repository.
|
||||
func TestState(t *testing.T, conn *db.DB, sessionId string, state Status) *State {
|
||||
func TestState(t testing.TB, conn *db.DB, sessionId string, state Status) *State {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
@@ -69,7 +70,7 @@ func TestState(t *testing.T, conn *db.DB, sessionId string, state Status) *State
|
||||
|
||||
// TestSession creates a test session composed of c in the repository. Options
|
||||
// are passed into New, and withServerId is handled locally.
|
||||
func TestSession(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, c ComposedOf, opt ...Option) *Session {
|
||||
func TestSession(t testing.TB, conn *db.DB, wrapper wrapping.Wrapper, c ComposedOf, opt ...Option) *Session {
|
||||
t.Helper()
|
||||
ctx := context.Background()
|
||||
opts := getOpts(opt...)
|
||||
@@ -94,6 +95,11 @@ func TestSession(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, c Composed
|
||||
}
|
||||
require.NoError(err)
|
||||
err = rw.Create(ctx, s, opts.withDbOpts...)
|
||||
if e, ok := err.(*errors.Err); err != nil && ok && e.Code == errors.NotUnique {
|
||||
// Sometimes we can get unique constraint errors when creating a session,
|
||||
// if that happens, just retry.
|
||||
err = rw.Create(ctx, s, opts.withDbOpts...)
|
||||
}
|
||||
require.NoError(err)
|
||||
|
||||
for _, cred := range s.DynamicCredentials {
|
||||
@@ -110,7 +116,7 @@ func TestSession(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, c Composed
|
||||
}
|
||||
|
||||
// TestDefaultSession creates a test session in the repository using defaults.
|
||||
func TestDefaultSession(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, iamRepo *iam.Repository, opt ...Option) *Session {
|
||||
func TestDefaultSession(t testing.TB, conn *db.DB, wrapper wrapping.Wrapper, iamRepo *iam.Repository, opt ...Option) *Session {
|
||||
t.Helper()
|
||||
composedOf := TestSessionParams(t, conn, wrapper, iamRepo)
|
||||
future := timestamppb.New(time.Now().Add(time.Hour))
|
||||
@@ -120,7 +126,7 @@ func TestDefaultSession(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, iam
|
||||
|
||||
// TestSessionParams returns an initialized ComposedOf which can be used to
|
||||
// create a session in the repository.
|
||||
func TestSessionParams(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, iamRepo *iam.Repository) ComposedOf {
|
||||
func TestSessionParams(t testing.TB, conn *db.DB, wrapper wrapping.Wrapper, iamRepo *iam.Repository) ComposedOf {
|
||||
t.Helper()
|
||||
ctx := context.Background()
|
||||
|
||||
@@ -166,7 +172,7 @@ func TestSessionParams(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, iamR
|
||||
}
|
||||
|
||||
// TestTofu will create a test "trust on first use" token
|
||||
func TestTofu(t *testing.T) []byte {
|
||||
func TestTofu(t testing.TB) []byte {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
tofu, err := base62.Random(20)
|
||||
@@ -176,7 +182,7 @@ func TestTofu(t *testing.T) []byte {
|
||||
|
||||
// TestWorker inserts a worker into the db to satisfy foreign key constraints.
|
||||
// Supports the WithServerId option.
|
||||
func TestWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...Option) *servers.Server {
|
||||
func TestWorker(t testing.TB, conn *db.DB, wrapper wrapping.Wrapper, opt ...Option) *servers.Server {
|
||||
t.Helper()
|
||||
rw := db.New(conn)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
|
||||
@@ -35,6 +35,7 @@ type options struct {
|
||||
WithSessionConnectionLimit int32
|
||||
WithPublicId string
|
||||
WithWorkerFilter string
|
||||
WithTargetIds []string
|
||||
}
|
||||
|
||||
func getDefaultOptions() options {
|
||||
@@ -161,3 +162,10 @@ func WithWorkerFilter(filter string) Option {
|
||||
o.WithWorkerFilter = filter
|
||||
}
|
||||
}
|
||||
|
||||
// WithTargetIds provides an option to search by specific target IDs
|
||||
func WithTargetIds(with []string) Option {
|
||||
return func(o *options) {
|
||||
o.WithTargetIds = with
|
||||
}
|
||||
}
|
||||
@@ -43,5 +43,11 @@ final (action, library_id) as (
|
||||
)
|
||||
select * from final
|
||||
order by action, library_id;
|
||||
`
|
||||
|
||||
targetPublicIdList = `
|
||||
select public_id, scope_id from target
|
||||
%s
|
||||
;
|
||||
`
|
||||
)
|
||||
@@ -2,14 +2,17 @@ package target
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/boundary"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
dbcommon "github.com/hashicorp/boundary/internal/db/common"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/oplog"
|
||||
"github.com/hashicorp/boundary/internal/types/scope"
|
||||
)
|
||||
|
||||
// Cloneable provides a cloning interface
|
||||
@@ -138,21 +141,96 @@ func (r *Repository) LookupTarget(ctx context.Context, publicIdOrName string, op
|
||||
return subtype, hostSources, credSources, nil
|
||||
}
|
||||
|
||||
// FetchAuthzProtectedEntitiesByScope implements boundary.AuthzProtectedEntityProvider
|
||||
func (r *Repository) FetchAuthzProtectedEntitiesByScope(ctx context.Context, scopeIds []string) (map[string][]boundary.AuthzProtectedEntity, error) {
|
||||
const op = "target.(Repository).FetchAuthzProtectedEntitiesByScope"
|
||||
|
||||
var where string
|
||||
var args []interface{}
|
||||
|
||||
inClauseCnt := 0
|
||||
|
||||
switch len(scopeIds) {
|
||||
case 0:
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "no scopes given")
|
||||
case 1:
|
||||
if scopeIds[0] != scope.Global.String() {
|
||||
inClauseCnt += 1
|
||||
where, args = fmt.Sprintf("where scope_id = @%d", inClauseCnt), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), scopeIds[0]))
|
||||
}
|
||||
default:
|
||||
idsInClause := make([]string, 0, len(scopeIds))
|
||||
for _, id := range scopeIds {
|
||||
inClauseCnt += 1
|
||||
idsInClause, args = append(idsInClause, fmt.Sprintf("@%d", inClauseCnt)), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), id))
|
||||
}
|
||||
where = fmt.Sprintf("where scope_id in (%s)", strings.Join(idsInClause, ","))
|
||||
}
|
||||
|
||||
q := targetPublicIdList
|
||||
query := fmt.Sprintf(q, where)
|
||||
|
||||
rows, err := r.reader.Query(ctx, query, args)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
targetsMap := map[string][]boundary.AuthzProtectedEntity{}
|
||||
for rows.Next() {
|
||||
var tv targetView
|
||||
if err := r.reader.ScanRows(ctx, rows, &tv); err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("scan row failed"))
|
||||
}
|
||||
targetsMap[tv.GetScopeId()] = append(targetsMap[tv.GetScopeId()], tv)
|
||||
}
|
||||
|
||||
return targetsMap, nil
|
||||
}
|
||||
|
||||
// ListTargets in targets in a scope. Supports the WithScopeId, WithLimit, WithType options.
|
||||
func (r *Repository) ListTargets(ctx context.Context, opt ...Option) ([]Target, error) {
|
||||
const op = "target.(Repository).ListTargets"
|
||||
opts := GetOpts(opt...)
|
||||
if len(opts.WithScopeIds) == 0 && opts.WithUserId == "" {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "must specify either scope id or user id")
|
||||
if len(opts.WithScopeIds) == 0 && opts.WithUserId == "" && len(opts.WithTargetIds) == 0 {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "must specify either scope ids, target ids, or user id")
|
||||
}
|
||||
// TODO (jimlambrt 8/2020) - implement WithUserId() optional filtering.
|
||||
var where []string
|
||||
var args []interface{}
|
||||
if len(opts.WithScopeIds) != 0 {
|
||||
where, args = append(where, "scope_id in (?)"), append(args, opts.WithScopeIds)
|
||||
inClauseCnt := 0
|
||||
|
||||
switch len(opts.WithScopeIds) {
|
||||
case 0:
|
||||
case 1:
|
||||
inClauseCnt += 1
|
||||
where, args = append(where, fmt.Sprintf("scope_id = @%d", inClauseCnt)), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), opts.WithScopeIds[0]))
|
||||
default:
|
||||
idsInClause := make([]string, 0, len(opts.WithScopeIds))
|
||||
for _, id := range opts.WithScopeIds {
|
||||
inClauseCnt += 1
|
||||
idsInClause, args = append(idsInClause, fmt.Sprintf("@%d", inClauseCnt)), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), id))
|
||||
}
|
||||
where = append(where, fmt.Sprintf("scope_id in (%s)", strings.Join(idsInClause, ",")))
|
||||
}
|
||||
|
||||
switch len(opts.WithTargetIds) {
|
||||
case 0:
|
||||
case 1:
|
||||
inClauseCnt += 1
|
||||
where, args = append(where, fmt.Sprintf("public_id = @%d", inClauseCnt)), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), opts.WithTargetIds[0]))
|
||||
default:
|
||||
idsInClause := make([]string, 0, len(opts.WithTargetIds))
|
||||
for _, id := range opts.WithTargetIds {
|
||||
inClauseCnt += 1
|
||||
idsInClause, args = append(idsInClause, fmt.Sprintf("@%d", inClauseCnt)), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), id))
|
||||
}
|
||||
where = append(where, fmt.Sprintf("public_id in (%s)", strings.Join(idsInClause, ",")))
|
||||
}
|
||||
|
||||
if opts.WithType != "" {
|
||||
where, args = append(where, "type = ?"), append(args, opts.WithType.String())
|
||||
inClauseCnt += 1
|
||||
where, args = append(where, fmt.Sprintf("type = @%d", inClauseCnt)), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), opts.WithType.String()))
|
||||
}
|
||||
|
||||
var foundTargets []*targetView
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/boundary"
|
||||
"github.com/hashicorp/boundary/internal/db/timestamp"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/hashicorp/boundary/internal/oplog"
|
||||
@@ -44,6 +45,8 @@ const (
|
||||
targetsViewDefaultTable = "target_all_subtypes"
|
||||
)
|
||||
|
||||
var _ boundary.AuthzProtectedEntity = (*targetView)(nil)
|
||||
|
||||
// targetView provides a common way to return targets regardless of their
|
||||
// underlying type.
|
||||
type targetView struct {
|
||||
@@ -77,6 +80,22 @@ func (t *targetView) SetTableName(n string) {
|
||||
}
|
||||
}
|
||||
|
||||
// GetPublicId satisfies boundary.AuthzProtectedEntity
|
||||
func (t targetView) GetPublicId() string {
|
||||
return t.PublicId
|
||||
}
|
||||
|
||||
// GetScopeId satisfies boundary.AuthzProtectedEntity
|
||||
func (t targetView) GetScopeId() string {
|
||||
return t.ScopeId
|
||||
}
|
||||
|
||||
// GetUserId satisfies boundary.AuthzProtectedEntity; targets are not associated
|
||||
// with a user ID so this always returns an empty string
|
||||
func (t targetView) GetUserId() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (t *targetView) Subtype() subtypes.Subtype {
|
||||
return subtypes.Subtype(t.Type)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
// TestTarget is used to create a Target that can be used by tests in other packages.
|
||||
func TestTarget(ctx context.Context, t *testing.T, conn *db.DB, scopeId, name string, opt ...target.Option) target.Target {
|
||||
func TestTarget(ctx context.Context, t testing.TB, conn *db.DB, scopeId, name string, opt ...target.Option) target.Target {
|
||||
t.Helper()
|
||||
opt = append(opt, target.WithName(name))
|
||||
opts := target.GetOpts(opt...)
|
||||
@@ -48,12 +48,12 @@ func TestTarget(ctx context.Context, t *testing.T, conn *db.DB, scopeId, name st
|
||||
return tar
|
||||
}
|
||||
|
||||
func testTargetName(t *testing.T, scopeId string) string {
|
||||
func testTargetName(t testing.TB, scopeId string) string {
|
||||
t.Helper()
|
||||
return fmt.Sprintf("%s-%s", scopeId, testId(t))
|
||||
}
|
||||
|
||||
func testId(t *testing.T) string {
|
||||
func testId(t testing.TB) string {
|
||||
t.Helper()
|
||||
id, err := uuid.GenerateUUID()
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -25,7 +25,7 @@ func TestNewCredentialLibrary(targetId, credentialLibraryId string, purpose cred
|
||||
|
||||
// TestCredentialLibrary creates a CredentialLibrary for targetId and
|
||||
// libraryId with the credential purpose of application.
|
||||
func TestCredentialLibrary(t *testing.T, conn *db.DB, targetId, libraryId string) *CredentialLibrary {
|
||||
func TestCredentialLibrary(t testing.TB, conn *db.DB, targetId, libraryId string) *CredentialLibrary {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
|
||||
// CloudEventFromFile will marshal a single cloud event from the provided file
|
||||
// name
|
||||
func CloudEventFromFile(t *testing.T, fileName string) *cloudevents.Event {
|
||||
func CloudEventFromFile(t testing.TB, fileName string) *cloudevents.Event {
|
||||
t.Helper()
|
||||
b, err := ioutil.ReadFile(fileName)
|
||||
assert.NoError(t, err)
|
||||
@@ -26,7 +26,7 @@ func CloudEventFromFile(t *testing.T, fileName string) *cloudevents.Event {
|
||||
|
||||
// GetEventDetails is a testing helper will return the details from the event
|
||||
// payload for a given messageType (request or response)
|
||||
func GetEventDetails(t *testing.T, e *cloudevents.Event, messageType string) map[string]interface{} {
|
||||
func GetEventDetails(t testing.TB, e *cloudevents.Event, messageType string) map[string]interface{} {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
require.NotNil(e)
|
||||
@@ -42,7 +42,7 @@ func GetEventDetails(t *testing.T, e *cloudevents.Event, messageType string) map
|
||||
|
||||
// AssertRedactedValues will assert that the values for the given keys within
|
||||
// the data have been redacted
|
||||
func AssertRedactedValues(t *testing.T, data interface{}, keys ...string) {
|
||||
func AssertRedactedValues(t testing.TB, data interface{}, keys ...string) {
|
||||
t.Helper()
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
require.NotNil(data)
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
// NewEncryptFilter is a copy of event.NewEncryptFilter since importing it would
|
||||
// case circular deps. The primary reason for this test func is to make sure
|
||||
// the proper IgnoreTypes are included for testing.
|
||||
func NewEncryptFilter(t *testing.T, w wrapping.Wrapper) *encrypt.Filter {
|
||||
func NewEncryptFilter(t testing.TB, w wrapping.Wrapper) *encrypt.Filter {
|
||||
t.Helper()
|
||||
return &encrypt.Filter{
|
||||
Wrapper: w,
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
)
|
||||
|
||||
// TestFreePort just returns an available free localhost port
|
||||
func TestFreePort(t *testing.T) int {
|
||||
func TestFreePort(t testing.TB) int {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
// TestWrapper initializes an AEAD wrapping.Wrapper for testing
|
||||
func TestWrapper(t *testing.T) wrapping.Wrapper {
|
||||
func TestWrapper(t testing.TB) wrapping.Wrapper {
|
||||
rootKey := make([]byte, 32)
|
||||
n, err := rand.Read(rootKey)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
FROM postgres:11-alpine
|
||||
|
||||
ADD init-db.sh /docker-entrypoint-initdb.d/00-init-db.sh
|
||||
ADD restore-benchmark-dumps.sh /docker-entrypoint-initdb.d/01-restore-benchmark-dumps.sh
|
||||
ADD postgresql.conf /etc/postgresql/postgresql.conf
|
||||
|
||||
CMD ["postgres", "-c", "config_file=/etc/postgresql/postgresql.conf"]
|
||||
@@ -1,6 +1,7 @@
|
||||
FROM postgres:12-alpine
|
||||
|
||||
ADD init-db.sh /docker-entrypoint-initdb.d/00-init-db.sh
|
||||
ADD restore-benchmark-dumps.sh /docker-entrypoint-initdb.d/01-restore-benchmark-dumps.sh
|
||||
ADD postgresql.conf /etc/postgresql/postgresql.conf
|
||||
|
||||
CMD ["postgres", "-c", "config_file=/etc/postgresql/postgresql.conf"]
|
||||
@@ -1,6 +1,7 @@
|
||||
FROM postgres:13-alpine
|
||||
|
||||
ADD init-db.sh /docker-entrypoint-initdb.d/00-init-db.sh
|
||||
ADD restore-benchmark-dumps.sh /docker-entrypoint-initdb.d/01-restore-benchmark-dumps.sh
|
||||
ADD postgresql.conf /etc/postgresql/postgresql.conf
|
||||
|
||||
CMD ["postgres", "-c", "config_file=/etc/postgresql/postgresql.conf"]
|
||||
@@ -1,6 +1,7 @@
|
||||
FROM postgres:alpine
|
||||
|
||||
ADD init-db.sh /docker-entrypoint-initdb.d/00-init-db.sh
|
||||
ADD restore-benchmark-dumps.sh /docker-entrypoint-initdb.d/01-restore-benchmark-dumps.sh
|
||||
ADD postgresql.conf /etc/postgresql/postgresql.conf
|
||||
|
||||
CMD ["postgres", "-c", "config_file=/etc/postgresql/postgresql.conf"]
|
||||
@@ -13,6 +13,7 @@ TEST_CONTAINER_NAME ?= boundary-sql-tests
|
||||
# Generate targets from dockerfiles
|
||||
dockerfiles = $(wildcard Dockerfile.*)
|
||||
docker-buildxs = $(patsubst Dockerfile.%,%-buildx, $(dockerfiles))
|
||||
docker-load-buildxs = $(patsubst Dockerfile.%,%-load-buildx, $(dockerfiles))
|
||||
|
||||
# Before running this target a builder instance needs to be setup, ie:
|
||||
# docker buildx create --driver docker-container --use
|
||||
@@ -25,6 +26,14 @@ ${docker-buildxs}: %-buildx:
|
||||
-t $(REGISTRY_NAME)/$(TEST_IMAGE_NAME):$* \
|
||||
-f Dockerfile.$* .
|
||||
|
||||
docker-load: ${docker-load-buildxs}
|
||||
|
||||
${docker-load-buildxs}: %-load-buildx:
|
||||
docker buildx build \
|
||||
--load \
|
||||
-t $(REGISTRY_NAME)/$(TEST_IMAGE_NAME):$* \
|
||||
-f Dockerfile.$* .
|
||||
|
||||
database-up:
|
||||
@echo "Using image: $(TEST_IMAGE_TAG)"
|
||||
@echo "Additional postgres configuration: $(PG_OPTS)"
|
||||
@@ -38,6 +47,7 @@ database-up:
|
||||
-e PGDATA=/pgdata \
|
||||
--mount type=tmpfs,destination=/pgdata \
|
||||
-v "$(CWD)/../../../internal/db/schema/migrations":/migrations \
|
||||
-v "$(CWD)/benchmark_dumps":/benchmark_dumps \
|
||||
$(TEST_IMAGE_TAG) \
|
||||
-c 'config_file=/etc/postgresql/postgresql.conf' \
|
||||
$(PG_OPTS) 1> /dev/null
|
||||
@@ -49,4 +59,12 @@ clean:
|
||||
docker stop $(TEST_CONTAINER_NAME) || true
|
||||
docker rm -v $(TEST_CONTAINER_NAME) || true
|
||||
|
||||
.PHONY: all docker-build database-up ${docker-buildxs} clean
|
||||
generate-database-dumps:
|
||||
BOUNDARY_DB_TEST_GENERATE_SESSION_BENCHMARK_TEMPLATE_DUMPS=1 go test \
|
||||
-v \
|
||||
-run '^TestGenerateSessionBenchmarkTemplateDumps$$' \
|
||||
--timeout=1000h \
|
||||
-count=1 \
|
||||
../
|
||||
|
||||
.PHONY: all docker-build database-up ${docker-buildxs} ${docker-load-buildxs} generate-database-dumps clean
|
||||
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
shopt -s globstar
|
||||
|
||||
for file in $(ls -v /benchmark_dumps/*.dump); do
|
||||
echo "Creating template database based on: ${file}"
|
||||
db_name="boundary_$(basename ${file} .dump)_template"
|
||||
psql -v "ON_ERROR_STOP=1" --username "$POSTGRES_USER" --dbname "${POSTGRES_DB}" -q <<EOSQL
|
||||
create database ${db_name} owner ${POSTGRES_USER};
|
||||
EOSQL
|
||||
echo "Restoring ${file} into ${db_name}"
|
||||
pg_restore -j $(nproc) --username "${POSTGRES_USER}" --dbname ${db_name} "${file}"
|
||||
psql -v "ON_ERROR_STOP=1" --username "${POSTGRES_USER}" --dbname "${POSTGRES_DB}" -q <<EOSQL
|
||||
update pg_database set datistemplate = true, datallowconn = false where datname = '${db_name}';
|
||||
EOSQL
|
||||
done
|
||||
@@ -0,0 +1,283 @@
|
||||
package dbtest_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/auth/password"
|
||||
"github.com/hashicorp/boundary/internal/authtoken"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/host/static"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/servers"
|
||||
"github.com/hashicorp/boundary/internal/session"
|
||||
"github.com/hashicorp/boundary/internal/target"
|
||||
"github.com/hashicorp/boundary/internal/target/tcp"
|
||||
"github.com/hashicorp/boundary/testing/dbtest"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
// TestGenerateSessionBenchmarkTemplateDumps is not really a test, it uses
|
||||
// the testing framework for easy access to a boundary server for
|
||||
// populating a database with test data. This database is then dumped
|
||||
// to predictable files for use in benchmarking.
|
||||
//
|
||||
// Each database dump contains:
|
||||
// * N number of sessions, with each session having M connections.
|
||||
// * P users, each owning an even amount of the sessions. All users
|
||||
// use the password "testpassword" to login.
|
||||
// It is safe for users of these dumps to assume all of this, for the variables
|
||||
// N, M and P defined in the "scenarios" struct below.
|
||||
func TestGenerateSessionBenchmarkTemplateDumps(t *testing.T) {
|
||||
if os.Getenv("BOUNDARY_DB_TEST_GENERATE_SESSION_BENCHMARK_TEMPLATE_DUMPS") == "" {
|
||||
t.Skip("BOUNDARY_DB_TEST_GENERATE_SESSION_BENCHMARK_TEMPLATE_DUMPS is not set")
|
||||
return
|
||||
}
|
||||
|
||||
pgDumpBinary := "pg_dump"
|
||||
if os.Getenv("PGDUMP_BINARY") != "" {
|
||||
pgDumpBinary = os.Getenv("PGDUMP_BINARY")
|
||||
}
|
||||
_, err := exec.LookPath(pgDumpBinary)
|
||||
require.NoError(t, err, "failed to find %s, please install it to generate benchmarking template dumps. Set PGDUMP_BINARY to configure a custom binary name", pgDumpBinary)
|
||||
|
||||
// Verify that the version of pg_dump used to create the dumps is the
|
||||
// same as the LOWEST version of Postgres that we support, since dumps are
|
||||
// only forward compatible, not backwards compatible, and we want to be able
|
||||
// to import these dumps into all the versions of Postgres that we support.
|
||||
out, err := exec.Command(pgDumpBinary, "--version").Output()
|
||||
require.NoError(t, err, "failed to check pg_dump version")
|
||||
major, _, err := parsePgDumpVersion(string(out))
|
||||
require.NoError(t, err, "failed to parse pg_dump version")
|
||||
if major != 11 {
|
||||
t.Fatal("pg_dump version is not 11, please install pg_dump 11 to generate benchmarking template dumps")
|
||||
}
|
||||
|
||||
scenarios := []struct {
|
||||
sessions int
|
||||
connsPerSession int
|
||||
users int
|
||||
}{
|
||||
{
|
||||
sessions: 1000,
|
||||
connsPerSession: 10,
|
||||
users: 10,
|
||||
},
|
||||
{
|
||||
sessions: 1000,
|
||||
connsPerSession: 10,
|
||||
users: 25,
|
||||
},
|
||||
{
|
||||
sessions: 1000,
|
||||
connsPerSession: 10,
|
||||
users: 50,
|
||||
},
|
||||
{
|
||||
sessions: 1000,
|
||||
connsPerSession: 10,
|
||||
users: 75,
|
||||
},
|
||||
{
|
||||
sessions: 1000,
|
||||
connsPerSession: 10,
|
||||
users: 100,
|
||||
},
|
||||
{
|
||||
sessions: 1000,
|
||||
connsPerSession: 10,
|
||||
users: 500,
|
||||
},
|
||||
}
|
||||
|
||||
// Create a global semaphore to limit the number of concurrent requests
|
||||
// across all tests.
|
||||
semaphore := make(chan struct{}, runtime.NumCPU())
|
||||
for _, scenario := range scenarios {
|
||||
scenario := scenario // Parallel test closures act as goroutines, copy iteration variable
|
||||
t.Run(fmt.Sprintf("Generate-%d-sessions-%d-conns-per-session-%d-users-dump", scenario.sessions, scenario.connsPerSession, scenario.users), func(t *testing.T) {
|
||||
t.Parallel() // Lets speed things up a bit
|
||||
ctx := context.Background()
|
||||
require := require.New(t)
|
||||
outputPath := fmt.Sprintf("./docker/benchmark_dumps/session_%d_%d_%d.dump", scenario.sessions, scenario.connsPerSession, scenario.users)
|
||||
if _, err := os.Lstat(outputPath); err == nil {
|
||||
t.Skipf("%q already exists, skipping", path.Base(outputPath))
|
||||
return
|
||||
}
|
||||
conn, dbURL := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
wrap, err := dbtest.GetBoundaryBenchmarksRootKeyWrapper(ctx)
|
||||
require.NoError(err)
|
||||
kms := kms.TestKms(t, conn, wrap)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrap)
|
||||
authTokenRepo, err := authtoken.NewRepository(rw, rw, kms)
|
||||
require.NoError(err)
|
||||
pwRepo, err := password.NewRepository(rw, rw, kms)
|
||||
require.NoError(err)
|
||||
sessRepo, err := session.NewRepository(rw, rw, kms)
|
||||
require.NoError(err)
|
||||
connRepo, err := session.NewConnectionRepository(ctx, rw, rw, kms)
|
||||
require.NoError(err)
|
||||
serversRepo, err := servers.NewRepository(rw, rw, kms)
|
||||
require.NoError(err)
|
||||
worker := &servers.Server{
|
||||
PrivateId: "test1",
|
||||
Type: "worker",
|
||||
Address: "127.0.0.1",
|
||||
}
|
||||
_, _, err = serversRepo.UpsertServer(ctx, worker)
|
||||
require.NoError(err)
|
||||
|
||||
usersStart := time.Now()
|
||||
t.Logf("Populating %d users", scenario.users)
|
||||
users := make([]*user, scenario.users)
|
||||
eg, gCtx := errgroup.WithContext(ctx)
|
||||
for i := 0; i < scenario.users; i++ {
|
||||
i := i
|
||||
// Parallelize user creation
|
||||
eg.Go(func() error {
|
||||
select {
|
||||
case semaphore <- struct{}{}:
|
||||
case <-gCtx.Done():
|
||||
return gCtx.Err()
|
||||
}
|
||||
defer func() {
|
||||
select {
|
||||
case <-semaphore:
|
||||
case <-gCtx.Done():
|
||||
}
|
||||
}()
|
||||
users[i] = newUser(t, gCtx, iamRepo, authTokenRepo, pwRepo, kms, conn, "user"+strconv.Itoa(i))
|
||||
return nil
|
||||
})
|
||||
}
|
||||
require.NoError(eg.Wait())
|
||||
t.Logf("Populated %d users in %s", scenario.users, time.Since(usersStart))
|
||||
|
||||
insertStart := time.Now()
|
||||
t.Logf("Populating %d sessions", scenario.sessions)
|
||||
eg, gCtx = errgroup.WithContext(ctx)
|
||||
for i := 0; i < scenario.sessions; i++ {
|
||||
i := i
|
||||
userIndex := i % len(users)
|
||||
// Parallelize session creation
|
||||
eg.Go(func() error {
|
||||
select {
|
||||
case semaphore <- struct{}{}:
|
||||
case <-gCtx.Done():
|
||||
return gCtx.Err()
|
||||
}
|
||||
defer func() {
|
||||
select {
|
||||
case <-semaphore:
|
||||
if i > 0 && i%(scenario.sessions/4) == 0 {
|
||||
t.Logf("%d%% done in %s", 100*i/scenario.sessions, time.Since(insertStart))
|
||||
}
|
||||
case <-gCtx.Done():
|
||||
}
|
||||
}()
|
||||
sess := session.TestSession(t, conn, wrap, session.ComposedOf{
|
||||
UserId: users[userIndex].id,
|
||||
HostId: users[userIndex].hostId,
|
||||
TargetId: users[userIndex].targetId,
|
||||
HostSetId: users[userIndex].hostSetId,
|
||||
AuthTokenId: users[userIndex].authTokenId,
|
||||
ScopeId: users[userIndex].scopeId,
|
||||
Endpoint: "tcp://127.0.0.1:22",
|
||||
})
|
||||
cycleSessionStates(t, ctx, sess, sessRepo, connRepo, conn, worker, scenario.connsPerSession)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
require.NoError(eg.Wait())
|
||||
_, err = sessRepo.TerminateCompletedSessions(ctx)
|
||||
require.NoError(err)
|
||||
t.Logf("Populated %d sessions in %s", scenario.sessions, time.Since(insertStart))
|
||||
|
||||
dumpStart := time.Now()
|
||||
t.Logf("Dumping %d sessions to %s", scenario.sessions, outputPath)
|
||||
cmd := exec.Command(
|
||||
pgDumpBinary,
|
||||
"--format=c", // Set custom postgres format for faster restore
|
||||
"--file="+outputPath,
|
||||
dbURL,
|
||||
)
|
||||
out, err := cmd.CombinedOutput()
|
||||
require.NoError(err, string(out))
|
||||
t.Logf("Dumped %d sessions to %s in %s", scenario.sessions, outputPath, time.Since(dumpStart))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func cycleSessionStates(t testing.TB, ctx context.Context, sess *session.Session, sessRepo *session.Repository, connRepo *session.ConnectionRepository, conn *db.DB, worker *servers.Server, numConns int) {
|
||||
sess, _, err := sessRepo.ActivateSession(ctx, sess.PublicId, sess.Version, worker.PrivateId, worker.Type, []byte(`tofu`))
|
||||
require.NoError(t, err)
|
||||
var closeWiths []session.CloseWith
|
||||
for i := 0; i < numConns; i++ {
|
||||
connID := session.TestConnection(t, conn, sess.PublicId, "127.0.0.1", 22, "127.0.0.2", 23, "127.0.0.1").PublicId
|
||||
closeWiths = append(closeWiths, session.CloseWith{ConnectionId: connID, ClosedReason: session.ConnectionCanceled})
|
||||
}
|
||||
// Cancel 50% of the sessions
|
||||
if rand.Intn(2) == 0 {
|
||||
sess, err = sessRepo.CancelSession(ctx, sess.PublicId, sess.Version)
|
||||
require.NoError(t, err)
|
||||
_, err = session.CloseConnections(ctx, sessRepo, connRepo, closeWiths)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
func parsePgDumpVersion(in string) (int, int, error) {
|
||||
var major, minor int
|
||||
_, err := fmt.Sscanf(in, "pg_dump (PostgreSQL) %d.%d", &major, &minor)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
return major, minor, nil
|
||||
}
|
||||
|
||||
type user struct {
|
||||
id string
|
||||
hostId string
|
||||
targetId string
|
||||
hostSetId string
|
||||
authTokenId string
|
||||
scopeId string
|
||||
}
|
||||
|
||||
func newUser(t testing.TB, ctx context.Context, iamRepo *iam.Repository, authTokenRepo *authtoken.Repository, pwRepo *password.Repository, kms *kms.Kms, conn *db.DB, name string) *user {
|
||||
require := require.New(t)
|
||||
o, pWithSessions := iam.TestScopes(t, iamRepo)
|
||||
am := password.TestAuthMethod(t, conn, o.GetPublicId())
|
||||
acct, err := password.NewAccount(am.GetPublicId(), password.WithLoginName(name))
|
||||
require.NoError(err)
|
||||
acct, err = pwRepo.CreateAccount(ctx, o.PublicId, acct, password.WithPassword(dbtest.BoundaryBenchmarksUserPassword))
|
||||
require.NoError(err)
|
||||
u := iam.TestUser(t, iamRepo, o.GetPublicId(), iam.WithAccountIds(acct.PublicId), iam.WithName(name))
|
||||
at, err := authTokenRepo.CreateAuthToken(ctx, u, acct.GetPublicId())
|
||||
require.NoError(err)
|
||||
require.Equal(name, u.Name)
|
||||
hc := static.TestCatalogs(t, conn, pWithSessions.GetPublicId(), 1)[0]
|
||||
hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0]
|
||||
h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0]
|
||||
static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h})
|
||||
tar := tcp.TestTarget(ctx, t, conn, pWithSessions.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()}))
|
||||
return &user{
|
||||
id: u.GetPublicId(),
|
||||
hostId: h.GetPublicId(),
|
||||
targetId: tar.GetPublicId(),
|
||||
hostSetId: hs.GetPublicId(),
|
||||
authTokenId: at.GetPublicId(),
|
||||
scopeId: pWithSessions.GetPublicId(),
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package dbtest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
@@ -9,6 +11,8 @@ import (
|
||||
// postgres dialect
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db/common"
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
|
||||
"github.com/hashicorp/go-kms-wrapping/v2/aead"
|
||||
_ "github.com/jackc/pgx/v4/stdlib"
|
||||
)
|
||||
|
||||
@@ -31,10 +35,20 @@ const (
|
||||
Postgres = "postgres"
|
||||
)
|
||||
|
||||
// BoundaryBenchmarksUserPassword is the password used for all users
|
||||
// created by the boundary benchmarks dump generator.
|
||||
const BoundaryBenchmarksUserPassword = "testpassword"
|
||||
|
||||
// Template Database Names
|
||||
const (
|
||||
BoundaryTemplate = "boundary_template"
|
||||
Template1 = "template1"
|
||||
BoundaryTemplate = "boundary_template"
|
||||
Template1 = "template1"
|
||||
Boundary1000Sessions10ConnsPerSession10Template = "boundary_session_1000_10_10_template"
|
||||
Boundary1000Sessions10ConnsPerSession25Template = "boundary_session_1000_10_25_template"
|
||||
Boundary1000Sessions10ConnsPerSession50Template = "boundary_session_1000_10_50_template"
|
||||
Boundary1000Sessions10ConnsPerSession75Template = "boundary_session_1000_10_75_template"
|
||||
Boundary1000Sessions10ConnsPerSession100Template = "boundary_session_1000_10_100_template"
|
||||
Boundary1000Sessions10ConnsPerSession500Template = "boundary_session_1000_10_500_template"
|
||||
)
|
||||
|
||||
var supportedDialects = map[string]struct{}{
|
||||
@@ -44,6 +58,12 @@ var supportedDialects = map[string]struct{}{
|
||||
var supportedTemplates = map[string]struct{}{
|
||||
BoundaryTemplate: {},
|
||||
Template1: {},
|
||||
Boundary1000Sessions10ConnsPerSession10Template: {},
|
||||
Boundary1000Sessions10ConnsPerSession25Template: {},
|
||||
Boundary1000Sessions10ConnsPerSession50Template: {},
|
||||
Boundary1000Sessions10ConnsPerSession75Template: {},
|
||||
Boundary1000Sessions10ConnsPerSession100Template: {},
|
||||
Boundary1000Sessions10ConnsPerSession500Template: {},
|
||||
}
|
||||
|
||||
const letterBytes = "abcdefghijklmnopqrstuvwxyz"
|
||||
@@ -53,6 +73,22 @@ const (
|
||||
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
|
||||
)
|
||||
|
||||
const boundaryBenchmarksRootKey = "rootkey1rootkey1rootkey1rootkey1" // 32 bytes for AES-256
|
||||
|
||||
// GetBoundaryBenchmarksRootKeyWrapper returns the root key wrapper used for the benchmark dumps.
|
||||
func GetBoundaryBenchmarksRootKeyWrapper(ctx context.Context) (wrapping.Wrapper, error) {
|
||||
wrap := aead.NewWrapper()
|
||||
_, err := wrap.SetConfig(ctx, wrapping.WithKeyId(base64.StdEncoding.EncodeToString([]byte(boundaryBenchmarksRootKey))))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = wrap.SetAesGcmKeyBytes([]byte(boundaryBenchmarksRootKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return wrap, nil
|
||||
}
|
||||
|
||||
func randStr(n int) string {
|
||||
b := make([]byte, n)
|
||||
// A rand.Int63() generates 63 random bits, enough for letterIdxMax letters!
|
||||
|
||||
Reference in new issue
Block a user