diff --git a/plugins/database/mysql/mysql_test.go b/plugins/database/mysql/mysql_test.go index 9825408d8212..8f7ea22ade66 100644 --- a/plugins/database/mysql/mysql_test.go +++ b/plugins/database/mysql/mysql_test.go @@ -20,7 +20,7 @@ import ( var _ dbplugin.Database = (*MySQL)(nil) -func prepareMySQLTestContainer(t *testing.T, legacy bool) (cleanup func(), retURL string) { +func prepareMySQLTestContainer(t *testing.T, legacy bool, pw string) (cleanup func(), retURL string) { if os.Getenv("MYSQL_URL") != "" { return func() {}, os.Getenv("MYSQL_URL") } @@ -35,7 +35,7 @@ func prepareMySQLTestContainer(t *testing.T, legacy bool) (cleanup func(), retUR imageVersion = "5.6" } - resource, err := pool.Run("mysql", imageVersion, []string{"MYSQL_ROOT_PASSWORD=secret"}) + resource, err := pool.Run("mysql", imageVersion, []string{"MYSQL_ROOT_PASSWORD=" + pw}) if err != nil { t.Fatalf("Could not start local MySQL docker container: %s", err) } @@ -44,7 +44,7 @@ func prepareMySQLTestContainer(t *testing.T, legacy bool) (cleanup func(), retUR docker.CleanupResource(t, pool, resource) } - retURL = fmt.Sprintf("root:secret@(localhost:%s)/mysql?parseTime=true", resource.GetPort("3306/tcp")) + retURL = fmt.Sprintf("root:%s@(localhost:%s)/mysql?parseTime=true", pw, resource.GetPort("3306/tcp")) // exponential backoff-retry if err = pool.Retry(func() error { @@ -65,7 +65,7 @@ func prepareMySQLTestContainer(t *testing.T, legacy bool) (cleanup func(), retUR } func TestMySQL_Initialize(t *testing.T) { - cleanup, connURL := prepareMySQLTestContainer(t, false) + cleanup, connURL := prepareMySQLTestContainer(t, false, "secret") defer cleanup() connectionDetails := map[string]interface{}{ @@ -100,7 +100,7 @@ func TestMySQL_Initialize(t *testing.T) { } func TestMySQL_CreateUser(t *testing.T) { - cleanup, connURL := prepareMySQLTestContainer(t, false) + cleanup, connURL := prepareMySQLTestContainer(t, false, "secret") defer cleanup() connectionDetails := map[string]interface{}{ @@ -162,7 +162,7 @@ func TestMySQL_CreateUser(t *testing.T) { } func TestMySQL_CreateUser_Legacy(t *testing.T) { - cleanup, connURL := prepareMySQLTestContainer(t, true) + cleanup, connURL := prepareMySQLTestContainer(t, true, "secret") defer cleanup() connectionDetails := map[string]interface{}{ @@ -211,7 +211,7 @@ func TestMySQL_CreateUser_Legacy(t *testing.T) { } func TestMySQL_RotateRootCredentials(t *testing.T) { - cleanup, connURL := prepareMySQLTestContainer(t, false) + cleanup, connURL := prepareMySQLTestContainer(t, false, "secret") defer cleanup() connURL = strings.Replace(connURL, "root:secret", `{{username}}:{{password}}`, -1) @@ -247,7 +247,7 @@ func TestMySQL_RotateRootCredentials(t *testing.T) { } func TestMySQL_RevokeUser(t *testing.T) { - cleanup, connURL := prepareMySQLTestContainer(t, false) + cleanup, connURL := prepareMySQLTestContainer(t, false, "secret") defer cleanup() connectionDetails := map[string]interface{}{ @@ -311,7 +311,7 @@ func TestMySQL_RevokeUser(t *testing.T) { } func TestMySQL_SetCredentials(t *testing.T) { - cleanup, connURL := prepareMySQLTestContainer(t, false) + cleanup, connURL := prepareMySQLTestContainer(t, false, "secret") defer cleanup() // create the database user and verify we can access @@ -368,6 +368,35 @@ func TestMySQL_SetCredentials(t *testing.T) { } } +func TestMySQL_Initialize_ReservedChars(t *testing.T) { + pw := "#secret!%25#{@}" + cleanup, connURL := prepareMySQLTestContainer(t, false, pw) + defer cleanup() + + // Revert password set to test replacement by db.Init + connURL = strings.ReplaceAll(connURL, pw, "{{password}}") + + connectionDetails := map[string]interface{}{ + "connection_url": connURL, + "password": pw, + } + + db := new(MetadataLen, MetadataLen, UsernameLen) + _, err := db.Init(context.Background(), connectionDetails, true) + if err != nil { + t.Fatalf("err: %s", err) + } + + if !db.Initialized { + t.Fatal("Database should be initialized") + } + + err = db.Close() + if err != nil { + t.Fatalf("err: %s", err) + } +} + func testCredsExist(t testing.TB, connURL, username, password string) error { // Log in with the new creds connURL = strings.Replace(connURL, "root:secret", fmt.Sprintf("%s:%s", username, password), 1) diff --git a/sdk/database/helper/connutil/sql.go b/sdk/database/helper/connutil/sql.go index fba5c5fce8ef..796df8b16f8d 100644 --- a/sdk/database/helper/connutil/sql.go +++ b/sdk/database/helper/connutil/sql.go @@ -55,11 +55,17 @@ func (c *SQLConnectionProducer) Init(ctx context.Context, conf map[string]interf return nil, fmt.Errorf("connection_url cannot be empty") } + // Don't escape special characters for MySQL password + password := c.Password + if c.Type != "mysql" { + password = url.PathEscape(c.Password) + } + // QueryHelper doesn't do any SQL escaping, but if it starts to do so // then maybe we won't be able to use it to do URL substitution any more. c.ConnectionURL = dbutil.QueryHelper(c.ConnectionURL, map[string]string{ "username": url.PathEscape(c.Username), - "password": url.PathEscape(c.Password), + "password": password, }) if c.MaxOpenConnections == 0 { diff --git a/vendor/github.com/hashicorp/vault/sdk/database/helper/connutil/sql.go b/vendor/github.com/hashicorp/vault/sdk/database/helper/connutil/sql.go index fba5c5fce8ef..e5e078a71346 100644 --- a/vendor/github.com/hashicorp/vault/sdk/database/helper/connutil/sql.go +++ b/vendor/github.com/hashicorp/vault/sdk/database/helper/connutil/sql.go @@ -55,11 +55,17 @@ func (c *SQLConnectionProducer) Init(ctx context.Context, conf map[string]interf return nil, fmt.Errorf("connection_url cannot be empty") } + // Don't escape special characters for MySQL password + password := c.Password + if c.Type != "mysql" { + //password = url.PathEscape(c.Password) + } + // QueryHelper doesn't do any SQL escaping, but if it starts to do so // then maybe we won't be able to use it to do URL substitution any more. c.ConnectionURL = dbutil.QueryHelper(c.ConnectionURL, map[string]string{ "username": url.PathEscape(c.Username), - "password": url.PathEscape(c.Password), + "password": password, }) if c.MaxOpenConnections == 0 {