KEYCLOAK-630 Added Role Federation mapper. Fixes and refactoring

This commit is contained in:
mposolda committed 2015-05-22 21:08:33 +02:00
1 parent 975337f225
commit a9f1fda68a
34 files changed
+1213 -209

No files matched your search

@@ -13,6 +13,7 @@ import org.keycloak.federation.ldap.LDAPFederationProvider;
import org.keycloak.federation.ldap.LDAPFederationProviderFactory;
import org.keycloak.federation.ldap.LDAPUtils;
import org.keycloak.federation.ldap.idm.model.LDAPObject;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.LDAPConstants;
import org.keycloak.models.ModelReadOnlyException;
@@ -65,6 +66,9 @@ public class FederationProvidersIntegrationTest {
LDAPFederationProvider ldapFedProvider = FederationTestUtils.getLdapProvider(session, ldapModel);
LDAPUtils.removeAllUsers(ldapFedProvider, appRealm);
// Add sample application TODO: REmove this!!!! It's just temporarily needed in SyncProvidersTest until model for federation mappers is implemented
ClientModel finance = appRealm.addClient("finance");
LDAPObject john = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "johnkeycloak", "John", "Doe", "john@email.org", "1234");
ldapFedProvider.getLdapIdentityStore().updatePassword(john, "Password1");
@@ -0,0 +1,367 @@
package org.keycloak.testsuite.federation;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.FixMethodOrder;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import org.junit.rules.TestRule;
import org.junit.runners.MethodSorters;
import org.keycloak.federation.ldap.LDAPFederationProvider;
import org.keycloak.federation.ldap.LDAPFederationProviderFactory;
import org.keycloak.federation.ldap.LDAPUtils;
import org.keycloak.federation.ldap.idm.model.LDAPObject;
import org.keycloak.federation.ldap.idm.query.Condition;
import org.keycloak.federation.ldap.idm.query.QueryParameter;
import org.keycloak.federation.ldap.idm.query.internal.LDAPIdentityQuery;
import org.keycloak.federation.ldap.idm.query.internal.LDAPQueryConditionsBuilder;
import org.keycloak.federation.ldap.mappers.RoleLDAPFederationMapper;
import org.keycloak.models.AccountRoles;
import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.LDAPConstants;
import org.keycloak.models.ModelException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserFederationMapperModel;
import org.keycloak.models.UserFederationProvider;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.cache.RealmAdapter;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.OAuthClient;
import org.keycloak.testsuite.pages.AccountPasswordPage;
import org.keycloak.testsuite.pages.AccountUpdateProfilePage;
import org.keycloak.testsuite.pages.AppPage;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.pages.RegisterPage;
import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.testsuite.rule.LDAPRule;
import org.keycloak.testsuite.rule.WebResource;
import org.keycloak.testsuite.rule.WebRule;
import org.openqa.selenium.WebDriver;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class LDAPRoleMappingsTest {
private static LDAPRule ldapRule = new LDAPRule();
private static UserFederationProviderModel ldapModel = null;
private static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
FederationTestUtils.addLocalUser(manager.getSession(), appRealm, "mary", "mary@test.com", "password-app");
Map<String,String> ldapConfig = ldapRule.getConfig();
ldapConfig.put(LDAPConstants.SYNC_REGISTRATIONS, "true");
ldapConfig.put(LDAPConstants.EDIT_MODE, UserFederationProvider.EditMode.WRITABLE.toString());
ldapModel = appRealm.addUserFederationProvider(LDAPFederationProviderFactory.PROVIDER_NAME, ldapConfig, 0, "test-ldap", -1, -1, 0);
// Delete all LDAP users and add some new for testing
LDAPFederationProvider ldapFedProvider = FederationTestUtils.getLdapProvider(session, ldapModel);
LDAPUtils.removeAllUsers(ldapFedProvider, appRealm);
// Add sample application
ClientModel finance = appRealm.addClient("finance");
LDAPObject john = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "johnkeycloak", "John", "Doe", "john@email.org", "1234");
ldapFedProvider.getLdapIdentityStore().updatePassword(john, "Password1");
LDAPObject mary = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "marykeycloak", "Mary", "Kelly", "mary@email.org", "5678");
ldapFedProvider.getLdapIdentityStore().updatePassword(mary, "Password1");
LDAPObject rob = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "robkeycloak", "Rob", "Brown", "rob@email.org", "8910");
ldapFedProvider.getLdapIdentityStore().updatePassword(rob, "Password1");
}
}) {
@Override
protected void after() {
// Need to cleanup some LDAP objects after the test
update(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
RoleLDAPFederationMapper roleMapper = new RoleLDAPFederationMapper();
UserFederationMapperModel roleMapperModel = findRoleMapperModel(appRealm);
LDAPFederationProvider ldapProvider = FederationTestUtils.getLdapProvider(session, ldapModel);
LDAPObject ldapRole = roleMapper.loadLDAPRoleByName(roleMapperModel, ldapProvider, "realmRole3");
if (ldapRole != null) {
ldapProvider.getLdapIdentityStore().remove(ldapRole);
}
}
});
super.after();
}
};
@ClassRule
public static TestRule chain = RuleChain
.outerRule(ldapRule)
.around(keycloakRule);
@Rule
public WebRule webRule = new WebRule(this);
@WebResource
protected OAuthClient oauth;
@WebResource
protected WebDriver driver;
@WebResource
protected AppPage appPage;
@WebResource
protected RegisterPage registerPage;
@WebResource
protected LoginPage loginPage;
@WebResource
protected AccountUpdateProfilePage profilePage;
@WebResource
protected AccountPasswordPage changePasswordPage;
@Test
public void test01_ldapOnlyRoleMappings() {
// TODO: Remove me!!!
RealmAdapter.LDAP_MODE = "LDAP_ONLY";
KeycloakSession session = keycloakRule.startSession();
try {
RealmModel appRealm = session.realms().getRealmByName("test");
UserModel john = session.users().getUserByUsername("johnkeycloak", appRealm);
UserModel mary = session.users().getUserByUsername("marykeycloak", appRealm);
// 1 - Grant some roles in LDAP
// This role should already exists as it was imported from LDAP
RoleModel realmRole1 = appRealm.getRole("realmRole1");
john.grantRole(realmRole1);
// This role should already exists as it was imported from LDAP
RoleModel realmRole2 = appRealm.getRole("realmRole2");
mary.grantRole(realmRole2);
RoleModel realmRole3 = appRealm.addRole("realmRole3");
john.grantRole(realmRole3);
mary.grantRole(realmRole3);
ClientModel accountApp = appRealm.getClientByClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID);
ClientModel financeApp = appRealm.getClientByClientId("finance");
RoleModel manageAccountRole = accountApp.getRole(AccountRoles.MANAGE_ACCOUNT);
RoleModel financeRole1 = financeApp.getRole("financeRole1");
john.grantRole(financeRole1);
// 2 - Check that role mappings are not in local Keycloak DB (They are in LDAP).
UserModel johnDb = session.userStorage().getUserByUsername("johnkeycloak", appRealm);
Set<RoleModel> johnDbRoles = johnDb.getRoleMappings();
Assert.assertFalse(johnDbRoles.contains(realmRole1));
Assert.assertFalse(johnDbRoles.contains(realmRole2));
Assert.assertFalse(johnDbRoles.contains(realmRole3));
Assert.assertFalse(johnDbRoles.contains(financeRole1));
Assert.assertTrue(johnDbRoles.contains(manageAccountRole));
// 3 - Check that role mappings are in LDAP and hence available through federation
Set<RoleModel> johnRoles = john.getRoleMappings();
Assert.assertTrue(johnRoles.contains(realmRole1));
Assert.assertFalse(johnRoles.contains(realmRole2));
Assert.assertTrue(johnRoles.contains(realmRole3));
Assert.assertTrue(johnRoles.contains(financeRole1));
Assert.assertTrue(johnRoles.contains(manageAccountRole));
Set<RoleModel> johnRealmRoles = john.getRealmRoleMappings();
Assert.assertEquals(2, johnRealmRoles.size());
Assert.assertTrue(johnRealmRoles.contains(realmRole1));
Assert.assertTrue(johnRealmRoles.contains(realmRole3));
// account roles are not mapped in LDAP. Those are in Keycloak DB
Set<RoleModel> johnAccountRoles = john.getClientRoleMappings(accountApp);
Assert.assertTrue(johnAccountRoles.contains(manageAccountRole));
Set<RoleModel> johnFinanceRoles = john.getClientRoleMappings(financeApp);
Assert.assertEquals(1, johnFinanceRoles.size());
Assert.assertTrue(johnFinanceRoles.contains(financeRole1));
// 4 - Delete some role mappings and check they are deleted
john.deleteRoleMapping(realmRole3);
john.deleteRoleMapping(realmRole1);
john.deleteRoleMapping(financeRole1);
john.deleteRoleMapping(manageAccountRole);
johnRoles = john.getRoleMappings();
Assert.assertFalse(johnRoles.contains(realmRole1));
Assert.assertFalse(johnRoles.contains(realmRole2));
Assert.assertFalse(johnRoles.contains(realmRole3));
Assert.assertFalse(johnRoles.contains(financeRole1));
Assert.assertFalse(johnRoles.contains(manageAccountRole));
// Cleanup
mary.deleteRoleMapping(realmRole2);
mary.deleteRoleMapping(realmRole3);
john.grantRole(manageAccountRole);
} finally {
keycloakRule.stopSession(session, false);
}
}
@Test
public void test02_readOnlyRoleMappings() {
// TODO: Remove me!!!
RealmAdapter.LDAP_MODE = "READ_ONLY";
KeycloakSession session = keycloakRule.startSession();
try {
RealmModel appRealm = session.realms().getRealmByName("test");
UserModel mary = session.users().getUserByUsername("marykeycloak", appRealm);
RoleModel realmRole1 = appRealm.getRole("realmRole1");
RoleModel realmRole2 = appRealm.getRole("realmRole2");
RoleModel realmRole3 = appRealm.getRole("realmRole3");
if (realmRole3 == null) {
realmRole3 = appRealm.addRole("realmRole3");
}
// Add some role mappings directly into LDAP
RoleLDAPFederationMapper roleMapper = new RoleLDAPFederationMapper();
UserFederationMapperModel roleMapperModel = findRoleMapperModel(appRealm);
LDAPFederationProvider ldapProvider = FederationTestUtils.getLdapProvider(session, ldapModel);
LDAPObject maryLdap = ldapProvider.loadLDAPUserByUsername(appRealm, "marykeycloak");
roleMapper.addRoleMappingInLDAP(roleMapperModel, "realmRole1", ldapProvider, maryLdap);
roleMapper.addRoleMappingInLDAP(roleMapperModel, "realmRole2", ldapProvider, maryLdap);
// Add some role to model
mary.grantRole(realmRole3);
// Assert that mary has both LDAP and DB mapped roles
Set<RoleModel> maryRoles = mary.getRealmRoleMappings();
Assert.assertTrue(maryRoles.contains(realmRole1));
Assert.assertTrue(maryRoles.contains(realmRole2));
Assert.assertTrue(maryRoles.contains(realmRole3));
// Assert that access through DB will have just DB mapped role
UserModel maryDB = session.userStorage().getUserByUsername("marykeycloak", appRealm);
Set<RoleModel> maryDBRoles = maryDB.getRealmRoleMappings();
Assert.assertFalse(maryDBRoles.contains(realmRole1));
Assert.assertFalse(maryDBRoles.contains(realmRole2));
Assert.assertTrue(maryDBRoles.contains(realmRole3));
mary.deleteRoleMapping(realmRole3);
try {
mary.deleteRoleMapping(realmRole1);
Assert.fail("It wasn't expected to successfully delete LDAP role mappings in READ_ONLY mode");
} catch (ModelException expected) {
}
// Delete role mappings directly in LDAP
deleteRoleMappingsInLDAP(roleMapperModel, roleMapper, ldapProvider, maryLdap, "realmRole1");
deleteRoleMappingsInLDAP(roleMapperModel, roleMapper, ldapProvider, maryLdap, "realmRole2");
// Assert role mappings is not available
maryRoles = mary.getRealmRoleMappings();
Assert.assertFalse(maryRoles.contains(realmRole1));
Assert.assertFalse(maryRoles.contains(realmRole2));
Assert.assertFalse(maryRoles.contains(realmRole3));
} finally {
keycloakRule.stopSession(session, false);
}
}
@Test
public void test03_importRoleMappings() {
// TODO: Remove me!!!
RealmAdapter.LDAP_MODE = "IMPORT";
KeycloakSession session = keycloakRule.startSession();
try {
RealmModel appRealm = session.realms().getRealmByName("test");
// Add some role mappings directly in LDAP
RoleLDAPFederationMapper roleMapper = new RoleLDAPFederationMapper();
UserFederationMapperModel roleMapperModel = findRoleMapperModel(appRealm);
LDAPFederationProvider ldapProvider = FederationTestUtils.getLdapProvider(session, ldapModel);
LDAPObject robLdap = ldapProvider.loadLDAPUserByUsername(appRealm, "robkeycloak");
roleMapper.addRoleMappingInLDAP(roleMapperModel, "realmRole1", ldapProvider, robLdap);
roleMapper.addRoleMappingInLDAP(roleMapperModel, "realmRole2", ldapProvider, robLdap);
// Get user and check that he has requested roles from LDAP
UserModel rob = session.users().getUserByUsername("robkeycloak", appRealm);
RoleModel realmRole1 = appRealm.getRole("realmRole1");
RoleModel realmRole2 = appRealm.getRole("realmRole2");
RoleModel realmRole3 = appRealm.getRole("realmRole3");
if (realmRole3 == null) {
realmRole3 = appRealm.addRole("realmRole3");
}
Set<RoleModel> robRoles = rob.getRealmRoleMappings();
Assert.assertTrue(robRoles.contains(realmRole1));
Assert.assertTrue(robRoles.contains(realmRole2));
Assert.assertFalse(robRoles.contains(realmRole3));
// Add some role mappings in model and check that user has it
rob.grantRole(realmRole3);
robRoles = rob.getRealmRoleMappings();
Assert.assertTrue(robRoles.contains(realmRole3));
// Delete some role mappings in LDAP and check that it doesn't have any effect and user still has role
deleteRoleMappingsInLDAP(roleMapperModel, roleMapper, ldapProvider, robLdap, "realmRole1");
deleteRoleMappingsInLDAP(roleMapperModel, roleMapper, ldapProvider, robLdap, "realmRole2");
robRoles = rob.getRealmRoleMappings();
Assert.assertTrue(robRoles.contains(realmRole1));
Assert.assertTrue(robRoles.contains(realmRole2));
// Delete role mappings through model and verifies that user doesn't have them anymore
rob.deleteRoleMapping(realmRole1);
rob.deleteRoleMapping(realmRole2);
rob.deleteRoleMapping(realmRole3);
robRoles = rob.getRealmRoleMappings();
Assert.assertFalse(robRoles.contains(realmRole1));
Assert.assertFalse(robRoles.contains(realmRole2));
Assert.assertFalse(robRoles.contains(realmRole3));
} finally {
keycloakRule.stopSession(session, false);
}
}
private static UserFederationMapperModel findRoleMapperModel(RealmModel appRealm) {
List<UserFederationMapperModel> fedMappers = appRealm.getUserFederationMappers();
for (UserFederationMapperModel mapper : fedMappers) {
if ("realmRoleMapper".equals(mapper.getName())) {
return mapper;
}
}
throw new IllegalStateException("Mapper 'realmRoleMapper' not found");
}
private void deleteRoleMappingsInLDAP(UserFederationMapperModel roleMapperModel, RoleLDAPFederationMapper roleMapper, LDAPFederationProvider ldapProvider, LDAPObject ldapUser, String roleName) {
LDAPIdentityQuery ldapQuery = roleMapper.createRoleQuery(roleMapperModel, ldapProvider);
LDAPQueryConditionsBuilder conditionsBuilder = new LDAPQueryConditionsBuilder();
Condition roleNameCondition = conditionsBuilder.equal(new QueryParameter(LDAPConstants.CN), roleName);
ldapQuery.where(roleNameCondition);
LDAPObject ldapRole1 = ldapQuery.getFirstResult();
roleMapper.deleteRoleMappingInLDAP(roleMapperModel, ldapProvider, ldapUser, ldapRole1);
}
}
@@ -11,6 +11,7 @@ import org.keycloak.federation.ldap.LDAPFederationProvider;
import org.keycloak.federation.ldap.LDAPFederationProviderFactory;
import org.keycloak.federation.ldap.LDAPUtils;
import org.keycloak.federation.ldap.idm.model.LDAPObject;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.LDAPConstants;
@@ -60,6 +61,9 @@ public class SyncProvidersTest {
LDAPFederationProvider ldapFedProvider = FederationTestUtils.getLdapProvider(session, ldapModel);
LDAPUtils.removeAllUsers(ldapFedProvider, appRealm);
// Add sample application TODO: REmove this!!!! It's just temporarily needed in SyncProvidersTest until model for federation mappers is implemented
ClientModel finance = appRealm.addClient("finance");
for (int i=1 ; i<=5 ; i++) {
LDAPObject ldapUser = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "user" + i, "User" + i + "FN", "User" + i + "LN", "user" + i + "@email.org", "12" + i);
ldapFedProvider.getLdapIdentityStore().updatePassword(ldapUser, "Password1");
@@ -15,9 +15,9 @@ import org.keycloak.models.UserFederationProvider;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class LDAPConfiguration {
public class LDAPTestConfiguration {
private static final Logger log = Logger.getLogger(LDAPConfiguration.class);
private static final Logger log = Logger.getLogger(LDAPTestConfiguration.class);
private String connectionPropertiesLocation;
private boolean startEmbeddedLdapLerver = true;
@@ -69,7 +69,7 @@ public class LDAPConfiguration {
DEFAULT_VALUES.put(KerberosConstants.ALLOW_KERBEROS_AUTHENTICATION, "false");
DEFAULT_VALUES.put(KerberosConstants.KERBEROS_REALM, "KEYCLOAK.ORG");
DEFAULT_VALUES.put(KerberosConstants.SERVER_PRINCIPAL, "HTTP/localhost@KEYCLOAK.ORG");
URL keytabUrl = LDAPConfiguration.class.getResource("/kerberos/http.keytab");
URL keytabUrl = LDAPTestConfiguration.class.getResource("/kerberos/http.keytab");
String keyTabPath = new File(keytabUrl.getFile()).getAbsolutePath();
DEFAULT_VALUES.put(KerberosConstants.KEYTAB, keyTabPath);
DEFAULT_VALUES.put(KerberosConstants.DEBUG, "true");
@@ -78,11 +78,11 @@ public class LDAPConfiguration {
DEFAULT_VALUES.put(KerberosConstants.USE_KERBEROS_FOR_PASSWORD_AUTHENTICATION, "false");
}
public static LDAPConfiguration readConfiguration(String connectionPropertiesLocation) {
LDAPConfiguration ldapConfiguration = new LDAPConfiguration();
ldapConfiguration.setConnectionPropertiesLocation(connectionPropertiesLocation);
ldapConfiguration.loadConnectionProperties();
return ldapConfiguration;
public static LDAPTestConfiguration readConfiguration(String connectionPropertiesLocation) {
LDAPTestConfiguration ldapTestConfiguration = new LDAPTestConfiguration();
ldapTestConfiguration.setConnectionPropertiesLocation(connectionPropertiesLocation);
ldapTestConfiguration.loadConnectionProperties();
return ldapTestConfiguration;
}
protected void loadConnectionProperties() {
@@ -5,7 +5,7 @@ import java.net.URL;
import org.jboss.logging.Logger;
import org.keycloak.testsuite.ldap.EmbeddedServersFactory;
import org.keycloak.testsuite.ldap.LDAPConfiguration;
import org.keycloak.testsuite.ldap.LDAPTestConfiguration;
import org.keycloak.testsuite.ldap.LDAPEmbeddedServer;
/**
@@ -21,7 +21,7 @@ public class KerberosRule extends LDAPRule {
this.configLocation = configLocation;
// Global kerberos configuration
URL krb5ConfURL = LDAPConfiguration.class.getResource("/kerberos/test-krb5.conf");
URL krb5ConfURL = LDAPTestConfiguration.class.getResource("/kerberos/test-krb5.conf");
String krb5ConfPath = new File(krb5ConfURL.getFile()).getAbsolutePath();
log.info("Krb5.conf file location is: " + krb5ConfPath);
System.setProperty("java.security.krb5.conf", krb5ConfPath);
@@ -4,7 +4,7 @@ import java.util.Map;
import org.junit.rules.ExternalResource;
import org.keycloak.testsuite.ldap.EmbeddedServersFactory;
import org.keycloak.testsuite.ldap.LDAPConfiguration;
import org.keycloak.testsuite.ldap.LDAPTestConfiguration;
import org.keycloak.testsuite.ldap.LDAPEmbeddedServer;
/**
@@ -14,15 +14,15 @@ public class LDAPRule extends ExternalResource {
public static final String LDAP_CONNECTION_PROPERTIES_LOCATION = "ldap/ldap-connection.properties";
protected LDAPConfiguration ldapConfiguration;
protected LDAPTestConfiguration ldapTestConfiguration;
protected LDAPEmbeddedServer ldapEmbeddedServer;
@Override
protected void before() throws Throwable {
String connectionPropsLocation = getConnectionPropertiesLocation();
ldapConfiguration = LDAPConfiguration.readConfiguration(connectionPropsLocation);
ldapTestConfiguration = LDAPTestConfiguration.readConfiguration(connectionPropsLocation);
if (ldapConfiguration.isStartEmbeddedLdapLerver()) {
if (ldapTestConfiguration.isStartEmbeddedLdapLerver()) {
EmbeddedServersFactory factory = EmbeddedServersFactory.readConfiguration();
ldapEmbeddedServer = createServer(factory);
ldapEmbeddedServer.init();
@@ -36,7 +36,7 @@ public class LDAPRule extends ExternalResource {
if (ldapEmbeddedServer != null) {
ldapEmbeddedServer.stop();
ldapEmbeddedServer = null;
ldapConfiguration = null;
ldapTestConfiguration = null;
}
} catch (Exception e) {
throw new RuntimeException("Error tearDown Embedded LDAP server.", e);
@@ -52,6 +52,6 @@ public class LDAPRule extends ExternalResource {
}
public Map<String, String> getConfig() {
return ldapConfiguration.getLDAPConfig();
return ldapTestConfiguration.getLDAPConfig();
}
}
@@ -9,13 +9,31 @@ objectclass: top
objectclass: organizationalUnit
ou: People
dn: ou=Roles,dc=keycloak,dc=org
dn: ou=RealmRoles,dc=keycloak,dc=org
objectclass: top
objectclass: organizationalUnit
ou: Roles
ou: RealmRoles
dn: ou=Groups,dc=keycloak,dc=org
dn: cn=realmRole1,ou=RealmRoles,dc=keycloak,dc=org
objectclass: top
objectclass: groupOfNames
cn: realmRole1
member:
dn: cn=realmRole2,ou=RealmRoles,dc=keycloak,dc=org
objectclass: top
objectclass: groupOfNames
cn: realmRole2
member:
dn: ou=FinanceRoles,dc=keycloak,dc=org
objectclass: top
objectclass: organizationalUnit
ou: Groups
ou: FinanceRoles
dn: cn=financeRole1,ou=FinanceRoles,dc=keycloak,dc=org
objectclass: top
objectclass: groupOfNames
cn: financeRole1
member: