keycloak-memoizeit

Details

diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/JpaUpdate4_0_0_DefaultClientScopes.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/JpaUpdate4_0_0_DefaultClientScopes.java
new file mode 100644
index 0000000..3a53f4a
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/JpaUpdate4_0_0_DefaultClientScopes.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2018 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.connections.jpa.updater.liquibase.custom;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import liquibase.exception.CustomChangeException;
+import liquibase.statement.core.InsertStatement;
+import liquibase.structure.core.Table;
+
+/**
+ *
+ * @author hmlnarik
+ */
+public class JpaUpdate4_0_0_DefaultClientScopes extends CustomKeycloakTask {
+
+    @Override
+    protected void generateStatementsImpl() throws CustomChangeException {
+        String clientTableName = database.correctObjectName("CLIENT", Table.class);
+        String clientScopeClientTableName = database.correctObjectName("CLIENT_SCOPE_CLIENT", Table.class);
+
+        try (PreparedStatement statement = jdbcConnection.prepareStatement("SELECT ID, CLIENT_TEMPLATE_ID FROM " + clientTableName);
+          ResultSet rs = statement.executeQuery()) {
+            while (rs.next()) {
+                String clientId = rs.getString(1);
+                String clientTemplateId = rs.getString(2);
+
+                if (clientId == null || clientId.trim().isEmpty()) {
+                    continue;
+                }
+                if (clientTemplateId == null || clientTemplateId.trim().isEmpty()) {
+                    continue;
+                }
+
+                statements.add(
+                  new InsertStatement(null, null, clientScopeClientTableName)
+                    .addColumnValue("CLIENT_ID", clientId.trim())
+                    .addColumnValue("SCOPE_ID", clientTemplateId.trim())
+                    .addColumnValue("DEFAULT_SCOPE", Boolean.TRUE)
+                );
+            }
+
+            confirmationMessage.append("Updated " + statements.size() + " records in CLIENT_SCOPE_CLIENT table");
+        } catch (Exception e) {
+            throw new CustomChangeException(getTaskId() + ": Exception when updating data from previous version", e);
+        }
+    }
+
+    @Override
+    protected String getTaskId() {
+        return "Update 4.0.0.Final (Default client scopes)";
+    }
+
+}
diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-4.0.0.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-4.0.0.xml
index e34f2d3..727852f 100644
--- a/model/jpa/src/main/resources/META-INF/jpa-changelog-4.0.0.xml
+++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-4.0.0.xml
@@ -85,7 +85,12 @@
 
      </changeSet>
 
-    <changeSet author="mposolda@redhat.com" id="4.0.0-KEYCLOAK-5579">
+    <changeSet author="mposolda@redhat.com" id="4.0.0-KEYCLOAK-5579-fixed">
+        <preConditions onFail="MARK_RAN" onSqlOutput="TEST">
+            <not>
+                <changeSetExecuted id="4.0.0-KEYCLOAK-5579" author="mposolda@redhat.com" changeLogFile="META-INF/jpa-changelog-4.0.0.xml" />
+            </not>
+        </preConditions>
 
         <!-- 1 - Rename clientTemplate to clientScope and drop some unused things from clientTemplate -->
         <dropForeignKeyConstraint baseTableName="CLIENT_TEMPLATE_ATTRIBUTES" constraintName="FK_CL_TEMPL_ATTR_TEMPL" />
@@ -147,15 +152,6 @@
                                  constraintName="FK_CL_SCOPE_ATTR_SCOPE" referencedTableName="CLIENT_SCOPE" referencedColumnNames="ID" />
 
         <!-- 2 - Client binding to more clientScopes -->
-        <!-- Foreign key dropped above TODO:mposolda migration existing clientTemplate to default clientScope -->
-        <dropColumn tableName="CLIENT" columnName="CLIENT_TEMPLATE_ID" />
-        <dropDefaultValue tableName="CLIENT" columnName="USE_TEMPLATE_CONFIG"/>
-        <dropDefaultValue tableName="CLIENT" columnName="USE_TEMPLATE_SCOPE" />
-        <dropDefaultValue tableName="CLIENT" columnName="USE_TEMPLATE_MAPPERS" />
-        <dropColumn tableName="CLIENT" columnName="USE_TEMPLATE_CONFIG" />
-        <dropColumn tableName="CLIENT" columnName="USE_TEMPLATE_SCOPE" />
-        <dropColumn tableName="CLIENT" columnName="USE_TEMPLATE_MAPPERS" />
-
         <createTable tableName="CLIENT_SCOPE_CLIENT">
             <column name="CLIENT_ID" type="VARCHAR(36)">
                 <constraints nullable="false"/>
@@ -171,6 +167,16 @@
         <addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="CLIENT_SCOPE_CLIENT" constraintName="FK_C_CLI_SCOPE_CLIENT" referencedColumnNames="ID" referencedTableName="CLIENT"/>
         <addForeignKeyConstraint baseColumnNames="SCOPE_ID" baseTableName="CLIENT_SCOPE_CLIENT" constraintName="FK_C_CLI_SCOPE_SCOPE" referencedColumnNames="ID" referencedTableName="CLIENT_SCOPE"/>
 
