keycloak-memoizeit

Details

diff --git a/distribution/wildfly-adapter-zip/pom.xml b/distribution/wildfly-adapter-zip/pom.xml
index e78caf3..f1479b3 100755
--- a/distribution/wildfly-adapter-zip/pom.xml
+++ b/distribution/wildfly-adapter-zip/pom.xml
@@ -13,14 +13,6 @@
     <name>Keycloak Wildfly Adapter Distro</name>
     <description/>
 
-    <dependencies>
-        <dependency>
-            <groupId>org.keycloak</groupId>
-            <artifactId>keycloak-undertow-adapter</artifactId>
-            <version>${project.version}</version>
-            <type>zip</type>
-        </dependency>
-    </dependencies>
     <build>
         <plugins>
             <plugin>
diff --git a/docbook/reference/en/en-US/modules/export-import.xml b/docbook/reference/en/en-US/modules/export-import.xml
index 13da6cb..492a847 100755
--- a/docbook/reference/en/en-US/modules/export-import.xml
+++ b/docbook/reference/en/en-US/modules/export-import.xml
@@ -12,6 +12,13 @@
             <listitem>Directory on local filesystem</listitem>
             <listitem>Single JSON file on your filesystem</listitem>
         </itemizedlist>
+
+        When importing using the "dir" or "zip" strategies, note that the files need to follow the naming convention specified below.
+        If you are importing files which were previously exported, the files already follow this convention.
+        <itemizedlist>
+            <listitem>{REALM_NAME}-realm.json, such as "acme-roadrunner-affairs-realm.json" for the realm named "acme-roadrunner-affairs"</listitem>
+            <listitem>{REALM_NAME}-users-{INDEX}.json, such as "acme-roadrunner-affairs-users-0.json" for the first users file of the realm named "acme-roadrunner-affairs"</listitem>
+        </itemizedlist>
     </para>
     <para>
         Encrypted ZIP is recommended as export contains many sensitive informations like passwords of your users (even if they are hashed),
@@ -111,5 +118,14 @@ bin/standalone.sh -Dkeycloak.migration.action=import
             </varlistentry>
         </variablelist>
     </para>
+    <para>
+        When importing realm files that weren't exported before, the option <literal>keycloak.import</literal> can be used. If more than one realm
+        file needs to be imported, a comma separated list of file names can be specified. This is more appropriate than the cases before, as this
+        will happen only after the master realm has been initialized. Examples:
+        <itemizedlist>
+            <listitem>-Dkeycloak.import=/tmp/realm1.json</listitem>
+            <listitem>-Dkeycloak.import=/tmp/realm1.json,/tmp/realm2.json</listitem>
+        </itemizedlist>
+    </para>
 
 </chapter>
\ No newline at end of file
diff --git a/examples/demo-template/example-ear/pom.xml b/examples/demo-template/example-ear/pom.xml
index 1cd2488..470113f 100755
--- a/examples/demo-template/example-ear/pom.xml
+++ b/examples/demo-template/example-ear/pom.xml
@@ -60,6 +60,13 @@
                     </modules>
                 </configuration>
             </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
         </plugins>
     </build>
 </project>
diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportUtils.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
index 14e3a1e..c9ba3dd 100755
--- a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
+++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
@@ -45,7 +45,7 @@ import java.util.Set;
 public class ExportUtils {
 
     public static RealmRepresentation exportRealm(KeycloakSession session, RealmModel realm, boolean includeUsers) {
-        RealmRepresentation rep = ModelToRepresentation.toRepresentation(realm);
+        RealmRepresentation rep = ModelToRepresentation.toRepresentation(realm, true);
 
         // Audit
         rep.setEventsEnabled(realm.isEventsEnabled());
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-ldap.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-ldap.html
index debd4d7..d94f8ab 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-ldap.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-ldap.html
@@ -63,35 +63,37 @@
                     <span tooltip-placement="right" tooltip="Should newly created users be created within LDAP store?  Priority effects which provider is chose to sync the new user." class="fa fa-info-circle"></span>
                 </div>
                 <div class="form-group clearfix">
-                    <label class="col-sm-2 control-label" for="vendor">Vendor</label>
+                    <label class="col-sm-2 control-label" for="vendor">Vendor<span class="required">*</span></label>
                     <div class="col-sm-4">
                         <div class="select-kc">
                             <select id="vendor"
                                     ng-model="instance.config.vendor"
-                                    ng-options="vendor.id as vendor.name for vendor in ldapVendors">
+                                    ng-options="vendor.id as vendor.name for vendor in ldapVendors"
+                                    required>
                             </select>
                         </div>
                     </div>
                     <span tooltip-placement="right" tooltip="LDAP vendor (provider)" class="fa fa-info-circle"></span>
                 </div>
                 <div class="form-group clearfix">
-                    <label class="col-sm-2 control-label" for="usernameLDAPAttribute">Username LDAP attribute </label>
+                    <label class="col-sm-2 control-label" for="usernameLDAPAttribute">Username LDAP attribute<span class="required">*</span></label>
                     <div class="col-sm-4">
                         <div class="select-kc">
                             <select id="usernameLDAPAttribute"
                                     ng-model="instance.config.usernameLDAPAttribute"
-                                    ng-options="usernameLDAPAttribute for usernameLDAPAttribute in usernameLDAPAttributes">
+                                    ng-options="usernameLDAPAttribute for usernameLDAPAttribute in usernameLDAPAttributes"
+                                    required>
                             </select>
                         </div>
                     </div>
                     <span tooltip-placement="right" tooltip="Name of LDAP attribute, which is mapped as Keycloak username" class="fa fa-info-circle"></span>
                 </div>
                 <div class="form-group clearfix">
-                    <label class="col-sm-2 control-label" for="userObjectClasses">User Object Classes </label>
+                    <label class="col-sm-2 control-label" for="userObjectClasses">User Object Classes<span class="required">*</span></label>
                     <div class="col-sm-4">
-                        <input class="form-control" id="userObjectClasses" type="text" ng-model="instance.config.userObjectClasses" placeholder="LDAP User Object Classes (div. by comma)">
+                        <input class="form-control" id="userObjectClasses" type="text" ng-model="instance.config.userObjectClasses" placeholder="LDAP User Object Classes (div. by comma)" required>
                     </div>
-                    <span tooltip-placement="right" tooltip="All values of LDAP objectClass attribute divided by comma, which are used for newly created LDAP users" class="fa fa-info-circle"></span>
+                    <span tooltip-placement="right" tooltip="All values of LDAP objectClass attribute for users in LDAP divided by comma" class="fa fa-info-circle"></span>
                 </div>
                 <div class="form-group clearfix">
                     <label class="col-sm-2 control-label" for="ldapConnectionUrl">Connection URL<span class="required">*</span></label>
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-keys.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-keys.html
index 7da6bc6..9545c60 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-keys.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-keys.html
@@ -5,9 +5,6 @@
     <div id="content">
         <h2><span>{{realm.realm}}</span> Realm Public Key <span tooltip-placement="right" tooltip="Realm's public key.  This is used to verify any signed tokens or documents created by the realm." class="fa fa-info-circle"></span></h2>
         <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
-            <div class="pull-right form-actions" data-ng-show="access.manageRealm">
-                <button class="btn btn-primary btn-lg" type="submit" data-ng-click="generate()">Generate new keys</button>
-            </div>
             <fieldset class="border-top">
                 <div class="form-group">
                     <label class="col-sm-2 control-label" for="publicKey">Public key</label>
@@ -18,7 +15,7 @@
                     </div>
                 </div>
                 <div class="form-group">
-                    <label class="col-sm-2 control-label" for="publicKey">Certificate</label>
+                    <label class="col-sm-2 control-label" for="certificate">Certificate</label>
 
                     <div class="col-sm-10">
                         <textarea type="text" id="certificate" name="certificate" class="form-control" rows="5"
@@ -26,6 +23,9 @@
                     </div>
                 </div>
             </fieldset>
+            <div class="pull-right form-actions" data-ng-show="access.manageRealm">
+                <button class="btn btn-primary btn-lg" type="submit" data-ng-click="generate()">Generate new keys</button>
+            </div>
         </form>
     </div>
 </div>
\ No newline at end of file
diff --git a/model/api/src/main/java/org/keycloak/models/PasswordPolicy.java b/model/api/src/main/java/org/keycloak/models/PasswordPolicy.java
index bea471d..dc82bcc 100755
--- a/model/api/src/main/java/org/keycloak/models/PasswordPolicy.java
+++ b/model/api/src/main/java/org/keycloak/models/PasswordPolicy.java
@@ -132,7 +132,7 @@ public class PasswordPolicy {
                     count++;
                 }
             }
-            return count < min ? "Invalid password: must contain at least " + count + " numerical digits" : null;
+            return count < min ? "Invalid password: must contain at least " + min + " numerical digits" : null;
         }
     }
 
