Skip to content

Commit

Permalink
Merge pull request #11 from coveooss/add-bcrypt
Browse files Browse the repository at this point in the history
Add new bcrypt output to quantum password
  • Loading branch information
tdegiacinto authored Dec 13, 2019
2 parents ca1bb1c + f19aece commit 80db953
Show file tree
Hide file tree
Showing 6 changed files with 543 additions and 5 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ terraform-provider-quantum

main.tf
crash.log

*.code-workspace
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
language: go
go:
- 1.9.x
- 1.13.x

sudo: false

Expand Down
10 changes: 10 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module github.com/coveo/terraform-provider-quantum

go 1.13

require (
github.com/hashicorp/terraform v0.12.18
github.com/tidwall/gjson v1.1.2
github.com/tidwall/match v1.0.0
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413
)
430 changes: 430 additions & 0 deletions go.sum

Large diffs are not rendered by default.

22 changes: 18 additions & 4 deletions resource_quantum_password.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"time"

"github.com/hashicorp/terraform/helper/schema"
"golang.org/x/crypto/bcrypt"
)

const minimumCharsPerCategory = 2
Expand Down Expand Up @@ -48,6 +49,10 @@ func resourceQuantumPassword() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"bcrypt": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
},
}
}
Expand All @@ -56,12 +61,13 @@ func resourceQuantumPasswordCreate(d *schema.ResourceData, meta interface{}) err
// Get parameters
args := getQuantumPasswordArgs(d)

password, genDate, err := generatePassword(args)
password, genDate, bcrypt, err := generatePassword(args)

if err == nil {
d.Set("password", password)
d.Set("last_update", genDate.Format(time.RFC3339))
d.SetId(getMD5Hash(fmt.Sprintf("%s-%v", password, d.Get("last_update"))))
d.Set("bcrypt", bcrypt)
}

return err
Expand Down Expand Up @@ -92,6 +98,7 @@ func update(d *schema.ResourceData, update bool) error {
// was expired, but was not really expired if we consider the new rotation (that
// is only available during the update phase).
d.Set("previous_password", d.Get("password"))
d.Set("previous_bcrypt", d.Get("bcrypt"))
}
err = resourceQuantumPasswordCreate(d, nil)
} else if update {
Expand All @@ -100,13 +107,14 @@ func update(d *schema.ResourceData, update bool) error {
// This was a false update, so we bring back the previous password
d.Set("password", previous)
d.Set("previous_password", "")
d.Set("bcrypt", d.Get("previous_bcrypt"))
}
}

return err
}

func generatePassword(args *QuantumPasswordArgs) (string, *time.Time, error) {
func generatePassword(args *QuantumPasswordArgs) (string, *time.Time, string, error) {
charSets := make([]string, 0, len(categories))
for category, charSet := range categories {
if category == specialChars {
Expand All @@ -119,7 +127,7 @@ func generatePassword(args *QuantumPasswordArgs) (string, *time.Time, error) {
}

if args.length < len(charSets) {
return "", nil, fmt.Errorf("The password must be at least %d chars long", len(charSets))
return "", nil, "", fmt.Errorf("The password must be at least %d chars long", len(charSets))
}

var password string
Expand All @@ -135,9 +143,15 @@ func generatePassword(args *QuantumPasswordArgs) (string, *time.Time, error) {
chars := charSets[group]
password += string(chars[randInt(len(chars))])
}
shuffled := shuffle(password)[:args.length]

bcrypt, err := bcrypt.GenerateFromPassword([]byte(shuffled), 12)
if err != nil {
return "", nil, "", fmt.Errorf("Could not create hash %s", err)
}

generated := time.Now()
return shuffle(password)[:args.length], &generated, nil
return shuffled, &generated, string(bcrypt), nil
}

func shuffle(password string) string {
Expand Down
82 changes: 82 additions & 0 deletions resource_quantum_password_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package main

import (
"fmt"
"regexp"
"testing"

"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"

"golang.org/x/crypto/bcrypt"
)

func testQuantumPasswordBasic(t *testing.T) {
passwordRegex12 := regexp.MustCompile(`^.{12}$`)
passwordRegex10 := regexp.MustCompile(`^.{10}$`)
bcryptRegex := regexp.MustCompile(`^\$2[ayb]\$.{56}$`)

resource.Test(t, resource.TestCase{
Steps: []resource.TestStep{
{
Config: testAccQuantumPasswordResource(12),
Check: resource.ComposeTestCheckFunc(
resource.TestMatchResourceAttr(
"quantum_password.test",
"password",
passwordRegex12,
),
resource.TestMatchResourceAttr(
"quantum_password.test",
"bcrypt",
bcryptRegex,
),
testAccQuantumPasswordBcrypt("quantum_password.test"),
),
},
// Force a rotation of password
{
Config: testAccQuantumPasswordResource(10),
Check: resource.ComposeTestCheckFunc(
resource.TestMatchResourceAttr(
"quantum_password.test",
"password",
passwordRegex10,
),
resource.TestMatchResourceAttr(
"quantum_password.test",
"bcrypt",
bcryptRegex,
),
testAccQuantumPasswordBcrypt("quantum_password.test"),
),
},
},
})
}

func testAccQuantumPasswordResource(length int) string {
return fmt.Sprintf(`
resource "quantum_password" "test" {
special_chars = ""
length = %d
}
`, length)
}

func testAccQuantumPasswordBcrypt(resourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
// retrieve the resource by name from state
rs, ok := s.RootModule().Resources[resourceName]
if !ok {
return fmt.Errorf("Not found: %s", resourceName)
}

err := bcrypt.CompareHashAndPassword([]byte(rs.Primary.Attributes["bcrypt"]), []byte(rs.Primary.Attributes["password"]))
if err != nil {
return fmt.Errorf("Bcrypt does not match: %s (%s)", resourceName, err)
}

return nil
}
}

0 comments on commit 80db953

Please sign in to comment.