Expand postgresql_role support to include all known options.

This commit is contained in:
Sean Chittenden 2016-12-12 01:37:00 -08:00
parent 4c6c52ee8d
commit 1a93309e50
No known key found for this signature in database
GPG Key ID: 4EBC9DC16C2E5E16
1 changed files with 253 additions and 60 deletions

View File

@ -181,10 +181,7 @@ func resourcePostgreSQLRoleCreate(d *schema.ResourceData, meta interface{}) erro
} else { } else {
createOpts = append(createOpts, "UNENCRYPTED") createOpts = append(createOpts, "UNENCRYPTED")
} }
escapedPassword := strconv.Quote(val) createOpts = append(createOpts, fmt.Sprintf("%s '%s'", opt.sqlKey, pqQuoteLiteral(val)))
escapedPassword = strings.TrimLeft(escapedPassword, `"`)
escapedPassword = strings.TrimRight(escapedPassword, `"`)
createOpts = append(createOpts, fmt.Sprintf("%s '%s'", opt.sqlKey, escapedPassword))
} }
case opt.hclKey == roleValidUntilAttr: case opt.hclKey == roleValidUntilAttr:
switch { switch {
@ -211,7 +208,6 @@ func resourcePostgreSQLRoleCreate(d *schema.ResourceData, meta interface{}) erro
continue continue
} }
val := d.Get(opt.hclKey).(bool) val := d.Get(opt.hclKey).(bool)
valStr := opt.sqlKeyDisable valStr := opt.sqlKeyDisable
if val { if val {
valStr = opt.sqlKeyEnable valStr = opt.sqlKeyEnable
@ -245,7 +241,6 @@ func resourcePostgreSQLRoleDelete(d *schema.ResourceData, meta interface{}) erro
defer conn.Close() defer conn.Close()
roleName := d.Get(roleNameAttr).(string) roleName := d.Get(roleNameAttr).(string)
query := fmt.Sprintf("DROP ROLE %s", pq.QuoteIdentifier(roleName)) query := fmt.Sprintf("DROP ROLE %s", pq.QuoteIdentifier(roleName))
_, err = conn.Query(query) _, err = conn.Query(query)
if err != nil { if err != nil {
@ -265,91 +260,289 @@ func resourcePostgreSQLRoleRead(d *schema.ResourceData, meta interface{}) error
} }
defer conn.Close() defer conn.Close()
roleName := d.Get(roleNameAttr).(string) roleId := d.Id()
if roleName == "" { var roleSuperuser, roleInherit, roleCreateRole, roleCreateDB, roleCanLogin, roleReplication, roleBypassRLS bool
roleName = d.Id() var roleConnLimit int
} var roleName, roleValidUntil string
err = conn.QueryRow("SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolconnlimit, COALESCE(rolvaliduntil::TEXT, 'infinity'), rolbypassrls FROM pg_catalog.pg_roles WHERE rolname=$1", roleId).Scan(&roleName, &roleSuperuser, &roleInherit, &roleCreateRole, &roleCreateDB, &roleCanLogin, &roleReplication, &roleConnLimit, &roleValidUntil, &roleBypassRLS)
var roleCanLogin bool
err = conn.QueryRow("SELECT rolcanlogin FROM pg_roles WHERE rolname=$1", roleName).Scan(&roleCanLogin)
switch { switch {
case err == sql.ErrNoRows: case err == sql.ErrNoRows:
log.Printf("[WARN] PostgreSQL database (%s) not found", d.Id()) log.Printf("[WARN] PostgreSQL role (%s) not found", roleId)
d.SetId("") d.SetId("")
return nil return nil
case err != nil: case err != nil:
return errwrap.Wrapf("Error reading role: {{err}}", err) return errwrap.Wrapf("Error reading role: {{err}}", err)
default: default:
d.Set(roleNameAttr, roleName) d.Set(roleNameAttr, roleName)
d.Set(roleBypassRLSAttr, roleBypassRLS)
d.Set(roleConnLimitAttr, roleConnLimit)
d.Set(roleCreateDBAttr, roleCreateDB)
d.Set(roleCreateRoleAttr, roleCreateRole)
d.Set(roleEncryptedPassAttr, true)
d.Set(roleInheritAttr, roleInherit)
d.Set(roleLoginAttr, roleCanLogin) d.Set(roleLoginAttr, roleCanLogin)
d.Set("encrypted", true) d.Set(roleReplicationAttr, roleReplication)
d.Set(roleSuperuserAttr, roleSuperuser)
d.Set(roleValidUntilAttr, roleValidUntil)
d.SetId(roleName) d.SetId(roleName)
}
if !roleSuperuser {
// Return early if not superuser user
return nil
}
var rolePassword string
err = conn.QueryRow("SELECT COALESCE(passwd, '') FROM pg_catalog.pg_shadow AS s WHERE s.usename = $1", roleId).Scan(&rolePassword)
switch {
case err == sql.ErrNoRows:
return fmt.Errorf("PostgreSQL role (%s) not found in shadow database: {{err}}", roleId)
case err != nil:
return errwrap.Wrapf("Error reading role: {{err}}", err)
default:
d.Set(rolePasswordAttr, rolePassword)
return nil return nil
} }
} }
func resourcePostgreSQLRoleUpdate(d *schema.ResourceData, meta interface{}) error { func resourcePostgreSQLRoleUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Client) c := meta.(*Client)
conn, err := client.Connect() conn, err := c.Connect()
if err != nil { if err != nil {
return err return err
} }
defer conn.Close() defer conn.Close()
d.Partial(true) if err := setRoleName(conn, d); err != nil {
return err
roleName := d.Get(roleNameAttr).(string)
if d.HasChange(roleLoginAttr) {
loginAttr := getLoginStr(d.Get(roleLoginAttr).(bool))
query := fmt.Sprintf("ALTER ROLE %s %s", pq.QuoteIdentifier(roleName), pq.QuoteIdentifier(loginAttr))
_, err := conn.Query(query)
if err != nil {
return errwrap.Wrapf("Error updating login attribute for role: {{err}}", err)
}
d.SetPartial(roleLoginAttr)
} }
password := d.Get(rolePasswordAttr).(string) if err := setRoleBypassRLS(conn, d); err != nil {
if d.HasChange(rolePasswordAttr) { return err
encryptedCfg := getEncryptedStr(d.Get("encrypted").(bool))
query := fmt.Sprintf("ALTER ROLE %s %s PASSWORD '%s'", pq.QuoteIdentifier(roleName), encryptedCfg, password)
_, err := conn.Query(query)
if err != nil {
return errwrap.Wrapf("Error updating password attribute for role: {{err}}", err)
}
d.SetPartial(rolePasswordAttr)
} }
if d.HasChange("encrypted") { if err := setRoleConnLimit(conn, d); err != nil {
encryptedCfg := getEncryptedStr(d.Get("encrypted").(bool)) return err
}
query := fmt.Sprintf("ALTER ROLE %s %s PASSWORD '%s'", pq.QuoteIdentifier(roleName), encryptedCfg, password)
_, err := conn.Query(query) if err := setRoleCreateDB(conn, d); err != nil {
if err != nil { return err
return errwrap.Wrapf("Error updating encrypted attribute for role: {{err}}", err) }
}
if err := setRoleCreateRole(conn, d); err != nil {
d.SetPartial("encrypted") return err
}
if err := setRoleInherit(conn, d); err != nil {
return err
}
if err := setRoleLogin(conn, d); err != nil {
return err
}
if err := setRoleReplication(conn, d); err != nil {
return err
}
if err := setRoleSuperuser(conn, d); err != nil {
return err
}
if err := setRoleValidUntil(conn, d); err != nil {
return err
} }
d.Partial(false)
return resourcePostgreSQLRoleRead(d, meta) return resourcePostgreSQLRoleRead(d, meta)
} }
func getLoginStr(canLogin bool) string { func setRoleName(conn *sql.DB, d *schema.ResourceData) error {
if canLogin { if !d.HasChange(roleNameAttr) {
return "login" return nil
} }
return "nologin"
oraw, nraw := d.GetChange(roleNameAttr)
o := oraw.(string)
n := nraw.(string)
if n == "" {
return errors.New("Error setting role name to an empty string")
}
query := fmt.Sprintf("ALTER ROLE %s RENAME TO %s", pq.QuoteIdentifier(o), pq.QuoteIdentifier(n))
if _, err := conn.Query(query); err != nil {
return errwrap.Wrapf("Error updating role NAME: {{err}}", err)
}
d.SetId(n)
return nil
} }
func getEncryptedStr(isEncrypted bool) string { func setRoleBypassRLS(conn *sql.DB, d *schema.ResourceData) error {
if isEncrypted { if !d.HasChange(roleBypassRLSAttr) {
return "encrypted" return nil
} }
return "unencrypted"
bypassRLS := d.Get(roleBypassRLSAttr).(bool)
tok := "NOBYPASSRLS"
if bypassRLS {
tok = "BYPASSRLS"
}
roleName := d.Get(roleNameAttr).(string)
query := fmt.Sprintf("ALTER ROLE %s WITH %s", pq.QuoteIdentifier(roleName), tok)
if _, err := conn.Query(query); err != nil {
return errwrap.Wrapf("Error updating role BYPASSRLS: {{err}}", err)
}
return nil
}
func setRoleConnLimit(conn *sql.DB, d *schema.ResourceData) error {
if !d.HasChange(roleConnLimitAttr) {
return nil
}
connLimit := d.Get(roleConnLimitAttr).(int)
roleName := d.Get(roleNameAttr).(string)
query := fmt.Sprintf("ALTER ROLE %s CONNECTION LIMIT = %d", pq.QuoteIdentifier(roleName), connLimit)
if _, err := conn.Query(query); err != nil {
return errwrap.Wrapf("Error updating role CONNECTION LIMIT: {{err}}", err)
}
return nil
}
func setRoleCreateDB(conn *sql.DB, d *schema.ResourceData) error {
if !d.HasChange(roleCreateDBAttr) {
return nil
}
createDB := d.Get(roleCreateDBAttr).(bool)
tok := "NOCREATEDB"
if createDB {
tok = "CREATEDB"
}
roleName := d.Get(roleNameAttr).(string)
query := fmt.Sprintf("ALTER ROLE %s WITH %s", pq.QuoteIdentifier(roleName), tok)
if _, err := conn.Query(query); err != nil {
return errwrap.Wrapf("Error updating role CREATEDB: {{err}}", err)
}
return nil
}
func setRoleCreateRole(conn *sql.DB, d *schema.ResourceData) error {
if !d.HasChange(roleCreateRoleAttr) {
return nil
}
createRole := d.Get(roleCreateRoleAttr).(bool)
tok := "NOCREATEROLE"
if createRole {
tok = "CREATEROLE"
}
roleName := d.Get(roleNameAttr).(string)
query := fmt.Sprintf("ALTER ROLE %s WITH %s", pq.QuoteIdentifier(roleName), tok)
if _, err := conn.Query(query); err != nil {
return errwrap.Wrapf("Error updating role CREATEROLE: {{err}}", err)
}
return nil
}
func setRoleInherit(conn *sql.DB, d *schema.ResourceData) error {
if !d.HasChange(roleInheritAttr) {
return nil
}
inherit := d.Get(roleInheritAttr).(bool)
tok := "NOINHERIT"
if inherit {
tok = "INHERIT"
}
roleName := d.Get(roleNameAttr).(string)
query := fmt.Sprintf("ALTER ROLE %s WITH %s", pq.QuoteIdentifier(roleName), tok)
if _, err := conn.Query(query); err != nil {
return errwrap.Wrapf("Error updating role INHERIT: {{err}}", err)
}
return nil
}
func setRoleLogin(conn *sql.DB, d *schema.ResourceData) error {
if !d.HasChange(roleLoginAttr) {
return nil
}
login := d.Get(roleLoginAttr).(bool)
tok := "NOLOGIN"
if login {
tok = "LOGIN"
}
roleName := d.Get(roleNameAttr).(string)
query := fmt.Sprintf("ALTER ROLE %s WITH %s", pq.QuoteIdentifier(roleName), tok)
if _, err := conn.Query(query); err != nil {
return errwrap.Wrapf("Error updating role LOGIN: {{err}}", err)
}
return nil
}
func setRoleReplication(conn *sql.DB, d *schema.ResourceData) error {
if !d.HasChange(roleReplicationAttr) {
return nil
}
replication := d.Get(roleReplicationAttr).(bool)
tok := "NOREPLICATION"
if replication {
tok = "REPLICATION"
}
roleName := d.Get(roleNameAttr).(string)
query := fmt.Sprintf("ALTER ROLE %s WITH %s", pq.QuoteIdentifier(roleName), tok)
if _, err := conn.Query(query); err != nil {
return errwrap.Wrapf("Error updating role REPLICATION: {{err}}", err)
}
return nil
}
func setRoleSuperuser(conn *sql.DB, d *schema.ResourceData) error {
if !d.HasChange(roleSuperuserAttr) {
return nil
}
superuser := d.Get(roleSuperuserAttr).(bool)
tok := "NOSUPERUSER"
if superuser {
tok = "SUPERUSER"
}
roleName := d.Get(roleNameAttr).(string)
query := fmt.Sprintf("ALTER ROLE %s WITH %s", pq.QuoteIdentifier(roleName), tok)
if _, err := conn.Query(query); err != nil {
return errwrap.Wrapf("Error updating role SUPERUSER: {{err}}", err)
}
return nil
}
func setRoleValidUntil(conn *sql.DB, d *schema.ResourceData) error {
if !d.HasChange(roleValidUntilAttr) {
return nil
}
validUntil := d.Get(roleValidUntilAttr).(string)
if validUntil == "" {
return nil
} else if strings.ToLower(validUntil) == "infinity" {
validUntil = "infinity"
}
roleName := d.Get(roleNameAttr).(string)
query := fmt.Sprintf("ALTER ROLE %s VALID UNTIL '%s'", pq.QuoteIdentifier(roleName), pqQuoteLiteral(validUntil))
if _, err := conn.Query(query); err != nil {
return errwrap.Wrapf("Error updating role VALID UNTIL: {{err}}", err)
}
return nil
} }