From 21cb3201b7c6bc7869bf0c5690a4db2a7f795563 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Travais?= Date: Tue, 28 Sep 2021 20:34:13 +0200 Subject: [PATCH 1/3] feat: add the LDAP hardcoded attribute mapper #423 --- .../ldap_hardcoded_attribute_mapper.md | 100 +++++++++ keycloak/ldap_hardcoded_attribute_mapper.go | 83 ++++++++ keycloak/ldap_user_federation.go | 3 + mkdocs.yml | 1 + provider/provider.go | 1 + ...eycloak_ldap_hardcoded_attribute_mapper.go | 135 +++++++++++++ ...ak_ldap_hardcoded_attribute_mapper_test.go | 189 ++++++++++++++++++ ...eycloak_ldap_hardcoded_role_mapper_test.go | 2 +- 8 files changed, 513 insertions(+), 1 deletion(-) create mode 100644 docs/resources/ldap_hardcoded_attribute_mapper.md create mode 100644 keycloak/ldap_hardcoded_attribute_mapper.go create mode 100644 provider/resource_keycloak_ldap_hardcoded_attribute_mapper.go create mode 100644 provider/resource_keycloak_ldap_hardcoded_attribute_mapper_test.go diff --git a/docs/resources/ldap_hardcoded_attribute_mapper.md b/docs/resources/ldap_hardcoded_attribute_mapper.md new file mode 100644 index 000000000..e2b7a6535 --- /dev/null +++ b/docs/resources/ldap_hardcoded_attribute_mapper.md @@ -0,0 +1,100 @@ +--- +page_title: "keycloak_ldap_hardcoded_attribute_mapper Resource" +--- + +# keycloak_ldap_hardcoded_attribute_mapper Resource + +Allows for creating and managing hardcoded attribute mappers for Keycloak users federated via LDAP. + +The LDAP hardcoded attribute mapper will set the specified value to the LDAP attribute. + +**NOTE**: This mapper is supported just if syncRegistrations is enabled. + +## Example Usage (random password) + +```hcl +resource "keycloak_realm" "realm" { + realm = "my-realm" + enabled = true +} + +resource "keycloak_ldap_user_federation" "ldap_user_federation" { + name = "openldap" + realm_id = keycloak_realm.realm.id + + username_ldap_attribute = "cn" + rdn_ldap_attribute = "cn" + uuid_ldap_attribute = "entryDN" + user_object_classes = [ + "simpleSecurityObject", + "organizationalRole" + ] + + connection_url = "ldap://openldap" + users_dn = "dc=example,dc=org" + bind_dn = "cn=admin,dc=example,dc=org" + bind_credential = "admin" +} + +resource "keycloak_ldap_hardcoded_attribute_mapper" "assign_random_password" { + realm_id = keycloak_realm.realm.id + ldap_user_federation_id = keycloak_ldap_user_federation.ldap_user_federation.id + name = "assign-random-password" + attribute_name = "password" + attribute_value = "${RANDOM}" +} +``` + +## Example Usage (simple string) + +```hcl +resource "keycloak_realm" "realm" { + realm = "my-realm" + enabled = true +} + +resource "keycloak_ldap_user_federation" "ldap_user_federation" { + name = "openldap" + realm_id = keycloak_realm.realm.id + + username_ldap_attribute = "cn" + rdn_ldap_attribute = "cn" + uuid_ldap_attribute = "entryDN" + user_object_classes = [ + "simpleSecurityObject", + "organizationalRole" + ] + + connection_url = "ldap://openldap" + users_dn = "dc=example,dc=org" + bind_dn = "cn=admin,dc=example,dc=org" + bind_credential = "admin" +} + +resource "keycloak_ldap_hardcoded_attribute_mapper" "assign_bar_to_foo" { + realm_id = keycloak_realm.realm.id + ldap_user_federation_id = keycloak_ldap_user_federation.ldap_user_federation.id + name = "assign-foo-to-bar" + attribute_name = "foo" + attribute_value = "bar" +} +``` + +## Argument Reference + +- `realm_id` - (Required) The realm that this LDAP mapper will exist in. +- `ldap_user_federation_id` - (Required) The ID of the LDAP user federation provider to attach this mapper to. +- `name` - (Required) Display name of this mapper when displayed in the console. +- `attribute_name` - (Required) The name of the LDAP attribute to set. +- `attribute_value` - (Optional) The value to set to the LDAP attribute. You can either hardcode any value like 'foo' but you can also use the special token '${RANDOM}', which will be replaced with some randomly generated String. + +## Import + +LDAP mappers can be imported using the format `{{realm_id}}/{{ldap_user_federation_id}}/{{ldap_mapper_id}}`. +The ID of the LDAP user federation provider and the mapper can be found within the Keycloak GUI, and they are typically GUIDs. + +Example: + +```bash +$ terraform import keycloak_ldap_hardcoded_attribute_mapper.assign_random_password my-realm/af2a6ca3-e4d7-49c3-b08b-1b3c70b4b860/3d923ece-1a91-4bf7-adaf-3b82f2a12b67 +``` diff --git a/keycloak/ldap_hardcoded_attribute_mapper.go b/keycloak/ldap_hardcoded_attribute_mapper.go new file mode 100644 index 000000000..fc3143bf1 --- /dev/null +++ b/keycloak/ldap_hardcoded_attribute_mapper.go @@ -0,0 +1,83 @@ +package keycloak + +import "fmt" + +type LdapHardcodedAttributeMapper struct { + Id string + Name string + RealmId string + LdapUserFederationId string + AttributeName string + AttributeValue string +} + +func convertFromLdapHardcodedAttributeMapperToComponent(ldapMapper *LdapHardcodedAttributeMapper) *component { + return &component{ + Id: ldapMapper.Id, + Name: ldapMapper.Name, + ProviderId: "hardcoded-ldap-attribute-mapper", + ProviderType: "org.keycloak.storage.ldap.mappers.LDAPStorageMapper", + ParentId: ldapMapper.LdapUserFederationId, + + Config: map[string][]string{ + "ldap.attribute.name": { + ldapMapper.AttributeName, + }, + "ldap.attribute.value": { + ldapMapper.AttributeValue, + }, + }, + } +} + +func convertFromComponentToLdapHardcodedAttributeMapper(component *component, realmId string) *LdapHardcodedAttributeMapper { + return &LdapHardcodedAttributeMapper{ + Id: component.Id, + Name: component.Name, + RealmId: realmId, + LdapUserFederationId: component.ParentId, + + AttributeName: component.getConfig("ldap.attribute.name"), + AttributeValue: component.getConfig("ldap.attribute.value"), + } +} + +func (keycloakClient *KeycloakClient) ValidateLdapHardcodedAttributeMapper(ldapMapper *LdapHardcodedAttributeMapper) error { + if len(ldapMapper.AttributeName) == 0 { + return fmt.Errorf("validation error: hardcoded attribute name must not be empty") + } + if len(ldapMapper.AttributeValue) == 0 { + return fmt.Errorf("validation error: hardcoded attribute value must not be empty") + } + return nil +} + +func (keycloakClient *KeycloakClient) NewLdapHardcodedAttributeMapper(ldapMapper *LdapHardcodedAttributeMapper) error { + _, location, err := keycloakClient.post(fmt.Sprintf("/realms/%s/components", ldapMapper.RealmId), convertFromLdapHardcodedAttributeMapperToComponent(ldapMapper)) + if err != nil { + return err + } + + ldapMapper.Id = getIdFromLocationHeader(location) + + return nil +} + +func (keycloakClient *KeycloakClient) GetLdapHardcodedAttributeMapper(realmId, id string) (*LdapHardcodedAttributeMapper, error) { + var component *component + + err := keycloakClient.get(fmt.Sprintf("/realms/%s/components/%s", realmId, id), &component, nil) + if err != nil { + return nil, err + } + + return convertFromComponentToLdapHardcodedAttributeMapper(component, realmId), nil +} + +func (keycloakClient *KeycloakClient) UpdateLdapHardcodedAttributeMapper(ldapMapper *LdapHardcodedAttributeMapper) error { + return keycloakClient.put(fmt.Sprintf("/realms/%s/components/%s", ldapMapper.RealmId, ldapMapper.Id), convertFromLdapHardcodedAttributeMapperToComponent(ldapMapper)) +} + +func (keycloakClient *KeycloakClient) DeleteLdapHardcodedAttributeMapper(realmId, id string) error { + return keycloakClient.delete(fmt.Sprintf("/realms/%s/components/%s", realmId, id), nil) +} diff --git a/keycloak/ldap_user_federation.go b/keycloak/ldap_user_federation.go index a206cdb2a..43cd7114d 100644 --- a/keycloak/ldap_user_federation.go +++ b/keycloak/ldap_user_federation.go @@ -472,6 +472,9 @@ func (keycloakClient *KeycloakClient) GetLdapUserFederationMappers(realmId, id s case "hardcoded-ldap-role-mapper": mapper := convertFromComponentToLdapHardcodedRoleMapper(component, realmId) ldapUserFederationMappers = append(ldapUserFederationMappers, mapper) + case "hardcoded-ldap-attribute-mapper": + mapper := convertFromComponentToLdapHardcodedAttributeMapper(component, realmId) + ldapUserFederationMappers = append(ldapUserFederationMappers, mapper) case "msad-lds-user-account-control-mapper": mapper, err := convertFromComponentToLdapMsadLdsUserAccountControlMapper(component, realmId) if err != nil { diff --git a/mkdocs.yml b/mkdocs.yml index 1334cb7bb..92b0ba57a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -44,6 +44,7 @@ nav: - keycloak_ldap_user_federation: resources/keycloak_ldap_user_federation.md - keycloak_ldap_full_name_mapper: resources/keycloak_ldap_full_name_mapper.md - keycloak_ldap_group_mapper: resources/keycloak_ldap_group_mapper.md + - keycloak_ldap_hardcoded_attribute_mapper: resources/keycloak_ldap_hardcoded_attribute_mapper.md - keycloak_ldap_hardcoded_role_mapper: resources/keycloak_ldap_hardcoded_role_mapper.md - keycloak_ldap_msad_user_account_control_mapper: resources/keycloak_ldap_msad_user_account_control_mapper.md - keycloak_ldap_msad_lds_user_account_control_mapper: resources/keycloak_ldap_msad_lds_user_account_control_mapper.md diff --git a/provider/provider.go b/provider/provider.go index 89a9bee98..ed5460fa2 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -45,6 +45,7 @@ func KeycloakProvider(client *keycloak.KeycloakClient) *schema.Provider { "keycloak_ldap_group_mapper": resourceKeycloakLdapGroupMapper(), "keycloak_ldap_role_mapper": resourceKeycloakLdapRoleMapper(), "keycloak_ldap_hardcoded_role_mapper": resourceKeycloakLdapHardcodedRoleMapper(), + "keycloak_ldap_hardcoded_attribute_mapper": resourceKeycloakLdapHardcodedAttributeMapper(), "keycloak_ldap_hardcoded_group_mapper": resourceKeycloakLdapHardcodedGroupMapper(), "keycloak_ldap_msad_user_account_control_mapper": resourceKeycloakLdapMsadUserAccountControlMapper(), "keycloak_ldap_msad_lds_user_account_control_mapper": resourceKeycloakLdapMsadLdsUserAccountControlMapper(), diff --git a/provider/resource_keycloak_ldap_hardcoded_attribute_mapper.go b/provider/resource_keycloak_ldap_hardcoded_attribute_mapper.go new file mode 100644 index 000000000..ec8d59438 --- /dev/null +++ b/provider/resource_keycloak_ldap_hardcoded_attribute_mapper.go @@ -0,0 +1,135 @@ +package provider + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/mrparkers/terraform-provider-keycloak/keycloak" +) + +func resourceKeycloakLdapHardcodedAttributeMapper() *schema.Resource { + return &schema.Resource{ + Create: resourceKeycloakLdapHardcodedAttributeMapperCreate, + Read: resourceKeycloakLdapHardcodedAttributeMapperRead, + Update: resourceKeycloakLdapHardcodedAttributeMapperUpdate, + Delete: resourceKeycloakLdapHardcodedAttributeMapperDelete, + // This resource can be imported using {{realm}}/{{provider_id}}/{{mapper_id}}. The Provider and Mapper IDs are displayed in the GUI + Importer: &schema.ResourceImporter{ + State: resourceKeycloakLdapGenericMapperImport, + }, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: "Display name of the mapper when displayed in the console.", + }, + "realm_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The realm in which the ldap user federation provider exists.", + }, + "ldap_user_federation_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The ldap user federation provider to attach this mapper to.", + }, + "attribute_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Name of the LDAP attribute", + }, + "attribute_value": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Value of the LDAP attribute. You can either hardcode any value like 'foo' but you can also use the special token '${RANDOM}', which will be replaced with some randomly generated String.", + }, + }, + } +} + +func getLdapHardcodedAttributeMapperFromData(data *schema.ResourceData) *keycloak.LdapHardcodedAttributeMapper { + return &keycloak.LdapHardcodedAttributeMapper{ + Id: data.Id(), + Name: data.Get("name").(string), + RealmId: data.Get("realm_id").(string), + LdapUserFederationId: data.Get("ldap_user_federation_id").(string), + AttributeName: data.Get("attribute_name").(string), + AttributeValue: data.Get("attribute_value").(string), + } +} + +func setLdapHardcodedAttributeMapperData(data *schema.ResourceData, ldapMapper *keycloak.LdapHardcodedAttributeMapper) { + data.SetId(ldapMapper.Id) + data.Set("name", ldapMapper.Name) + data.Set("realm_id", ldapMapper.RealmId) + data.Set("ldap_user_federation_id", ldapMapper.LdapUserFederationId) + data.Set("attribute_name", ldapMapper.AttributeName) + data.Set("attribute_value", ldapMapper.AttributeValue) +} + +func resourceKeycloakLdapHardcodedAttributeMapperCreate(data *schema.ResourceData, meta interface{}) error { + keycloakClient := meta.(*keycloak.KeycloakClient) + + ldapMapper := getLdapHardcodedAttributeMapperFromData(data) + + err := keycloakClient.ValidateLdapHardcodedAttributeMapper(ldapMapper) + if err != nil { + return err + } + + err = keycloakClient.NewLdapHardcodedAttributeMapper(ldapMapper) + if err != nil { + return err + } + + setLdapHardcodedAttributeMapperData(data, ldapMapper) + + return resourceKeycloakLdapHardcodedAttributeMapperRead(data, meta) +} + +func resourceKeycloakLdapHardcodedAttributeMapperRead(data *schema.ResourceData, meta interface{}) error { + keycloakClient := meta.(*keycloak.KeycloakClient) + + realmId := data.Get("realm_id").(string) + id := data.Id() + + ldapMapper, err := keycloakClient.GetLdapHardcodedAttributeMapper(realmId, id) + if err != nil { + return handleNotFoundError(err, data) + } + + setLdapHardcodedAttributeMapperData(data, ldapMapper) + + return nil +} + +func resourceKeycloakLdapHardcodedAttributeMapperUpdate(data *schema.ResourceData, meta interface{}) error { + keycloakClient := meta.(*keycloak.KeycloakClient) + + ldapMapper := getLdapHardcodedAttributeMapperFromData(data) + + err := keycloakClient.ValidateLdapHardcodedAttributeMapper(ldapMapper) + if err != nil { + return err + } + + err = keycloakClient.UpdateLdapHardcodedAttributeMapper(ldapMapper) + if err != nil { + return err + } + + setLdapHardcodedAttributeMapperData(data, ldapMapper) + + return nil +} + +func resourceKeycloakLdapHardcodedAttributeMapperDelete(data *schema.ResourceData, meta interface{}) error { + keycloakClient := meta.(*keycloak.KeycloakClient) + + realmId := data.Get("realm_id").(string) + id := data.Id() + + return keycloakClient.DeleteLdapHardcodedAttributeMapper(realmId, id) +} diff --git a/provider/resource_keycloak_ldap_hardcoded_attribute_mapper_test.go b/provider/resource_keycloak_ldap_hardcoded_attribute_mapper_test.go new file mode 100644 index 000000000..da1f76c10 --- /dev/null +++ b/provider/resource_keycloak_ldap_hardcoded_attribute_mapper_test.go @@ -0,0 +1,189 @@ +package provider + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/mrparkers/terraform-provider-keycloak/keycloak" +) + +func TestAccKeycloakLdapHardcodedAttributeMapper_basic(t *testing.T) { + t.Parallel() + attributeName := acctest.RandomWithPrefix("tf-acc") + attributeValue := acctest.RandomWithPrefix("tf-acc") + attributeMapperName := acctest.RandomWithPrefix("tf-acc") + + resource.Test(t, resource.TestCase{ + ProviderFactories: testAccProviderFactories, + PreCheck: func() { testAccPreCheck(t) }, + CheckDestroy: testAccCheckKeycloakLdapHardcodedAttributeMapperDestroy(), + Steps: []resource.TestStep{ + { + Config: testKeycloakLdapHardcodedAttributeMapper(attributeMapperName, attributeName, attributeValue), + Check: testAccCheckKeycloakLdapHardcodedAttributeMapperExists("keycloak_ldap_hardcoded_attribute_mapper.hardcoded_attribute_mapper"), + }, + { + ResourceName: "keycloak_ldap_hardcoded_attribute_mapper.hardcoded_attribute_mapper", + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: getLdapGenericMapperImportId("keycloak_ldap_hardcoded_attribute_mapper.hardcoded_attribute_mapper"), + }, + }, + }) +} + +func TestAccKeycloakLdapHardcodedAttributeMapper_randomPassword(t *testing.T) { + t.Parallel() + attributeName := acctest.RandomWithPrefix("tf-acc") + attributeValue := "${RANDOM}" + attributeMapperName := acctest.RandomWithPrefix("tf-acc") + + resource.Test(t, resource.TestCase{ + ProviderFactories: testAccProviderFactories, + PreCheck: func() { testAccPreCheck(t) }, + CheckDestroy: testAccCheckKeycloakLdapHardcodedAttributeMapperDestroy(), + Steps: []resource.TestStep{ + { + Config: testKeycloakLdapHardcodedAttributeMapper(attributeMapperName, attributeName, attributeValue), + Check: testAccCheckKeycloakLdapHardcodedAttributeMapperExists("keycloak_ldap_hardcoded_attribute_mapper.hardcoded_attribute_mapper"), + }, + { + ResourceName: "keycloak_ldap_hardcoded_attribute_mapper.hardcoded_attribute_mapper", + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: getLdapGenericMapperImportId("keycloak_ldap_hardcoded_attribute_mapper.hardcoded_attribute_mapper"), + }, + }, + }) +} + +func TestAccKeycloakLdapHardcodedAttributeMapper_createAfterManualDestroy(t *testing.T) { + t.Parallel() + var mapper = &keycloak.LdapHardcodedAttributeMapper{} + + attributeName := acctest.RandomWithPrefix("tf-acc") + attributeValue := acctest.RandomWithPrefix("tf-acc") + attributeMapperName := acctest.RandomWithPrefix("tf-acc") + + resource.Test(t, resource.TestCase{ + ProviderFactories: testAccProviderFactories, + PreCheck: func() { testAccPreCheck(t) }, + CheckDestroy: testAccCheckKeycloakLdapHardcodedAttributeMapperDestroy(), + Steps: []resource.TestStep{ + { + Config: testKeycloakLdapHardcodedAttributeMapper(attributeMapperName, attributeName, attributeValue), + Check: testAccCheckKeycloakLdapHardcodedAttributeMapperFetch("keycloak_ldap_hardcoded_attribute_mapper.hardcoded_attribute_mapper", mapper), + }, + { + PreConfig: func() { + err := keycloakClient.DeleteLdapHardcodedAttributeMapper(mapper.RealmId, mapper.Id) + if err != nil { + t.Fatal(err) + } + }, + Config: testKeycloakLdapHardcodedAttributeMapper(attributeMapperName, attributeName, attributeValue), + Check: testAccCheckKeycloakLdapHardcodedAttributeMapperExists("keycloak_ldap_hardcoded_attribute_mapper.hardcoded_attribute_mapper"), + }, + }, + }) +} + +func testAccCheckKeycloakLdapHardcodedAttributeMapperExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + _, err := getLdapHardcodedAttributeMapperFromState(s, resourceName) + if err != nil { + return err + } + + return nil + } +} + +func testAccCheckKeycloakLdapHardcodedAttributeMapperFetch(resourceName string, mapper *keycloak.LdapHardcodedAttributeMapper) resource.TestCheckFunc { + return func(s *terraform.State) error { + fetchedMapper, err := getLdapHardcodedAttributeMapperFromState(s, resourceName) + if err != nil { + return err + } + + mapper.Id = fetchedMapper.Id + mapper.RealmId = fetchedMapper.RealmId + + return nil + } +} + +func testAccCheckKeycloakLdapHardcodedAttributeMapperDestroy() resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "keycloak_ldap_hardcoded_attribute_mapper" { + continue + } + + id := rs.Primary.ID + realm := rs.Primary.Attributes["realm_id"] + + ldapMapper, _ := keycloakClient.GetLdapHardcodedAttributeMapper(realm, id) + if ldapMapper != nil { + return fmt.Errorf("ldap hardcoded attribute mapper with id %s still exists", id) + } + } + + return nil + } +} + +func getLdapHardcodedAttributeMapperFromState(s *terraform.State, resourceName string) (*keycloak.LdapHardcodedAttributeMapper, error) { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return nil, fmt.Errorf("resource not found: %s", resourceName) + } + + id := rs.Primary.ID + realm := rs.Primary.Attributes["realm_id"] + + ldapMapper, err := keycloakClient.GetLdapHardcodedAttributeMapper(realm, id) + if err != nil { + return nil, fmt.Errorf("error getting ldap attribute mapper with id %s: %s", id, err) + } + + return ldapMapper, nil +} + +func testKeycloakLdapHardcodedAttributeMapper(attributeMapperName, attributeName, attributeValue string) string { + return fmt.Sprintf(` +data "keycloak_realm" "realm" { + realm = "%s" +} + +resource "keycloak_ldap_user_federation" "openldap" { + name = "openldap" + realm_id = data.keycloak_realm.realm.id + + enabled = true + + username_ldap_attribute = "cn" + rdn_ldap_attribute = "cn" + uuid_ldap_attribute = "entryDN" + user_object_classes = [ + "simpleSecurityObject", + "organizationalRole" + ] + connection_url = "ldap://openldap" + users_dn = "dc=example,dc=org" + bind_dn = "cn=admin,dc=example,dc=org" + bind_credential = "admin" +} + +resource "keycloak_ldap_hardcoded_attribute_mapper" "hardcoded_attribute_mapper" { + name = "%s" + realm_id = data.keycloak_realm.realm.id + ldap_user_federation_id = keycloak_ldap_user_federation.openldap.id + attribute_name = "%s" + attribute_value = "%s" +} + `, testAccRealmUserFederation.Realm, attributeMapperName, attributeName, attributeValue) +} diff --git a/provider/resource_keycloak_ldap_hardcoded_role_mapper_test.go b/provider/resource_keycloak_ldap_hardcoded_role_mapper_test.go index 4d9e51808..ca1ee776f 100644 --- a/provider/resource_keycloak_ldap_hardcoded_role_mapper_test.go +++ b/provider/resource_keycloak_ldap_hardcoded_role_mapper_test.go @@ -120,7 +120,7 @@ func getLdapHardcodedRoleMapperFromState(s *terraform.State, resourceName string ldapMapper, err := keycloakClient.GetLdapHardcodedRoleMapper(realm, id) if err != nil { - return nil, fmt.Errorf("error getting ldap group mapper with id %s: %s", id, err) + return nil, fmt.Errorf("error getting ldap role mapper with id %s: %s", id, err) } return ldapMapper, nil From 2b3f689dcef30a7094c29e1258028d819f4284d2 Mon Sep 17 00:00:00 2001 From: Jochen Date: Fri, 2 Sep 2022 10:41:15 +0200 Subject: [PATCH 2/3] fix compiler errors --- keycloak/ldap_hardcoded_attribute_mapper.go | 23 +++++---- ...eycloak_ldap_hardcoded_attribute_mapper.go | 47 ++++++++++--------- ...ak_ldap_hardcoded_attribute_mapper_test.go | 7 +-- 3 files changed, 43 insertions(+), 34 deletions(-) diff --git a/keycloak/ldap_hardcoded_attribute_mapper.go b/keycloak/ldap_hardcoded_attribute_mapper.go index fc3143bf1..39bcedf90 100644 --- a/keycloak/ldap_hardcoded_attribute_mapper.go +++ b/keycloak/ldap_hardcoded_attribute_mapper.go @@ -1,6 +1,9 @@ package keycloak -import "fmt" +import ( + "context" + "fmt" +) type LdapHardcodedAttributeMapper struct { Id string @@ -37,7 +40,7 @@ func convertFromComponentToLdapHardcodedAttributeMapper(component *component, re RealmId: realmId, LdapUserFederationId: component.ParentId, - AttributeName: component.getConfig("ldap.attribute.name"), + AttributeName: component.getConfig("ldap.attribute.name"), AttributeValue: component.getConfig("ldap.attribute.value"), } } @@ -52,8 +55,8 @@ func (keycloakClient *KeycloakClient) ValidateLdapHardcodedAttributeMapper(ldapM return nil } -func (keycloakClient *KeycloakClient) NewLdapHardcodedAttributeMapper(ldapMapper *LdapHardcodedAttributeMapper) error { - _, location, err := keycloakClient.post(fmt.Sprintf("/realms/%s/components", ldapMapper.RealmId), convertFromLdapHardcodedAttributeMapperToComponent(ldapMapper)) +func (keycloakClient *KeycloakClient) NewLdapHardcodedAttributeMapper(ctx context.Context, ldapMapper *LdapHardcodedAttributeMapper) error { + _, location, err := keycloakClient.post(ctx, fmt.Sprintf("/realms/%s/components", ldapMapper.RealmId), convertFromLdapHardcodedAttributeMapperToComponent(ldapMapper)) if err != nil { return err } @@ -63,10 +66,10 @@ func (keycloakClient *KeycloakClient) NewLdapHardcodedAttributeMapper(ldapMapper return nil } -func (keycloakClient *KeycloakClient) GetLdapHardcodedAttributeMapper(realmId, id string) (*LdapHardcodedAttributeMapper, error) { +func (keycloakClient *KeycloakClient) GetLdapHardcodedAttributeMapper(ctx context.Context, realmId, id string) (*LdapHardcodedAttributeMapper, error) { var component *component - err := keycloakClient.get(fmt.Sprintf("/realms/%s/components/%s", realmId, id), &component, nil) + err := keycloakClient.get(ctx, fmt.Sprintf("/realms/%s/components/%s", realmId, id), &component, nil) if err != nil { return nil, err } @@ -74,10 +77,10 @@ func (keycloakClient *KeycloakClient) GetLdapHardcodedAttributeMapper(realmId, i return convertFromComponentToLdapHardcodedAttributeMapper(component, realmId), nil } -func (keycloakClient *KeycloakClient) UpdateLdapHardcodedAttributeMapper(ldapMapper *LdapHardcodedAttributeMapper) error { - return keycloakClient.put(fmt.Sprintf("/realms/%s/components/%s", ldapMapper.RealmId, ldapMapper.Id), convertFromLdapHardcodedAttributeMapperToComponent(ldapMapper)) +func (keycloakClient *KeycloakClient) UpdateLdapHardcodedAttributeMapper(ctx context.Context, ldapMapper *LdapHardcodedAttributeMapper) error { + return keycloakClient.put(ctx, fmt.Sprintf("/realms/%s/components/%s", ldapMapper.RealmId, ldapMapper.Id), convertFromLdapHardcodedAttributeMapperToComponent(ldapMapper)) } -func (keycloakClient *KeycloakClient) DeleteLdapHardcodedAttributeMapper(realmId, id string) error { - return keycloakClient.delete(fmt.Sprintf("/realms/%s/components/%s", realmId, id), nil) +func (keycloakClient *KeycloakClient) DeleteLdapHardcodedAttributeMapper(ctx context.Context, realmId, id string) error { + return keycloakClient.delete(ctx, fmt.Sprintf("/realms/%s/components/%s", realmId, id), nil) } diff --git a/provider/resource_keycloak_ldap_hardcoded_attribute_mapper.go b/provider/resource_keycloak_ldap_hardcoded_attribute_mapper.go index ec8d59438..1dad13dc8 100644 --- a/provider/resource_keycloak_ldap_hardcoded_attribute_mapper.go +++ b/provider/resource_keycloak_ldap_hardcoded_attribute_mapper.go @@ -1,19 +1,22 @@ package provider import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/mrparkers/terraform-provider-keycloak/keycloak" ) func resourceKeycloakLdapHardcodedAttributeMapper() *schema.Resource { return &schema.Resource{ - Create: resourceKeycloakLdapHardcodedAttributeMapperCreate, - Read: resourceKeycloakLdapHardcodedAttributeMapperRead, - Update: resourceKeycloakLdapHardcodedAttributeMapperUpdate, - Delete: resourceKeycloakLdapHardcodedAttributeMapperDelete, + CreateContext: resourceKeycloakLdapHardcodedAttributeMapperCreate, + ReadContext: resourceKeycloakLdapHardcodedAttributeMapperRead, + UpdateContext: resourceKeycloakLdapHardcodedAttributeMapperUpdate, + DeleteContext: resourceKeycloakLdapHardcodedAttributeMapperDelete, // This resource can be imported using {{realm}}/{{provider_id}}/{{mapper_id}}. The Provider and Mapper IDs are displayed in the GUI Importer: &schema.ResourceImporter{ - State: resourceKeycloakLdapGenericMapperImport, + StateContext: resourceKeycloakLdapGenericMapperImport, }, Schema: map[string]*schema.Schema{ "name": { @@ -69,67 +72,69 @@ func setLdapHardcodedAttributeMapperData(data *schema.ResourceData, ldapMapper * data.Set("attribute_value", ldapMapper.AttributeValue) } -func resourceKeycloakLdapHardcodedAttributeMapperCreate(data *schema.ResourceData, meta interface{}) error { +func resourceKeycloakLdapHardcodedAttributeMapperCreate(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics { keycloakClient := meta.(*keycloak.KeycloakClient) ldapMapper := getLdapHardcodedAttributeMapperFromData(data) err := keycloakClient.ValidateLdapHardcodedAttributeMapper(ldapMapper) if err != nil { - return err + return diag.FromErr(err) } - err = keycloakClient.NewLdapHardcodedAttributeMapper(ldapMapper) + err = keycloakClient.NewLdapHardcodedAttributeMapper(ctx, ldapMapper) if err != nil { - return err + return diag.FromErr(err) } setLdapHardcodedAttributeMapperData(data, ldapMapper) - return resourceKeycloakLdapHardcodedAttributeMapperRead(data, meta) + return resourceKeycloakLdapHardcodedAttributeMapperRead(ctx, data, meta) } -func resourceKeycloakLdapHardcodedAttributeMapperRead(data *schema.ResourceData, meta interface{}) error { +func resourceKeycloakLdapHardcodedAttributeMapperRead(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics { keycloakClient := meta.(*keycloak.KeycloakClient) realmId := data.Get("realm_id").(string) id := data.Id() - ldapMapper, err := keycloakClient.GetLdapHardcodedAttributeMapper(realmId, id) + ldapMapper, err := keycloakClient.GetLdapHardcodedAttributeMapper(ctx, realmId, id) if err != nil { - return handleNotFoundError(err, data) + return handleNotFoundError(ctx, err, data) } setLdapHardcodedAttributeMapperData(data, ldapMapper) - return nil + return diag.FromErr(nil) } -func resourceKeycloakLdapHardcodedAttributeMapperUpdate(data *schema.ResourceData, meta interface{}) error { +func resourceKeycloakLdapHardcodedAttributeMapperUpdate(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics { keycloakClient := meta.(*keycloak.KeycloakClient) ldapMapper := getLdapHardcodedAttributeMapperFromData(data) err := keycloakClient.ValidateLdapHardcodedAttributeMapper(ldapMapper) if err != nil { - return err + return diag.FromErr(err) } - err = keycloakClient.UpdateLdapHardcodedAttributeMapper(ldapMapper) + err = keycloakClient.UpdateLdapHardcodedAttributeMapper(ctx, ldapMapper) if err != nil { - return err + return diag.FromErr(err) } setLdapHardcodedAttributeMapperData(data, ldapMapper) - return nil + return diag.FromErr(nil) } -func resourceKeycloakLdapHardcodedAttributeMapperDelete(data *schema.ResourceData, meta interface{}) error { +func resourceKeycloakLdapHardcodedAttributeMapperDelete(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics { keycloakClient := meta.(*keycloak.KeycloakClient) realmId := data.Get("realm_id").(string) id := data.Id() - return keycloakClient.DeleteLdapHardcodedAttributeMapper(realmId, id) + err := keycloakClient.DeleteLdapHardcodedAttributeMapper(ctx, realmId, id) + + return diag.FromErr(err) } diff --git a/provider/resource_keycloak_ldap_hardcoded_attribute_mapper_test.go b/provider/resource_keycloak_ldap_hardcoded_attribute_mapper_test.go index da1f76c10..179e12128 100644 --- a/provider/resource_keycloak_ldap_hardcoded_attribute_mapper_test.go +++ b/provider/resource_keycloak_ldap_hardcoded_attribute_mapper_test.go @@ -1,6 +1,7 @@ package provider import ( + "context" "fmt" "testing" @@ -79,7 +80,7 @@ func TestAccKeycloakLdapHardcodedAttributeMapper_createAfterManualDestroy(t *tes }, { PreConfig: func() { - err := keycloakClient.DeleteLdapHardcodedAttributeMapper(mapper.RealmId, mapper.Id) + err := keycloakClient.DeleteLdapHardcodedAttributeMapper(context.Background(), mapper.RealmId, mapper.Id) if err != nil { t.Fatal(err) } @@ -126,7 +127,7 @@ func testAccCheckKeycloakLdapHardcodedAttributeMapperDestroy() resource.TestChec id := rs.Primary.ID realm := rs.Primary.Attributes["realm_id"] - ldapMapper, _ := keycloakClient.GetLdapHardcodedAttributeMapper(realm, id) + ldapMapper, _ := keycloakClient.GetLdapHardcodedAttributeMapper(context.Background(), realm, id) if ldapMapper != nil { return fmt.Errorf("ldap hardcoded attribute mapper with id %s still exists", id) } @@ -145,7 +146,7 @@ func getLdapHardcodedAttributeMapperFromState(s *terraform.State, resourceName s id := rs.Primary.ID realm := rs.Primary.Attributes["realm_id"] - ldapMapper, err := keycloakClient.GetLdapHardcodedAttributeMapper(realm, id) + ldapMapper, err := keycloakClient.GetLdapHardcodedAttributeMapper(context.Background(), realm, id) if err != nil { return nil, fmt.Errorf("error getting ldap attribute mapper with id %s: %s", id, err) } From 4a2ecc34cc9638316a0e7339566eadc6b4e63f88 Mon Sep 17 00:00:00 2001 From: Jochen Date: Fri, 2 Sep 2022 11:14:34 +0200 Subject: [PATCH 3/3] remove random password tests and docs --- .../ldap_hardcoded_attribute_mapper.md | 39 +------------------ ...eycloak_ldap_hardcoded_attribute_mapper.go | 2 +- ...ak_ldap_hardcoded_attribute_mapper_test.go | 25 ------------ 3 files changed, 3 insertions(+), 63 deletions(-) diff --git a/docs/resources/ldap_hardcoded_attribute_mapper.md b/docs/resources/ldap_hardcoded_attribute_mapper.md index e2b7a6535..aa6ae8094 100644 --- a/docs/resources/ldap_hardcoded_attribute_mapper.md +++ b/docs/resources/ldap_hardcoded_attribute_mapper.md @@ -10,41 +10,6 @@ The LDAP hardcoded attribute mapper will set the specified value to the LDAP att **NOTE**: This mapper is supported just if syncRegistrations is enabled. -## Example Usage (random password) - -```hcl -resource "keycloak_realm" "realm" { - realm = "my-realm" - enabled = true -} - -resource "keycloak_ldap_user_federation" "ldap_user_federation" { - name = "openldap" - realm_id = keycloak_realm.realm.id - - username_ldap_attribute = "cn" - rdn_ldap_attribute = "cn" - uuid_ldap_attribute = "entryDN" - user_object_classes = [ - "simpleSecurityObject", - "organizationalRole" - ] - - connection_url = "ldap://openldap" - users_dn = "dc=example,dc=org" - bind_dn = "cn=admin,dc=example,dc=org" - bind_credential = "admin" -} - -resource "keycloak_ldap_hardcoded_attribute_mapper" "assign_random_password" { - realm_id = keycloak_realm.realm.id - ldap_user_federation_id = keycloak_ldap_user_federation.ldap_user_federation.id - name = "assign-random-password" - attribute_name = "password" - attribute_value = "${RANDOM}" -} -``` - ## Example Usage (simple string) ```hcl @@ -86,7 +51,7 @@ resource "keycloak_ldap_hardcoded_attribute_mapper" "assign_bar_to_foo" { - `ldap_user_federation_id` - (Required) The ID of the LDAP user federation provider to attach this mapper to. - `name` - (Required) Display name of this mapper when displayed in the console. - `attribute_name` - (Required) The name of the LDAP attribute to set. -- `attribute_value` - (Optional) The value to set to the LDAP attribute. You can either hardcode any value like 'foo' but you can also use the special token '${RANDOM}', which will be replaced with some randomly generated String. +- `attribute_value` - (Optional) The value to set to the LDAP attribute. You can hardcode any value like 'foo'. ## Import @@ -96,5 +61,5 @@ The ID of the LDAP user federation provider and the mapper can be found within t Example: ```bash -$ terraform import keycloak_ldap_hardcoded_attribute_mapper.assign_random_password my-realm/af2a6ca3-e4d7-49c3-b08b-1b3c70b4b860/3d923ece-1a91-4bf7-adaf-3b82f2a12b67 +$ terraform import keycloak_ldap_hardcoded_attribute_mapper.assign_bar_to_foo my-realm/af2a6ca3-e4d7-49c3-b08b-1b3c70b4b860/3d923ece-1a91-4bf7-adaf-3b82f2a12b67 ``` diff --git a/provider/resource_keycloak_ldap_hardcoded_attribute_mapper.go b/provider/resource_keycloak_ldap_hardcoded_attribute_mapper.go index 1dad13dc8..9cdb667a2 100644 --- a/provider/resource_keycloak_ldap_hardcoded_attribute_mapper.go +++ b/provider/resource_keycloak_ldap_hardcoded_attribute_mapper.go @@ -46,7 +46,7 @@ func resourceKeycloakLdapHardcodedAttributeMapper() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - Description: "Value of the LDAP attribute. You can either hardcode any value like 'foo' but you can also use the special token '${RANDOM}', which will be replaced with some randomly generated String.", + Description: "Value of the LDAP attribute. You can hardcode any value like 'foo'", }, }, } diff --git a/provider/resource_keycloak_ldap_hardcoded_attribute_mapper_test.go b/provider/resource_keycloak_ldap_hardcoded_attribute_mapper_test.go index 179e12128..f5aefbfc4 100644 --- a/provider/resource_keycloak_ldap_hardcoded_attribute_mapper_test.go +++ b/provider/resource_keycloak_ldap_hardcoded_attribute_mapper_test.go @@ -36,31 +36,6 @@ func TestAccKeycloakLdapHardcodedAttributeMapper_basic(t *testing.T) { }) } -func TestAccKeycloakLdapHardcodedAttributeMapper_randomPassword(t *testing.T) { - t.Parallel() - attributeName := acctest.RandomWithPrefix("tf-acc") - attributeValue := "${RANDOM}" - attributeMapperName := acctest.RandomWithPrefix("tf-acc") - - resource.Test(t, resource.TestCase{ - ProviderFactories: testAccProviderFactories, - PreCheck: func() { testAccPreCheck(t) }, - CheckDestroy: testAccCheckKeycloakLdapHardcodedAttributeMapperDestroy(), - Steps: []resource.TestStep{ - { - Config: testKeycloakLdapHardcodedAttributeMapper(attributeMapperName, attributeName, attributeValue), - Check: testAccCheckKeycloakLdapHardcodedAttributeMapperExists("keycloak_ldap_hardcoded_attribute_mapper.hardcoded_attribute_mapper"), - }, - { - ResourceName: "keycloak_ldap_hardcoded_attribute_mapper.hardcoded_attribute_mapper", - ImportState: true, - ImportStateVerify: true, - ImportStateIdFunc: getLdapGenericMapperImportId("keycloak_ldap_hardcoded_attribute_mapper.hardcoded_attribute_mapper"), - }, - }, - }) -} - func TestAccKeycloakLdapHardcodedAttributeMapper_createAfterManualDestroy(t *testing.T) { t.Parallel() var mapper = &keycloak.LdapHardcodedAttributeMapper{}