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

added identity provider resource #92

Merged
merged 72 commits into from
Apr 18, 2019
Merged

added identity provider resource #92

merged 72 commits into from
Apr 18, 2019

Conversation

AndrewChubatiuk
Copy link
Contributor

Restored #61 PR

@mrparkers
Copy link
Contributor

@AndrewChubatiuk what are your thoughts into splitting each identity provider into multiple resources? I'm concerned that trying to handle every identity provider in one resource would make it too complex and difficult to properly test.

@AndrewChubatiuk
Copy link
Contributor Author

Okay, I can split them

@mrparkers
Copy link
Contributor

Cool. Let's just start with a couple to see if that route makes sense. I understand that, for the most part, the APIs are very similar, so I expect a decent amount of code duplication.

@AndrewChubatiuk
Copy link
Contributor Author

AndrewChubatiuk commented Feb 14, 2019

Well, yes, code duplications is a problem and suppose it'll be painful to support this. I have one suggestion:
I could leave only generic attributes in keycloak_identity_provider and add config attribute of type map that accepts any key-value pairs and create datasources which configure and make validation of each identity provider:

resource keycloak_identity_provider oidc1 {
  alias   = "oidc"
  realm   = "master"
  enabled = true
  config {
      "token_url" = "value"
      "lol" = ""
  }
}

resource keycloak_identity_provider oidc2 {
  realm   = "master"
  enabled = true
  config = "${data.keycloak_google_identity_provider.example.config}" {
}

data keycloak_oidc_identity_provider_config example {
  token_url         = "https://example.com"
  authorization_url = "https://example.com"
  client_id         = "<your_client_id>"
  client_secret     = "<your_client_secret>"
}

@AndrewChubatiuk
Copy link
Contributor Author

This approach is similar to aws_iam_policy_document datasource in terraform-provider-aws but it's less complex

@mrparkers
Copy link
Contributor

That's an interesting way to approach this, but I don't think data sources are intended to be used that way. Typically, a data source is used when you want Terraform to fetch some configuration from a remote server so you can use it to help configure other resources.

I agree that the code duplication this would produce is unfortunate, but a lot of the Keycloak APIs for different resources are very similar so I don't think there is much we can do to avoid that. There is already a fair amount of this in the keycloak_*_protocol_mapper resources, and the keycloak package tries to consolidate similar API fields into a single file to help with this.

In general, I think I still prefer code duplication over a single resource that attempts to handle many use cases. I want to avoid a scenario where a change to this resource to fix an issue with the Google identity provider (for example) may inadvertently break the support for the Twitter identity provider.

@AndrewChubatiuk
Copy link
Contributor Author

AndrewChubatiuk commented Feb 14, 2019

aws_iam_policy_document datasource in aws provider doesn't fetch data, it just makes a policy validation.
On a top of this page page there's a definition of data source. It allows data not only to be fetched but also computed.
Also all ignition datasources do not fetch data from internet, they just prepare a valid configuration in the output

@mrparkers
Copy link
Contributor

Thanks for sharing. I see how having a data source that provides a consistent interface for configuration could be useful, especially for validation purposes.

Despite this, I am still of the opinion that these identity providers should be split to multiple resources. If the code duplication issue becomes a problem, we can take advantage of other solutions, such as code generation or factory functions/methods.

@AndrewChubatiuk
Copy link
Contributor Author

@mrparkers added some tests

@mrparkers
Copy link
Contributor

Thanks, I'll take some time this weekend to review it.

} else if attr, ok := data.GetOk("attribute_name"); ok {
rec.Config.Attribute = attr.(string)
} else {
return nil, fmt.Errorf(`provider.keycloak: keycloak_attribute_importer_identity_provider_mapper: %s: neither "attribute_name" nor "attribute_friendly_name" are set`, data.Get("name").(string))
Copy link
Contributor

Choose a reason for hiding this comment

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

I am not sure if this was present in my last review or if I just missed it, but this feels like we are re-implementing the Required schema attribute that comes with the Terraform SDK out of the box.

In my opinion, this resource could probably be split into oidc and saml versions since there are some attributes that are required or not allowed depending on the value of providerId.

Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There are about 15 identity providers and mappers for these providers are the same or almost same as for OIDC or SAML provider. If we're going to have a separate mapper for each IDP type there'll be about 60 different mappers which confuses more than several conditions in the same provider.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What do you think about it?

Copy link
Contributor

Choose a reason for hiding this comment

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

That's a fair point. I didn't consider that there were so many different mappers for different identity providers.

With this in mind, what would you think about updating the error message to explain that a particular field is only required because this is a saml or oidc mapper as opposed to re-using the Terraform core error string for required fields? This will make it more clear to the user that, while an attribute is not marked as Required, it is still required because of the provider id.

If you're on board with this, then I am fine with you just leaving it as-is as long as you're okay with me updating this in the future.

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

} else if attr, ok := data.GetOk("attribute_name"); ok {
rec.Config.Attribute = attr.(string)
} else {
return nil, fmt.Errorf(`provider.keycloak: keycloak_attribute_to_role_identity_provider_mapper: %s: neither "attribute_name" nor "attribute_friendly_name" are set`, data.Get("name").(string))
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment as above

Optional: true,
Default: "",
ValidateFunc: validation.StringInSlice(keys(nameIdPolicyFormats), false),
StateFunc: func(value interface{}) string {
Copy link
Contributor

Choose a reason for hiding this comment

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

What is this needed for?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What exactly?

Copy link
Contributor

Choose a reason for hiding this comment

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

The StateFunc. I don't necessarily have a problem with it, it's just the first time it has been used in this provider and I am curious what the need was.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Allowed values for name_id_policy_format attribute are same as in Keycloak UI (Windows Domain Qualified Name, Persistent, etc) and StateFunc converts this value to an actual value which is used in API requests

Copy link
Contributor

Choose a reason for hiding this comment

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

Okay, so users would be expected to do something like this?

name_id_policy_format = "Windows Domain Qualified Name"

Copy link
Contributor Author

@AndrewChubatiuk AndrewChubatiuk Apr 15, 2019

Choose a reason for hiding this comment

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

Yes. It's same as in Keycloak UI

rec.ProviderId = "oidc"
rec.Config = &keycloak.IdentityProviderConfig{
BackchannelSupported: keycloak.KeycloakBoolQuoted(data.Get("backchannel_supported").(bool)),
UseJwksUrl: keycloak.KeycloakBoolQuoted(true),
Copy link
Contributor

Choose a reason for hiding this comment

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

I left this comment originally and I don't think it was addressed - I don't think this attribute is useful without the ability to customize the jwks url (correct me if I'm wrong here). If this is the case, I think this attribute should either be removed or updated to allow users to customize this url

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

@mrparkers
Copy link
Contributor

A couple of general comments unrelated to the review -

  1. Apologies for taking so long with getting this PR across the finish line, I spent all of last week at a Conference so I wasn't able to dedicate time into reviewing this.
  2. After my comments from the last review are addressed and this is merged, I'll spend some time within the next few weeks writing docs for this so I can better learn how to use this feature myself. I'll cut a 1.7.0 release so you can use this right away, but I may make slight changes after I understand this feature better and release them in 1.7.1. Is that alright with you?

@AndrewChubatiuk
Copy link
Contributor Author

  1. That's okay.
  2. Yes, no problem

Copy link
Contributor

@mrparkers mrparkers left a comment

Choose a reason for hiding this comment

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

One last review (I swear I will merge this after this is addressed).

I attempted to run the example/main.tf configuration and ran into a few issues.

example/main.tf Outdated
realm = "${keycloak_realm.test.id}"
name = "Attribute: email"
claim_name = "upn"
identity_provider_alias = "${keycloak_oidc_identity_provider.adfs.alias}"
Copy link
Contributor

Choose a reason for hiding this comment

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

This reference is invalid - it doesn't point to another resource within this tf file

example/main.tf Outdated
user_attribute = "email"
}

resource keycloak_oidc_user_attribute_idp_mapper email {
Copy link
Contributor

Choose a reason for hiding this comment

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

It doesn't look like this resource is defined within provider.go - was this renamed to something else after this was committed?

example/main.tf Outdated
single_sign_on_service_url = "https://example.com/auth"
}

resource keycloak_saml_user_attribute_idp_mapper email {
Copy link
Contributor

Choose a reason for hiding this comment

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

It doesn't look like this resource is defined within provider.go - was this renamed to something else after this was committed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Forgot to update example.tf after I updated naming of idp mapper resources. Just pushed my changes


func getHardcodedRoleIdentityProviderMapperFromData(data *schema.ResourceData, _ interface{}) (*keycloak.IdentityProviderMapper, error) {
rec, _ := getIdentityProviderMapperFromData(data)
rec.IdentityProviderMapper = "oidc-hardcoded-role-idp-mapper"
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this resource not be used for SAML?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This resource is also used for SAML, but looks like it's an issue in Keycloak API

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 can try to create this mapper manually

Copy link
Contributor

@mrparkers mrparkers left a comment

Choose a reason for hiding this comment

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

Thanks for the contribution and for your patience with my reviews!

@mrparkers mrparkers merged commit e899c32 into keycloak:master Apr 18, 2019
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.

2 participants