Skip to content

Commit

Permalink
Support for users in redshift_default_privileges resources
Browse files Browse the repository at this point in the history
  • Loading branch information
winglot committed Feb 11, 2022
1 parent bda65cb commit 9c41446
Show file tree
Hide file tree
Showing 2 changed files with 175 additions and 89 deletions.
125 changes: 95 additions & 30 deletions redshift/resource_redshift_default_privileges.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
)

const (
defaultPrivilegesUserAttr = "user"
defaultPrivilegesGroupAttr = "group"
defaultPrivilegesOwnerAttr = "owner"
defaultPrivilegesSchemaAttr = "schema"
Expand Down Expand Up @@ -49,19 +50,27 @@ func redshiftDefaultPrivileges() *schema.Resource {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Description: "The database schema to set default privileges for this group.",
Description: "If set, the specified default privileges are applied to new objects created in the specified schema. In this case, the user or user group that is the target of ALTER DEFAULT PRIVILEGES must have CREATE privilege for the specified schema. Default privileges that are specific to a schema are added to existing global default privileges. By default, default privileges are applied globally to the entire database.",
},
defaultPrivilegesGroupAttr: {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "The name of the group to which grant default privileges on.",
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ExactlyOneOf: []string{defaultPrivilegesGroupAttr, defaultPrivilegesUserAttr},
Description: "The name of the group to which the specified default privileges are applied.",
},
defaultPrivilegesUserAttr: {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ExactlyOneOf: []string{defaultPrivilegesGroupAttr, defaultPrivilegesUserAttr},
Description: "The name of the user to which the specified default privileges are applied.",
},
defaultPrivilegesOwnerAttr: {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "Target user for which to alter default privileges.",
Description: "The name of the user for which default privileges are defined. Only a superuser can specify default privileges for other users.",
},
defaultPrivilegesObjectTypeAttr: {
Type: schema.TypeString,
Expand Down Expand Up @@ -147,8 +156,9 @@ func resourceRedshiftDefaultPrivilegesRead(db *DBConnection, d *schema.ResourceD
}

func resourceRedshiftDefaultPrivilegesReadImpl(db *DBConnection, d *schema.ResourceData) error {
var entityID int
var entityIsUser bool
schemaName, schemaNameSet := d.GetOk(defaultPrivilegesSchemaAttr)
groupName := d.Get(defaultPrivilegesGroupAttr).(string)
ownerName := d.Get(defaultPrivilegesOwnerAttr).(string)

tx, err := startTransaction(db.client, "")
Expand All @@ -166,10 +176,20 @@ func resourceRedshiftDefaultPrivilegesReadImpl(db *DBConnection, d *schema.Resou
}
}

log.Printf("[DEBUG] getting ID for group %s\n", groupName)
groupID, err := getGroupIDFromName(tx, groupName)
if err != nil {
return fmt.Errorf("failed to get group ID: %w", err)
if groupName, groupNameSet := d.GetOk(defaultPrivilegesGroupAttr); groupNameSet {
log.Printf("[DEBUG] getting ID for group %s\n", groupName.(string))
entityID, err = getGroupIDFromName(tx, groupName.(string))
entityIsUser = false
if err != nil {
return fmt.Errorf("failed to get group ID: %w", err)
}
} else if userName, userNameSet := d.GetOk(defaultPrivilegesUserAttr); userNameSet {
log.Printf("[DEBUG] getting ID for user %s\n", userName.(string))
entityID, err = getUserIDFromName(tx, userName.(string))
entityIsUser = true
if err != nil {
return fmt.Errorf("failed to get user ID: %w", err)
}
}

log.Printf("[DEBUG] getting ID for owner %s\n", ownerName)
Expand All @@ -181,7 +201,7 @@ func resourceRedshiftDefaultPrivilegesReadImpl(db *DBConnection, d *schema.Resou
switch strings.ToUpper(d.Get(defaultPrivilegesObjectTypeAttr).(string)) {
case "TABLE":
log.Println("[DEBUG] reading default privileges")
if err := readGroupTableDefaultPrivileges(tx, d, groupID, schemaID, ownerID); err != nil {
if err := readGroupTableDefaultPrivileges(tx, d, entityID, schemaID, ownerID, entityIsUser); err != nil {
return fmt.Errorf("failed to read table privileges: %w", err)
}
}
Expand All @@ -193,9 +213,29 @@ func resourceRedshiftDefaultPrivilegesReadImpl(db *DBConnection, d *schema.Resou
return nil
}

func readGroupTableDefaultPrivileges(tx *sql.Tx, d *schema.ResourceData, groupID, schemaID, ownerID int) error {
func readGroupTableDefaultPrivileges(tx *sql.Tx, d *schema.ResourceData, entityID, schemaID, ownerID int, entityIsUser bool) error {
var tableSelect, tableUpdate, tableInsert, tableDelete, tableDrop, tableReferences bool
tableDefaultPrivilegeQuery := `
var query string

if entityIsUser {
query = `
SELECT
decode(charindex('r',split_part(split_part(regexp_replace(array_to_string(defaclacl, '|'), 'group '||u.usename), u.usename||'=', 2) ,'/',1)),0,0,1) as select,
decode(charindex('w',split_part(split_part(regexp_replace(array_to_string(defaclacl, '|'), 'group '||u.usename), u.usename||'=', 2) ,'/',1)),0,0,1) as update,
decode(charindex('a',split_part(split_part(regexp_replace(array_to_string(defaclacl, '|'), 'group '||u.usename), u.usename||'=', 2) ,'/',1)),0,0,1) as insert,
decode(charindex('d',split_part(split_part(regexp_replace(array_to_string(defaclacl, '|'), 'group '||u.usename), u.usename||'=', 2) ,'/',1)),0,0,1) as delete,
decode(charindex('D',split_part(split_part(regexp_replace(array_to_string(defaclacl, '|'), 'group '||u.usename), u.usename||'=', 2) ,'/',1)),0,0,1) as drop,
decode(charindex('x',split_part(split_part(regexp_replace(array_to_string(defaclacl, '|'), 'group '||u.usename), u.usename||'=', 2) ,'/',1)),0,0,1) as references
FROM pg_user u, pg_default_acl acl
WHERE
acl.defaclnamespace = $1
AND regexp_replace(array_to_string(acl.defaclacl, '|'), 'group '||u.usename) LIKE '%' || u.usename || '=%'
AND u.usesysid = $2
AND acl.defaclobjtype = $3
AND acl.defacluser = $4
`
} else {
query = `
SELECT
decode(charindex('r',split_part(split_part(array_to_string(defaclacl, '|'),'group ' || gr.groname,2 ) ,'/',1)),0,0,1) as select,
decode(charindex('w',split_part(split_part(array_to_string(defaclacl, '|'),'group ' || gr.groname,2 ) ,'/',1)),0,0,1) as update,
Expand All @@ -209,16 +249,18 @@ func readGroupTableDefaultPrivileges(tx *sql.Tx, d *schema.ResourceData, groupID
AND array_to_string(acl.defaclacl, '|') LIKE '%' || 'group ' || gr.groname || '=%'
AND gr.grosysid = $2
AND acl.defaclobjtype = $3
AND acl.defacluser = $4`
AND acl.defacluser = $4
`
}

if err := tx.QueryRow(tableDefaultPrivilegeQuery, schemaID, groupID, defaultPrivilegesObjectTypesCodes["table"], ownerID).Scan(
if err := tx.QueryRow(query, schemaID, entityID, defaultPrivilegesObjectTypesCodes["table"], ownerID).Scan(
&tableSelect,
&tableUpdate,
&tableInsert,
&tableDelete,
&tableDrop,
&tableReferences); err != nil && err != sql.ErrNoRows {
return fmt.Errorf("failed to collect group privileges: %w", err)
return fmt.Errorf("failed to collect privileges: %w", err)
}

privileges := []string{}
Expand All @@ -229,66 +271,89 @@ func readGroupTableDefaultPrivileges(tx *sql.Tx, d *schema.ResourceData, groupID
appendIfTrue(tableDrop, "drop", &privileges)
appendIfTrue(tableReferences, "references", &privileges)

log.Printf("[DEBUG] Collected privileges for group ID %d: %v\n", groupID, privileges)
log.Printf("[DEBUG] Collected privileges for ID %d: %v\n", entityID, privileges)

d.Set(defaultPrivilegesPrivilegesAttr, privileges)

return nil
}

func generateDefaultPrivilegesID(d *schema.ResourceData) string {
schemaName, schemaNameSet := d.GetOk(defaultPrivilegesSchemaAttr)
var entityName, schemaName string

groupName := d.Get(defaultPrivilegesGroupAttr).(string)
ownerName := d.Get(defaultPrivilegesOwnerAttr).(string)
objectType := d.Get(defaultPrivilegesObjectTypeAttr).(string)
if groupName, isGroup := d.GetOk(defaultPrivilegesGroupAttr); isGroup {
entityName = fmt.Sprintf("gn:%s", groupName.(string))
} else if userName, isUser := d.GetOk(defaultPrivilegesUserAttr); isUser {
entityName = fmt.Sprintf("un:%s", userName.(string))
}

if !schemaNameSet {
if schemaNameRaw, schemaNameSet := d.GetOk(defaultPrivilegesSchemaAttr); schemaNameSet {
schemaName = fmt.Sprintf("sn:%s", schemaNameRaw.(string))
} else {
schemaName = "noschema"
}

ownerName := fmt.Sprintf("on:%s", d.Get(defaultPrivilegesOwnerAttr).(string))
objectType := fmt.Sprintf("ot:%s", d.Get(defaultPrivilegesObjectTypeAttr).(string))

return strings.Join([]string{
groupName, schemaName.(string), ownerName, objectType,
entityName, schemaName, ownerName, objectType,
}, "_")
}

func createAlterDefaultsGrantQuery(d *schema.ResourceData, privileges []string) string {
schemaName, schemaNameSet := d.GetOk(defaultPrivilegesSchemaAttr)
groupName := d.Get(defaultPrivilegesGroupAttr).(string)
ownerName := d.Get(defaultPrivilegesOwnerAttr).(string)
objectType := strings.ToUpper(d.Get(defaultPrivilegesObjectTypeAttr).(string))

var entityName, toWhomIndicator string
if groupName, isGroup := d.GetOk(defaultPrivilegesGroupAttr); isGroup {
entityName = groupName.(string)
toWhomIndicator = "GROUP"
} else if userName, isUser := d.GetOk(defaultPrivilegesUserAttr); isUser {
entityName = userName.(string)
}

alterQuery := fmt.Sprintf("ALTER DEFAULT PRIVILEGES FOR USER %s", pq.QuoteIdentifier(ownerName))

if schemaNameSet {
alterQuery = fmt.Sprintf("%s IN SCHEMA %s", alterQuery, pq.QuoteIdentifier(schemaName.(string)))
}

return fmt.Sprintf(
"%s GRANT %s ON %sS TO GROUP %s",
"%s GRANT %s ON %sS TO %s %s",
alterQuery,
strings.Join(privileges, ","),
objectType,
pq.QuoteIdentifier(groupName),
toWhomIndicator,
pq.QuoteIdentifier(entityName),
)
}

func createAlterDefaultsRevokeQuery(d *schema.ResourceData) string {
schemaName, schemaNameSet := d.GetOk(defaultPrivilegesSchemaAttr)
groupName := d.Get(defaultPrivilegesGroupAttr).(string)
ownerName := d.Get(defaultPrivilegesOwnerAttr).(string)
objectType := strings.ToUpper(d.Get(defaultPrivilegesObjectTypeAttr).(string))

var entityName, fromWhomIndicator string
if groupName, isGroup := d.GetOk(defaultPrivilegesGroupAttr); isGroup {
entityName = groupName.(string)
fromWhomIndicator = "GROUP"
} else if userName, isUser := d.GetOk(defaultPrivilegesUserAttr); isUser {
entityName = userName.(string)
}

alterQuery := fmt.Sprintf("ALTER DEFAULT PRIVILEGES FOR USER %s", pq.QuoteIdentifier(ownerName))

if schemaNameSet {
alterQuery = fmt.Sprintf("%s IN SCHEMA %s", alterQuery, pq.QuoteIdentifier(schemaName.(string)))
}

return fmt.Sprintf(
"%s REVOKE ALL PRIVILEGES ON %sS FROM GROUP %s",
"%s REVOKE ALL PRIVILEGES ON %sS FROM %s %s",
alterQuery,
objectType,
pq.QuoteIdentifier(groupName),
fromWhomIndicator,
pq.QuoteIdentifier(entityName),
)
}
Loading

0 comments on commit 9c41446

Please sign in to comment.