Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add authentication policy resource #3098

Open
wants to merge 14 commits into
base: main
Choose a base branch
from

Conversation

Relativity74205
Copy link
Contributor

Added the following resources:

  • authentication_policy
  • account_authentication_policy_attachment
  • user_authentication_policy_attachment

Test Plan

  • acceptance tests (have been mostly added; could not be tested locally due to difficulties with acceptance test setup
  • manual tests

References

@Relativity74205
Copy link
Contributor Author

@sfc-gh-jcieslak
Here are some details about my problems with running the acceptance tests locally:

For starters I had to remove the following code from test setup, why do I need a SCIM Provisioner Role for running the acceptance tests?

	if err := helpers.EnsureScimProvisionerRolesExist(atc.client, ctx); err != nil {
		t.Fatal(err)
	}
	
	if err := helpers.EnsureScimProvisionerRolesExist(atc.secondaryClient, ctx); err != nil {
		t.Fatal(err)
	}

In addition, I noticed after some testing, that I had 10+ warehouses running in the snowflake account of my company, which I use for testing. Althorugh all warehouses were of size XS, the suspend time was set to 600 seconds. At this point I stopped setting up my test setup, because there seems to be something very odd. Perhaps I am making something wrong?

@csp33
Copy link

csp33 commented Sep 23, 2024

Hi! Thanks for this PR, it is a really important one for my team :)
In v0.95, many user parameters (such as +network_policy) were moved to the snowflake_user object (source). I wonder if this user_authentication_policy_attachment should also be moved (or duplicated) in snowflake_user.authentication_policy

@sfc-gh-jmichalak
Copy link
Collaborator

sfc-gh-jmichalak commented Sep 23, 2024

Hi @Relativity74205 @csp33 👋

Thanks a lot for your contribution :) We'll probably review this PR this week.

The code you quoted checks for some roles that are required for other acceptance tests to work. You can disable them for your tests (remove the code).

About these warehouses, we have sweepers which should remove stale objects. You can enable them with TEST_SF_TF_ENABLE_SWEEP=1.

I think this should be added as a part of the snowflake_user resource. WDYT @sfc-gh-jcieslak @sfc-gh-asawicki ?

@sfc-gh-jcieslak
Copy link
Collaborator

sfc-gh-jcieslak commented Sep 23, 2024

Hey
@Relativity74205 Thanks for the pr! As far as I remember, SCIM provisioner roles are only needed for security integration tests, so they're not needed for your tests. Regarding warehouses, I think the issue is that in our CI, we are running sweepers (sweepers_test.go) that remove those predefined warehouses/databases/schemas (those objects have a common prefix that allows us to differentiate between manually created objects and the one produced by acceptance test). You can specify your own suffix by specifying the TEST_SF_TF_TEST_OBJECT_SUFFIX env variable (you can see how the names will be generated in acceptance/testing.go). That way you can track created objects and drop them after your testing is done. It's not ideal, and I think we could improve this so that it will be easier for ppl that want to contribute, but that's what we have to work with right now. I'll try to review this today/tomorrow. Important note to what @sfc-gh-jmichalak proposed: if you have other objects and you are sharing the account with someone else, I wouldn't consider sweeping because it's very destructive. After setting TEST_SF_TF_TEST_OBJECT_SUFFIX our pre-test function will only create warehouse/database/schema once and every other test won't create anything because the suffix will be the same and objects will be already present.

@csp33 We have a ticket to add policies to user resources; it's assigned to @sfc-gh-asawicki. He's on vacation right now, but I'm guessing that would be one of his priorities when he'll be back (this week).

"snowflake_account_password_policy_attachment": resources.AccountPasswordPolicyAttachment(),
"snowflake_account_parameter": resources.AccountParameter(),
"snowflake_alert": resources.Alert(),
"snowflake_account": resources.Account(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please format your code with make fmt.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is how it is auto formatted for some reason...

Copy link
Collaborator

@sfc-gh-jmichalak sfc-gh-jmichalak Oct 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's interesting, but we can fix this on our side. Let's leave this unresolved.

"snowflake_account_parameter": resources.AccountParameter(),
"snowflake_alert": resources.Alert(),
"snowflake_account": resources.Account(),
"snowflake_account_authentication_policy_attachment": resources.AccountAuthenticationPolicyAttachment(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need these attachment resources - we should attach directly in users and accounts resources.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean similar to the network policy attachment? I have taken the password policy resource and its attachment as an example.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I thought that they could be specified in the user's resource.

What way should we take here? @sfc-gh-jcieslak @sfc-gh-asawicki

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sfc-gh-jmichalak How shall I procede here?

pkg/resources/authentication_policy.go Outdated Show resolved Hide resolved
pkg/resources/authentication_policy.go Outdated Show resolved Hide resolved
pkg/resources/authentication_policy.go Outdated Show resolved Hide resolved
pkg/resources/authentication_policy.go Outdated Show resolved Hide resolved
)

func TestAcc_AuthenticationPolicy(t *testing.T) {
accName := acc.TestClient().Ids.Alpha()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need more tests, like

  • basic flow
    1. create without optional
    2. set optional fields
    3. import
    4. handle external change (one field is enough)
    5. unset optional fields
    6. import
  • renaming
    Please take a look at row_access_policy_acceptance_test.go - specifically TestAcc_RowAccessPolicy and TestAcc_RowAccessPolicy_Rename.

We use custom generated models in there, but it can be tricky to set up, and I think you can use TF files + config map.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sfc-gh-jmichalak
I have not fixed my local test setup and can now finalize the tests. However, one question, how to do "handle external change (one field is enough)"?

pkg/resources/authentication_policy.go Show resolved Hide resolved
pkg/resources/authentication_policy.go Outdated Show resolved Hide resolved
pkg/resources/authentication_policy.go Outdated Show resolved Hide resolved
pkg/resources/authentication_policy.go Outdated Show resolved Hide resolved
@csp33
Copy link

csp33 commented Oct 3, 2024

hi @sfc-gh-jmichalak @sfc-gh-jcieslak

Is there progress related to this PR?
Thanks! 😄

@sfc-gh-jcieslak
Copy link
Collaborator

Hey @csp33
That's more of a question for @Relativity74205 as this is his pull request. After applying essential changes we'll be able to merge it and make some of the adjustments ourselves to apply the standards (or at least some of them) we defined for v1 resource refactoring.

@Relativity74205
Copy link
Contributor Author

I have started to look on the PR comments, I guess I can fix most of the stuff today/tomorrow. However, I need to get the testsuite running, before I start adding some additional tests. In contrast to the past, I had some major issues last week and after I noticed 15-20 warehouses running in parallel in th snowflake account, I stopped testing.

I will have a look on the hints given above by @sfc-gh-jcieslak and @sfc-gh-jmichalak , perhaps I will be more successful. However, I would suggest to make the usability of the test suite a little more user friendly.

@Relativity74205
Copy link
Contributor Author

I think I have resolved most issues, however, some are still open, because I need some clarifications. And I need to finish to setup the test suite and finish the tests after that.

Type: schema.TypeSet,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateDiagFunc: StringInSlice([]string{"ALL", "SAML", "PASSWORD", "OAUTH", "KEYPAIR"}, false),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, please check out warehouse enums in sdk - To... functions.

authenticationMethods := expandStringList(v.(*schema.Set).List())
authenticationMethodsValues := make([]sdk.AuthenticationMethods, len(authenticationMethods))
for i, v := range authenticationMethods {
authenticationMethodsValues[i] = sdk.AuthenticationMethods{Method: sdk.AuthenticationMethodsOption(v)}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After implementing To... enum validators, they should be used in all places when we convert from strings in user inputs to SDK fields.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done, please check

mfaAuthenticationMethodsValues[i] = sdk.MfaAuthenticationMethods{Method: sdk.MfaAuthenticationMethodsOption(v)}
// change to authentication methods
if d.HasChange("authentication_methods") {
if v, ok := d.GetOk("authentication_methods"); ok {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When this list is empty in the new config, this block won't execute. I think the check if len(authenticationMethodsValues) == 0 should be in else block for this if. Same for other fields.

In general, we do it like:

if v, ok := d.GetOk("authentication_methods"); ok {
	// handle changed, non-empty field
} else {
	// handle empty field
}

Copy link
Contributor Author

@Relativity74205 Relativity74205 Oct 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

pkg/resources/authentication_policy.go Outdated Show resolved Hide resolved
pkg/resources/authentication_policy.go Show resolved Hide resolved
@sfc-gh-jcieslak
Copy link
Collaborator

Thank you for your feedback @Relativity74205 I added you suggestion to our tasks about documentation improvement to clearly state how to use the testing suite and also added suggestion in other tasks to make it more friendly for newcomers.

@Relativity74205
Copy link
Contributor Author

Relativity74205 commented Oct 18, 2024

@sfc-gh-jcieslak @sfc-gh-jmichalak
I noticed a "bigger" problem, when I was testing the authentication policy manually. The problem are the set-parameters, because they have defaults. When running a terraform plan on an authentication policy, where e.g. authentication_methods was not set, I get

  # snowflake_authentication_policy.name will be updated in-place
  ~ resource "snowflake_authentication_policy" "name" {
      ~ authentication_methods     = [
          - "ALL",
        ]
        ....
    }

where ["ALL"] is the Snowflake default for this parameter.

However, setting the Default property in the schema definition of the resource, does not work, because of authentication_methods: Default is not valid for lists or sets. Therefore, what is the best way to resolve this? The only thing, which comes to my mind, is building a manual check inside of the ReadContextAuthenticationPolicy function.

PS: It works now, after I have added a custom check to ReadContextAuthenticationPolicy, it looks in general like this:

	authenticationPolicyDescriptions, err := client.AuthenticationPolicies.Describe(ctx, id)
	if err != nil {
		return diag.FromErr(err)
	}

	authenticationMethodsIs := make([]string, 0)
	if authenticationMethodsProperty, err := collections.FindFirst(authenticationPolicyDescriptions, func(prop sdk.AuthenticationPolicyDescription) bool { return prop.Property == "AUTHENTICATION_METHODS" }); err == nil {
		authenticationMethodsIs = append(authenticationMethodsIs, sdk.ParseCommaSeparatedStringArray(authenticationMethodsProperty.Value, false)...)
	}
	authenticationMethodsShould := d.Get("authentication_methods").(*schema.Set).List()
	if stringSlicesEqual(authenticationMethodsIs, []string{"ALL"}) && len(authenticationMethodsShould) == 0 {
		authenticationMethodsIs = []string{}
	}
	if err = d.Set("authentication_methods", authenticationMethodsIs); err != nil {
		return diag.FromErr(err)
	}

PS2: The unwanted consequence of the "hack" in ReadContext is that also the Import function needs a "hack":

	authenticationPolicyDescriptions, err := client.AuthenticationPolicies.Describe(ctx, id)
	securityIntegrationsIs := make([]string, 0)
	if securityIntegrationsProperty, err := collections.FindFirst(authenticationPolicyDescriptions, func(prop sdk.AuthenticationPolicyDescription) bool { return prop.Property == "SECURITY_INTEGRATIONS" }); err == nil {
		securityIntegrationsIs = append(securityIntegrationsIs, sdk.ParseCommaSeparatedStringArray(securityIntegrationsProperty.Value, false)...)
	}
	if err = d.Set("security_integrations", securityIntegrationsIs); err != nil {
		return nil, err
	}

PS3: I pushed a version, which seems to be working fine, however, I don't like it with the "hacks". Is there a better solution?

Actually, one other solution comes to my mind, which would prevent this entire problem: Removing the optional attribute of these parameters. When the list parameters are required, then the default check isn't needed. However, this wouldn't be intuitive for the user as these parameters are optional in Snowflake.

@sfc-gh-jcieslak
Copy link
Collaborator

Hey @Relativity74205 👋
We'll have some time on Monday to review the change again. Regarding authentication_methods, Terraform SDKv2 is pretty limited when it comes to ignoring such changes. We had to come up with pretty tricky solutions to handle some of the cases. This case looks pretty similar to the warehouse_type field in the warehouse resource. The solution is to not set this value in Read at all (only set it in Import). The Terraform should handle the changes correctly after that. You can try that or leave it as is, and we'll take a closer look at it after we merge it (there may be some additional difficulties with handling that, because it's a List/Set and not a primitive type which are usually easier to handle).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants