Repository: mrparkers/terraform-provider-keycloak
Pull request #264 (2020-03-30 15:13:27)

feat: add ldap hardcoded group mapper and add new mappers to GetLdapUserFederationMappers
- add ldap hardcoded group mapper (almost a copy of ldap hardcoded role mapper)
- add hardcoded-ldap-role-mapper and msad-lds-user-account-control-mapper mappers to GetLdapUserFederationMappers


Patch:
diff --git a/keycloak/ldap_hardcoded_group_mapper.go b/keycloak/ldap_hardcoded_group_mapper.go
new file mode 100644
index 0000000..cda8003
--- /dev/null
+++ b/keycloak/ldap_hardcoded_group_mapper.go
@@ -0,0 +1,75 @@
+package keycloak
+
+import "fmt"
+
+type LdapHardcodedGroupMapper struct {
+	Id                   string
+	Name                 string
+	RealmId              string
+	LdapUserFederationId string
+	Group                string
+}
+
+func convertFromLdapHardcodedGroupMapperToComponent(ldapMapper *LdapHardcodedGroupMapper) *component {
+	return &component{
+		Id:           ldapMapper.Id,
+		Name:         ldapMapper.Name,
+		ProviderId:   "hardcoded-ldap-group-mapper",
+		ProviderType: "org.keycloak.storage.ldap.mappers.LDAPStorageMapper",
+		ParentId:     ldapMapper.LdapUserFederationId,
+
+		Config: map[string][]string{
+			"group": {
+				ldapMapper.Group,
+			},
+		},
+	}
+}
+
+func convertFromComponentToLdapHardcodedGroupMapper(component *component, realmId string) *LdapHardcodedGroupMapper {
+	return &LdapHardcodedGroupMapper{
+		Id:                   component.Id,
+		Name:                 component.Name,
+		RealmId:              realmId,
+		LdapUserFederationId: component.ParentId,
+
+		Group: component.getConfig("group"),
+	}
+}
+
+func (keycloakClient *KeycloakClient) ValidateLdapHardcodedGroupMapper(ldapMapper *LdapHardcodedGroupMapper) error {
+	if len(ldapMapper.Group) == 0 {
+		return fmt.Errorf("validation error: hardcoded group name must not be empty")
+	}
+	return nil
+}
+
+func (keycloakClient *KeycloakClient) NewLdapHardcodedGroupMapper(ldapMapper *LdapHardcodedGroupMapper) error {
+	_, location, err := keycloakClient.post(fmt.Sprintf("/realms/%s/components", ldapMapper.RealmId), convertFromLdapHardcodedGroupMapperToComponent(ldapMapper))
+	if err != nil {
+		return err
+	}
+
+	ldapMapper.Id = getIdFromLocationHeader(location)
+
+	return nil
+}
+
+func (keycloakClient *KeycloakClient) GetLdapHardcodedGroupMapper(realmId, id string) (*LdapHardcodedGroupMapper, error) {
+	var component *component
+
+	err := keycloakClient.get(fmt.Sprintf("/realms/%s/components/%s", realmId, id), &component, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	return convertFromComponentToLdapHardcodedGroupMapper(component, realmId), nil
+}
+
+func (keycloakClient *KeycloakClient) UpdateLdapHardcodedGroupMapper(ldapMapper *LdapHardcodedGroupMapper) error {
+	return keycloakClient.put(fmt.Sprintf("/realms/%s/components/%s", ldapMapper.RealmId, ldapMapper.Id), convertFromLdapHardcodedGroupMapperToComponent(ldapMapper))
+}
+
+func (keycloakClient *KeycloakClient) DeleteLdapHardcodedGroupMapper(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 15c775d..15cf6c9 100644
--- a/keycloak/ldap_user_federation.go
+++ b/keycloak/ldap_user_federation.go
@@ -332,7 +332,6 @@ func (keycloakClient *KeycloakClient) GetLdapUserFederationMappers(realmId, id s
 	if err != nil {
 		return nil, err
 	}
-
 	for _, component := range components {
 		switch component.ProviderId {
 		case "full-name-ldap-mapper":
@@ -347,6 +346,18 @@ func (keycloakClient *KeycloakClient) GetLdapUserFederationMappers(realmId, id s
 				return nil, err
 			}
 			ldapUserFederationMappers = append(ldapUserFederationMappers, mapper)
+		case "hardcoded-ldap-group-mapper":
+			mapper := convertFromComponentToLdapHardcodedGroupMapper(component, realmId)
+			ldapUserFederationMappers = append(ldapUserFederationMappers, mapper)
+		case "hardcoded-ldap-role-mapper":
+			mapper := convertFromComponentToLdapHardcodedRoleMapper(component, realmId)
+			ldapUserFederationMappers = append(ldapUserFederationMappers, mapper)
+		case "msad-lds-user-account-control-mapper":
+			mapper, err := convertFromComponentToLdapMsadLdsUserAccountControlMapper(component, realmId)
+			if err != nil {
+				return nil, err
+			}
+			ldapUserFederationMappers = append(ldapUserFederationMappers, mapper)
 		case "msad-user-account-control-mapper":
 			mapper, err := convertFromComponentToLdapMsadUserAccountControlMapper(component, realmId)
 			if err != nil {
diff --git a/provider/provider.go b/provider/provider.go
index 1415344..319cbac 100644
--- a/provider/provider.go
+++ b/provider/provider.go
@@ -31,6 +31,7 @@ func KeycloakProvider() *schema.Provider {
 			"keycloak_ldap_user_attribute_mapper":                      resourceKeycloakLdapUserAttributeMapper(),
 			"keycloak_ldap_group_mapper":                               resourceKeycloakLdapGroupMapper(),
 			"keycloak_ldap_hardcoded_role_mapper":                      resourceKeycloakLdapHardcodedRoleMapper(),
+			"keycloak_ldap_hardcoded_group_mapper":                     resourceKeycloakLdapHardcodedGroupMapper(),
 			"keycloak_ldap_msad_user_account_control_mapper":           resourceKeycloakLdapMsadUserAccountControlMapper(),
 			"keycloak_ldap_msad_lds_user_account_control_mapper":       resourceKeycloakLdapMsadLdsUserAccountControlMapper(),
 			"keycloak_ldap_full_name_mapper":                           resourceKeycloakLdapFullNameMapper(),
diff --git a/provider/resource_keycloak_ldap_hardcoded_group_mapper.go b/provider/resource_keycloak_ldap_hardcoded_group_mapper.go
new file mode 100644
index 0000000..62e7ea2
--- /dev/null
+++ b/provider/resource_keycloak_ldap_hardcoded_group_mapper.go
@@ -0,0 +1,127 @@
+package provider
+
+import (
+	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
+	"github.com/mrparkers/terraform-provider-keycloak/keycloak"
+)
+
+func resourceKeycloakLdapHardcodedGroupMapper() *schema.Resource {
+	return &schema.Resource{
+		Create: resourceKeycloakLdapHardcodedGroupMapperCreate,
+		Read:   resourceKeycloakLdapHardcodedGroupMapperRead,
+		Update: resourceKeycloakLdapHardcodedGroupMapperUpdate,
+		Delete: resourceKeycloakLdapHardcodedGroupMapperDelete,
+		// 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.",
+			},
+			"group": {
+				Type:        schema.TypeString,
+				Required:    true,
+				ForceNew:    true,
+				Description: "Group to grant to user.",
+			},
+		},
+	}
+}
+
+func getLdapHardcodedGroupMapperFromData(data *schema.ResourceData) *keycloak.LdapHardcodedGroupMapper {
+	return &keycloak.LdapHardcodedGroupMapper{
+		Id:                   data.Id(),
+		Name:                 data.Get("name").(string),
+		RealmId:              data.Get("realm_id").(string),
+		LdapUserFederationId: data.Get("ldap_user_federation_id").(string),
+		Group:                data.Get("group").(string),
+	}
+}
+
+func setLdapHardcodedGroupMapperData(data *schema.ResourceData, ldapMapper *keycloak.LdapHardcodedGroupMapper) {
+	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("group", ldapMapper.Group)
+}
+
+func resourceKeycloakLdapHardcodedGroupMapperCreate(data *schema.ResourceData, meta interface{}) error {
+	keycloakClient := meta.(*keycloak.KeycloakClient)
+
+	ldapMapper := getLdapHardcodedGroupMapperFromData(data)
+
+	err := keycloakClient.ValidateLdapHardcodedGroupMapper(ldapMapper)
+	if err != nil {
+		return err
+	}
+
+	err = keycloakClient.NewLdapHardcodedGroupMapper(ldapMapper)
+	if err != nil {
+		return err
+	}
+
+	setLdapHardcodedGroupMapperData(data, ldapMapper)
+
+	return resourceKeycloakLdapHardcodedGroupMapperRead(data, meta)
+}
+
+func resourceKeycloakLdapHardcodedGroupMapperRead(data *schema.ResourceData, meta interface{}) error {
+	keycloakClient := meta.(*keycloak.KeycloakClient)
+
+	realmId := data.Get("realm_id").(string)
+	id := data.Id()
+
+	ldapMapper, err := keycloakClient.GetLdapHardcodedGroupMapper(realmId, id)
+	if err != nil {
+		return handleNotFoundError(err, data)
+	}
+
+	setLdapHardcodedGroupMapperData(data, ldapMapper)
+
+	return nil
+}
+
+func resourceKeycloakLdapHardcodedGroupMapperUpdate(data *schema.ResourceData, meta interface{}) error {
+	keycloakClient := meta.(*keycloak.KeycloakClient)
+
+	ldapMapper := getLdapHardcodedGroupMapperFromData(data)
+
+	err := keycloakClient.ValidateLdapHardcodedGroupMapper(ldapMapper)
+	if err != nil {
+		return err
+	}
+
+	err = keycloakClient.UpdateLdapHardcodedGroupMapper(ldapMapper)
+	if err != nil {
+		return err
+	}
+
+	setLdapHardcodedGroupMapperData(data, ldapMapper)
+
+	return nil
+}
+
+func resourceKeycloakLdapHardcodedGroupMapperDelete(data *schema.ResourceData, meta interface{}) error {
+	keycloakClient := meta.(*keycloak.KeycloakClient)
+
+	realmId := data.Get("realm_id").(string)
+	id := data.Id()
+
+	return keycloakClient.DeleteLdapHardcodedGroupMapper(realmId, id)
+}


Test patch:
diff --git a/provider/resource_keycloak_ldap_hardcoded_group_mapper_test.go b/provider/resource_keycloak_ldap_hardcoded_group_mapper_test.go
new file mode 100644
index 0000000..0e5eaee
--- /dev/null
+++ b/provider/resource_keycloak_ldap_hardcoded_group_mapper_test.go
@@ -0,0 +1,170 @@
+package provider
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
+	"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
+	"github.com/hashicorp/terraform-plugin-sdk/terraform"
+	"github.com/mrparkers/terraform-provider-keycloak/keycloak"
+)
+
+func TestAccKeycloakLdapHardcodedGroupMapper_basic(t *testing.T) {
+	realmName := "terraform-" + acctest.RandString(10)
+	groupMapperName := "terraform-" + acctest.RandString(10)
+
+	resource.Test(t, resource.TestCase{
+		Providers:    testAccProviders,
+		PreCheck:     func() { testAccPreCheck(t) },
+		CheckDestroy: testAccCheckKeycloakLdapHardcodedGroupMapperDestroy(),
+		Steps: []resource.TestStep{
+			{
+				Config: testKeycloakLdapHardcodedGroupMapper(realmName, groupMapperName),
+				Check:  testAccCheckKeycloakLdapHardcodedGroupMapperExists("keycloak_ldap_hardcoded_group_mapper.hardcoded_group_mapper"),
+			},
+			{
+				ResourceName:      "keycloak_ldap_hardcoded_group_mapper.hardcoded_group_mapper",
+				ImportState:       true,
+				ImportStateVerify: true,
+				ImportStateIdFunc: getLdapGenericMapperImportId("keycloak_ldap_hardcoded_group_mapper.hardcoded_group_mapper"),
+			},
+		},
+	})
+}
+
+func TestAccKeycloakLdapHardcodedGroupMapper_createAfterManualDestroy(t *testing.T) {
+	var mapper = &keycloak.LdapHardcodedGroupMapper{}
+
+	realmName := "terraform-" + acctest.RandString(10)
+	groupMapperName := "terraform-" + acctest.RandString(10)
+
+	resource.Test(t, resource.TestCase{
+		Providers:    testAccProviders,
+		PreCheck:     func() { testAccPreCheck(t) },
+		CheckDestroy: testAccCheckKeycloakLdapHardcodedGroupMapperDestroy(),
+		Steps: []resource.TestStep{
+			{
+				Config: testKeycloakLdapHardcodedGroupMapper(realmName, groupMapperName),
+				Check:  testAccCheckKeycloakLdapHardcodedGroupMapperFetch("keycloak_ldap_hardcoded_group_mapper.hardcoded_group_mapper", mapper),
+			},
+			{
+				PreConfig: func() {
+					keycloakClient := testAccProvider.Meta().(*keycloak.KeycloakClient)
+
+					err := keycloakClient.DeleteLdapHardcodedGroupMapper(mapper.RealmId, mapper.Id)
+					if err != nil {
+						t.Fatal(err)
+					}
+				},
+				Config: testKeycloakLdapHardcodedGroupMapper(realmName, groupMapperName),
+				Check:  testAccCheckKeycloakLdapHardcodedGroupMapperExists("keycloak_ldap_hardcoded_group_mapper.hardcoded_group_mapper"),
+			},
+		},
+	})
+}
+
+func testAccCheckKeycloakLdapHardcodedGroupMapperExists(resourceName string) resource.TestCheckFunc {
+	return func(s *terraform.State) error {
+		_, err := getLdapHardcodedGroupMapperFromState(s, resourceName)
+		if err != nil {
+			return err
+		}
+
+		return nil
+	}
+}
+
+func testAccCheckKeycloakLdapHardcodedGroupMapperFetch(resourceName string, mapper *keycloak.LdapHardcodedGroupMapper) resource.TestCheckFunc {
+	return func(s *terraform.State) error {
+		fetchedMapper, err := getLdapHardcodedGroupMapperFromState(s, resourceName)
+		if err != nil {
+			return err
+		}
+
+		mapper.Id = fetchedMapper.Id
+		mapper.RealmId = fetchedMapper.RealmId
+
+		return nil
+	}
+}
+
+func testAccCheckKeycloakLdapHardcodedGroupMapperDestroy() resource.TestCheckFunc {
+	return func(s *terraform.State) error {
+		for _, rs := range s.RootModule().Resources {
+			if rs.Type != "keycloak_ldap_hardcoded_group_mapper" {
+				continue
+			}
+
+			id := rs.Primary.ID
+			realm := rs.Primary.Attributes["realm_id"]
+
+			keycloakClient := testAccProvider.Meta().(*keycloak.KeycloakClient)
+
+			ldapMapper, _ := keycloakClient.GetLdapHardcodedGroupMapper(realm, id)
+			if ldapMapper != nil {
+				return fmt.Errorf("ldap hardcoded group mapper with id %s still exists", id)
+			}
+		}
+
+		return nil
+	}
+}
+
+func getLdapHardcodedGroupMapperFromState(s *terraform.State, resourceName string) (*keycloak.LdapHardcodedGroupMapper, error) {
+	keycloakClient := testAccProvider.Meta().(*keycloak.KeycloakClient)
+
+	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.GetLdapHardcodedGroupMapper(realm, id)
+	if err != nil {
+		return nil, fmt.Errorf("error getting ldap group mapper with id %s: %s", id, err)
+	}
+
+	return ldapMapper, nil
+}
+
+func testKeycloakLdapHardcodedGroupMapper(realm, groupMapperName string) string {
+	return fmt.Sprintf(`
+resource "keycloak_realm" "realm" {
+	realm = "%s"
+}
+
+resource "keycloak_ldap_user_federation" "openldap" {
+	name                    = "openldap"
+	realm_id                = keycloak_realm.realm.id
+
+	enabled                 = true
+
+	username_ldap_attribute = "cn"
+	rdn_ldap_attribute      = "cn"
+	uuid_ldap_attribute     = "entryDN"
+	user_object_classes     = [
+		"simpleSecurityObject",
+		"organizationalGroup"
+	]
+	connection_url          = "ldap://openldap"
+	users_dn                = "dc=example,dc=org"
+	bind_dn                 = "cn=admin,dc=example,dc=org"
+	bind_credential         = "admin"
+}
+
+resource "keycloak_group" "hardcoded_group_mapper_test" {
+    realm_id    = keycloak_realm.realm.id
+    name        = "hardcoded-group-test"
+}
+
+resource "keycloak_ldap_hardcoded_group_mapper" "hardcoded_group_mapper" {
+	name                        = "%s"
+	realm_id                    = keycloak_realm.realm.id
+	ldap_user_federation_id     = keycloak_ldap_user_federation.openldap.id
+	group                        = keycloak_group.hardcoded_group_mapper_test.name
+}
+	`, realm, groupMapperName)
+}