Skip to content

Commit

Permalink
Allow okta_authenticator to Import resources (okta#907)
Browse files Browse the repository at this point in the history
  • Loading branch information
virgofx committed Jan 21, 2022
1 parent d472d1a commit 790be87
Show file tree
Hide file tree
Showing 14 changed files with 350 additions and 201 deletions.
20 changes: 20 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
version: '3.7'

services:
gw:
container_name: okta
image: golang:latest
stdin_open: true
tty: true
volumes:
- .:/main
working_dir: /main


# TO BUILD
#
# > go get github.com/bflad/tfproviderlint/cmd/tfproviderlint
# > make tools
# > make build
# > env GOOS=windows GOARCH=amd64 go build -o terraform-provider-okta_v3.20.2.exe

2 changes: 1 addition & 1 deletion okta/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ func (c *Config) loadAndValidate(ctx context.Context) error {
okta.WithRateLimitMaxBackOff(int64(c.maxWait)),
okta.WithRequestTimeout(int64(c.requestTimeout)),
okta.WithRateLimitMaxRetries(int32(c.retryCount)),
okta.WithUserAgentExtra("okta-terraform/3.20.2"),
okta.WithUserAgentExtra("okta-terraform/3.20.3"),
}
if c.apiToken == "" {
setters = append(setters, okta.WithAuthorizationMode("PrivateKey"))
Expand Down
4 changes: 2 additions & 2 deletions okta/internal/apimutex/apimutex_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ func TestGet(t *testing.T) {
if err != nil {
t.Fatalf("api mutex constructor had error %+v", err)
}
if len(amu.status) != 13 {
t.Fatalf("amu status map should sized 13 but was sized %d", len(amu.status))
if len(amu.status) != 14 {
t.Fatalf("amu status map should sized 14 but was sized %d", len(amu.status))
}
keys := []string{
"users",
Expand Down
22 changes: 22 additions & 0 deletions okta/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,14 @@ var (
Default: statusActive,
ValidateDiagFunc: elemInSlice([]string{statusActive, statusInactive}),
}

isOieSchema = &schema.Schema{
Type: schema.TypeBool,
//Computed: true,
Optional: true,
Default: false,
Description: "Is the policy using Okta Identity Engine (OIE) with authenticators instead of factors?",
}
)

func findPolicy(ctx context.Context, m interface{}, name, policyType string) (*okta.Policy, error) {
Expand Down Expand Up @@ -137,6 +145,20 @@ func buildPolicySchema(target map[string]*schema.Schema) map[string]*schema.Sche
return buildSchema(basePolicySchema, target)
}

func buildDefaultMfaPolicySchema(target map[string]*schema.Schema) map[string]*schema.Schema {
schema := buildDefaultPolicySchema(target)
schema["is_oie"] = isOieSchema

return schema
}

func buildMfaPolicySchema(target map[string]*schema.Schema) map[string]*schema.Schema {
schema := buildPolicySchema(target)
schema["is_oie"] = isOieSchema

return schema
}

func createPolicy(ctx context.Context, d *schema.ResourceData, m interface{}, template sdk.Policy) error {
logger(m).Info("creating policy", "name", template.Name, "type", template.Type)
if err := ensureNotDefaultPolicy(d); err != nil {
Expand Down
16 changes: 8 additions & 8 deletions okta/resource_okta_authenticator.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@ func resourceAuthenticator() *schema.Resource {
ReadContext: resourceAuthenticatorRead,
UpdateContext: resourceAuthenticatorUpdate,
DeleteContext: resourceAuthenticatorDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
Schema: map[string]*schema.Schema{
"key": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "A human-readable string that identifies the Authenticator",
ValidateDiagFunc: elemInSlice([]string{
"okta_email", "google_otp", "okta_verify", "onprem_mfa", "okta_password",
"phone_number", "rsa_token", "security_question", "duo",
}),
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "A human-readable string that identifies the Authenticator",
ValidateDiagFunc: elemInSlice(sdk.AuthenticatorProviders),
},
"name": {
Type: schema.TypeString,
Expand Down
26 changes: 5 additions & 21 deletions okta/resource_okta_factor.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,11 @@ func resourceFactor() *schema.Resource {
},
Schema: map[string]*schema.Schema{
"provider_id": {
Type: schema.TypeString,
Required: true,
ValidateDiagFunc: elemInSlice([]string{
sdk.DuoFactor,
sdk.FidoU2fFactor,
sdk.FidoWebauthnFactor,
sdk.GoogleOtpFactor,
sdk.OktaCallFactor,
sdk.OktaOtpFactor,
sdk.OktaPasswordFactor,
sdk.OktaPushFactor,
sdk.OktaQuestionFactor,
sdk.OktaSmsFactor,
sdk.OktaEmailFactor,
sdk.RsaTokenFactor,
sdk.SymantecVipFactor,
sdk.YubikeyTokenFactor,
sdk.HotpFactor,
}),
Description: "Factor provider ID",
ForceNew: true,
Type: schema.TypeString,
Required: true,
ValidateDiagFunc: elemInSlice(sdk.FactorProviders),
Description: "Factor provider ID",
ForceNew: true,
},
"active": {
Type: schema.TypeBool,
Expand Down
167 changes: 103 additions & 64 deletions okta/resource_okta_policy_mfa.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func resourcePolicyMfa() *schema.Resource {
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
Schema: buildPolicySchema(buildFactorProviders()),
Schema: buildMfaPolicySchema(buildFactorSchemaProviders()),
}
}

Expand All @@ -41,21 +41,11 @@ func resourcePolicyMfaRead(ctx context.Context, d *schema.ResourceData, m interf
if policy == nil {
return nil
}
syncFactor(d, sdk.DuoFactor, policy.Settings.Factors.Duo)
syncFactor(d, sdk.FidoU2fFactor, policy.Settings.Factors.FidoU2f)
syncFactor(d, sdk.FidoWebauthnFactor, policy.Settings.Factors.FidoWebauthn)
syncFactor(d, sdk.GoogleOtpFactor, policy.Settings.Factors.GoogleOtp)
syncFactor(d, sdk.OktaCallFactor, policy.Settings.Factors.OktaCall)
syncFactor(d, sdk.OktaOtpFactor, policy.Settings.Factors.OktaOtp)
syncFactor(d, sdk.OktaPasswordFactor, policy.Settings.Factors.OktaPassword)
syncFactor(d, sdk.OktaPushFactor, policy.Settings.Factors.OktaPush)
syncFactor(d, sdk.OktaQuestionFactor, policy.Settings.Factors.OktaQuestion)
syncFactor(d, sdk.OktaSmsFactor, policy.Settings.Factors.OktaSms)
syncFactor(d, sdk.OktaEmailFactor, policy.Settings.Factors.OktaEmail)
syncFactor(d, sdk.RsaTokenFactor, policy.Settings.Factors.RsaToken)
syncFactor(d, sdk.SymantecVipFactor, policy.Settings.Factors.SymantecVip)
syncFactor(d, sdk.YubikeyTokenFactor, policy.Settings.Factors.YubikeyToken)
syncFactor(d, sdk.HotpFactor, policy.Settings.Factors.Hotp)

_ = d.Set("is_oie", policy.Settings.Type == "AUTHENTICATORS")

syncSettings(d, policy.Settings)

err = syncPolicyFromUpstream(d, policy)
if err != nil {
return diag.Errorf("failed to sync policy: %v", err)
Expand All @@ -80,23 +70,6 @@ func resourcePolicyMfaDelete(ctx context.Context, d *schema.ResourceData, m inte
return nil
}

func buildFactorProvider(d *schema.ResourceData, key string) *sdk.PolicyFactor {
rawFactor := d.Get(key).(map[string]interface{})
consent := rawFactor["consent_type"]
enroll := rawFactor["enroll"]
if consent == nil && enroll == nil {
return nil
}
f := &sdk.PolicyFactor{}
if consent != nil {
f.Consent = &sdk.Consent{Type: consent.(string)}
}
if enroll != nil {
f.Enroll = &sdk.Enroll{Self: enroll.(string)}
}
return f
}

// create or update a MFA policy
func buildMFAPolicy(d *schema.ResourceData) sdk.Policy {
policy := sdk.MfaPolicy()
Expand All @@ -106,12 +79,47 @@ func buildMFAPolicy(d *schema.ResourceData) sdk.Policy {
if priority, ok := d.GetOk("priority"); ok {
policy.Priority = int64(priority.(int))
}
policy.Settings = &sdk.PolicySettings{
policy.Settings = buildSettings(d)
policy.Conditions = &okta.PolicyRuleConditions{
People: getGroups(d),
}
return policy
}

// Opposite of syncSettings(): Build the corresponding sdk.PolicySettings based on the schema.ResourceData
func buildSettings(d *schema.ResourceData) *sdk.PolicySettings {
if d.Get("is_oie") == true {
authenticators := []*sdk.PolicyAuthenticator{}

for _, key := range remove(sdk.AuthenticatorProviders, sdk.OktaPasswordFactor) {
rawFactor := d.Get(key).(map[string]interface{})
enroll := rawFactor["enroll"]
if enroll == nil {
continue
}

authenticator := &sdk.PolicyAuthenticator{}
authenticator.Key = key
if enroll != nil {
authenticator.Enroll = &sdk.Enroll{Self: enroll.(string)}
}
authenticators = append(authenticators, authenticator)
}

return &sdk.PolicySettings{
Type: "AUTHENTICATORS",
Authenticators: authenticators,
}
}

return &sdk.PolicySettings{
Type: "FACTORS",
Factors: &sdk.PolicyFactorsSettings{
Duo: buildFactorProvider(d, sdk.DuoFactor),
FidoU2f: buildFactorProvider(d, sdk.FidoU2fFactor),
FidoWebauthn: buildFactorProvider(d, sdk.FidoWebauthnFactor),
GoogleOtp: buildFactorProvider(d, sdk.GoogleOtpFactor),
Hotp: buildFactorProvider(d, sdk.HotpFactor),
OktaCall: buildFactorProvider(d, sdk.OktaCallFactor),
OktaOtp: buildFactorProvider(d, sdk.OktaOtpFactor),
OktaPassword: buildFactorProvider(d, sdk.OktaPasswordFactor),
Expand All @@ -122,51 +130,82 @@ func buildMFAPolicy(d *schema.ResourceData) sdk.Policy {
RsaToken: buildFactorProvider(d, sdk.RsaTokenFactor),
SymantecVip: buildFactorProvider(d, sdk.SymantecVipFactor),
YubikeyToken: buildFactorProvider(d, sdk.YubikeyTokenFactor),
Hotp: buildFactorProvider(d, sdk.HotpFactor),
},
}
policy.Conditions = &okta.PolicyRuleConditions{
People: getGroups(d),
}

func buildFactorProvider(d *schema.ResourceData, key string) *sdk.PolicyFactor {
rawFactor := d.Get(key).(map[string]interface{})
consent := rawFactor["consent_type"]
enroll := rawFactor["enroll"]
if consent == nil && enroll == nil {
return nil
}
f := &sdk.PolicyFactor{}
if consent != nil {
f.Consent = &sdk.Consent{Type: consent.(string)}
}
if enroll != nil {
f.Enroll = &sdk.Enroll{Self: enroll.(string)}
}
return f
}

// Syncs either classic factors or OIE authenticators into the resource data.
func syncSettings(d *schema.ResourceData, settings *sdk.PolicySettings) {
if settings.Type == "AUTHENTICATORS" {
for _, key := range remove(sdk.AuthenticatorProviders, sdk.OktaPasswordFactor) {
syncAuthenticator(d, key, settings.Authenticators)
}
} else {
syncFactor(d, sdk.DuoFactor, settings.Factors.Duo)
syncFactor(d, sdk.HotpFactor, settings.Factors.YubikeyToken)
syncFactor(d, sdk.FidoU2fFactor, settings.Factors.FidoU2f)
syncFactor(d, sdk.FidoWebauthnFactor, settings.Factors.FidoWebauthn)
syncFactor(d, sdk.GoogleOtpFactor, settings.Factors.GoogleOtp)
syncFactor(d, sdk.OktaCallFactor, settings.Factors.OktaCall)
syncFactor(d, sdk.OktaOtpFactor, settings.Factors.OktaOtp)
syncFactor(d, sdk.OktaPasswordFactor, settings.Factors.OktaPassword)
syncFactor(d, sdk.OktaPushFactor, settings.Factors.OktaPush)
syncFactor(d, sdk.OktaQuestionFactor, settings.Factors.OktaQuestion)
syncFactor(d, sdk.OktaSmsFactor, settings.Factors.OktaSms)
syncFactor(d, sdk.OktaEmailFactor, settings.Factors.OktaEmail)
syncFactor(d, sdk.RsaTokenFactor, settings.Factors.RsaToken)
syncFactor(d, sdk.SymantecVipFactor, settings.Factors.SymantecVip)
syncFactor(d, sdk.YubikeyTokenFactor, settings.Factors.YubikeyToken)
}
return policy
}

func syncFactor(d *schema.ResourceData, k string, f *sdk.PolicyFactor) {
if f == nil {
if f != nil {
_ = d.Set(k, map[string]interface{}{
"consent_type": "NONE",
"enroll": "NOT_ALLOWED",
"consent_type": f.Consent.Type,
"enroll": f.Enroll.Self,
})
return
}
_ = d.Set(k, map[string]interface{}{
"consent_type": f.Consent.Type,
"enroll": f.Enroll.Self,
})
}

var factorProviders = []string{
sdk.DuoFactor,
sdk.FidoU2fFactor,
sdk.FidoWebauthnFactor,
sdk.GoogleOtpFactor,
sdk.OktaCallFactor,
sdk.OktaOtpFactor,
sdk.OktaPasswordFactor,
sdk.OktaPushFactor,
sdk.OktaQuestionFactor,
sdk.OktaSmsFactor,
sdk.OktaEmailFactor,
sdk.RsaTokenFactor,
sdk.SymantecVipFactor,
sdk.YubikeyTokenFactor,
sdk.HotpFactor,
func syncAuthenticator(d *schema.ResourceData, k string, authenticators []*sdk.PolicyAuthenticator) {
for _, authenticator := range authenticators {
if authenticator.Key == k {
// Skip OktaPassword as this should never be returned for MFA policies using authenticator.
// Enrollment policy changes for OIE for password
// https://help.okta.com/okta_help.htm?type=oie&id=ext-about-mfa-enrol-policies
if k != sdk.OktaPasswordFactor {
_ = d.Set(k, map[string]interface{}{
"enroll": authenticator.Enroll.Self,
})
}
return
}
}
}

// List of factor provider above, they all follow the same schema
func buildFactorProviders() map[string]*schema.Schema {
func buildFactorSchemaProviders() map[string]*schema.Schema {
res := make(map[string]*schema.Schema)
for _, key := range factorProviders {
// Note: It's okay to append and have duplicates as we're setting back into a map here
for _, key := range append(sdk.FactorProviders, sdk.AuthenticatorProviders...) {
res[key] = &schema.Schema{
Optional: true,
Type: schema.TypeMap,
Expand Down
Loading

0 comments on commit 790be87

Please sign in to comment.