@@ -152,7 +152,7 @@ public class PasswordPolicy {
                     count++;
                 }
             }
-            return count < min ? "Invalid password: must contain at least " + count + " lower case characters": null;
+            return count < min ? "Invalid password: must contain at least " + min + " lower case characters": null;
         }
     }
 
@@ -172,7 +172,7 @@ public class PasswordPolicy {
                     count++;
                 }
             }
-            return count < min ? "Invalid password: must contain at least " + count + " upper case characters" : null;
+            return count < min ? "Invalid password: must contain at least " + min + " upper case characters" : null;
         }
     }
 
@@ -192,7 +192,7 @@ public class PasswordPolicy {
                     count++;
                 }
             }
-            return count < min ? "Invalid password: must contain at least " + count + " special characters" : null;
+            return count < min ? "Invalid password: must contain at least " + min + " special characters" : null;
         }
     }
 
diff --git a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index 7d431c3..0be1dc7 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -75,7 +75,7 @@ public class ModelToRepresentation {
         return rep;
     }
 
-    public static RealmRepresentation toRepresentation(RealmModel realm) {
+    public static RealmRepresentation toRepresentation(RealmModel realm, boolean internal) {
         RealmRepresentation rep = new RealmRepresentation();
         rep.setId(realm.getId());
         rep.setRealm(realm.getName());
@@ -85,13 +85,15 @@ public class ModelToRepresentation {
         rep.setUpdateProfileOnInitialSocialLogin(realm.isUpdateProfileOnInitialSocialLogin());
         rep.setSslRequired(realm.getSslRequired().name().toLowerCase());
         rep.setPublicKey(realm.getPublicKeyPem());
-        rep.setPrivateKey(realm.getPrivateKeyPem());
-        String privateKeyPem = realm.getPrivateKeyPem();
-        if (realm.getCertificatePem() == null && privateKeyPem != null) {
-            KeycloakModelUtils.generateRealmCertificate(realm);
+        if (internal) {
+            rep.setPrivateKey(realm.getPrivateKeyPem());
+            String privateKeyPem = realm.getPrivateKeyPem();
+            if (realm.getCertificatePem() == null && privateKeyPem != null) {
+                KeycloakModelUtils.generateRealmCertificate(realm);
+            }
+            rep.setCodeSecret(realm.getCodeSecret());
         }
         rep.setCertificate(realm.getCertificatePem());
-        rep.setCodeSecret(realm.getCodeSecret());
         rep.setPasswordCredentialGrantAllowed(realm.isPasswordCredentialGrantAllowed());
         rep.setRegistrationAllowed(realm.isRegistrationAllowed());
         rep.setRememberMe(realm.isRememberMe());
diff --git a/model/api/src/test/java/org/keycloak/models/PasswordPolicyTest.java b/model/api/src/test/java/org/keycloak/models/PasswordPolicyTest.java
index 3fd7271..3481498 100644
--- a/model/api/src/test/java/org/keycloak/models/PasswordPolicyTest.java
+++ b/model/api/src/test/java/org/keycloak/models/PasswordPolicyTest.java
@@ -11,55 +11,55 @@ public class PasswordPolicyTest {
     @Test
     public void testLength() {
         PasswordPolicy policy = new PasswordPolicy("length");
-        Assert.assertNotNull(policy.validate("1234567"));
+        Assert.assertEquals("Invalid password: minimum length 8", policy.validate("1234567"));
         Assert.assertNull(policy.validate("12345678"));
 
         policy = new PasswordPolicy("length(4)");
-        Assert.assertNotNull(policy.validate("123"));
+        Assert.assertEquals("Invalid password: minimum length 4", policy.validate("123"));
         Assert.assertNull(policy.validate("1234"));
     }
 
     @Test
     public void testDigits() {
         PasswordPolicy policy = new PasswordPolicy("digits");
-        Assert.assertNotNull(policy.validate("abcd"));
+        Assert.assertEquals("Invalid password: must contain at least 1 numerical digits", policy.validate("abcd"));
         Assert.assertNull(policy.validate("abcd1"));
 
         policy = new PasswordPolicy("digits(2)");
-        Assert.assertNotNull(policy.validate("abcd1"));
+        Assert.assertEquals("Invalid password: must contain at least 2 numerical digits", policy.validate("abcd1"));
         Assert.assertNull(policy.validate("abcd12"));
     }
 
     @Test
     public void testLowerCase() {
         PasswordPolicy policy = new PasswordPolicy("lowerCase");
-        Assert.assertNotNull(policy.validate("ABCD1234"));
+        Assert.assertEquals("Invalid password: must contain at least 1 lower case characters", policy.validate("ABCD1234"));
         Assert.assertNull(policy.validate("ABcD1234"));
 
         policy = new PasswordPolicy("lowerCase(2)");
-        Assert.assertNotNull(policy.validate("ABcD1234"));
+        Assert.assertEquals("Invalid password: must contain at least 2 lower case characters", policy.validate("ABcD1234"));
         Assert.assertNull(policy.validate("aBcD1234"));
     }
 
     @Test
     public void testUpperCase() {
         PasswordPolicy policy = new PasswordPolicy("upperCase");
-        Assert.assertNotNull(policy.validate("abcd1234"));
+        Assert.assertEquals("Invalid password: must contain at least 1 upper case characters", policy.validate("abcd1234"));
         Assert.assertNull(policy.validate("abCd1234"));
 
         policy = new PasswordPolicy("upperCase(2)");
-        Assert.assertNotNull(policy.validate("abCd1234"));
+        Assert.assertEquals("Invalid password: must contain at least 2 upper case characters", policy.validate("abCd1234"));
         Assert.assertNull(policy.validate("AbCd1234"));
     }
 
     @Test
     public void testSpecialChars() {
         PasswordPolicy policy = new PasswordPolicy("specialChars");
-        Assert.assertNotNull(policy.validate("abcd1234"));
+        Assert.assertEquals("Invalid password: must contain at least 1 special characters", policy.validate("abcd1234"));
         Assert.assertNull(policy.validate("ab&d1234"));
 
         policy = new PasswordPolicy("specialChars(2)");
-        Assert.assertNotNull(policy.validate("ab&d1234"));
+        Assert.assertEquals("Invalid password: must contain at least 2 special characters", policy.validate("ab&d1234"));
         Assert.assertNull(policy.validate("ab&d-234"));
     }
 
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index a8f3707..b2766bb 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -158,7 +158,7 @@ public class RealmAdminResource {
     @Produces("application/json")
     public RealmRepresentation getRealm() {
         if (auth.hasView()) {
-            RealmRepresentation rep = ModelToRepresentation.toRepresentation(realm);
+            RealmRepresentation rep = ModelToRepresentation.toRepresentation(realm, false);
             if (session.realms() instanceof CacheRealmProvider) {
                 CacheRealmProvider cacheRealmProvider = (CacheRealmProvider)session.realms();
                 rep.setRealmCacheEnabled(cacheRealmProvider.isEnabled());
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java
index 0eab7ef..e434738 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java
@@ -100,7 +100,7 @@ public class RealmsAdminResource {
 
     protected void addRealmRep(List<RealmRepresentation> reps, RealmModel realm, ApplicationModel realmManagementApplication) {
         if (auth.hasAppRole(realmManagementApplication, AdminRoles.MANAGE_REALM)) {
-            reps.add(ModelToRepresentation.toRepresentation(realm));
+            reps.add(ModelToRepresentation.toRepresentation(realm, false));
         } else if (auth.hasOneOfAppRole(realmManagementApplication, AdminRoles.ALL_REALM_ROLES)) {
             RealmRepresentation rep = new RealmRepresentation();
             rep.setRealm(realm.getName());
diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
index a2d5a60..3667912 100755
--- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
+++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
@@ -194,15 +194,19 @@ public class KeycloakApplication extends Application {
     }
 
     public void importRealmFile() {
-        String file = System.getProperty("keycloak.import");
-        if (file != null) {
-            RealmRepresentation rep = null;
-            try {
-                rep = loadJson(new FileInputStream(file), RealmRepresentation.class);
-            } catch (FileNotFoundException e) {
-                throw new RuntimeException(e);
+        String files = System.getProperty("keycloak.import");
+        if (files != null) {
+            StringTokenizer tokenizer = new StringTokenizer(files, ",");
+            while (tokenizer.hasMoreTokens()) {
+                String file = tokenizer.nextToken().trim();
+                RealmRepresentation rep = null;
+                try {
+                    rep = loadJson(new FileInputStream(file), RealmRepresentation.class);
+                } catch (FileNotFoundException e) {
+                    throw new RuntimeException(e);
+                }
+                importRealm(rep, "file " + file);
             }
-            importRealm(rep, "file " + file);
         }
     }
 
@@ -223,11 +227,14 @@ public class KeycloakApplication extends Application {
                 return;
             }
 
-            RealmModel realm = manager.importRealm(rep);
-
-            log.info("Imported realm " + realm.getName() + " from " + from);
-
-            session.getTransaction().commit();
+            try {
+                RealmModel realm = manager.importRealm(rep);
+                session.getTransaction().commit();
+                log.info("Imported realm " + realm.getName() + " from " + from);
+            } catch (Throwable t) {
+                session.getTransaction().rollback();
+                log.warn("Unable to import realm " + rep.getRealm() + " from " + from + ". Cause: " + t.getMessage());
+            }
         } finally {
             session.close();
         }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AbstractClientTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AbstractClientTest.java
index 9a57d39..e0d29e9 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AbstractClientTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AbstractClientTest.java
@@ -7,6 +7,7 @@ import org.keycloak.admin.client.Keycloak;
 import org.keycloak.admin.client.resource.RealmResource;
 import org.keycloak.models.Constants;
 import org.keycloak.models.RealmModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.representations.idm.ApplicationRepresentation;
 import org.keycloak.representations.idm.OAuthClientRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
@@ -42,6 +43,7 @@ public abstract class AbstractClientTest {
 
                 RealmModel testRealm = manager.createRealm(REALM_NAME);
                 testRealm.setEnabled(true);
+                KeycloakModelUtils.generateRealmKeys(testRealm);
             }
         });
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/RealmTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/RealmTest.java
index d3bbca9..6626b60 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/RealmTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/RealmTest.java
@@ -6,7 +6,11 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.services.managers.RealmManager;
 
+import java.util.List;
+
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 /**
@@ -16,7 +20,15 @@ public class RealmTest extends AbstractClientTest {
 
     @Test
     public void getRealms() {
-        assertNames(keycloak.realms().findAll(), "master", "test", REALM_NAME);
+        List<RealmRepresentation> realms = keycloak.realms().findAll();
+        assertNames(realms, "master", "test", REALM_NAME);
+
+        for (RealmRepresentation rep : realms) {
+            assertNull(rep.getPrivateKey());
+            assertNull(rep.getCodeSecret());
+            assertNotNull(rep.getPublicKey());
+            assertNotNull(rep.getCertificate());
+        }
     }
 
     @Test
@@ -65,6 +77,11 @@ public class RealmTest extends AbstractClientTest {
         RealmRepresentation rep = realm.toRepresentation();
         assertEquals(REALM_NAME, rep.getRealm());
         assertTrue(rep.isEnabled());
+
+        assertNull(rep.getPrivateKey());
+        assertNull(rep.getCodeSecret());
+        assertNotNull(rep.getPublicKey());
+        assertNotNull(rep.getCertificate());
     }
 
 }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ModelTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ModelTest.java
index 2346bd8..eb64196 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ModelTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ModelTest.java
@@ -67,7 +67,7 @@ public class ModelTest extends AbstractModelTest {
     }
 
     private RealmModel importExport(RealmModel src, String copyName) {
-        RealmRepresentation representation = ModelToRepresentation.toRepresentation(src);
+        RealmRepresentation representation = ModelToRepresentation.toRepresentation(src, true);
         representation.setRealm(copyName);
         representation.setId(copyName);
         RealmModel copy = realmManager.importRealm(representation);