diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
index 8c55586..3c9a1cd 100755
--- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
@@ -241,12 +241,6 @@ public class RealmManager {
}
}
- if (rep.getRoles() != null && rep.getRoles().getRealm() != null) {
- for (RoleRepresentation roleRep : rep.getRoles().getRealm()) {
- createRole(newRealm, roleRep);
- }
- }
-
if (rep.getRoles() != null) {
if (rep.getRoles().getRealm() != null) { // realm roles
for (RoleRepresentation roleRep : rep.getRoles().getRealm()) {
@@ -266,10 +260,11 @@ public class RealmManager {
}
}
}
- // now that all roles are created, re-interate and set up composites
+ // now that all roles are created, re-iterate and set up composites
if (rep.getRoles().getRealm() != null) { // realm roles
for (RoleRepresentation roleRep : rep.getRoles().getRealm()) {
- createRole(newRealm, roleRep);
+ RoleModel role = newRealm.getRole(roleRep.getName());
+ addComposites(role, roleRep, newRealm);
}
}
if (rep.getRoles().getApplication() != null) {
@@ -279,9 +274,8 @@ public class RealmManager {
throw new RuntimeException("App doesn't exist in role definitions: " + entry.getKey());
}
for (RoleRepresentation roleRep : entry.getValue()) {
- RoleModel role = app.addRole(roleRep.getName());
- role.setDescription(roleRep.getDescription());
- role.setComposite(roleRep.isComposite());
+ RoleModel role = app.getRole(roleRep.getName());
+ addComposites(role, roleRep, newRealm);
}
}
}
@@ -294,27 +288,6 @@ public class RealmManager {
}
}
- if (rep.getRoles() != null) {
- if (rep.getRoles().getRealm() != null) { // realm roles
- for (RoleRepresentation roleRep : rep.getRoles().getRealm()) {
- RoleModel role = newRealm.getRole(roleRep.getName());
- addComposites(role, roleRep, newRealm);
- }
- }
- if (rep.getRoles().getApplication() != null) {
- for (Map.Entry<String, List<RoleRepresentation>> entry : rep.getRoles().getApplication().entrySet()) {
- ApplicationModel app = newRealm.getApplicationByName(entry.getKey());
- if (app == null) {
- throw new RuntimeException("App doesn't exist in role definitions: " + entry.getKey());
- }
- for (RoleRepresentation roleRep : entry.getValue()) {
- RoleModel role = app.getRole(roleRep.getName());
- addComposites(role, roleRep, newRealm);
- }
- }
- }
- }
-
if (rep.getOauthClients() != null) {
Map<String, OAuthClientModel> oauthMap = createOAuthClients(rep, newRealm);
for (OAuthClientModel app : oauthMap.values()) {
@@ -427,6 +400,7 @@ public class RealmManager {
public void createRole(RealmModel newRealm, RoleRepresentation roleRep) {
RoleModel role = newRealm.addRole(roleRep.getName());
if (roleRep.getDescription() != null) role.setDescription(roleRep.getDescription());
+ role.setComposite(roleRep.isComposite());
}
public void createRole(RealmModel newRealm, ApplicationModel app, RoleRepresentation roleRep) {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeImportRoleTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeImportRoleTest.java
new file mode 100755
index 0000000..ddd15ed
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeImportRoleTest.java
@@ -0,0 +1,200 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite.composites;
+
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.representations.SkeletonKeyToken;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.services.managers.ApplicationManager;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.testsuite.ApplicationServlet;
+import org.keycloak.testsuite.OAuthClient;
+import org.keycloak.testsuite.OAuthClient.AccessTokenResponse;
+import org.keycloak.testsuite.pages.LoginPage;
+import org.keycloak.testsuite.rule.AbstractKeycloakRule;
+import org.keycloak.testsuite.rule.WebResource;
+import org.keycloak.testsuite.rule.WebRule;
+import org.openqa.selenium.WebDriver;
+
+import java.security.PublicKey;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class CompositeImportRoleTest {
+
+ public static PublicKey realmPublicKey;
+ @ClassRule
+ public static AbstractKeycloakRule keycloakRule = new AbstractKeycloakRule(){
+ @Override
+ protected void configure(RealmManager manager, RealmModel adminRealm) {
+ server.importRealm(getClass().getResourceAsStream("/testcomposite.json"));
+ RealmModel realm = manager.getRealmByName("Test");
+ realmPublicKey = realm.getPublicKey();
+
+
+
+ deployServlet("app", "/app", ApplicationServlet.class);
+
+ }
+ };
+
+ @Rule
+ public WebRule webRule = new WebRule(this);
+
+ @WebResource
+ protected WebDriver driver;
+
+ @WebResource
+ protected OAuthClient oauth;
+
+ @WebResource
+ protected LoginPage loginPage;
+
+ @Test
+ public void testAppCompositeUser() throws Exception {
+ oauth.realm("Test");
+ oauth.realmPublicKey(realmPublicKey);
+ oauth.clientId("APP_COMPOSITE_APPLICATION");
+ oauth.doLogin("APP_COMPOSITE_USER", "password");
+
+ String code = oauth.getCurrentQuery().get("code");
+ AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
+
+ Assert.assertEquals(200, response.getStatusCode());
+
+ Assert.assertEquals("bearer", response.getTokenType());
+
+ SkeletonKeyToken token = oauth.verifyToken(response.getAccessToken());
+
+ Assert.assertEquals("APP_COMPOSITE_USER", token.getSubject());
+
+ Assert.assertEquals(1, token.getResourceAccess("APP_ROLE_APPLICATION").getRoles().size());
+ Assert.assertEquals(1, token.getRealmAccess().getRoles().size());
+ Assert.assertTrue(token.getResourceAccess("APP_ROLE_APPLICATION").isUserInRole("APP_ROLE_1"));
+ Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_ROLE_1"));
+ }
+
+
+ @Test
+ public void testRealmAppCompositeUser() throws Exception {
+ oauth.realm("Test");
+ oauth.realmPublicKey(realmPublicKey);
+ oauth.clientId("APP_ROLE_APPLICATION");
+ oauth.doLogin("REALM_APP_COMPOSITE_USER", "password");
+
+ String code = oauth.getCurrentQuery().get("code");
+ AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
+
+ Assert.assertEquals(200, response.getStatusCode());
+
+ Assert.assertEquals("bearer", response.getTokenType());
+
+ SkeletonKeyToken token = oauth.verifyToken(response.getAccessToken());
+
+ Assert.assertEquals("REALM_APP_COMPOSITE_USER", token.getSubject());
+
+ Assert.assertEquals(1, token.getResourceAccess("APP_ROLE_APPLICATION").getRoles().size());
+ Assert.assertTrue(token.getResourceAccess("APP_ROLE_APPLICATION").isUserInRole("APP_ROLE_1"));
+ }
+
+
+
+ @Test
+ public void testRealmOnlyWithUserCompositeAppComposite() throws Exception {
+ oauth.realm("Test");
+ oauth.realmPublicKey(realmPublicKey);
+ oauth.clientId("REALM_COMPOSITE_1_APPLICATION");
+ oauth.doLogin("REALM_COMPOSITE_1_USER", "password");
+
+ String code = oauth.getCurrentQuery().get("code");
+ AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
+
+ Assert.assertEquals(200, response.getStatusCode());
+
+ Assert.assertEquals("bearer", response.getTokenType());
+
+ SkeletonKeyToken token = oauth.verifyToken(response.getAccessToken());
+
+ Assert.assertEquals("REALM_COMPOSITE_1_USER", token.getSubject());
+
+ Assert.assertEquals(2, token.getRealmAccess().getRoles().size());
+ Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_COMPOSITE_1"));
+ Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_ROLE_1"));
+ }
+
+ @Test
+ public void testRealmOnlyWithUserCompositeAppRole() throws Exception {
+ oauth.realm("Test");
+ oauth.realmPublicKey(realmPublicKey);
+ oauth.clientId("REALM_ROLE_1_APPLICATION");
+ oauth.doLogin("REALM_COMPOSITE_1_USER", "password");
+
+ String code = oauth.getCurrentQuery().get("code");
+ AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
+
+ Assert.assertEquals(200, response.getStatusCode());
+
+ Assert.assertEquals("bearer", response.getTokenType());
+
+ SkeletonKeyToken token = oauth.verifyToken(response.getAccessToken());
+
+ Assert.assertEquals("REALM_COMPOSITE_1_USER", token.getSubject());
+
+ Assert.assertEquals(1, token.getRealmAccess().getRoles().size());
+ Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_ROLE_1"));
+ }
+
+ @Test
+ public void testRealmOnlyWithUserRoleAppComposite() throws Exception {
+ oauth.realm("Test");
+ oauth.realmPublicKey(realmPublicKey);
+ oauth.clientId("REALM_COMPOSITE_1_APPLICATION");
+ oauth.doLogin("REALM_ROLE_1_USER", "password");
+
+ String code = oauth.getCurrentQuery().get("code");
+ AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
+
+ Assert.assertEquals(200, response.getStatusCode());
+
+ Assert.assertEquals("bearer", response.getTokenType());
+
+ SkeletonKeyToken token = oauth.verifyToken(response.getAccessToken());
+
+ Assert.assertEquals("REALM_ROLE_1_USER", token.getSubject());
+
+ Assert.assertEquals(1, token.getRealmAccess().getRoles().size());
+ Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_ROLE_1"));
+ }
+
+
+
+
+}
diff --git a/testsuite/integration/src/test/resources/testcomposite.json b/testsuite/integration/src/test/resources/testcomposite.json
new file mode 100755
index 0000000..2b6a48a
--- /dev/null
+++ b/testsuite/integration/src/test/resources/testcomposite.json
@@ -0,0 +1,234 @@
+{
+ "id": "Test",
+ "realm": "Test",
+ "enabled": true,
+ "tokenLifespan": 600,
+ "accessCodeLifespan": 600,
+ "accessCodeLifespanUserAction": 600,
+ "sslNotRequired": true,
+ "registrationAllowed": true,
+ "resetPasswordAllowed": true,
+ "requiredCredentials": [ "password" ],
+ "requiredApplicationCredentials": [ "password" ],
+ "requiredOAuthClientCredentials": [ "password" ],
+ "smtpServer": {
+ "from": "auto@keycloak.org",
+ "host": "localhost",
+ "port":"3025"
+ },
+ "users" : [
+ {
+ "username" : "REALM_COMPOSITE_1_USER",
+ "enabled": true,
+ "email" : "test-user@localhost",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "password" }
+ ]
+ },
+ {
+ "username" : "REALM_ROLE_1_USER",
+ "enabled": true,
+ "email" : "test-user@localhost",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "password" }
+ ]
+ },
+ {
+ "username" : "REALM_APP_COMPOSITE_USER",
+ "enabled": true,
+ "email" : "test-user@localhost",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "password" }
+ ]
+ },
+ {
+ "username" : "REALM_APP_ROLE_USER",
+ "enabled": true,
+ "email" : "test-user@localhost",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "password" }
+ ]
+ },
+ {
+ "username" : "APP_COMPOSITE_USER",
+ "enabled": true,
+ "email" : "test-user@localhost",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "password" }
+ ]
+ }
+ ],
+ "oauthClients" : [
+ {
+ "name" : "third-party",
+ "enabled": true,
+ "credentials" : [
+ { "type" : "password",
+ "value" : "password" }
+ ]
+ }
+ ],
+ "roleMappings": [
+ {
+ "username": "REALM_COMPOSITE_1_USER",
+ "roles": ["REALM_COMPOSITE_1"]
+ },
+ {
+ "username": "REALM_ROLE_1_USER",
+ "roles": ["REALM_ROLE_1"]
+ },
+ {
+ "username": "REALM_APP_COMPOSITE_USER",
+ "roles": ["REALM_APP_COMPOSITE_ROLE"]
+ },
+ {
+ "username": "APP_COMPOSITE_USER",
+ "roles": ["REALM_APP_COMPOSITE_ROLE", "REALM_COMPOSITE_1"]
+ }
+ ],
+ "scopeMappings": [
+ {
+ "username": "REALM_COMPOSITE_1_APPLICATION",
+ "roles": ["REALM_COMPOSITE_1"]
+ },
+ {
+ "username": "REALM_ROLE_1_APPLICATION",
+ "roles": ["REALM_ROLE_1"]
+ }
+ ],
+ "applications": [
+ {
+ "name": "REALM_COMPOSITE_1_APPLICATION",
+ "enabled": true,
+ "baseUrl": "http://localhost:8081/app",
+ "adminUrl": "http://localhost:8081/app/logout",
+ "credentials": [
+ {
+ "type": "password",
+ "value": "password"
+ }
+ ]
+ },
+ {
+ "name": "REALM_ROLE_1_APPLICATION",
+ "enabled": true,
+ "baseUrl": "http://localhost:8081/app",
+ "adminUrl": "http://localhost:8081/app/logout",
+ "credentials": [
+ {
+ "type": "password",
+ "value": "password"
+ }
+ ]
+ },
+ {
+ "name": "APP_ROLE_APPLICATION",
+ "enabled": true,
+ "baseUrl": "http://localhost:8081/app",
+ "adminUrl": "http://localhost:8081/app/logout",
+ "credentials": [
+ {
+ "type": "password",
+ "value": "password"
+ }
+ ]
+ },
+ {
+ "name": "APP_COMPOSITE_APPLICATION",
+ "enabled": true,
+ "baseUrl": "http://localhost:8081/app",
+ "adminUrl": "http://localhost:8081/app/logout",
+ "credentials": [
+ {
+ "type": "password",
+ "value": "password"
+ }
+ ]
+ }
+ ],
+ "roles" : {
+ "realm" : [
+ {
+ "name": "REALM_ROLE_1"
+ },
+ {
+ "name": "REALM_ROLE_2"
+ },
+ {
+ "name": "REALM_ROLE_3"
+ },
+ {
+ "name": "REALM_COMPOSITE_1",
+ "composite": true,
+ "composites": {
+ "realm": ["REALM_ROLE_1"]
+ }
+ },
+ {
+ "name": "REALM_APP_COMPOSITE_ROLE",
+ "composite": true,
+ "composites": {
+ "application": {
+ "APP_ROLE_APPLICATION" :[
+ "APP_ROLE_1"
+ ]
+ }
+ }
+ }
+ ],
+ "application" : {
+ "APP_ROLE_APPLICATION" : [
+ {
+ "name": "APP_ROLE_1"
+ },
+ {
+ "name": "APP_ROLE_2"
+ }
+ ],
+ "APP_COMPOSITE_APPLICATION" : [
+ {
+ "name": "APP_COMPOSITE_ROLE",
+ "composite": true,
+ "composites": {
+ "realm" : [
+ "REALM_ROLE_1",
+ "REALM_ROLE_2",
+ "REALM_ROLE_3"
+ ],
+ "application": {
+ "APP_ROLE_APPLICATION" :[
+ "APP_ROLE_1"
+ ]
+ }
+ }
+ },
+ {
+ "name": "APP_ROLE_2"
+ }
+ ]
+ }
+
+ },
+
+ "applicationRoleMappings": {
+ "APP_ROLE_APPLICATION": [
+ {
+ "username": "REALM_APP_ROLE_USER",
+ "roles": ["APP_ROLE_2"]
+ }
+ ]
+ },
+ "applicationScopeMappings": {
+ "APP_ROLE_APPLICATION": [
+ {
+ "username": "APP_COMPOSITE_APPLICATION",
+ "roles": ["APP_ROLE_2"]
+ }
+ ]
+ }
+}
\ No newline at end of file