Skip to content

Commit

Permalink
Add default password and MFA policy (#378)
Browse files Browse the repository at this point in the history
Added default password and MFA policy resources
  • Loading branch information
bogdanprodan-okta authored Mar 15, 2021
1 parent 947405a commit 92957b7
Show file tree
Hide file tree
Showing 20 changed files with 865 additions and 39 deletions.
5 changes: 5 additions & 0 deletions examples/okta_policy_mfa_default/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# okta_policy_mfa_default

This resource represents Okta default MFA Policy. For more information see the [API docs](https://developer.okta.com/docs/api/resources/policy)

- Example of default MFA policy [can be found here](./basic.tf)
3 changes: 3 additions & 0 deletions examples/okta_policy_mfa_default/basic.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
resource "okta_policy_mfa_default" "test" {

}
10 changes: 10 additions & 0 deletions examples/okta_policy_mfa_default/basic_updated.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
resource "okta_policy_mfa_default" "test" {
google_otp = {
enroll = "OPTIONAL"
}
depends_on = [okta_factor.google_otp]
}

resource "okta_factor" "google_otp" {
provider_id = "google_otp"
}
5 changes: 5 additions & 0 deletions examples/okta_policy_password_default/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# okta_policy_password_default

This resource represents Okta default Password Policy. For more information see the [API docs](https://developer.okta.com/docs/api/resources/policy)

- Example of default password policy [can be found here](./basic.tf)
3 changes: 3 additions & 0 deletions examples/okta_policy_password_default/basic.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
resource "okta_policy_password_default" "test" {
sms_recovery = "ACTIVE"
}
3 changes: 3 additions & 0 deletions examples/okta_policy_password_default/basic_updated.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
resource "okta_policy_password_default" "test" {
sms_recovery = "INACTIVE"
}
12 changes: 10 additions & 2 deletions okta/data_source_okta_default_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,16 @@ func dataSourceDefaultPolicies() *schema.Resource {

func dataSourceDefaultPolicyRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
policyType := d.Get("type").(string)
var name string
if policyType == sdk.IdpDiscoveryType {
return setPolicyByName(ctx, d, m, "Idp Discovery Policy")
name = "Idp Discovery Policy"
} else {
name = "Default Policy"
}
return setPolicyByName(ctx, d, m, "Default Policy")
policy, err := findPolicy(ctx, m, name, policyType)
if err != nil {
return diag.FromErr(err)
}
d.SetId(policy.Id)
return nil
}
5 changes: 3 additions & 2 deletions okta/data_source_okta_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,15 @@ func findGroup(ctx context.Context, name string, d *schema.ResourceData, m inter
groups, _, err := client.Group.ListGroups(ctx, searchParams)
if err != nil {
return diag.Errorf("failed to query for groups: %v", err)
} else if len(groups) < 1 {
} else if len(groups) == 0 {
if okType {
return diag.Errorf("group with name '%s' and type '%s' does not exist", name, d.Get("type").(string))
}
return diag.Errorf("group with name '%s' does not exist", name)
} else if len(groups) > 1 {
// TODO try to find exact match
logger(m).Warn("Found multiple groups with the supplied parameters: using the first one which may only be a partial match", "name", groups[0].Profile.Name)
} else if len(groups[0].Profile.Name) != len(name) {
} else if groups[0].Profile.Name != name {
logger(m).Warn("The group with an exact match to the supplied name was not found: using partial match which contains name as a substring", "name", groups[0].Profile.Name)
}
d.SetId(groups[0].Id)
Expand Down
21 changes: 5 additions & 16 deletions okta/data_source_okta_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/okta/okta-sdk-golang/v2/okta/query"
"github.com/okta/terraform-provider-okta/sdk"
)

Expand All @@ -16,7 +15,7 @@ func dataSourcePolicy() *schema.Resource {
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Description: "name of policy",
Description: "Name of policy",
Required: true,
},
"type": {
Expand All @@ -35,20 +34,10 @@ func dataSourcePolicy() *schema.Resource {
}

func dataSourcePolicyRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
return setPolicyByName(ctx, d, m, d.Get("name").(string))
}

func setPolicyByName(ctx context.Context, d *schema.ResourceData, m interface{}, name string) diag.Diagnostics {
policyType := d.Get("type").(string)
policies, _, err := getOktaClientFromMetadata(m).Policy.ListPolicies(ctx, &query.Params{Type: policyType})
policy, err := findPolicy(ctx, m, d.Get("name").(string), d.Get("type").(string))
if err != nil {
return diag.Errorf("failed to list policies: %v", err)
}
for _, policy := range policies {
if policy.Name == name {
d.SetId(policy.Id)
return nil
}
return diag.FromErr(err)
}
return diag.Errorf("no policies retrieved for policy type %s, name %s", policyType, name)
d.SetId(policy.Id)
return nil
}
79 changes: 79 additions & 0 deletions okta/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/okta/okta-sdk-golang/v2/okta"
"github.com/okta/okta-sdk-golang/v2/okta/query"
"github.com/okta/terraform-provider-okta/sdk"
)

Expand Down Expand Up @@ -46,6 +47,34 @@ var (
},
}

defaultPolicySchema = map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Computed: true,
Description: "Default policy name",
},
"description": {
Type: schema.TypeString,
Computed: true,
Description: "Default policy description",
},
"priority": {
Type: schema.TypeInt,
Computed: true,
Description: "Default policy priority",
},
"status": {
Type: schema.TypeString,
Computed: true,
Description: "Default policy status",
},
"default_included_group_id": {
Type: schema.TypeString,
Computed: true,
Description: "Default group ID (always included)",
},
}

statusSchema = &schema.Schema{
Type: schema.TypeString,
Optional: true,
Expand All @@ -54,6 +83,52 @@ var (
}
)

func findPolicy(ctx context.Context, m interface{}, name, policyType string) (*okta.Policy, error) {
policies, resp, err := getOktaClientFromMetadata(m).Policy.ListPolicies(ctx, &query.Params{Type: policyType})
if err != nil {
return nil, fmt.Errorf("failed to list policies: %v", err)
}
for {
for _, policy := range policies {
if policy.Name == name {
return policy, nil
}
}
if resp.HasNextPage() {
resp, err = resp.Next(ctx, &policies)
if err != nil {
return nil, fmt.Errorf("failed to list policies: %v", err)
}
continue
} else {
break
}
}
return nil, fmt.Errorf("no policies retrieved for policy type '%s' and name '%s'", policyType, name)
}

func setDefaultPolicy(ctx context.Context, d *schema.ResourceData, m interface{}, policyType string) (*okta.Policy, error) {
policy, err := findPolicy(ctx, m, "Default Policy", policyType)
if err != nil {
return nil, err
}
groups, _, err := getOktaClientFromMetadata(m).Group.ListGroups(ctx, &query.Params{Q: "Everyone"})
if err != nil {
return nil, fmt.Errorf("failed find default group for default password policy: %v", err)
}
for i := range groups {
if groups[i].Profile.Name == "Everyone" {
_ = d.Set("default_included_group_id", groups[i].Id)
}
}
_ = d.Set("name", policy.Name)
_ = d.Set("description", policy.Description)
_ = d.Set("status", policy.Status)
_ = d.Set("priority", policy.Priority)
d.SetId(policy.Id)
return policy, nil
}

