Skip to content

Commit

Permalink
Merge pull request #22 from phekmat/feature/min_characters
Browse files Browse the repository at this point in the history
Add minimum character constraints to random_string
  • Loading branch information
mildwonkey authored May 21, 2018
2 parents 22a76f0 + dfabf07 commit 8eb87a8
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 12 deletions.
83 changes: 75 additions & 8 deletions random/resource_string.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ package random

import (
"crypto/rand"
"math/big"
"sort"

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

Expand Down Expand Up @@ -53,6 +56,34 @@ func resourceString() *schema.Resource {
ForceNew: true,
},

"min_numeric": {
Type: schema.TypeInt,
Optional: true,
Default: 0,
ForceNew: true,
},

"min_upper": {
Type: schema.TypeInt,
Optional: true,
Default: 0,
ForceNew: true,
},

"min_lower": {
Type: schema.TypeInt,
Optional: true,
Default: 0,
ForceNew: true,
},

"min_special": {
Type: schema.TypeInt,
Optional: true,
Default: 0,
ForceNew: true,
},

"override_special": {
Type: schema.TypeString,
Optional: true,
Expand All @@ -75,9 +106,13 @@ func CreateString(d *schema.ResourceData, meta interface{}) error {

length := d.Get("length").(int)
upper := d.Get("upper").(bool)
minUpper := d.Get("min_upper").(int)
lower := d.Get("lower").(bool)
minLower := d.Get("min_lower").(int)
number := d.Get("number").(bool)
minNumeric := d.Get("min_numeric").(int)
special := d.Get("special").(bool)
minSpecial := d.Get("min_special").(int)
overrideSpecial := d.Get("override_special").(string)

if overrideSpecial != "" {
Expand All @@ -98,17 +133,49 @@ func CreateString(d *schema.ResourceData, meta interface{}) error {
chars += specialChars
}

var bytes = make([]byte, length)
var l = byte(len(chars))
minMapping := map[string]int{
numChars: minNumeric,
lowerChars: minLower,
upperChars: minUpper,
specialChars: minSpecial,
}
var result = make([]byte, 0, length)
for k, v := range minMapping {
s, err := generateRandomBytes(&k, v)
if err != nil {
return errwrap.Wrapf("error generating random bytes: {{err}}", err)
}
result = append(result, s...)
}
s, err := generateRandomBytes(&chars, length-len(result))
if err != nil {
return errwrap.Wrapf("error generating random bytes: {{err}}", err)
}
result = append(result, s...)
order := make([]byte, len(result))
if _, err := rand.Read(order); err != nil {
return errwrap.Wrapf("error generating random bytes: {{err}}", err)
}
sort.Slice(result, func(i, j int) bool {
return order[i] < order[j]
})

rand.Read(bytes)
d.Set("result", string(result))
d.SetId(string(result))
return nil
}

for i, b := range bytes {
bytes[i] = chars[b%l]
func generateRandomBytes(charSet *string, length int) ([]byte, error) {
bytes := make([]byte, length)
setLen := big.NewInt(int64(len(*charSet)))
for i := range bytes {
idx, err := rand.Int(rand.Reader, setLen)
if err != nil {
return nil, err
}
bytes[i] = (*charSet)[idx.Int64()]
}
d.Set("result", string(bytes))
d.SetId(string(bytes))
return nil
return bytes, nil
}

func ReadString(d *schema.ResourceData, meta interface{}) error {
Expand Down
36 changes: 36 additions & 0 deletions random/resource_string_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package random

import (
"fmt"
"regexp"
"testing"

"github.com/hashicorp/terraform/helper/resource"
Expand Down Expand Up @@ -30,6 +31,13 @@ func TestAccResourceString(t *testing.T) {
customLen: 4,
}),
patternMatch("random_string.three", "!!!!"),
testAccResourceStringCheck("random_string.min", &customLens{
customLen: 12,
}),
regexMatch("random_string.min", regexp.MustCompile(`([a-z])`), 2),
regexMatch("random_string.min", regexp.MustCompile(`([A-Z])`), 3),
regexMatch("random_string.min", regexp.MustCompile(`([0-9])`), 4),
regexMatch("random_string.min", regexp.MustCompile(`([!#@])`), 1),
),
},
},
Expand All @@ -56,6 +64,25 @@ func testAccResourceStringCheck(id string, want *customLens) resource.TestCheckF
}
}

func regexMatch(id string, exp *regexp.Regexp, requiredMatches int) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[id]
if !ok {
return fmt.Errorf("Not found: %s", id)
}
if rs.Primary.ID == "" {
return fmt.Errorf("No ID is set")
}

customStr := rs.Primary.Attributes["result"]

if matches := exp.FindAllStringSubmatchIndex(customStr, -1); len(matches) < requiredMatches {
return fmt.Errorf("custom string is %s; did not match %s", customStr, exp)
}

return nil
}
}
func patternMatch(id string, want string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[id]
Expand Down Expand Up @@ -93,5 +120,14 @@ resource "random_string" "three" {
number = false
}
resource "random_string" "min" {
length = 12
override_special = "!#@"
min_lower = 2
min_upper = 3
min_special = 1
min_numeric = 4
}
`
)
17 changes: 13 additions & 4 deletions website/docs/r/string.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@ description: |-
# random\_string

The resource `random_string` generates a random permutation of alphanumeric
characters and optionally special characters. This resource does not provide
any guarantee that the random string will contain specific characters.
ie. if length = 4 and special = true, output could be 'Aa0#' or '1111'

characters and optionally special characters.

## Example Usage

Expand All @@ -37,15 +34,27 @@ The following arguments are supported:
* `upper` - (Optional) (default true) Include uppercase alphabet characters
in random string.

* `min_upper` - (Optional) (default 0) Minimum number of uppercase alphabet
characters in random string.

* `lower` - (Optional) (default true) Include lowercase alphabet characters
in random string.

* `min_lower` - (Optional) (default 0) Minimum number of lowercase alphabet
characters in random string.

* `number` - (Optional) (default true) Include numeric characters in random
string.

* `min_numeric` - (Optional) (default 0) Minimum number of numeric characters
in random string.

* `special` - (Optional) (default true) Include special characters in random
string. These are '!@#$%&*()-_=+[]{}<>:?'

* `min_special` - (Optional) (default 0) Minimum number of special characters
in random string.

* `override_special` - (Optional) Supply your own list of special characters to
use for string generation. This overrides characters list in the special
argument. The special argument must still be set to true for any overwritten
Expand Down

0 comments on commit 8eb87a8

Please sign in to comment.