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

feat: add groups path to LDAP group mapper #436

Merged
merged 4 commits into from
Dec 10, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs-old/resources/keycloak_ldap_group_mapper.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ The following arguments are supported:
- `memberof_ldap_attribute` - (Optional) Specifies the name of the LDAP attribute on the LDAP user that contains the groups the user is a member of. Defaults to `memberOf`.
- `mapped_group_attributes` - (Optional) Array of strings representing attributes on the LDAP group which will be mapped to attributes on the Keycloak group.
- `drop_non_existing_groups_during_sync` - (Optional) When `true`, groups that no longer exist within LDAP will be dropped in Keycloak during sync. Defaults to `false`.
- `groups_path` - (Optional) Keycloak group path the LDAP groups are added to. For example if value '/Applications/App1' is used, then LDAP groups will be available in Keycloak under group 'App1', which is child of top level group 'Applications'. The configured group path must already exists in the Keycloak when creating this mapper. The default value is '/' so LDAP groups will be mapped to the Keycloak groups at the top level.

### Import

Expand Down
1 change: 1 addition & 0 deletions docs/resources/ldap_group_mapper.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ resource "keycloak_ldap_group_mapper" "ldap_group_mapper" {
- `memberof_ldap_attribute` - (Optional) Specifies the name of the LDAP attribute on the LDAP user that contains the groups the user is a member of. Defaults to `memberOf`.
- `mapped_group_attributes` - (Optional) Array of strings representing attributes on the LDAP group which will be mapped to attributes on the Keycloak group.
- `drop_non_existing_groups_during_sync` - (Optional) When `true`, groups that no longer exist within LDAP will be dropped in Keycloak during sync. Defaults to `false`.
- `groups_path` - (Optional) Keycloak group path the LDAP groups are added to. For example if value '/Applications/App1' is used, then LDAP groups will be available in Keycloak under group 'App1', which is child of top level group 'Applications'. The configured group path must already exists in the Keycloak when creating this mapper. The default value is '/' so LDAP groups will be mapped to the Keycloak groups at the top level.

## Import

Expand Down
6 changes: 6 additions & 0 deletions keycloak/ldap_group_mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ type LdapGroupMapper struct {
MappedGroupAttributes []string

DropNonExistingGroupsDuringSync bool

GroupsPath string
}

func convertFromLdapGroupMapperToComponent(ldapGroupMapper *LdapGroupMapper) *component {
Expand Down Expand Up @@ -67,6 +69,9 @@ func convertFromLdapGroupMapperToComponent(ldapGroupMapper *LdapGroupMapper) *co
"drop.non.existing.groups.during.sync": {
strconv.FormatBool(ldapGroupMapper.DropNonExistingGroupsDuringSync),
},
"groups.path": {
ldapGroupMapper.GroupsPath,
},
}

if ldapGroupMapper.GroupsLdapFilter != "" {
Expand Down Expand Up @@ -126,6 +131,7 @@ func convertFromComponentToLdapGroupMapper(component *component, realmId string)
UserRolesRetrieveStrategy: component.getConfig("user.roles.retrieve.strategy"),
MemberofLdapAttribute: component.getConfig("memberof.ldap.attribute"),
DropNonExistingGroupsDuringSync: dropNonExistingGroupsDuringSync,
GroupsPath: component.getConfig("groups.path"),
}

if groupsLdapFilter := component.getConfig("groups.ldap.filter"); groupsLdapFilter != "" {
Expand Down
31 changes: 23 additions & 8 deletions provider/resource_keycloak_ldap_group_mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,16 @@ func resourceKeycloakLdapGroupMapper() *schema.Resource {
Optional: true,
Default: false,
},
"groups_path": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
},
}
}

func getLdapGroupMapperFromData(data *schema.ResourceData) *keycloak.LdapGroupMapper {
func getLdapGroupMapperFromData(keycloakClient *keycloak.KeycloakClient, data *schema.ResourceData) *keycloak.LdapGroupMapper {
var groupObjectClasses []string

for _, groupObjectClass := range data.Get("group_object_classes").([]interface{}) {
Expand All @@ -128,7 +133,7 @@ func getLdapGroupMapperFromData(data *schema.ResourceData) *keycloak.LdapGroupMa
mappedGroupAttributes = append(mappedGroupAttributes, mappedGroupAttribute.(string))
}

return &keycloak.LdapGroupMapper{
mapper := &keycloak.LdapGroupMapper{
Id: data.Id(),
Name: data.Get("name").(string),
RealmId: data.Get("realm_id").(string),
Expand All @@ -149,9 +154,15 @@ func getLdapGroupMapperFromData(data *schema.ResourceData) *keycloak.LdapGroupMa
MappedGroupAttributes: mappedGroupAttributes,
DropNonExistingGroupsDuringSync: data.Get("drop_non_existing_groups_during_sync").(bool),
}

if groupsPath, ok := data.GetOk("groups_path"); ok && keycloakClient.VersionIsGreaterThanOrEqualTo(keycloak.Version_11) {
mapper.GroupsPath = groupsPath.(string)
}

return mapper
}

func setLdapGroupMapperData(data *schema.ResourceData, ldapGroupMapper *keycloak.LdapGroupMapper) {
func setLdapGroupMapperData(keycloakClient *keycloak.KeycloakClient, data *schema.ResourceData, ldapGroupMapper *keycloak.LdapGroupMapper) {
data.SetId(ldapGroupMapper.Id)

data.Set("name", ldapGroupMapper.Name)
Expand All @@ -172,12 +183,16 @@ func setLdapGroupMapperData(data *schema.ResourceData, ldapGroupMapper *keycloak
data.Set("memberof_ldap_attribute", ldapGroupMapper.MemberofLdapAttribute)
data.Set("mapped_group_attributes", ldapGroupMapper.MappedGroupAttributes)
data.Set("drop_non_existing_groups_during_sync", ldapGroupMapper.DropNonExistingGroupsDuringSync)

if ldapGroupMapper.GroupsPath != "" && keycloakClient.VersionIsGreaterThanOrEqualTo(keycloak.Version_11) {
data.Set("groups_path", ldapGroupMapper.GroupsPath)
}
}

func resourceKeycloakLdapGroupMapperCreate(data *schema.ResourceData, meta interface{}) error {
keycloakClient := meta.(*keycloak.KeycloakClient)

ldapGroupMapper := getLdapGroupMapperFromData(data)
ldapGroupMapper := getLdapGroupMapperFromData(keycloakClient, data)

err := keycloakClient.ValidateLdapGroupMapper(ldapGroupMapper)
if err != nil {
Expand All @@ -189,7 +204,7 @@ func resourceKeycloakLdapGroupMapperCreate(data *schema.ResourceData, meta inter
return err
}

setLdapGroupMapperData(data, ldapGroupMapper)
setLdapGroupMapperData(keycloakClient, data, ldapGroupMapper)

return resourceKeycloakLdapGroupMapperRead(data, meta)
}
Expand All @@ -205,15 +220,15 @@ func resourceKeycloakLdapGroupMapperRead(data *schema.ResourceData, meta interfa
return handleNotFoundError(err, data)
}

setLdapGroupMapperData(data, ldapGroupMapper)
setLdapGroupMapperData(keycloakClient, data, ldapGroupMapper)

return nil
}

func resourceKeycloakLdapGroupMapperUpdate(data *schema.ResourceData, meta interface{}) error {
keycloakClient := meta.(*keycloak.KeycloakClient)

ldapGroupMapper := getLdapGroupMapperFromData(data)
ldapGroupMapper := getLdapGroupMapperFromData(keycloakClient, data)

err := keycloakClient.ValidateLdapGroupMapper(ldapGroupMapper)
if err != nil {
Expand All @@ -225,7 +240,7 @@ func resourceKeycloakLdapGroupMapperUpdate(data *schema.ResourceData, meta inter
return err
}

setLdapGroupMapperData(data, ldapGroupMapper)
setLdapGroupMapperData(keycloakClient, data, ldapGroupMapper)

return nil
}
Expand Down
77 changes: 77 additions & 0 deletions provider/resource_keycloak_ldap_group_mapper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,30 @@ func TestAccKeycloakLdapGroupMapper_updateLdapUserFederationInPlace(t *testing.T
})
}

func TestAccKeycloakLdapGroupMapper_groupsPath(t *testing.T) {
keycloakClient := testAccProvider.Meta().(*keycloak.KeycloakClient)

if !keycloakClient.VersionIsGreaterThanOrEqualTo(keycloak.Version_11) {
t.Skip()
}

realmName := "terraform-" + acctest.RandString(10)
groupName := "terraform-" + acctest.RandString(10)
groupMapperName := "terraform-" + acctest.RandString(10)

resource.Test(t, resource.TestCase{
ProviderFactories: testAccProviderFactories,
PreCheck: func() { testAccPreCheck(t) },
CheckDestroy: testAccCheckKeycloakLdapGroupMapperDestroy(),
Steps: []resource.TestStep{
{
Config: testKeycloakLdapGroupMapper_groupsPath(realmName, groupName, groupMapperName),
Check: testAccCheckKeycloakLdapGroupMapperExists("keycloak_ldap_group_mapper.group_mapper"),
},
},
})
}

func testAccCheckKeycloakLdapGroupMapperExists(resourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
_, err := getLdapGroupMapperFromState(s, resourceName)
Expand Down Expand Up @@ -401,6 +425,7 @@ resource "keycloak_ldap_group_mapper" "group_mapper" {
membership_ldap_attribute = "member"
membership_user_ldap_attribute = "cn"
memberof_ldap_attribute = "memberOf"
groups_path = "/"
}
`, realm, groupMapperName, attr, val)
}
Expand Down Expand Up @@ -446,6 +471,7 @@ resource "keycloak_ldap_group_mapper" "group_mapper" {
membership_ldap_attribute = "member"
membership_user_ldap_attribute = "cn"
memberof_ldap_attribute = "memberOf"
groups_path = "/"
}
`, realm, groupMapperName)
}
Expand Down Expand Up @@ -560,6 +586,7 @@ resource "keycloak_ldap_group_mapper" "group_mapper" {
membership_ldap_attribute = "member"
membership_user_ldap_attribute = "cn"
memberof_ldap_attribute = "memberOf"
groups_path = "/"
}
`, realmOne, realmTwo, groupMapperName)
}
Expand Down Expand Up @@ -629,3 +656,53 @@ resource "keycloak_ldap_group_mapper" "group_mapper" {
}
`, realmOne, realmTwo, groupMapperName)
}

func testKeycloakLdapGroupMapper_groupsPath(realm, groupName, groupMapperName string) string {
return fmt.Sprintf(`
resource "keycloak_realm" "realm" {
realm = "%s"
}

resource "keycloak_group" "group" {
realm_id = keycloak_realm.realm.id
name = "%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",
"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_group_mapper" "group_mapper" {
name = "%s"
realm_id = keycloak_realm.realm.id
ldap_user_federation_id = keycloak_ldap_user_federation.openldap.id

ldap_groups_dn = "dc=example,dc=org"
group_name_ldap_attribute = "cn"
group_object_classes = [
"groupOfNames"
]
membership_attribute_type = "DN"
membership_ldap_attribute = "member"
membership_user_ldap_attribute = "cn"
memberof_ldap_attribute = "memberOf"

groups_path = keycloak_group.group.path
}
`, realm, groupName, groupMapperName)
}