+        <customChange class="org.keycloak.connections.jpa.updater.liquibase.custom.JpaUpdate4_0_0_DefaultClientScopes" />
+
+        <dropColumn tableName="CLIENT" columnName="CLIENT_TEMPLATE_ID" />
+        <dropDefaultValue tableName="CLIENT" columnName="USE_TEMPLATE_CONFIG"/>
+        <dropDefaultValue tableName="CLIENT" columnName="USE_TEMPLATE_SCOPE" />
+        <dropDefaultValue tableName="CLIENT" columnName="USE_TEMPLATE_MAPPERS" />
+        <dropColumn tableName="CLIENT" columnName="USE_TEMPLATE_CONFIG" />
+        <dropColumn tableName="CLIENT" columnName="USE_TEMPLATE_SCOPE" />
+        <dropColumn tableName="CLIENT" columnName="USE_TEMPLATE_MAPPERS" />
+
         <!-- Default client scopes (global scopes configured at realm level) -->
         <createTable tableName="DEFAULT_CLIENT_SCOPE">
             <column name="REALM_ID" type="VARCHAR(36)">
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportUtil.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportUtil.java
index eb27600..95c8120 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportUtil.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportUtil.java
@@ -66,10 +66,13 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
-import org.keycloak.common.Profile;
+import org.hamcrest.Matcher;
+import org.hamcrest.Matchers;
+import static org.junit.Assert.assertThat;
 
 /**
  *
@@ -635,21 +638,39 @@ public class ExportImportUtil {
         Assert.assertTrue(!source.stream().filter(object -> !predicate.stream().filter(predicate1 -> predicate1.test(object)).findFirst().isPresent()).findAny().isPresent());
     }
 
+    private static Matcher<Iterable<? super String>> getDefaultClientScopeNameMatcher(ClientRepresentation rep) {
+        switch (rep.getClientId()) {
+            case "client-with-template":
+                return Matchers.hasItem("Default_test_template");
+            default:
+                return Matchers.not(Matchers.hasItem("Default_test_template"));
+        }
+    }
+
+    public static void testClientDefaultClientScopes(RealmResource realm) {
+        for (ClientRepresentation rep : realm.clients().findAll(true)) {
+            Matcher<Iterable<? super String>> expectedDefaultClientScopeNames = getDefaultClientScopeNameMatcher(rep);
+
+            assertThat("Default client scopes for " + rep.getClientId(), rep.getDefaultClientScopes(), expectedDefaultClientScopeNames);
+        }
+    }
 
     public static void testRealmDefaultClientScopes(RealmResource realm) {
         // Assert built-in scopes were created in realm
         List<ClientScopeRepresentation> clientScopes = realm.clientScopes().findAll();
-        Map<String, ClientScopeRepresentation> clientScopesMap = clientScopes
-                .stream().collect(Collectors.toMap(clientScope -> clientScope.getName(), clientScope -> clientScope));
-
-        org.keycloak.testsuite.Assert.assertTrue(clientScopesMap.containsKey(OAuth2Constants.SCOPE_PROFILE));
-        org.keycloak.testsuite.Assert.assertTrue(clientScopesMap.containsKey(OAuth2Constants.SCOPE_EMAIL));
-        org.keycloak.testsuite.Assert.assertTrue(clientScopesMap.containsKey(OAuth2Constants.SCOPE_ADDRESS));
-        org.keycloak.testsuite.Assert.assertTrue(clientScopesMap.containsKey(OAuth2Constants.SCOPE_PHONE));
-        org.keycloak.testsuite.Assert.assertTrue(clientScopesMap.containsKey(OAuth2Constants.OFFLINE_ACCESS));
-        org.keycloak.testsuite.Assert.assertTrue(clientScopesMap.containsKey(OIDCLoginProtocolFactory.ROLES_SCOPE));
-        org.keycloak.testsuite.Assert.assertTrue(clientScopesMap.containsKey(OIDCLoginProtocolFactory.WEB_ORIGINS_SCOPE));
-        org.keycloak.testsuite.Assert.assertTrue(clientScopesMap.containsKey(SamlProtocolFactory.SCOPE_ROLE_LIST));
+        Map<String, ClientScopeRepresentation> clientScopesMap = clientScopes.stream()
+          .collect(Collectors.toMap(ClientScopeRepresentation::getName, Function.identity()));
+
+        assertThat(clientScopesMap.keySet(), Matchers.hasItems(
+          OAuth2Constants.SCOPE_PROFILE,
+          OAuth2Constants.SCOPE_EMAIL,
+          OAuth2Constants.SCOPE_ADDRESS,
+          OAuth2Constants.SCOPE_PHONE,
+          OAuth2Constants.OFFLINE_ACCESS,
+          OIDCLoginProtocolFactory.ROLES_SCOPE,
+          OIDCLoginProtocolFactory.WEB_ORIGINS_SCOPE,
+          SamlProtocolFactory.SCOPE_ROLE_LIST
+        ));
 
         // Check content of some client scopes
         Map<String, ProtocolMapperRepresentation> protocolMappers = clientScopesMap.get(OAuth2Constants.SCOPE_EMAIL).getProtocolMappers()
@@ -662,17 +683,21 @@ public class ExportImportUtil {
         org.keycloak.testsuite.Assert.assertNames(offlineRoleScopes, OAuth2Constants.OFFLINE_ACCESS);
 
         // Check default client scopes and optional client scopes expected
-        Map<String, ClientScopeRepresentation> defaultClientScopes = realm.getDefaultDefaultClientScopes()
-                .stream().collect(Collectors.toMap(clientScope -> clientScope.getName(), clientScope -> clientScope));
-        org.keycloak.testsuite.Assert.assertTrue(defaultClientScopes.containsKey(OAuth2Constants.SCOPE_PROFILE));
-        org.keycloak.testsuite.Assert.assertTrue(defaultClientScopes.containsKey(OAuth2Constants.SCOPE_EMAIL));
-        org.keycloak.testsuite.Assert.assertTrue(defaultClientScopes.containsKey(OIDCLoginProtocolFactory.ROLES_SCOPE));
-        org.keycloak.testsuite.Assert.assertTrue(defaultClientScopes.containsKey(OIDCLoginProtocolFactory.WEB_ORIGINS_SCOPE));
-
-        Map<String, ClientScopeRepresentation> optionalClientScopes = realm.getDefaultOptionalClientScopes()
-                .stream().collect(Collectors.toMap(clientScope -> clientScope.getName(), clientScope -> clientScope));
-        org.keycloak.testsuite.Assert.assertTrue(optionalClientScopes.containsKey(OAuth2Constants.SCOPE_ADDRESS));
-        org.keycloak.testsuite.Assert.assertTrue(optionalClientScopes.containsKey(OAuth2Constants.SCOPE_PHONE));
-        org.keycloak.testsuite.Assert.assertTrue(optionalClientScopes.containsKey(OAuth2Constants.OFFLINE_ACCESS));
+        Set<String> defaultClientScopes = realm.getDefaultDefaultClientScopes()
+                .stream().map(ClientScopeRepresentation::getName).collect(Collectors.toSet());
+        assertThat(defaultClientScopes, Matchers.hasItems(
+          OAuth2Constants.SCOPE_PROFILE,
+          OAuth2Constants.SCOPE_EMAIL,
+          OIDCLoginProtocolFactory.ROLES_SCOPE,
+          OIDCLoginProtocolFactory.WEB_ORIGINS_SCOPE
+        ));
+
+        Set<String> optionalClientScopes = realm.getDefaultOptionalClientScopes()
+                .stream().map(ClientScopeRepresentation::getName).collect(Collectors.toSet());
+        assertThat(optionalClientScopes, Matchers.hasItems(
+          OAuth2Constants.SCOPE_ADDRESS,
+          OAuth2Constants.SCOPE_PHONE,
+          OAuth2Constants.OFFLINE_ACCESS
+        ));
     }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/AbstractMigrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/AbstractMigrationTest.java
index 0b0b99d..a837683 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/AbstractMigrationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/AbstractMigrationTest.java
@@ -106,6 +106,7 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
 
         if (supportsAuthzService) {
             expectedClientIds.add("authz-servlet");
+            expectedClientIds.add("client-with-template");
         }
 
         assertNames(migrationRealm.clients().findAll(), expectedClientIds.toArray(new String[expectedClientIds.size()]));
@@ -211,6 +212,7 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
     protected void testMigrationTo4_0_0() {
         testRealmDefaultClientScopes(this.masterRealm);
         testRealmDefaultClientScopes(this.migrationRealm);
+        testClientDefaultClientScopes(this.migrationRealm);
         testOfflineScopeAddedToClient();
     }
 
@@ -493,6 +495,11 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
         ExportImportUtil.testRealmDefaultClientScopes(realm);
     }
 
+    private void testClientDefaultClientScopes(RealmResource realm) {
+        log.info("Testing default client scopes transferred from client scope in realm: " + realm.toRepresentation().getRealm());
+        ExportImportUtil.testClientDefaultClientScopes(realm);
+    }
+
     private void testOfflineScopeAddedToClient() {
         log.infof("Testing offline_access optional scope present in realm %s for client migration-test-client", migrationRealm.toRepresentation().getRealm());
 
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport255MigrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport255MigrationTest.java
index 61afd71..0b2b99b 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport255MigrationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport255MigrationTest.java
@@ -25,7 +25,6 @@ import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.testsuite.arquillian.DeploymentTargetModifier;
 import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
 import org.keycloak.testsuite.utils.io.IOUtil;
-import org.keycloak.testsuite.util.WaitUtils;
 import org.keycloak.util.JsonSerialization;
 
 import java.io.IOException;
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/migration-test/migration-realm-2.5.5.Final.json b/testsuite/integration-arquillian/tests/base/src/test/resources/migration-test/migration-realm-2.5.5.Final.json
index e67ee6a..bf34696 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/migration-test/migration-realm-2.5.5.Final.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/migration-test/migration-realm-2.5.5.Final.json
@@ -2108,7 +2108,15 @@
     }, {
       "client" : "security-admin-console",
       "roles" : [ "realm-admin" ]
-    } ]
+    } ],
+    "migration-test-client": [
+      {
+        "clientTemplate": "Default test template",
+        "roles": [
+          "migration-test-client-role"
+        ]
+      }
+    ]
   },
   "clients" : [ {
     "id" : "6f27b0c3-9fc0-4e04-b69a-2031349acf04",
@@ -2803,6 +2811,33 @@
     "useTemplateMappers" : false
   },
   {
+    "id": "26519045-3cd4-4f2a-974b-e1447907834a",
+    "clientId": "client-with-template",
+    "surrogateAuthRequired": false,
+    "enabled": true,
+    "clientAuthenticatorType": "client-secret",
+    "secret": "**********",
+    "redirectUris": [],
+    "webOrigins": [],
+    "notBefore": 0,
+    "bearerOnly": false,
+    "consentRequired": false,
+    "standardFlowEnabled": true,
+    "implicitFlowEnabled": false,
+    "directAccessGrantsEnabled": true,
+    "serviceAccountsEnabled": false,
+    "publicClient": true,
+    "frontchannelLogout": false,
+    "protocol": "openid-connect",
+    "attributes": {},
+    "fullScopeAllowed": false,
+    "nodeReRegistrationTimeout": -1,
+    "clientTemplate": "Default test template",
+    "useTemplateConfig": false,
+    "useTemplateScope": true,
+    "useTemplateMappers": true
+  },
+  {
     "id": "70e8e897-82d4-49ab-82c9-c37e1a48b6bb",
     "clientId": "authz-servlet",
     "adminUrl": "http://localhost:8080/authz-servlet",
@@ -2858,7 +2893,15 @@
       ]
     }
   }],
-  "clientTemplates" : [ ],
+  "clientTemplates": [
+    {
+      "id": "d43ae38d-80a1-471a-9a80-5f9a0d34d7a4",
+      "name": "Default test template",
+      "description": "Test client template",
+      "protocol": "openid-connect",
+      "fullScopeAllowed": false
+    }
+  ],
   "browserSecurityHeaders" : {
     "xContentTypeOptions" : "nosniff",
     "xFrameOptions" : "SAMEORIGIN",
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/migration-test/migration-realm-3.4.3.Final.json b/testsuite/integration-arquillian/tests/base/src/test/resources/migration-test/migration-realm-3.4.3.Final.json
index c104dbb..87e6073 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/migration-test/migration-realm-3.4.3.Final.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/migration-test/migration-realm-3.4.3.Final.json
@@ -313,6 +313,16 @@
     "notBefore" : 0,
     "groups" : [ ]
   } ],
+  "clientScopeMappings" : {
+    "migration-test-client": [
+      {
+        "clientTemplate": "Default test template",
+        "roles": [
+          "migration-test-client-role"
+        ]
+      }
+    ]
+  },
   "clients" : [ {
     "id" : "6b9ba4ca-fb7c-4e17-a3e6-88f3a17397cc",
     "clientId" : "account",
@@ -1011,6 +1021,33 @@
     "useTemplateMappers" : false
   },
   {
+    "id": "26519045-3cd4-4f2a-974b-e1447907834a",
+    "clientId": "client-with-template",
+    "surrogateAuthRequired": false,
+    "enabled": true,
+    "clientAuthenticatorType": "client-secret",
+    "secret": "**********",
+    "redirectUris": [],
+    "webOrigins": [],
+    "notBefore": 0,
+    "bearerOnly": false,
+    "consentRequired": false,
+    "standardFlowEnabled": true,
+    "implicitFlowEnabled": false,
+    "directAccessGrantsEnabled": true,
+    "serviceAccountsEnabled": false,
+    "publicClient": true,
+    "frontchannelLogout": false,
+    "protocol": "openid-connect",
+    "attributes": {},
+    "fullScopeAllowed": false,
+    "nodeReRegistrationTimeout": -1,
+    "clientTemplate": "Default test template",
+    "useTemplateConfig": false,
+    "useTemplateScope": true,
+    "useTemplateMappers": true
+  },
+  {
       "id": "70e8e897-82d4-49ab-82c9-c37e1a48b6bb",
       "clientId": "authz-servlet",
       "adminUrl": "http://localhost:8080/authz-servlet",
@@ -1066,7 +1103,15 @@
         ]
       }
     }],
-  "clientTemplates" : [ ],
+  "clientTemplates": [
+    {
+      "id": "d43ae38d-80a1-471a-9a80-5f9a0d34d7a4",
+      "name": "Default test template",
+      "description": "Test client template",
+      "protocol": "openid-connect",
+      "fullScopeAllowed": false
+    }
+  ],
   "browserSecurityHeaders" : {
     "xContentTypeOptions" : "nosniff",
     "xRobotsTag" : "none",