From 223041bc6b0c29fbe82fc62e7da018a52104835e Mon Sep 17 00:00:00 2001 From: Hynek Mlnarik Date: Mon, 14 Nov 2016 13:57:57 +0100 Subject: [PATCH 1/3] KEYCLOAK-3734 Unify master changelog of DB2 and other databases --- .../LiquibaseJpaUpdaterProvider.java | 3 +- .../DefaultLiquibaseConnectionProvider.java | 5 +-- .../META-INF/db2-jpa-changelog-master.xml | 40 ------------------- ....xml => jpa-changelog-1.0.0.Final-db2.xml} | 6 ++- .../META-INF/jpa-changelog-1.0.0.Final.xml | 6 +++ ....xml => jpa-changelog-1.2.0.Beta1-db2.xml} | 6 ++- .../META-INF/jpa-changelog-1.2.0.Beta1.xml | 6 +++ ...R1.xml => jpa-changelog-1.2.0.CR1-db2.xml} | 6 ++- .../META-INF/jpa-changelog-1.2.0.CR1.xml | 6 +++ ...-1.4.0.xml => jpa-changelog-1.4.0-db2.xml} | 6 ++- .../META-INF/jpa-changelog-1.4.0.xml | 6 +++ ...-1.8.0.xml => jpa-changelog-1.8.0-db2.xml} | 9 ++++- .../META-INF/jpa-changelog-1.8.0.xml | 11 +++++ ...-1.9.1.xml => jpa-changelog-1.9.1-db2.xml} | 6 ++- .../META-INF/jpa-changelog-1.9.1.xml | 6 +++ .../META-INF/jpa-changelog-master.xml | 6 +++ 16 files changed, 83 insertions(+), 51 deletions(-) delete mode 100644 model/jpa/src/main/resources/META-INF/db2-jpa-changelog-master.xml rename model/jpa/src/main/resources/META-INF/{db2-jpa-changelog-1.0.0.Final.xml => jpa-changelog-1.0.0.Final-db2.xml} (98%) rename model/jpa/src/main/resources/META-INF/{db2-jpa-changelog-1.2.0.Beta1.xml => jpa-changelog-1.2.0.Beta1-db2.xml} (96%) rename model/jpa/src/main/resources/META-INF/{db2-jpa-changelog-1.2.0.CR1.xml => jpa-changelog-1.2.0.CR1-db2.xml} (96%) rename model/jpa/src/main/resources/META-INF/{db2-jpa-changelog-1.4.0.xml => jpa-changelog-1.4.0-db2.xml} (94%) rename model/jpa/src/main/resources/META-INF/{db2-jpa-changelog-1.8.0.xml => jpa-changelog-1.8.0-db2.xml} (93%) rename model/jpa/src/main/resources/META-INF/{db2-jpa-changelog-1.9.1.xml => jpa-changelog-1.9.1-db2.xml} (75%) diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java index 48c41fddf2..8e50205fd6 100755 --- a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java +++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java @@ -55,7 +55,6 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider { private static final Logger logger = Logger.getLogger(LiquibaseJpaUpdaterProvider.class); public static final String CHANGELOG = "META-INF/jpa-changelog-master.xml"; - public static final String DB2_CHANGELOG = "META-INF/db2-jpa-changelog-master.xml"; private final KeycloakSession session; @@ -99,7 +98,7 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider { updateChangeSet(liquibase, liquibase.getChangeLogFile(), exportWriter); } } - } catch (Exception e) { + } catch (LiquibaseException | IOException e) { throw new RuntimeException("Failed to update database", e); } finally { ThreadLocalSessionContext.removeCurrentSession(); diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/conn/DefaultLiquibaseConnectionProvider.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/conn/DefaultLiquibaseConnectionProvider.java index 3bd2643d78..438d76b0f7 100644 --- a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/conn/DefaultLiquibaseConnectionProvider.java +++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/conn/DefaultLiquibaseConnectionProvider.java @@ -22,7 +22,6 @@ import liquibase.changelog.ChangeSet; import liquibase.changelog.DatabaseChangeLog; import liquibase.database.Database; import liquibase.database.DatabaseFactory; -import liquibase.database.core.DB2Database; import liquibase.database.jvm.JdbcConnection; import liquibase.exception.LiquibaseException; import liquibase.logging.LogFactory; @@ -129,7 +128,7 @@ public class DefaultLiquibaseConnectionProvider implements LiquibaseConnectionPr database.setDefaultSchemaName(defaultSchema); } - String changelog = (database instanceof DB2Database) ? LiquibaseJpaUpdaterProvider.DB2_CHANGELOG : LiquibaseJpaUpdaterProvider.CHANGELOG; + String changelog = LiquibaseJpaUpdaterProvider.CHANGELOG; ResourceAccessor resourceAccessor = new ClassLoaderResourceAccessor(getClass().getClassLoader()); logger.debugf("Using changelog file %s and changelogTableName %s", changelog, database.getDatabaseChangeLogTableName()); @@ -154,7 +153,7 @@ public class DefaultLiquibaseConnectionProvider implements LiquibaseConnectionPr private static class LogWrapper extends LogFactory { - private liquibase.logging.Logger logger = new liquibase.logging.Logger() { + private static final liquibase.logging.Logger logger = new liquibase.logging.Logger() { @Override public void setName(String name) { } diff --git a/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-master.xml b/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-master.xml deleted file mode 100644 index cc4c9ff016..0000000000 --- a/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-master.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-1.0.0.Final.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-1.0.0.Final-db2.xml similarity index 98% rename from model/jpa/src/main/resources/META-INF/db2-jpa-changelog-1.0.0.Final.xml rename to model/jpa/src/main/resources/META-INF/jpa-changelog-1.0.0.Final-db2.xml index 6e1c8af731..b1817c1909 100644 --- a/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-1.0.0.Final.xml +++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-1.0.0.Final-db2.xml @@ -16,8 +16,12 @@ ~ limitations under the License. --> - + + + + + diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-1.0.0.Final.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-1.0.0.Final.xml index a113601ed8..7869f1e26a 100644 --- a/model/jpa/src/main/resources/META-INF/jpa-changelog-1.0.0.Final.xml +++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-1.0.0.Final.xml @@ -18,6 +18,12 @@ + + + + + + diff --git a/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-1.2.0.Beta1.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-1.2.0.Beta1-db2.xml similarity index 96% rename from model/jpa/src/main/resources/META-INF/db2-jpa-changelog-1.2.0.Beta1.xml rename to model/jpa/src/main/resources/META-INF/jpa-changelog-1.2.0.Beta1-db2.xml index a23c6b2912..41abd07729 100644 --- a/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-1.2.0.Beta1.xml +++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-1.2.0.Beta1-db2.xml @@ -16,8 +16,12 @@ ~ limitations under the License. --> - + + + + + diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-1.2.0.Beta1.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-1.2.0.Beta1.xml index de34d28a0e..4e072673c8 100755 --- a/model/jpa/src/main/resources/META-INF/jpa-changelog-1.2.0.Beta1.xml +++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-1.2.0.Beta1.xml @@ -18,6 +18,12 @@ + + + + + + diff --git a/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-1.2.0.CR1.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-1.2.0.CR1-db2.xml similarity index 96% rename from model/jpa/src/main/resources/META-INF/db2-jpa-changelog-1.2.0.CR1.xml rename to model/jpa/src/main/resources/META-INF/jpa-changelog-1.2.0.CR1-db2.xml index edb8ca7248..49e647c6f5 100644 --- a/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-1.2.0.CR1.xml +++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-1.2.0.CR1-db2.xml @@ -16,8 +16,12 @@ ~ limitations under the License. --> - + + + + + diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-1.2.0.CR1.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-1.2.0.CR1.xml index 3a7c6baa9f..5c9b676ac1 100755 --- a/model/jpa/src/main/resources/META-INF/jpa-changelog-1.2.0.CR1.xml +++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-1.2.0.CR1.xml @@ -18,6 +18,12 @@ + + + + + + diff --git a/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-1.4.0.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-1.4.0-db2.xml similarity index 94% rename from model/jpa/src/main/resources/META-INF/db2-jpa-changelog-1.4.0.xml rename to model/jpa/src/main/resources/META-INF/jpa-changelog-1.4.0-db2.xml index 498810c3a2..b16fbbedf4 100644 --- a/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-1.4.0.xml +++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-1.4.0-db2.xml @@ -16,8 +16,12 @@ ~ limitations under the License. --> - + + + + + diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-1.4.0.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-1.4.0.xml index 1b47050117..a8bfc97e6a 100755 --- a/model/jpa/src/main/resources/META-INF/jpa-changelog-1.4.0.xml +++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-1.4.0.xml @@ -18,6 +18,12 @@ + + + + + + diff --git a/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-1.8.0.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-1.8.0-db2.xml similarity index 93% rename from model/jpa/src/main/resources/META-INF/db2-jpa-changelog-1.8.0.xml rename to model/jpa/src/main/resources/META-INF/jpa-changelog-1.8.0-db2.xml index dfbf38b9c9..f43b8a4fa8 100644 --- a/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-1.8.0.xml +++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-1.8.0-db2.xml @@ -16,8 +16,11 @@ ~ limitations under the License. --> - + + + + @@ -134,6 +137,10 @@ + + + + diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-1.8.0.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-1.8.0.xml index 79ba2fc3d1..5ebd8d40f7 100755 --- a/model/jpa/src/main/resources/META-INF/jpa-changelog-1.8.0.xml +++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-1.8.0.xml @@ -18,6 +18,11 @@ + + + + + @@ -130,6 +135,12 @@ + + + + + + diff --git a/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-1.9.1.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-1.9.1-db2.xml similarity index 75% rename from model/jpa/src/main/resources/META-INF/db2-jpa-changelog-1.9.1.xml rename to model/jpa/src/main/resources/META-INF/jpa-changelog-1.9.1-db2.xml index d0b387dab2..42558a7063 100644 --- a/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-1.9.1.xml +++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-1.9.1-db2.xml @@ -16,8 +16,12 @@ ~ limitations under the License. --> - + + + + + diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-1.9.1.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-1.9.1.xml index f801ddc19e..c083bc9a2b 100755 --- a/model/jpa/src/main/resources/META-INF/jpa-changelog-1.9.1.xml +++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-1.9.1.xml @@ -18,6 +18,12 @@ + + + + + + diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml index 8990fc438c..dc16e0d351 100755 --- a/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml +++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml @@ -18,19 +18,25 @@ + + + + + + From b612415a882055dc4530122181aa76e68bacd83a Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 14 Nov 2016 13:34:35 -0200 Subject: [PATCH 2/3] [KEYCLOAK-3900] - SSSD Provider: NullPointerException when SSSD is stopped --- .../federation/sssd/api/SSSDException.java | 17 +++++++++++++++++ .../org/keycloak/federation/sssd/api/Sssd.java | 6 +++--- 2 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 federation/sssd/src/main/java/org/keycloak/federation/sssd/api/SSSDException.java diff --git a/federation/sssd/src/main/java/org/keycloak/federation/sssd/api/SSSDException.java b/federation/sssd/src/main/java/org/keycloak/federation/sssd/api/SSSDException.java new file mode 100644 index 0000000000..c03c9263a4 --- /dev/null +++ b/federation/sssd/src/main/java/org/keycloak/federation/sssd/api/SSSDException.java @@ -0,0 +1,17 @@ +package org.keycloak.federation.sssd.api; + +/** + * @author Bruno Oliveira + */ +public class SSSDException extends RuntimeException { + public SSSDException() { + } + + public SSSDException(String message) { + super(message); + } + + public SSSDException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/federation/sssd/src/main/java/org/keycloak/federation/sssd/api/Sssd.java b/federation/sssd/src/main/java/org/keycloak/federation/sssd/api/Sssd.java index 5d62ce4ba7..9cfee78daa 100644 --- a/federation/sssd/src/main/java/org/keycloak/federation/sssd/api/Sssd.java +++ b/federation/sssd/src/main/java/org/keycloak/federation/sssd/api/Sssd.java @@ -94,19 +94,19 @@ public class Sssd { InfoPipe infoPipe = infopipe(); attributes = infoPipe.getUserAttributes(username, Arrays.asList(attr)); } catch (Exception e) { - logger.error("Failed to retrieve user's attributes from SSSD", e); + throw new SSSDException("Failed to retrieve user's attributes. Check if SSSD service is active."); } return attributes; } public List getUserGroups() { - List userGroups = null; + List userGroups; try { InfoPipe infoPipe = Sssd.infopipe(); userGroups = infoPipe.getUserGroups(username); } catch (Exception e) { - logger.error("Failed to retrieve user's groups from SSSD", e); + throw new SSSDException("Failed to retrieve user's groups from SSSD. Check if SSSD service is active."); } return userGroups; } From fb1cd9d27d075589b893848c9107d9723e108c5d Mon Sep 17 00:00:00 2001 From: Pedro Igor Date: Mon, 14 Nov 2016 18:55:53 +0000 Subject: [PATCH 3/3] [KEYCLOAK-3554] - Properly handle dependencies between policies when importing settings --- .../models/utils/RepresentationToModel.java | 85 ++++++-- .../ImportAuthorizationSettingsTest.java | 60 ++++++ ...port-authorization-unordered-settings.json | 183 ++++++++++++++++++ 3 files changed, 314 insertions(+), 14 deletions(-) create mode 100644 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ImportAuthorizationSettingsTest.java create mode 100644 testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/import-authorization-unordered-settings.json diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java index c2e654efac..1261a41631 100755 --- a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java +++ b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java @@ -31,10 +31,7 @@ import org.keycloak.authorization.store.ScopeStore; import org.keycloak.authorization.store.StoreFactory; import org.keycloak.common.enums.SslRequired; import org.keycloak.common.util.Base64; -import org.keycloak.common.util.CertificateUtils; -import org.keycloak.common.util.KeyUtils; import org.keycloak.common.util.MultivaluedHashMap; -import org.keycloak.common.util.PemUtils; import org.keycloak.common.util.UriUtils; import org.keycloak.component.ComponentModel; import org.keycloak.credential.CredentialModel; @@ -105,11 +102,7 @@ import org.keycloak.storage.federated.UserFederatedStorageProvider; import org.keycloak.util.JsonSerialization; import java.io.IOException; -import java.security.KeyPair; -import java.security.cert.Certificate; -import java.security.cert.X509Certificate; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -1866,7 +1859,18 @@ public class RepresentationToModel { toModel(resourceRepresentation, resourceServer, authorization); }); - rep.getPolicies().forEach(policyRepresentation -> { + importPolicies(authorization, resourceServer, rep.getPolicies(), null); + } + + private static Policy importPolicies(AuthorizationProvider authorization, ResourceServer resourceServer, List policiesToImport, String parentPolicyName) { + StoreFactory storeFactory = authorization.getStoreFactory(); + KeycloakSession session = authorization.getKeycloakSession(); + RealmModel realm = authorization.getRealm(); + for (PolicyRepresentation policyRepresentation : policiesToImport) { + if (parentPolicyName != null && !parentPolicyName.equals(policyRepresentation.getName())) { + continue; + } + Map config = policyRepresentation.getConfig(); String roles = config.get("roles"); @@ -1899,6 +1903,16 @@ public class RepresentationToModel { .findFirst().orElse(null); } + if (role == null) { + role = realm.getRoleById(roleName); + + if (role == null) { + String finalRoleName1 = roleName; + role = realm.getClients().stream().map(clientModel -> clientModel.getRole(finalRoleName1)).filter(roleModel -> roleModel != null) + .findFirst().orElse(null); + } + } + if (role == null) { throw new RuntimeException("Error while importing configuration. Role [" + roleName + "] could not be found."); } @@ -1916,7 +1930,19 @@ public class RepresentationToModel { if (users != null && !users.isEmpty()) { try { List usersMap = JsonSerialization.readValue(users, List.class); - config.put("users", JsonSerialization.writeValueAsString(usersMap.stream().map(userName -> session.users().getUserByUsername(userName, realm).getId()).collect(Collectors.toList()))); + config.put("users", JsonSerialization.writeValueAsString(usersMap.stream().map(userId -> { + UserModel user = session.users().getUserByUsername(userId, realm); + + if (user == null) { + user = session.users().getUserById(userId, realm); + } + + if (user == null) { + throw new RuntimeException("Error while importing configuration. User [" + userId + "] could not be found."); + } + + return user.getId(); + }).collect(Collectors.toList()))); } catch (Exception e) { throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e); } @@ -1926,10 +1952,15 @@ public class RepresentationToModel { if (scopes != null && !scopes.isEmpty()) { try { + ScopeStore scopeStore = storeFactory.getScopeStore(); List scopesMap = JsonSerialization.readValue(scopes, List.class); config.put("scopes", JsonSerialization.writeValueAsString(scopesMap.stream().map(scopeName -> { Scope newScope = scopeStore.findByName(scopeName, resourceServer.getId()); + if (newScope == null) { + newScope = scopeStore.findById(scopeName); + } + if (newScope == null) { throw new RuntimeException("Scope with name [" + scopeName + "] not defined."); } @@ -1947,8 +1978,21 @@ public class RepresentationToModel { ResourceStore resourceStore = storeFactory.getResourceStore(); try { List resources = JsonSerialization.readValue(policyResources, List.class); - config.put("resources", JsonSerialization.writeValueAsString(resources.stream().map(resourceName -> { - return resourceStore.findByName(resourceName, resourceServer.getId()).getId(); + config.put("resources", JsonSerialization.writeValueAsString(resources.stream().map(new Function() { + @Override + public String apply(String resourceName) { + Resource resource = resourceStore.findByName(resourceName, resourceServer.getId()); + + if (resource == null) { + resource = resourceStore.findById(resourceName); + } + + if (resource == null) { + throw new RuntimeException("Resource with name [" + resourceName + "] not defined."); + } + + return resource.getId(); + } }).collect(Collectors.toList()))); } catch (Exception e) { throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e); @@ -1965,7 +2009,14 @@ public class RepresentationToModel { Policy policy = policyStore.findByName(policyName, resourceServer.getId()); if (policy == null) { - throw new RuntimeException("Policy with name [" + policyName + "] not defined."); + policy = policyStore.findById(policyName); + } + + if (policy == null) { + policy = importPolicies(authorization, resourceServer, policiesToImport, policyName); + if (policy == null) { + throw new RuntimeException("Policy with name [" + policyName + "] not defined."); + } } return policy.getId(); @@ -1975,8 +2026,14 @@ public class RepresentationToModel { } } - toModel(policyRepresentation, resourceServer, authorization); - }); + if (parentPolicyName == null) { + toModel(policyRepresentation, resourceServer, authorization); + } else if (parentPolicyName.equals(policyRepresentation.getName())) { + return toModel(policyRepresentation, resourceServer, authorization); + } + } + + return null; } public static Policy toModel(PolicyRepresentation policy, ResourceServer resourceServer, AuthorizationProvider authorization) { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ImportAuthorizationSettingsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ImportAuthorizationSettingsTest.java new file mode 100644 index 0000000000..2922a3ef2c --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ImportAuthorizationSettingsTest.java @@ -0,0 +1,60 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.admin.client.authorization; + +import static com.sun.corba.se.impl.oa.poa.Policies.defaultPolicies; +import static org.junit.Assert.assertEquals; + +import java.util.List; + +import org.junit.Test; +import org.keycloak.admin.client.resource.AuthorizationResource; +import org.keycloak.admin.client.resource.ClientResource; +import org.keycloak.representations.adapters.config.PolicyEnforcerConfig; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.idm.RoleRepresentation; +import org.keycloak.representations.idm.authorization.PolicyRepresentation; +import org.keycloak.representations.idm.authorization.ResourceRepresentation; +import org.keycloak.representations.idm.authorization.ResourceServerRepresentation; +import org.keycloak.util.JsonSerialization; + +/** + * + * @author Pedro Igor + */ +public class ImportAuthorizationSettingsTest extends AbstractAuthorizationTest { + + @Test + public void testImportUnorderedSettings() throws Exception { + ClientResource clientResource = getClientResource(); + + enableAuthorizationServices(); + + ResourceServerRepresentation toImport = JsonSerialization.readValue(getClass().getResourceAsStream("/authorization-test/import-authorization-unordered-settings.json"), ResourceServerRepresentation.class); + + realmsResouce().realm(getRealmId()).roles().create(new RoleRepresentation("user", null, false)); + clientResource.roles().create(new RoleRepresentation("manage-albums", null, false)); + + AuthorizationResource authorizationResource = clientResource.authorization(); + + authorizationResource.importSettings(toImport); + + assertEquals(13, authorizationResource.policies().policies().size()); + } +} \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/import-authorization-unordered-settings.json b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/import-authorization-unordered-settings.json new file mode 100644 index 0000000000..4b30031a1a --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/import-authorization-unordered-settings.json @@ -0,0 +1,183 @@ +{ + "allowRemoteResourceManagement": true, + "policyEnforcementMode": "ENFORCING", + "resources": [ + { + "name": "User Profile Resource", + "uri": "/profile", + "type": "http://photoz.com/profile", + "scopes": [ + { + "name": "urn:photoz.com:scopes:profile:view" + } + ] + }, + { + "name": "Album Resource", + "uri": "/album/*", + "type": "http://photoz.com/album", + "scopes": [ + { + "name": "urn:photoz.com:scopes:album:view" + }, + { + "name": "urn:photoz.com:scopes:album:delete" + }, + { + "name": "urn:photoz.com:scopes:album:create" + } + ] + }, + { + "name": "Admin Resources", + "uri": "/admin/*", + "type": "http://photoz.com/admin", + "scopes": [ + { + "name": "urn:photoz.com:scopes:album:admin:manage" + } + ] + } + ], + "policies": [ + { + "name": "View User Permission", + "description": "Defines who is allowed to view an user profile", + "type": "scope", + "logic": "POSITIVE", + "decisionStrategy": "UNANIMOUS", + "config": { + "applyPolicies": "[\"Only From @keycloak.org or Admin\"]", + "scopes": "[\"urn:photoz.com:scopes:profile:view\"]" + } + }, + { + "name": "Only Owner Policy", + "description": "Defines that only the resource owner is allowed to do something", + "type": "drools", + "logic": "POSITIVE", + "decisionStrategy": "UNANIMOUS", + "config": { + "mavenArtifactVersion": "${project.version}", + "mavenArtifactId": "photoz-authz-policy", + "sessionName": "MainOwnerSession", + "mavenArtifactGroupId": "org.keycloak", + "moduleName": "PhotozAuthzOwnerPolicy", + "scannerPeriod": "1", + "scannerPeriodUnit": "Hours" + } + }, + { + "name": "Any User Policy", + "description": "Defines that only users from well known clients are allowed to access", + "type": "role", + "logic": "POSITIVE", + "decisionStrategy": "UNANIMOUS", + "config": { + "roles": "[{\"id\":\"user\"},{\"id\":\"manage-albums\",\"required\":true}]" + } + }, + { + "name": "Only From a Specific Client Address", + "description": "Defines that only clients from a specific address can do something", + "type": "js", + "logic": "POSITIVE", + "decisionStrategy": "UNANIMOUS", + "config": { + "code": "var contextAttributes = $evaluation.getContext().getAttributes();\n\nif (contextAttributes.containsValue('kc.client.network.ip_address', '127.0.0.1')) {\n $evaluation.grant();\n}" + } + }, + { + "name": "Administration Policy", + "description": "Defines that only administrators from a specific network address can do something.", + "type": "aggregate", + "logic": "POSITIVE", + "decisionStrategy": "UNANIMOUS", + "config": { + "applyPolicies": "[\"Only From a Specific Client Address\",\"Any Admin Policy\"]" + } + }, + { + "name": "Only Owner and Administrators Policy", + "description": "Defines that only the resource owner and administrators can do something", + "type": "aggregate", + "logic": "POSITIVE", + "decisionStrategy": "AFFIRMATIVE", + "config": { + "applyPolicies": "[\"Administration Policy\",\"Only Owner Policy\"]" + } + }, + { + "name": "Album Resource Permission", + "description": "General policies that apply to all album resources.", + "type": "resource", + "logic": "POSITIVE", + "decisionStrategy": "AFFIRMATIVE", + "config": { + "defaultResourceType": "http://photoz.com/album", + "default": "true", + "applyPolicies": "[\"Any User Policy\",\"Administration Policy\"]" + } + }, + { + "name": "Admin Resource Permission", + "description": "General policy for any administrative resource.", + "type": "resource", + "logic": "POSITIVE", + "decisionStrategy": "UNANIMOUS", + "config": { + "defaultResourceType": "http://photoz.com/admin", + "default": "true", + "applyPolicies": "[\"Administration Policy\"]" + } + }, + { + "name": "Delete Album Permission", + "description": "A policy that only allows the owner to delete his albums.", + "type": "scope", + "logic": "POSITIVE", + "decisionStrategy": "UNANIMOUS", + "config": { + "applyPolicies": "[\"Only Owner and Administrators Policy\"]", + "scopes": "[\"urn:photoz.com:scopes:album:delete\"]" + } + }, + { + "name": "Any Admin Policy", + "description": "Defines that adminsitrators can do something", + "type": "role", + "logic": "POSITIVE", + "decisionStrategy": "UNANIMOUS", + "config": { + "roles": "[{\"id\":\"admin\",\"required\":true}]" + } + }, + { + "name": "Only From @keycloak.org or Admin", + "description": "Defines that only users from @keycloak.org", + "type": "js", + "logic": "POSITIVE", + "decisionStrategy": "UNANIMOUS", + "config": { + "code": "var context = $evaluation.getContext();\nvar identity = context.getIdentity();\nvar attributes = identity.getAttributes();\nvar email = attributes.getValue('email').asString(0);\n\nif (identity.hasRole('admin') || email.endsWith('@keycloak.org')) {\n $evaluation.grant();\n}" + } + } + ], + "scopes": [ + { + "name": "urn:photoz.com:scopes:profile:view" + }, + { + "name": "urn:photoz.com:scopes:album:view" + }, + { + "name": "urn:photoz.com:scopes:album:create" + }, + { + "name": "urn:photoz.com:scopes:album:delete" + }, + { + "name": "urn:photoz.com:scopes:album:admin:manage" + } + ] +} \ No newline at end of file