Skip to content

Commit

Permalink
Merge pull request petoju#26 from SeismicGames/master
Browse files Browse the repository at this point in the history
Adding authentication plugin support, mainly for AWSAuthenticationPlu…
  • Loading branch information
vancluever authored Mar 12, 2018
2 parents db9aacd + 5b3ee76 commit 51c5bcb
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 4 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ $ # wait for a few seconds to let MySQL stand up, check the logs with: docker lo
$ export MYSQL_USERNAME=root
$ export MYSQL_ENDPOINT=localhost:3306
$ export MYSQL_PASSWORD=my-secret-pw
$ mysql -h localhost -u root -p -e "INSTALL PLUGIN mysql_no_login SONAME 'mysql_no_login.so';"
$ make testacc
$ docker rm -f some-mysql
```
43 changes: 41 additions & 2 deletions mysql/resource_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import (
"fmt"
"log"

"errors"
"github.com/hashicorp/go-version"

"github.com/hashicorp/terraform/helper/schema"
)

Expand Down Expand Up @@ -36,20 +36,43 @@ func resourceUser() *schema.Resource {
Sensitive: true,
StateFunc: hashSum,
},

"password": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ConflictsWith: []string{"plaintext_password"},
Sensitive: true,
Deprecated: "Please use plaintext_password instead",
},

"auth_plugin": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ConflictsWith: []string{"plaintext_password", "password"},
},
},
}
}

func CreateUser(d *schema.ResourceData, meta interface{}) error {
db := meta.(*providerConfiguration).DB

var authStm string
var auth string
if v, ok := d.GetOk("auth_plugin"); ok {
auth = v.(string)
}

if len(auth) > 0 {
switch auth {
case "AWSAuthenticationPlugin":
authStm = " IDENTIFIED WITH AWSAuthenticationPlugin as 'RDS'"
case "mysql_no_login":
authStm = " IDENTIFIED WITH mysql_no_login"
}
}

stmtSQL := fmt.Sprintf("CREATE USER '%s'@'%s'",
d.Get("user").(string),
d.Get("host").(string))
Expand All @@ -61,7 +84,13 @@ func CreateUser(d *schema.ResourceData, meta interface{}) error {
password = d.Get("password").(string)
}

if password != "" {
if auth == "AWSAuthenticationPlugin" && d.Get("host").(string) == "localhost" {
return errors.New("cannot use IAM auth against localhost")
}

if authStm != "" {
stmtSQL = stmtSQL + authStm
} else {
stmtSQL = stmtSQL + fmt.Sprintf(" IDENTIFIED BY '%s'", password)
}

Expand All @@ -80,6 +109,16 @@ func CreateUser(d *schema.ResourceData, meta interface{}) error {
func UpdateUser(d *schema.ResourceData, meta interface{}) error {
conf := meta.(*providerConfiguration)

var auth string
if v, ok := d.GetOk("auth_plugin"); ok {
auth = v.(string)
}

if len(auth) > 0 {
// nothing to change, return
return nil
}

var newpw interface{}
if d.HasChange("plaintext_password") {
_, newpw = d.GetChange("plaintext_password")
Expand Down
54 changes: 54 additions & 0 deletions mysql/resource_user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,25 @@ func TestAccUser_basic(t *testing.T) {
})
}

func TestAccUser_auth(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccUserCheckDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccUserConfig_auth_iam_plugin,
Check: resource.ComposeTestCheckFunc(
testAccUserAuthExists("mysql_user.test"),
resource.TestCheckResourceAttr("mysql_user.test", "user", "jdoe"),
resource.TestCheckResourceAttr("mysql_user.test", "host", "example.com"),
resource.TestCheckResourceAttr("mysql_user.test", "auth_plugin", "mysql_no_login"),
),
},
},
})
}

func TestAccUser_deprecated(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Expand Down Expand Up @@ -93,6 +112,33 @@ func testAccUserExists(rn string) resource.TestCheckFunc {
}
}

func testAccUserAuthExists(rn string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[rn]
if !ok {
return fmt.Errorf("resource not found: %s", rn)
}

if rs.Primary.ID == "" {
return fmt.Errorf("user id not set")
}

db := testAccProvider.Meta().(*providerConfiguration).DB
stmtSQL := fmt.Sprintf("SELECT count(*) from mysql.user where CONCAT(user, '@', host) = '%s' and plugin = 'mysql_no_login'", rs.Primary.ID)
log.Println("Executing statement:", stmtSQL)
var count int
err := db.QueryRow(stmtSQL).Scan(&count)
if err != nil {
if err == sql.ErrNoRows {
return fmt.Errorf("expected 1 row reading user but got no rows")
}
return fmt.Errorf("error reading user: %s", err)
}

return nil
}
}

func testAccUserCheckDestroy(s *terraform.State) error {
db := testAccProvider.Meta().(*providerConfiguration).DB

Expand Down Expand Up @@ -146,3 +192,11 @@ resource "mysql_user" "test" {
password = "password2"
}
`

const testAccUserConfig_auth_iam_plugin = `
resource "mysql_user" "test" {
user = "jdoe"
host = "example.com"
auth_plugin = "mysql_no_login"
}
`
29 changes: 27 additions & 2 deletions website/docs/r/user.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ resource "mysql_user" "jdoe" {
}
```

## Example Usage with an Authentication Plugin

```hcl
resource "mysql_user" "nologin" {
user = "nologin"
host = "example.com"
auth_plugin = "mysql_no_login"
}
```

## Argument Reference

The following arguments are supported:
Expand All @@ -36,11 +46,26 @@ The following arguments are supported:

* `plaintext_password` - (Optional) The password for the user. This must be
provided in plain text, so the data source for it must be secured.
An _unsalted_ hash of the provided password is stored in state.
An _unsalted_ hash of the provided password is stored in state. Conflicts
with `auth_plugin`.

* `password` - (Optional) Deprecated alias of `plaintext_password`, whose
value is *stored as plaintext in state*. Prefer to use `plaintext_password`
instead, which stores the password as an unsalted hash.
instead, which stores the password as an unsalted hash. Conflicts with
`auth_plugin`.

* `auth_plugin` - (Optional) Block which supports the use of authentication plugins.
Description of the fields allowed in the block below. Conflicts with `password`
and `plaintext_password`.

The `auth_plugin` value supports:

* `AWSAuthenticationPlugin` - For more information about "AWSAuthenticationPlugin"
and using it with Aurora:
http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.html#UsingWithRDS.IAMDBAuth.Creating
* `mysql_no_login` - Uses the MySQL No-Login Authentication Plugin. The No-Login
Authentication Plugin must be active in MySQL. For more information:
https://dev.mysql.com/doc/refman/5.7/en/no-login-pluggable-authentication.html

## Attributes Reference

Expand Down

0 comments on commit 51c5bcb

Please sign in to comment.