Skip to content
This repository was archived by the owner on Jan 17, 2025. It is now read-only.

Commit 4bbf628

Browse files
authored
Merge pull request #103 from hoxu/hashed-password-support
Support hashed user passwords
2 parents d7bc358 + fb930ce commit 4bbf628

File tree

3 files changed

+29
-15
lines changed

3 files changed

+29
-15
lines changed

docs/resources/user.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ resource "redshift_user" "user_with_unrestricted_syslog" {
3737
- **connection_limit** (Number) The maximum number of database connections the user is permitted to have open concurrently. The limit isn't enforced for superusers.
3838
- **create_database** (Boolean) Allows the user to create new databases. By default user can't create new databases.
3939
- **id** (String) The ID of this resource.
40-
- **password** (String, Sensitive) Sets the user's password. Users can change their own passwords, unless the password is disabled. To disable password, omit this parameter or set it to `null`.
40+
- **password** (String, Sensitive) Sets the user's password. Users can change their own passwords, unless the password is disabled. To disable password, omit this parameter or set it to `null`. Can also be a hashed password rather than the plaintext password. Please refer to the Redshift [CREATE USER documentation](https://docs.aws.amazon.com/redshift/latest/dg/r_CREATE_USER.html) for information on creating a password hash.
4141
- **session_timeout** (Number) The maximum time in seconds that a session remains inactive or idle. The range is 60 seconds (one minute) to 1,728,000 seconds (20 days). If no session timeout is set for the user, the cluster setting applies.
4242
- **superuser** (Boolean) Determine whether the user is a superuser with all database privileges.
4343
- **syslog_access** (String) A clause that specifies the level of access that the user has to the Amazon Redshift system tables and views. If `RESTRICTED` (default) is specified, the user can see only the rows generated by that user in user-visible system tables and views. If `UNRESTRICTED` is specified, the user can see all rows in user-visible system tables and views, including rows generated by another user. `UNRESTRICTED` doesn't give a regular user access to superuser-visible tables. Only superusers can see superuser-visible tables.

redshift/resource_redshift_user.go

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package redshift
22

33
import (
44
"context"
5-
"crypto/md5"
65
"database/sql"
76
"fmt"
87
"log"
@@ -87,7 +86,7 @@ Amazon Redshift user accounts can only be created and dropped by a database supe
8786
Type: schema.TypeString,
8887
Optional: true,
8988
Sensitive: true,
90-
Description: "Sets the user's password. Users can change their own passwords, unless the password is disabled. To disable password, omit this parameter or set it to `null`.",
89+
Description: "Sets the user's password. Users can change their own passwords, unless the password is disabled. To disable password, omit this parameter or set it to `null`. Can also be a hashed password rather than the plaintext password. Please refer to the Redshift [CREATE USER documentation](https://docs.aws.amazon.com/redshift/latest/dg/r_CREATE_USER.html) for information on creating a password hash.",
9190
},
9291
userValidUntilAttr: {
9392
Type: schema.TypeString,
@@ -187,7 +186,6 @@ func resourceRedshiftUserCreate(db *DBConnection, d *schema.ResourceData) error
187186
{userCreateDBAttr, "CREATEDB", "NOCREATEDB"},
188187
}
189188

190-
userName := d.Get(userNameAttr).(string)
191189
createOpts := make([]string, 0, len(stringOpts)+len(intOpts)+len(boolOpts))
192190
for _, opt := range stringOpts {
193191
v, ok := d.GetOk(opt.hclKey)
@@ -211,7 +209,7 @@ func resourceRedshiftUserCreate(db *DBConnection, d *schema.ResourceData) error
211209
if val != "" {
212210
switch {
213211
case opt.hclKey == userPasswordAttr:
214-
createOpts = append(createOpts, fmt.Sprintf("%s '%s'", opt.sqlKey, md5Password(userName, val)))
212+
createOpts = append(createOpts, fmt.Sprintf("%s '%s'", opt.sqlKey, pqQuoteLiteral(val)))
215213
case opt.hclKey == userValidUntilAttr:
216214
switch {
217215
case v.(string) == "", strings.ToLower(v.(string)) == "infinity":
@@ -245,6 +243,7 @@ func resourceRedshiftUserCreate(db *DBConnection, d *schema.ResourceData) error
245243
createOpts = append(createOpts, valStr)
246244
}
247245

246+
userName := d.Get(userNameAttr).(string)
248247
createStr := strings.Join(createOpts, " ")
249248
sql := fmt.Sprintf("CREATE USER %s WITH %s", pq.QuoteIdentifier(userName), createStr)
250249

@@ -654,13 +653,3 @@ func getDefaultSyslogAccess(d *schema.ResourceData) string {
654653

655654
return defaultUserSyslogAccess
656655
}
657-
658-
// Generates an md5 password for the user.
659-
// Per https://docs.aws.amazon.com/redshift/latest/dg/r_CREATE_USER.html,
660-
// the process is:
661-
// 1. concatenate the password and username
662-
// 2. convert the concatenated string to an md5 hash in hex format
663-
// 3. prefix the result with 'md5' (unquoted)
664-
func md5Password(userName string, password string) string {
665-
return fmt.Sprintf("md5%x", md5.Sum([]byte(fmt.Sprintf("%s%s", password, userName))))
666-
}

redshift/resource_redshift_user_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ func TestAccRedshiftUser_Basic(t *testing.T) {
3131
resource.TestCheckResourceAttr("redshift_user.with_email", "name", "[email protected]"),
3232
testAccCheckRedshiftUserCanLogin("[email protected]", "Foobarbaz1"),
3333

34+
testAccCheckRedshiftUserExists("hashed_password"),
35+
testAccCheckRedshiftUserCanLogin("hashed_password", "Foobarbaz2"),
36+
3437
testAccCheckRedshiftUserExists("user_defaults"),
3538
resource.TestCheckResourceAttr("redshift_user.user_with_defaults", "name", "user_defaults"),
3639
resource.TestCheckResourceAttr("redshift_user.user_with_defaults", "superuser", "false"),
@@ -80,6 +83,15 @@ resource "redshift_user" "update_user" {
8083
syslog_access = "UNRESTRICTED"
8184
create_database = true
8285
}
86+
`
87+
var configUpdate2 = `
88+
resource "redshift_user" "update_user" {
89+
name = "update_user2"
90+
connection_limit = 5
91+
password = "md508d5d11f1f947091b312fb36b25e621f"
92+
syslog_access = "UNRESTRICTED"
93+
create_database = true
94+
}
8395
`
8496
resource.Test(t, resource.TestCase{
8597
PreCheck: func() { testAccPreCheck(t) },
@@ -112,6 +124,14 @@ resource "redshift_user" "update_user" {
112124
resource.TestCheckResourceAttr("redshift_user.update_user", "create_database", "true"),
113125
),
114126
},
127+
{
128+
Config: configUpdate2,
129+
Check: resource.ComposeTestCheckFunc(
130+
testAccCheckRedshiftUserExists("update_user2"),
131+
testAccCheckRedshiftUserCanLogin("update_user2", "Foobarbaz6"),
132+
resource.TestCheckResourceAttr("redshift_user.update_user", "password", "md508d5d11f1f947091b312fb36b25e621f"),
133+
),
134+
},
115135
// apply the first one again to check if all parameters roll back properly
116136
{
117137
Config: configCreate,
@@ -413,6 +433,11 @@ resource "redshift_user" "with_email" {
413433
password = "Foobarbaz1"
414434
}
415435
436+
resource "redshift_user" "with_hashed_password" {
437+
name = "hashed_password"
438+
password = "md5ad3b897bab2474bc7e408326cb18c42f"
439+
}
440+
416441
resource "redshift_user" "user_with_defaults" {
417442
name = "user_defaults"
418443
valid_until = "infinity"

0 commit comments

Comments
 (0)