Add mongodbatlas static roles support (#8987)

* Refactor PG container creation.
* Rework rotation tests to use shorter sleeps.
* Refactor rotation tests.
* Add a static role rotation test for MongoDB Atlas.
This commit is contained in:
ncabatoff
2020-05-29 14:21:23 -04:00
committed by GitHub
parent 7561c04921
commit 44fdbc7dc9
15 changed files with 262 additions and 523 deletions

View File

@@ -4,64 +4,24 @@ import (
"context"
"database/sql"
"fmt"
"os"
"strings"
"testing"
"time"
"github.com/hashicorp/vault/helper/testhelpers/docker"
"github.com/hashicorp/vault/helper/testhelpers/postgresql"
"github.com/hashicorp/vault/sdk/database/dbplugin"
"github.com/hashicorp/vault/sdk/helper/dbtxn"
"github.com/lib/pq"
"github.com/ory/dockertest"
)
func preparePostgresTestContainer(t *testing.T) (cleanup func(), retURL string) {
if os.Getenv("PG_URL") != "" {
return func() {}, os.Getenv("PG_URL")
}
pool, err := dockertest.NewPool("")
if err != nil {
t.Fatalf("Failed to connect to docker: %s", err)
}
resource, err := pool.Run("postgres", "latest", []string{"POSTGRES_PASSWORD=secret", "POSTGRES_DB=database"})
if err != nil {
t.Fatalf("Could not start local PostgreSQL docker container: %s", err)
}
cleanup = func() {
docker.CleanupResource(t, pool, resource)
}
retURL = fmt.Sprintf("postgres://postgres:secret@localhost:%s/database?sslmode=disable", resource.GetPort("5432/tcp"))
// exponential backoff-retry
if err = pool.Retry(func() error {
var err error
var db *sql.DB
db, err = sql.Open("postgres", retURL)
if err != nil {
return err
}
defer db.Close()
return db.Ping()
}); err != nil {
cleanup()
t.Fatalf("Could not connect to PostgreSQL docker container: %s", err)
}
return
}
func TestPostgreSQL_Initialize(t *testing.T) {
cleanup, connURL := preparePostgresTestContainer(t)
defer cleanup()
func getPostgreSQL(t *testing.T, options map[string]interface{}) (*PostgreSQL, func()) {
cleanup, connURL := postgresql.PrepareTestContainer(t, "latest")
connectionDetails := map[string]interface{}{
"connection_url": connURL,
"max_open_connections": 5,
"connection_url": connURL,
}
for k, v := range options {
connectionDetails[k] = v
}
db := new()
@@ -73,23 +33,29 @@ func TestPostgreSQL_Initialize(t *testing.T) {
if !db.Initialized {
t.Fatal("Database should be initialized")
}
return db, cleanup
}
err = db.Close()
if err != nil {
func TestPostgreSQL_Initialize(t *testing.T) {
db, cleanup := getPostgreSQL(t, map[string]interface{}{
"max_open_connections": 5,
})
defer cleanup()
if err := db.Close(); err != nil {
t.Fatalf("err: %s", err)
}
}
// Test decoding a string value for max_open_connections
connectionDetails = map[string]interface{}{
"connection_url": connURL,
func TestPostgreSQL_InitializeWithStringVals(t *testing.T) {
db, cleanup := getPostgreSQL(t, map[string]interface{}{
"max_open_connections": "5",
}
})
defer cleanup()
_, err = db.Init(context.Background(), connectionDetails, true)
if err != nil {
if err := db.Close(); err != nil {
t.Fatalf("err: %s", err)
}
}
func TestPostgreSQL_CreateUser_missingArgs(t *testing.T) {
@@ -180,19 +146,9 @@ func TestPostgreSQL_CreateUser(t *testing.T) {
}
// Shared test container for speed - there should not be any overlap between the tests
cleanup, connURL := preparePostgresTestContainer(t)
db, cleanup := getPostgreSQL(t, nil)
defer cleanup()
connectionDetails := map[string]interface{}{
"connection_url": connURL,
}
db := new()
_, err := db.Init(context.Background(), connectionDetails, true)
if err != nil {
t.Fatalf("err: %s", err)
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
usernameConfig := dbplugin.UsernameConfig{
@@ -218,14 +174,14 @@ func TestPostgreSQL_CreateUser(t *testing.T) {
return
}
if err = testCredsExist(t, connURL, username, password); err != nil {
if err = testCredsExist(t, db.ConnectionURL, username, password); err != nil {
t.Fatalf("Could not connect with new credentials: %s", err)
}
// Ensure that the role doesn't expire immediately
time.Sleep(2 * time.Second)
if err = testCredsExist(t, connURL, username, password); err != nil {
if err = testCredsExist(t, db.ConnectionURL, username, password); err != nil {
t.Fatalf("Could not connect with new credentials: %s", err)
}
})
@@ -252,24 +208,9 @@ func TestPostgreSQL_RenewUser(t *testing.T) {
}
// Shared test container for speed - there should not be any overlap between the tests
cleanup, connURL := preparePostgresTestContainer(t)
db, cleanup := getPostgreSQL(t, nil)
defer cleanup()
connectionDetails := map[string]interface{}{
"connection_url": connURL,
}
db := new()
// Give a timeout just in case the test decides to be problematic
initCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
_, err := db.Init(initCtx, connectionDetails, true)
if err != nil {
t.Fatalf("err: %s", err)
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
statements := dbplugin.Statements{
@@ -291,7 +232,7 @@ func TestPostgreSQL_RenewUser(t *testing.T) {
t.Fatalf("err: %s", err)
}
if err = testCredsExist(t, connURL, username, password); err != nil {
if err = testCredsExist(t, db.ConnectionURL, username, password); err != nil {
t.Fatalf("Could not connect with new credentials: %s", err)
}
@@ -303,7 +244,7 @@ func TestPostgreSQL_RenewUser(t *testing.T) {
// Sleep longer than the initial expiration time
time.Sleep(2 * time.Second)
if err = testCredsExist(t, connURL, username, password); err != nil {
if err = testCredsExist(t, db.ConnectionURL, username, password); err != nil {
t.Fatalf("Could not connect with new credentials: %s", err)
}
})
@@ -331,9 +272,8 @@ func TestPostgreSQL_RotateRootCredentials(t *testing.T) {
for name, test := range tests {
t.Run(name, func(t *testing.T) {
cleanup, connURL := preparePostgresTestContainer(t)
cleanup, connURL := postgresql.PrepareTestContainer(t, "latest")
defer cleanup()
connURL = strings.Replace(connURL, "postgres:secret", `{{username}}:{{password}}`, -1)
connectionDetails := map[string]interface{}{
@@ -344,7 +284,6 @@ func TestPostgreSQL_RotateRootCredentials(t *testing.T) {
}
db := new()
connProducer := db.SQLConnectionProducer
// Give a timeout just in case the test decides to be problematic
@@ -400,24 +339,9 @@ func TestPostgreSQL_RevokeUser(t *testing.T) {
}
// Shared test container for speed - there should not be any overlap between the tests
cleanup, connURL := preparePostgresTestContainer(t)
db, cleanup := getPostgreSQL(t, nil)
defer cleanup()
connectionDetails := map[string]interface{}{
"connection_url": connURL,
}
db := new()
// Give a timeout just in case the test decides to be problematic
initCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
_, err := db.Init(initCtx, connectionDetails, true)
if err != nil {
t.Fatalf("err: %s", err)
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
statements := dbplugin.Statements{
@@ -435,7 +359,7 @@ func TestPostgreSQL_RevokeUser(t *testing.T) {
t.Fatalf("err: %s", err)
}
if err = testCredsExist(t, connURL, username, password); err != nil {
if err = testCredsExist(t, db.ConnectionURL, username, password); err != nil {
t.Fatalf("Could not connect with new credentials: %s", err)
}
@@ -445,7 +369,7 @@ func TestPostgreSQL_RevokeUser(t *testing.T) {
t.Fatalf("err: %s", err)
}
if err := testCredsExist(t, connURL, username, password); err == nil {
if err := testCredsExist(t, db.ConnectionURL, username, password); err == nil {
t.Fatal("Credentials were not revoked")
}
})
@@ -530,29 +454,13 @@ func TestPostgresSQL_SetCredentials(t *testing.T) {
for name, test := range tests {
t.Run(name, func(t *testing.T) {
// Shared test container for speed - there should not be any overlap between the tests
cleanup, connURL := preparePostgresTestContainer(t)
db, cleanup := getPostgreSQL(t, nil)
defer cleanup()
// create the database user
dbUser := "vaultstatictest"
initPassword := "password"
createTestPGUser(t, connURL, dbUser, initPassword, testRoleStaticCreate)
connectionDetails := map[string]interface{}{
"connection_url": connURL,
}
db := new()
// Give a timeout just in case the test decides to be problematic
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
_, err := db.Init(ctx, connectionDetails, true)
if err != nil {
t.Fatalf("err: %s", err)
}
createTestPGUser(t, db.ConnectionURL, dbUser, initPassword, testRoleStaticCreate)
statements := dbplugin.Statements{
Rotation: test.rotationStmts,
@@ -568,20 +476,20 @@ func TestPostgresSQL_SetCredentials(t *testing.T) {
Password: password,
}
if err := testCredsExist(t, connURL, dbUser, initPassword); err != nil {
if err := testCredsExist(t, db.ConnectionURL, dbUser, initPassword); err != nil {
t.Fatalf("Could not connect with initial credentials: %s", err)
}
username, password, err := db.SetCredentials(ctx, statements, usernameConfig)
username, password, err := db.SetCredentials(context.Background(), statements, usernameConfig)
if err != nil {
t.Fatalf("err: %s", err)
}
if err := testCredsExist(t, connURL, username, password); err != nil {
if err := testCredsExist(t, db.ConnectionURL, username, password); err != nil {
t.Fatalf("Could not connect with new credentials: %s", err)
}
if err := testCredsExist(t, connURL, username, initPassword); err == nil {
if err := testCredsExist(t, db.ConnectionURL, username, initPassword); err == nil {
t.Fatalf("Should not be able to connect with initial credentials")
}
})