Skip to content
This repository was archived by the owner on Jan 17, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions redshift/resource_redshift_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"database/sql"
"fmt"
"log"
"regexp"
"strconv"
"strings"

Expand All @@ -26,6 +27,17 @@ const (
defaultUserSuperuserSyslogAccess = "UNRESTRICTED"
)

// When authenticating using temporary credentials obtained by GetClusterCredentials,
// the resulting username is prefixed with either "IAM:"" or "IAMA:"
// This regexp is designed to match either prefix.
// See https://docs.aws.amazon.com/redshift/latest/APIReference/API_GetClusterCredentials.html
var temporaryCredentialsUsernamePrefixRegexp = regexp.MustCompile("^(?:IAMA?:)")

// Resolve the "real" username by stripping the temporary credentials prefix
func permanentUsername(username string) string {
return temporaryCredentialsUsernamePrefixRegexp.ReplaceAllString(username, "")
}

func redshiftUser() *schema.Resource {
return &schema.Resource{
Description: `
Expand Down Expand Up @@ -292,6 +304,7 @@ func resourceRedshiftUserReadImpl(db *DBConnection, d *schema.ResourceData) erro
func resourceRedshiftUserDelete(db *DBConnection, d *schema.ResourceData) error {
useSysID := d.Id()
userName := d.Get(userNameAttr).(string)
newOwnerName := permanentUsername(db.client.config.Username)

tx, err := startTransaction(db.client, "")
if err != nil {
Expand All @@ -304,28 +317,28 @@ func resourceRedshiftUserDelete(db *DBConnection, d *schema.ResourceData) error
FROM (
-- Functions owned by the user
SELECT pgu.usesysid,
'alter function ' || QUOTE_IDENT(nc.nspname) || '.' ||textin (regprocedureout (pproc.oid::regprocedure)) || ' owner to '
'alter function ' || QUOTE_IDENT(nc.nspname) || '.' ||textin (regprocedureout (pproc.oid::regprocedure)) || ' owner to ' || $2
FROM pg_proc pproc,pg_user pgu,pg_namespace nc
WHERE pproc.pronamespace = nc.oid
AND pproc.proowner = pgu.usesysid
UNION ALL
-- Databases owned by the user
SELECT pgu.usesysid,
'alter database ' || QUOTE_IDENT(pgd.datname) || ' owner to '
'alter database ' || QUOTE_IDENT(pgd.datname) || ' owner to ' || $2
FROM pg_database pgd,
pg_user pgu
WHERE pgd.datdba = pgu.usesysid
UNION ALL
-- Schemas owned by the user
SELECT pgu.usesysid,
'alter schema '|| QUOTE_IDENT(pgn.nspname) ||' owner to '
'alter schema '|| QUOTE_IDENT(pgn.nspname) ||' owner to ' || $2
FROM pg_namespace pgn,
pg_user pgu
WHERE pgn.nspowner = pgu.usesysid
UNION ALL
-- Tables or Views owned by the user
SELECT pgu.usesysid,
'alter table ' || QUOTE_IDENT(nc.nspname) || '.' || QUOTE_IDENT(pgc.relname) || ' owner to '
'alter table ' || QUOTE_IDENT(nc.nspname) || '.' || QUOTE_IDENT(pgc.relname) || ' owner to ' || $2
FROM pg_class pgc,
pg_user pgu,
pg_namespace nc
Expand All @@ -337,7 +350,7 @@ func resourceRedshiftUserDelete(db *DBConnection, d *schema.ResourceData) error
OWNER("userid", "ddl")
WHERE owner.userid = $1;`

rows, err := tx.Query(reassignOwnerGenerator, useSysID)
rows, err := tx.Query(reassignOwnerGenerator, useSysID, pq.QuoteIdentifier(newOwnerName))
if err != nil {
return err
}
Expand All @@ -354,7 +367,7 @@ func resourceRedshiftUserDelete(db *DBConnection, d *schema.ResourceData) error
}

for _, statement := range reassignStatements {
if _, err := tx.Exec(statement + pq.QuoteIdentifier(db.client.config.Username)); err != nil {
if _, err := tx.Exec(statement); err != nil {
log.Printf("error: %#v", err)
return err
}
Expand Down
13 changes: 13 additions & 0 deletions redshift/resource_redshift_user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,3 +271,16 @@ resource "redshift_user" "user_superuser" {
password = "FooBarBaz123"
}
`

func TestPermanentUsername(t *testing.T) {
expected := "user"
if result := permanentUsername(expected); result != expected {
t.Fatalf("Calling permanentUsername on a non-prefixed username should return the username. Expected %s but was %s", expected, result)
}
if result := permanentUsername(fmt.Sprintf("IAM:%s", expected)); result != expected {
t.Fatalf("permanentUsername should strip \"IAM:\" prefix. Expected %s but was %s", expected, result)
}
if result := permanentUsername(fmt.Sprintf("IAMA:%s", expected)); result != expected {
t.Fatalf("permanentUsername should strip \"IAMA:\" prefix. Expected %s but was %s", expected, result)
}
}