Skip to content

Commit 815d46c

Browse files
authored
Merge pull request #161 from SumoLogic/emichaeli-add-support-for-password-policy
Add password policy resource
2 parents 499a18d + c9868cf commit 815d46c

File tree

6 files changed

+478
-1
lines changed

6 files changed

+478
-1
lines changed

sumologic/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ func Provider() terraform.ResourceProvider {
7070
"sumologic_lookup_table": resourceSumologicLookupTable(),
7171
"sumologic_subdomain": resourceSumologicSubdomain(),
7272
"sumologic_dashboard": resourceSumologicDashboard(),
73+
"sumologic_password_policy": resourceSumologicPasswordPolicy(),
7374
},
7475
DataSourcesMap: map[string]*schema.Resource{
7576
"sumologic_caller_identity": dataSourceSumologicCallerIdentity(),
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
package sumologic
2+
3+
import (
4+
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
5+
)
6+
7+
var DefaultPasswordPolicy = PasswordPolicy{
8+
MinLength: 8,
9+
MaxLength: 128,
10+
MustContainLowercase: true,
11+
MustContainUppercase: true,
12+
MustContainDigits: true,
13+
MustContainSpecialChars: true,
14+
MaxPasswordAgeInDays: 365,
15+
MinUniquePasswords: 10,
16+
AccountLockoutThreshold: 6,
17+
FailedLoginResetDurationInMins: 10,
18+
AccountLockoutDurationInMins: 30,
19+
RequireMfa: false,
20+
RememberMfa: true,
21+
}
22+
23+
func resourceSumologicPasswordPolicy() *schema.Resource {
24+
return &schema.Resource{
25+
Create: resourceSumologicPasswordPolicyCreate,
26+
Read: resourceSumologicPasswordPolicyRead,
27+
Update: resourceSumologicPasswordPolicyUpdate,
28+
Delete: resourceSumologicPasswordPolicyDelete,
29+
30+
Schema: map[string]*schema.Schema{
31+
"min_length": {
32+
Type: schema.TypeInt,
33+
Optional: true,
34+
Default: DefaultPasswordPolicy.MinLength,
35+
},
36+
"max_length": {
37+
Type: schema.TypeInt,
38+
Optional: true,
39+
Default: DefaultPasswordPolicy.MaxLength,
40+
},
41+
"must_contain_lowercase": {
42+
Type: schema.TypeBool,
43+
Optional: true,
44+
Default: DefaultPasswordPolicy.MustContainLowercase,
45+
},
46+
"must_contain_uppercase": {
47+
Type: schema.TypeBool,
48+
Optional: true,
49+
Default: DefaultPasswordPolicy.MustContainUppercase,
50+
},
51+
"must_contain_digits": {
52+
Type: schema.TypeBool,
53+
Optional: true,
54+
Default: DefaultPasswordPolicy.MustContainDigits,
55+
},
56+
"must_contain_special_chars": {
57+
Type: schema.TypeBool,
58+
Optional: true,
59+
Default: DefaultPasswordPolicy.MustContainSpecialChars,
60+
},
61+
"max_password_age_in_days": {
62+
Type: schema.TypeInt,
63+
Optional: true,
64+
Default: DefaultPasswordPolicy.MaxPasswordAgeInDays,
65+
},
66+
"min_unique_passwords": {
67+
Type: schema.TypeInt,
68+
Optional: true,
69+
Default: DefaultPasswordPolicy.MinUniquePasswords,
70+
},
71+
"account_lockout_threshold": {
72+
Type: schema.TypeInt,
73+
Optional: true,
74+
Default: DefaultPasswordPolicy.AccountLockoutThreshold,
75+
},
76+
"failed_login_reset_duration_in_mins": {
77+
Type: schema.TypeInt,
78+
Optional: true,
79+
Default: DefaultPasswordPolicy.FailedLoginResetDurationInMins,
80+
},
81+
"account_lockout_duration_in_mins": {
82+
Type: schema.TypeInt,
83+
Optional: true,
84+
Default: DefaultPasswordPolicy.AccountLockoutDurationInMins,
85+
},
86+
"require_mfa": {
87+
Type: schema.TypeBool,
88+
Optional: true,
89+
Default: DefaultPasswordPolicy.RequireMfa,
90+
},
91+
"remember_mfa": {
92+
Type: schema.TypeBool,
93+
Optional: true,
94+
Default: DefaultPasswordPolicy.RememberMfa,
95+
},
96+
},
97+
}
98+
}
99+
100+
func resourceSumologicPasswordPolicyRead(d *schema.ResourceData, meta interface{}) error {
101+
c := meta.(*Client)
102+
103+
passwordPolicy, err := c.GetPasswordPolicy()
104+
if err != nil {
105+
return err
106+
}
107+
108+
setPasswordPolicyResource(d, passwordPolicy)
109+
return nil
110+
}
111+
112+
func resourceSumologicPasswordPolicyCreate(d *schema.ResourceData, meta interface{}) error {
113+
// Since password policy can only be set and not created, we just update the password policy with the given fields.
114+
err := resourceSumologicPasswordPolicyUpdate(d, meta)
115+
if err != nil {
116+
return err
117+
}
118+
119+
d.SetId("passwordPolicy")
120+
return nil
121+
}
122+
123+
func resourceSumologicPasswordPolicyDelete(d *schema.ResourceData, meta interface{}) error {
124+
c := meta.(*Client)
125+
return c.ResetPasswordPolicy()
126+
}
127+
128+
func resourceSumologicPasswordPolicyUpdate(d *schema.ResourceData, meta interface{}) error {
129+
passwordPolicy := resourceToPasswordPolicy(d)
130+
131+
c := meta.(*Client)
132+
updatedPasswordPolicy, err := c.UpdatePasswordPolicy(passwordPolicy)
133+
if err != nil {
134+
return err
135+
}
136+
137+
setPasswordPolicyResource(d, updatedPasswordPolicy)
138+
return nil
139+
}
140+
141+
func resourceToPasswordPolicy(d *schema.ResourceData) PasswordPolicy {
142+
return PasswordPolicy{
143+
MinLength: d.Get("min_length").(int),
144+
MaxLength: d.Get("max_length").(int),
145+
MustContainLowercase: d.Get("must_contain_lowercase").(bool),
146+
MustContainUppercase: d.Get("must_contain_uppercase").(bool),
147+
MustContainDigits: d.Get("must_contain_digits").(bool),
148+
MustContainSpecialChars: d.Get("must_contain_special_chars").(bool),
149+
MaxPasswordAgeInDays: d.Get("max_password_age_in_days").(int),
150+
MinUniquePasswords: d.Get("min_unique_passwords").(int),
151+
AccountLockoutThreshold: d.Get("account_lockout_threshold").(int),
152+
FailedLoginResetDurationInMins: d.Get("failed_login_reset_duration_in_mins").(int),
153+
AccountLockoutDurationInMins: d.Get("account_lockout_duration_in_mins").(int),
154+
RequireMfa: d.Get("require_mfa").(bool),
155+
RememberMfa: d.Get("remember_mfa").(bool),
156+
}
157+
}
158+
159+
func setPasswordPolicyResource(d *schema.ResourceData, passwordPolicy *PasswordPolicy) {
160+
d.Set("min_length", passwordPolicy.MinLength)
161+
d.Set("max_length", passwordPolicy.MaxLength)
162+
d.Set("must_contain_lowercase", passwordPolicy.MustContainLowercase)
163+
d.Set("must_contain_uppercase", passwordPolicy.MustContainUppercase)
164+
d.Set("must_contain_digits", passwordPolicy.MustContainDigits)
165+
d.Set("must_contain_special_chars", passwordPolicy.MustContainSpecialChars)
166+
d.Set("max_password_age_in_days", passwordPolicy.MaxPasswordAgeInDays)
167+
d.Set("min_unique_passwords", passwordPolicy.MinUniquePasswords)
168+
d.Set("account_lockout_threshold", passwordPolicy.AccountLockoutThreshold)
169+
d.Set("failed_login_reset_duration_in_mins", passwordPolicy.FailedLoginResetDurationInMins)
170+
d.Set("account_lockout_duration_in_mins", passwordPolicy.AccountLockoutDurationInMins)
171+
d.Set("require_mfa", passwordPolicy.RequireMfa)
172+
d.Set("remember_mfa", passwordPolicy.RememberMfa)
173+
}
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
package sumologic
2+
3+
import (
4+
"fmt"
5+
"strconv"
6+
"testing"
7+
8+
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
9+
"github.com/hashicorp/terraform-plugin-sdk/terraform"
10+
)
11+
12+
func TestAccPasswordPolicy_create(t *testing.T) {
13+
passwordPolicy := PasswordPolicy{
14+
MinLength: 9,
15+
MaxLength: 50,
16+
MustContainLowercase: false,
17+
MustContainUppercase: true,
18+
MustContainDigits: false,
19+
MustContainSpecialChars: true,
20+
MaxPasswordAgeInDays: 364,
21+
MinUniquePasswords: 4,
22+
AccountLockoutThreshold: 5,
23+
FailedLoginResetDurationInMins: 3,
24+
AccountLockoutDurationInMins: 30,
25+
RequireMfa: false,
26+
RememberMfa: false,
27+
}
28+
29+
resource.Test(t, resource.TestCase{
30+
PreCheck: func() { testAccPreCheck(t) },
31+
Providers: testAccProviders,
32+
CheckDestroy: testAccCheckPasswordPolicyDestroy(),
33+
Steps: []resource.TestStep{
34+
{
35+
Config: newPasswordPolicyConfig("test", &passwordPolicy),
36+
Check: resource.ComposeTestCheckFunc(
37+
testAccCheckPasswordPolicyExists("sumologic_password_policy.test"),
38+
testPasswordPolicyCheckResourceAttr("sumologic_password_policy.test", &passwordPolicy),
39+
),
40+
},
41+
},
42+
})
43+
}
44+
45+
func TestAccPasswordPolicy_update(t *testing.T) {
46+
passwordPolicy := PasswordPolicy{
47+
MinLength: 10,
48+
MaxLength: 51,
49+
MustContainLowercase: true,
50+
MustContainUppercase: true,
51+
MustContainDigits: false,
52+
MustContainSpecialChars: true,
53+
MaxPasswordAgeInDays: 364,
54+
MinUniquePasswords: 4,
55+
AccountLockoutThreshold: 5,
56+
FailedLoginResetDurationInMins: 3,
57+
AccountLockoutDurationInMins: 30,
58+
RequireMfa: false,
59+
RememberMfa: false,
60+
}
61+
62+
updatedPasswordPolicy := PasswordPolicy{
63+
MinLength: 12,
64+
MaxLength: 52,
65+
MustContainLowercase: false,
66+
MustContainUppercase: false,
67+
MustContainDigits: false,
68+
MustContainSpecialChars: true,
69+
MaxPasswordAgeInDays: 365,
70+
MinUniquePasswords: 5,
71+
AccountLockoutThreshold: 6,
72+
FailedLoginResetDurationInMins: 5,
73+
AccountLockoutDurationInMins: 31,
74+
RequireMfa: true,
75+
RememberMfa: true,
76+
}
77+
78+
resource.Test(t, resource.TestCase{
79+
PreCheck: func() { testAccPreCheck(t) },
80+
Providers: testAccProviders,
81+
CheckDestroy: testAccCheckPasswordPolicyDestroy(),
82+
Steps: []resource.TestStep{
83+
{
84+
Config: newPasswordPolicyConfig("test", &passwordPolicy),
85+
Check: resource.ComposeTestCheckFunc(
86+
testAccCheckPasswordPolicyExists("sumologic_password_policy.test"),
87+
testPasswordPolicyCheckResourceAttr("sumologic_password_policy.test", &passwordPolicy),
88+
),
89+
},
90+
{
91+
Config: newPasswordPolicyConfig("test", &updatedPasswordPolicy),
92+
Check: resource.ComposeTestCheckFunc(
93+
testAccCheckPasswordPolicyExists("sumologic_password_policy.test"),
94+
testPasswordPolicyCheckResourceAttr("sumologic_password_policy.test", &updatedPasswordPolicy),
95+
),
96+
},
97+
},
98+
})
99+
}
100+
101+
func testAccCheckPasswordPolicyDestroy() resource.TestCheckFunc {
102+
// This check will break if we change the default values for password policy in the API
103+
return func(s *terraform.State) error {
104+
client := testAccProvider.Meta().(*Client)
105+
for _, rs := range s.RootModule().Resources {
106+
if rs.Type != "sumologic_password_policy" {
107+
continue
108+
}
109+
110+
passwordPolicy, err := client.GetPasswordPolicy()
111+
if err != nil {
112+
return fmt.Errorf("Encountered an error: " + err.Error())
113+
}
114+
115+
if (*passwordPolicy) != DefaultPasswordPolicy {
116+
return fmt.Errorf("Password policy wasn't reset properly")
117+
}
118+
}
119+
return nil
120+
}
121+
}
122+
123+
func testAccCheckPasswordPolicyExists(name string) resource.TestCheckFunc {
124+
return func(s *terraform.State) error {
125+
_, ok := s.RootModule().Resources[name]
126+
if !ok {
127+
return fmt.Errorf("Password policy not found: %s", name)
128+
}
129+
return nil
130+
}
131+
}
132+
133+
func testPasswordPolicyCheckResourceAttr(resourceName string, passwordPolicy *PasswordPolicy) resource.TestCheckFunc {
134+
return func(s *terraform.State) error {
135+
f := resource.ComposeTestCheckFunc(
136+
resource.TestCheckResourceAttr(resourceName, "min_length", strconv.Itoa(passwordPolicy.MinLength)),
137+
resource.TestCheckResourceAttr(resourceName, "max_length", strconv.Itoa(passwordPolicy.MaxLength)),
138+
resource.TestCheckResourceAttr(resourceName, "must_contain_lowercase", strconv.FormatBool(passwordPolicy.MustContainLowercase)),
139+
resource.TestCheckResourceAttr(resourceName, "must_contain_uppercase", strconv.FormatBool(passwordPolicy.MustContainUppercase)),
140+
resource.TestCheckResourceAttr(resourceName, "must_contain_digits", strconv.FormatBool(passwordPolicy.MustContainDigits)),
141+
resource.TestCheckResourceAttr(resourceName, "must_contain_special_chars", strconv.FormatBool(passwordPolicy.MustContainSpecialChars)),
142+
resource.TestCheckResourceAttr(resourceName, "max_password_age_in_days", strconv.Itoa(passwordPolicy.MaxPasswordAgeInDays)),
143+
resource.TestCheckResourceAttr(resourceName, "min_unique_passwords", strconv.Itoa(passwordPolicy.MinUniquePasswords)),
144+
resource.TestCheckResourceAttr(resourceName, "account_lockout_threshold", strconv.Itoa(passwordPolicy.AccountLockoutThreshold)),
145+
resource.TestCheckResourceAttr(resourceName, "failed_login_reset_duration_in_mins", strconv.Itoa(passwordPolicy.FailedLoginResetDurationInMins)),
146+
resource.TestCheckResourceAttr(resourceName, "account_lockout_duration_in_mins", strconv.Itoa(passwordPolicy.AccountLockoutDurationInMins)),
147+
resource.TestCheckResourceAttr(resourceName, "require_mfa", strconv.FormatBool(passwordPolicy.RequireMfa)),
148+
resource.TestCheckResourceAttr(resourceName, "remember_mfa", strconv.FormatBool(passwordPolicy.RememberMfa)),
149+
)
150+
return f(s)
151+
}
152+
}
153+
154+
func newPasswordPolicyConfig(label string, passwordPolicy *PasswordPolicy) string {
155+
return fmt.Sprintf(`
156+
resource "sumologic_password_policy" "%s" {
157+
min_length = %d
158+
max_length = %d
159+
must_contain_lowercase = %t
160+
must_contain_uppercase = %t
161+
must_contain_digits = %t
162+
must_contain_special_chars = %t
163+
max_password_age_in_days = %d
164+
min_unique_passwords = %d
165+
account_lockout_threshold = %d
166+
failed_login_reset_duration_in_mins = %d
167+
account_lockout_duration_in_mins = %d
168+
require_mfa = %t
169+
remember_mfa = %t
170+
}`, label,
171+
passwordPolicy.MinLength,
172+
passwordPolicy.MaxLength,
173+
passwordPolicy.MustContainLowercase,
174+
passwordPolicy.MustContainUppercase,
175+
passwordPolicy.MustContainDigits,
176+
passwordPolicy.MustContainSpecialChars,
177+
passwordPolicy.MaxPasswordAgeInDays,
178+
passwordPolicy.MinUniquePasswords,
179+
passwordPolicy.AccountLockoutThreshold,
180+
passwordPolicy.FailedLoginResetDurationInMins,
181+
passwordPolicy.AccountLockoutDurationInMins,
182+
passwordPolicy.RequireMfa,
183+
passwordPolicy.RememberMfa)
184+
}

0 commit comments

Comments
 (0)