func getPeopleConditions(d *schema.ResourceData) *okta.GroupRulePeopleCondition {
return &okta.GroupRulePeopleCondition{
Groups: &okta.GroupRuleGroupCondition{
Expand All @@ -67,6 +142,10 @@ func getPeopleConditions(d *schema.ResourceData) *okta.GroupRulePeopleCondition
}
}

func buildDefaultPolicySchema(target map[string]*schema.Schema) map[string]*schema.Schema {
return buildSchema(defaultPolicySchema, target)
}

func buildPolicySchema(target map[string]*schema.Schema) map[string]*schema.Schema {
return buildSchema(basePolicySchema, target)
}
Expand Down
4 changes: 4 additions & 0 deletions okta/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ const (
oktaProfileMapping = "okta_profile_mapping"
oktaUser = "okta_user"
policyMfa = "okta_policy_mfa"
policyMfaDefault = "okta_policy_mfa_default"
policyPassword = "okta_policy_password"
policyPasswordDefault = "okta_policy_password_default"
policyRuleIdpDiscovery = "okta_policy_rule_idp_discovery"
policyRuleMfa = "okta_policy_rule_mfa"
policyRulePassword = "okta_policy_rule_password"
Expand Down Expand Up @@ -198,7 +200,9 @@ func Provider() *schema.Provider {
oktaProfileMapping: resourceOktaProfileMapping(),
oktaUser: resourceUser(),
policyMfa: resourcePolicyMfa(),
policyMfaDefault: resourcePolicyMfaDefault(),
policyPassword: resourcePolicyPassword(),
policyPasswordDefault: resourcePolicyPasswordDefault(),
policySignOn: resourcePolicySignOn(),
policyRuleIdpDiscovery: resourcePolicyRuleIdpDiscovery(),
policyRuleMfa: resourcePolicyMfaRule(),
Expand Down
1 change: 0 additions & 1 deletion okta/resource_okta_app_group_assignment.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ func resourceAppGroupAssignment() *schema.Resource {
return []*schema.ResourceData{d}, nil
},
},

Schema: map[string]*schema.Schema{
"app_id": {
Type: schema.TypeString,
Expand Down
24 changes: 7 additions & 17 deletions okta/resource_okta_policy_mfa.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func resourcePolicyMfa() *schema.Resource {
}

func resourcePolicyMfaCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
policy := buildMfaPolicy(d)
policy := buildMFAPolicy(d)
err := createPolicy(ctx, d, m, policy)
if err != nil {
return diag.Errorf("failed to create MFA policy: %v", err)
Expand Down Expand Up @@ -64,7 +64,7 @@ func resourcePolicyMfaRead(ctx context.Context, d *schema.ResourceData, m interf
}

func resourcePolicyMfaUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
policy := buildMfaPolicy(d)
policy := buildMFAPolicy(d)
err := updatePolicy(ctx, d, m, policy)
if err != nil {
return diag.Errorf("failed to update MFA policy: %v", err)
Expand Down Expand Up @@ -97,8 +97,8 @@ func buildFactorProvider(d *schema.ResourceData, key string) *sdk.PolicyFactor {
return f
}

// create or update a password policy
func buildMfaPolicy(d *schema.ResourceData) sdk.Policy {
// create or update a MFA policy
func buildMFAPolicy(d *schema.ResourceData) sdk.Policy {
policy := sdk.MfaPolicy()
policy.Name = d.Get("name").(string)
policy.Status = d.Get("status").(string)
Expand Down Expand Up @@ -163,18 +163,7 @@ var factorProviders = []string{
func buildFactorProviders() map[string]*schema.Schema {
res := make(map[string]*schema.Schema)
for _, key := range factorProviders {
sMap := getPolicyFactorSchema(key)
for nestedKey, nestedVal := range sMap {
res[nestedKey] = nestedVal
}
}
return res
}

func getPolicyFactorSchema(key string) map[string]*schema.Schema {
// These are primitives to allow defaulting. Terraform still does not support aggregate defaults.
return map[string]*schema.Schema{
key: {
res[key] = &schema.Schema{
Optional: true,
Type: schema.TypeMap,
Elem: &schema.Schema{
Expand All @@ -200,6 +189,7 @@ func getPolicyFactorSchema(key string) map[string]*schema.Schema {
}
return errs
},
},
}
}
return res
}
Loading

0 comments on commit 92957b7

Please sign in to comment.