Skip to content

Commit

Permalink
Merge pull request #4 from sworisbreathing/fix-delete-user-when-using…
Browse files Browse the repository at this point in the history
…-temporary-credentials

Fix error changing ownership when deleting a user, if the current user has temporary credentials
  • Loading branch information
winglot authored Jul 15, 2021
2 parents e5b0323 + 2773bab commit 4f02a78
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 6 deletions.
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)
}
}

0 comments on commit 4f02a78

Please sign in to comment.