github.com/ffrizzo/terraform@v0.8.2-0.20161219200057-992e12335f3d/builtin/providers/postgresql/resource_postgresql_role.go (about) 1 package postgresql 2 3 import ( 4 "database/sql" 5 "errors" 6 "fmt" 7 "log" 8 "strings" 9 10 "github.com/hashicorp/errwrap" 11 "github.com/hashicorp/terraform/helper/schema" 12 "github.com/lib/pq" 13 ) 14 15 const ( 16 roleBypassRLSAttr = "bypass_row_level_security" 17 roleConnLimitAttr = "connection_limit" 18 roleCreateDBAttr = "create_database" 19 roleCreateRoleAttr = "create_role" 20 roleEncryptedPassAttr = "encrypted_password" 21 roleInheritAttr = "inherit" 22 roleLoginAttr = "login" 23 roleNameAttr = "name" 24 rolePasswordAttr = "password" 25 roleReplicationAttr = "replication" 26 roleSuperuserAttr = "superuser" 27 roleValidUntilAttr = "valid_until" 28 29 // Deprecated options 30 roleDepEncryptedAttr = "encrypted" 31 ) 32 33 func resourcePostgreSQLRole() *schema.Resource { 34 return &schema.Resource{ 35 Create: resourcePostgreSQLRoleCreate, 36 Read: resourcePostgreSQLRoleRead, 37 Update: resourcePostgreSQLRoleUpdate, 38 Delete: resourcePostgreSQLRoleDelete, 39 Importer: &schema.ResourceImporter{ 40 State: schema.ImportStatePassthrough, 41 }, 42 43 Schema: map[string]*schema.Schema{ 44 roleNameAttr: { 45 Type: schema.TypeString, 46 Required: true, 47 Description: "The name of the role", 48 }, 49 rolePasswordAttr: { 50 Type: schema.TypeString, 51 Optional: true, 52 Computed: true, 53 Sensitive: true, 54 DefaultFunc: schema.EnvDefaultFunc("PGPASSWORD", nil), 55 Description: "Sets the role's password", 56 }, 57 roleDepEncryptedAttr: { 58 Type: schema.TypeString, 59 Optional: true, 60 Deprecated: fmt.Sprintf("Rename PostgreSQL role resource attribute %q to %q", roleDepEncryptedAttr, roleEncryptedPassAttr), 61 }, 62 roleEncryptedPassAttr: { 63 Type: schema.TypeBool, 64 Optional: true, 65 Default: true, 66 Description: "Control whether the password is stored encrypted in the system catalogs", 67 }, 68 roleValidUntilAttr: { 69 Type: schema.TypeString, 70 Optional: true, 71 Default: "infinity", 72 Description: "Sets a date and time after which the role's password is no longer valid", 73 }, 74 roleConnLimitAttr: { 75 Type: schema.TypeInt, 76 Optional: true, 77 Computed: true, 78 Description: "How many concurrent connections can be made with this role", 79 ValidateFunc: validateConnLimit, 80 }, 81 roleSuperuserAttr: { 82 Type: schema.TypeBool, 83 Optional: true, 84 Default: false, 85 Description: `Determine whether the new role is a "superuser"`, 86 }, 87 roleCreateDBAttr: { 88 Type: schema.TypeBool, 89 Optional: true, 90 Default: false, 91 Description: "Define a role's ability to create databases", 92 }, 93 roleCreateRoleAttr: { 94 Type: schema.TypeBool, 95 Optional: true, 96 Default: false, 97 Description: "Determine whether this role will be permitted to create new roles", 98 }, 99 roleInheritAttr: { 100 Type: schema.TypeBool, 101 Optional: true, 102 Default: true, 103 Description: `Determine whether a role "inherits" the privileges of roles it is a member of`, 104 }, 105 roleLoginAttr: { 106 Type: schema.TypeBool, 107 Optional: true, 108 Default: false, 109 Description: "Determine whether a role is allowed to log in", 110 }, 111 roleReplicationAttr: { 112 Type: schema.TypeBool, 113 Optional: true, 114 Default: false, 115 Description: "Determine whether a role is allowed to initiate streaming replication or put the system in and out of backup mode", 116 }, 117 roleBypassRLSAttr: { 118 Type: schema.TypeBool, 119 Optional: true, 120 Default: false, 121 Description: "Determine whether a role bypasses every row-level security (RLS) policy", 122 }, 123 }, 124 } 125 } 126 127 func resourcePostgreSQLRoleCreate(d *schema.ResourceData, meta interface{}) error { 128 c := meta.(*Client) 129 conn, err := c.Connect() 130 if err != nil { 131 return errwrap.Wrapf("Error connecting to PostgreSQL: {{err}}", err) 132 } 133 defer conn.Close() 134 135 stringOpts := []struct { 136 hclKey string 137 sqlKey string 138 }{ 139 {rolePasswordAttr, "PASSWORD"}, 140 {roleValidUntilAttr, "VALID UNTIL"}, 141 } 142 intOpts := []struct { 143 hclKey string 144 sqlKey string 145 }{ 146 {roleConnLimitAttr, "CONNECTION LIMIT"}, 147 } 148 boolOpts := []struct { 149 hclKey string 150 sqlKeyEnable string 151 sqlKeyDisable string 152 }{ 153 {roleSuperuserAttr, "CREATEDB", "NOCREATEDB"}, 154 {roleCreateRoleAttr, "CREATEROLE", "NOCREATEROLE"}, 155 {roleInheritAttr, "INHERIT", "NOINHERIT"}, 156 {roleLoginAttr, "LOGIN", "NOLOGIN"}, 157 {roleReplicationAttr, "REPLICATION", "NOREPLICATION"}, 158 {roleBypassRLSAttr, "BYPASSRLS", "NOBYPASSRLS"}, 159 160 // roleEncryptedPassAttr is used only when rolePasswordAttr is set. 161 // {roleEncryptedPassAttr, "ENCRYPTED", "UNENCRYPTED"}, 162 } 163 164 createOpts := make([]string, 0, len(stringOpts)+len(intOpts)+len(boolOpts)) 165 166 for _, opt := range stringOpts { 167 v, ok := d.GetOk(opt.hclKey) 168 if !ok { 169 continue 170 } 171 172 val := v.(string) 173 if val != "" { 174 switch { 175 case opt.hclKey == rolePasswordAttr: 176 if strings.ToUpper(v.(string)) == "NULL" { 177 createOpts = append(createOpts, "PASSWORD NULL") 178 } else { 179 if d.Get(roleEncryptedPassAttr).(bool) { 180 createOpts = append(createOpts, "ENCRYPTED") 181 } else { 182 createOpts = append(createOpts, "UNENCRYPTED") 183 } 184 createOpts = append(createOpts, fmt.Sprintf("%s '%s'", opt.sqlKey, pqQuoteLiteral(val))) 185 } 186 case opt.hclKey == roleValidUntilAttr: 187 switch { 188 case v.(string) == "", strings.ToLower(v.(string)) == "infinity": 189 createOpts = append(createOpts, fmt.Sprintf("%s '%s'", opt.sqlKey, "infinity")) 190 default: 191 createOpts = append(createOpts, fmt.Sprintf("%s %s", opt.sqlKey, pq.QuoteIdentifier(val))) 192 } 193 default: 194 createOpts = append(createOpts, fmt.Sprintf("%s %s", opt.sqlKey, pq.QuoteIdentifier(val))) 195 } 196 } 197 } 198 199 for _, opt := range intOpts { 200 val := d.Get(opt.hclKey).(int) 201 createOpts = append(createOpts, fmt.Sprintf("%s %d", opt.sqlKey, val)) 202 } 203 204 for _, opt := range boolOpts { 205 if opt.hclKey == roleEncryptedPassAttr { 206 // This attribute is handled above in the stringOpts 207 // loop. 208 continue 209 } 210 val := d.Get(opt.hclKey).(bool) 211 valStr := opt.sqlKeyDisable 212 if val { 213 valStr = opt.sqlKeyEnable 214 } 215 createOpts = append(createOpts, valStr) 216 } 217 218 roleName := d.Get(roleNameAttr).(string) 219 createStr := strings.Join(createOpts, " ") 220 if len(createOpts) > 0 { 221 createStr = " WITH " + createStr 222 } 223 224 query := fmt.Sprintf("CREATE ROLE %s%s", pq.QuoteIdentifier(roleName), createStr) 225 _, err = conn.Query(query) 226 if err != nil { 227 return errwrap.Wrapf(fmt.Sprintf("Error creating role %s: {{err}}", roleName), err) 228 } 229 230 d.SetId(roleName) 231 232 return resourcePostgreSQLRoleRead(d, meta) 233 } 234 235 func resourcePostgreSQLRoleDelete(d *schema.ResourceData, meta interface{}) error { 236 client := meta.(*Client) 237 conn, err := client.Connect() 238 if err != nil { 239 return err 240 } 241 defer conn.Close() 242 243 roleName := d.Get(roleNameAttr).(string) 244 query := fmt.Sprintf("DROP ROLE %s", pq.QuoteIdentifier(roleName)) 245 _, err = conn.Query(query) 246 if err != nil { 247 return errwrap.Wrapf("Error deleting role: {{err}}", err) 248 } 249 250 d.SetId("") 251 252 return nil 253 } 254 255 func resourcePostgreSQLRoleRead(d *schema.ResourceData, meta interface{}) error { 256 c := meta.(*Client) 257 conn, err := c.Connect() 258 if err != nil { 259 return err 260 } 261 defer conn.Close() 262 263 roleId := d.Id() 264 var roleSuperuser, roleInherit, roleCreateRole, roleCreateDB, roleCanLogin, roleReplication, roleBypassRLS bool 265 var roleConnLimit int 266 var roleName, roleValidUntil string 267 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) 268 switch { 269 case err == sql.ErrNoRows: 270 log.Printf("[WARN] PostgreSQL role (%s) not found", roleId) 271 d.SetId("") 272 return nil 273 case err != nil: 274 return errwrap.Wrapf("Error reading role: {{err}}", err) 275 default: 276 d.Set(roleNameAttr, roleName) 277 d.Set(roleBypassRLSAttr, roleBypassRLS) 278 d.Set(roleConnLimitAttr, roleConnLimit) 279 d.Set(roleCreateDBAttr, roleCreateDB) 280 d.Set(roleCreateRoleAttr, roleCreateRole) 281 d.Set(roleEncryptedPassAttr, true) 282 d.Set(roleInheritAttr, roleInherit) 283 d.Set(roleLoginAttr, roleCanLogin) 284 d.Set(roleReplicationAttr, roleReplication) 285 d.Set(roleSuperuserAttr, roleSuperuser) 286 d.Set(roleValidUntilAttr, roleValidUntil) 287 d.SetId(roleName) 288 } 289 290 if !roleSuperuser { 291 // Return early if not superuser user 292 return nil 293 } 294 295 var rolePassword string 296 err = conn.QueryRow("SELECT COALESCE(passwd, '') FROM pg_catalog.pg_shadow AS s WHERE s.usename = $1", roleId).Scan(&rolePassword) 297 switch { 298 case err == sql.ErrNoRows: 299 return fmt.Errorf("PostgreSQL role (%s) not found in shadow database: {{err}}", roleId) 300 case err != nil: 301 return errwrap.Wrapf("Error reading role: {{err}}", err) 302 default: 303 d.Set(rolePasswordAttr, rolePassword) 304 return nil 305 } 306 } 307 308 func resourcePostgreSQLRoleUpdate(d *schema.ResourceData, meta interface{}) error { 309 c := meta.(*Client) 310 conn, err := c.Connect() 311 if err != nil { 312 return err 313 } 314 defer conn.Close() 315 316 if err := setRoleName(conn, d); err != nil { 317 return err 318 } 319 320 if err := setRoleBypassRLS(conn, d); err != nil { 321 return err 322 } 323 324 if err := setRoleConnLimit(conn, d); err != nil { 325 return err 326 } 327 328 if err := setRoleCreateDB(conn, d); err != nil { 329 return err 330 } 331 332 if err := setRoleCreateRole(conn, d); err != nil { 333 return err 334 } 335 336 if err := setRoleInherit(conn, d); err != nil { 337 return err 338 } 339 340 if err := setRoleLogin(conn, d); err != nil { 341 return err 342 } 343 344 if err := setRoleReplication(conn, d); err != nil { 345 return err 346 } 347 348 if err := setRoleSuperuser(conn, d); err != nil { 349 return err 350 } 351 352 if err := setRoleValidUntil(conn, d); err != nil { 353 return err 354 } 355 356 return resourcePostgreSQLRoleRead(d, meta) 357 } 358 359 func setRoleName(conn *sql.DB, d *schema.ResourceData) error { 360 if !d.HasChange(roleNameAttr) { 361 return nil 362 } 363 364 oraw, nraw := d.GetChange(roleNameAttr) 365 o := oraw.(string) 366 n := nraw.(string) 367 if n == "" { 368 return errors.New("Error setting role name to an empty string") 369 } 370 371 query := fmt.Sprintf("ALTER ROLE %s RENAME TO %s", pq.QuoteIdentifier(o), pq.QuoteIdentifier(n)) 372 if _, err := conn.Query(query); err != nil { 373 return errwrap.Wrapf("Error updating role NAME: {{err}}", err) 374 } 375 d.SetId(n) 376 377 return nil 378 } 379 380 func setRoleBypassRLS(conn *sql.DB, d *schema.ResourceData) error { 381 if !d.HasChange(roleBypassRLSAttr) { 382 return nil 383 } 384 385 bypassRLS := d.Get(roleBypassRLSAttr).(bool) 386 tok := "NOBYPASSRLS" 387 if bypassRLS { 388 tok = "BYPASSRLS" 389 } 390 roleName := d.Get(roleNameAttr).(string) 391 query := fmt.Sprintf("ALTER ROLE %s WITH %s", pq.QuoteIdentifier(roleName), tok) 392 if _, err := conn.Query(query); err != nil { 393 return errwrap.Wrapf("Error updating role BYPASSRLS: {{err}}", err) 394 } 395 396 return nil 397 } 398 399 func setRoleConnLimit(conn *sql.DB, d *schema.ResourceData) error { 400 if !d.HasChange(roleConnLimitAttr) { 401 return nil 402 } 403 404 connLimit := d.Get(roleConnLimitAttr).(int) 405 roleName := d.Get(roleNameAttr).(string) 406 query := fmt.Sprintf("ALTER ROLE %s CONNECTION LIMIT = %d", pq.QuoteIdentifier(roleName), connLimit) 407 if _, err := conn.Query(query); err != nil { 408 return errwrap.Wrapf("Error updating role CONNECTION LIMIT: {{err}}", err) 409 } 410 411 return nil 412 } 413 414 func setRoleCreateDB(conn *sql.DB, d *schema.ResourceData) error { 415 if !d.HasChange(roleCreateDBAttr) { 416 return nil 417 } 418 419 createDB := d.Get(roleCreateDBAttr).(bool) 420 tok := "NOCREATEDB" 421 if createDB { 422 tok = "CREATEDB" 423 } 424 roleName := d.Get(roleNameAttr).(string) 425 query := fmt.Sprintf("ALTER ROLE %s WITH %s", pq.QuoteIdentifier(roleName), tok) 426 if _, err := conn.Query(query); err != nil { 427 return errwrap.Wrapf("Error updating role CREATEDB: {{err}}", err) 428 } 429 430 return nil 431 } 432 433 func setRoleCreateRole(conn *sql.DB, d *schema.ResourceData) error { 434 if !d.HasChange(roleCreateRoleAttr) { 435 return nil 436 } 437 438 createRole := d.Get(roleCreateRoleAttr).(bool) 439 tok := "NOCREATEROLE" 440 if createRole { 441 tok = "CREATEROLE" 442 } 443 roleName := d.Get(roleNameAttr).(string) 444 query := fmt.Sprintf("ALTER ROLE %s WITH %s", pq.QuoteIdentifier(roleName), tok) 445 if _, err := conn.Query(query); err != nil { 446 return errwrap.Wrapf("Error updating role CREATEROLE: {{err}}", err) 447 } 448 449 return nil 450 } 451 452 func setRoleInherit(conn *sql.DB, d *schema.ResourceData) error { 453 if !d.HasChange(roleInheritAttr) { 454 return nil 455 } 456 457 inherit := d.Get(roleInheritAttr).(bool) 458 tok := "NOINHERIT" 459 if inherit { 460 tok = "INHERIT" 461 } 462 roleName := d.Get(roleNameAttr).(string) 463 query := fmt.Sprintf("ALTER ROLE %s WITH %s", pq.QuoteIdentifier(roleName), tok) 464 if _, err := conn.Query(query); err != nil { 465 return errwrap.Wrapf("Error updating role INHERIT: {{err}}", err) 466 } 467 468 return nil 469 } 470 471 func setRoleLogin(conn *sql.DB, d *schema.ResourceData) error { 472 if !d.HasChange(roleLoginAttr) { 473 return nil 474 } 475 476 login := d.Get(roleLoginAttr).(bool) 477 tok := "NOLOGIN" 478 if login { 479 tok = "LOGIN" 480 } 481 roleName := d.Get(roleNameAttr).(string) 482 query := fmt.Sprintf("ALTER ROLE %s WITH %s", pq.QuoteIdentifier(roleName), tok) 483 if _, err := conn.Query(query); err != nil { 484 return errwrap.Wrapf("Error updating role LOGIN: {{err}}", err) 485 } 486 487 return nil 488 } 489 490 func setRoleReplication(conn *sql.DB, d *schema.ResourceData) error { 491 if !d.HasChange(roleReplicationAttr) { 492 return nil 493 } 494 495 replication := d.Get(roleReplicationAttr).(bool) 496 tok := "NOREPLICATION" 497 if replication { 498 tok = "REPLICATION" 499 } 500 roleName := d.Get(roleNameAttr).(string) 501 query := fmt.Sprintf("ALTER ROLE %s WITH %s", pq.QuoteIdentifier(roleName), tok) 502 if _, err := conn.Query(query); err != nil { 503 return errwrap.Wrapf("Error updating role REPLICATION: {{err}}", err) 504 } 505 506 return nil 507 } 508 509 func setRoleSuperuser(conn *sql.DB, d *schema.ResourceData) error { 510 if !d.HasChange(roleSuperuserAttr) { 511 return nil 512 } 513 514 superuser := d.Get(roleSuperuserAttr).(bool) 515 tok := "NOSUPERUSER" 516 if superuser { 517 tok = "SUPERUSER" 518 } 519 roleName := d.Get(roleNameAttr).(string) 520 query := fmt.Sprintf("ALTER ROLE %s WITH %s", pq.QuoteIdentifier(roleName), tok) 521 if _, err := conn.Query(query); err != nil { 522 return errwrap.Wrapf("Error updating role SUPERUSER: {{err}}", err) 523 } 524 525 return nil 526 } 527 528 func setRoleValidUntil(conn *sql.DB, d *schema.ResourceData) error { 529 if !d.HasChange(roleValidUntilAttr) { 530 return nil 531 } 532 533 validUntil := d.Get(roleValidUntilAttr).(string) 534 if validUntil == "" { 535 return nil 536 } else if strings.ToLower(validUntil) == "infinity" { 537 validUntil = "infinity" 538 } 539 540 roleName := d.Get(roleNameAttr).(string) 541 query := fmt.Sprintf("ALTER ROLE %s VALID UNTIL '%s'", pq.QuoteIdentifier(roleName), pqQuoteLiteral(validUntil)) 542 543 if _, err := conn.Query(query); err != nil { 544 return errwrap.Wrapf("Error updating role VALID UNTIL: {{err}}", err) 545 } 546 547 return nil 548 }