diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderMapperSpi.java b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderMapperSpi.java index 44fb65d5ed..b0ac20a4e8 100755 --- a/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderMapperSpi.java +++ b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderMapperSpi.java @@ -10,8 +10,8 @@ import org.keycloak.provider.Spi; public class IdentityProviderMapperSpi implements Spi { @Override - public boolean isPrivate() { - return false; + public boolean isInternal() { + return true; } @Override diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderSpi.java b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderSpi.java index b507e551c2..3348bd2523 100644 --- a/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderSpi.java +++ b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderSpi.java @@ -29,7 +29,7 @@ public class IdentityProviderSpi implements Spi { public static final String IDENTITY_PROVIDER_SPI_NAME = "identity_provider"; @Override - public boolean isPrivate() { + public boolean isInternal() { return false; } diff --git a/connections/file/src/main/java/org/keycloak/connections/file/FileConnectionSpi.java b/connections/file/src/main/java/org/keycloak/connections/file/FileConnectionSpi.java index ec64192281..5929a0ba61 100644 --- a/connections/file/src/main/java/org/keycloak/connections/file/FileConnectionSpi.java +++ b/connections/file/src/main/java/org/keycloak/connections/file/FileConnectionSpi.java @@ -10,7 +10,7 @@ import org.keycloak.provider.Spi; public class FileConnectionSpi implements Spi { @Override - public boolean isPrivate() { + public boolean isInternal() { return true; } diff --git a/connections/http-client/src/main/java/org/keycloak/connections/httpclient/HttpClientSpi.java b/connections/http-client/src/main/java/org/keycloak/connections/httpclient/HttpClientSpi.java index 510b1648bc..01cbaa3f4f 100755 --- a/connections/http-client/src/main/java/org/keycloak/connections/httpclient/HttpClientSpi.java +++ b/connections/http-client/src/main/java/org/keycloak/connections/httpclient/HttpClientSpi.java @@ -10,7 +10,7 @@ import org.keycloak.provider.Spi; public class HttpClientSpi implements Spi { @Override - public boolean isPrivate() { + public boolean isInternal() { return true; } diff --git a/connections/infinispan/src/main/java/org/keycloak/connections/infinispan/InfinispanConnectionSpi.java b/connections/infinispan/src/main/java/org/keycloak/connections/infinispan/InfinispanConnectionSpi.java index f76c070673..a4db87ec43 100644 --- a/connections/infinispan/src/main/java/org/keycloak/connections/infinispan/InfinispanConnectionSpi.java +++ b/connections/infinispan/src/main/java/org/keycloak/connections/infinispan/InfinispanConnectionSpi.java @@ -10,7 +10,7 @@ import org.keycloak.provider.Spi; public class InfinispanConnectionSpi implements Spi { @Override - public boolean isPrivate() { + public boolean isInternal() { return true; } diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.3.0.Beta1.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.3.0.Beta1.xml index 5ce589128d..86b9d75fa2 100755 --- a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.3.0.Beta1.xml +++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.3.0.Beta1.xml @@ -19,7 +19,7 @@ - + @@ -28,7 +28,7 @@ - + @@ -94,6 +94,9 @@ + + + @@ -107,6 +110,19 @@ + + + + + + + + + + UPDATE_PROFILE_FIRST_LOGIN = false + + + @@ -147,5 +163,7 @@ + + diff --git a/connections/jpa/src/main/java/org/keycloak/connections/jpa/JpaConnectionSpi.java b/connections/jpa/src/main/java/org/keycloak/connections/jpa/JpaConnectionSpi.java index d565357534..61eb8165fb 100644 --- a/connections/jpa/src/main/java/org/keycloak/connections/jpa/JpaConnectionSpi.java +++ b/connections/jpa/src/main/java/org/keycloak/connections/jpa/JpaConnectionSpi.java @@ -10,7 +10,7 @@ import org.keycloak.provider.Spi; public class JpaConnectionSpi implements Spi { @Override - public boolean isPrivate() { + public boolean isInternal() { return true; } diff --git a/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterSpi.java b/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterSpi.java index c9bd8ee380..eaddc278ce 100644 --- a/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterSpi.java +++ b/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterSpi.java @@ -10,7 +10,7 @@ import org.keycloak.provider.Spi; public class JpaUpdaterSpi implements Spi { @Override - public boolean isPrivate() { + public boolean isInternal() { return true; } diff --git a/connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/DefaultMongoUpdaterProvider.java b/connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/DefaultMongoUpdaterProvider.java index 551d0e688e..c8097f6cf9 100644 --- a/connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/DefaultMongoUpdaterProvider.java +++ b/connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/DefaultMongoUpdaterProvider.java @@ -6,11 +6,7 @@ import com.mongodb.DBCollection; import com.mongodb.DBCursor; import org.jboss.logging.Logger; import org.keycloak.connections.mongo.updater.MongoUpdaterProvider; -import org.keycloak.connections.mongo.updater.impl.updates.Update; -import org.keycloak.connections.mongo.updater.impl.updates.Update1_0_0_Final; -import org.keycloak.connections.mongo.updater.impl.updates.Update1_1_0_Beta1; -import org.keycloak.connections.mongo.updater.impl.updates.Update1_2_0_Beta1; -import org.keycloak.connections.mongo.updater.impl.updates.Update1_2_0_CR1; +import org.keycloak.connections.mongo.updater.impl.updates.*; import org.keycloak.models.KeycloakSession; import java.util.Date; @@ -30,7 +26,8 @@ public class DefaultMongoUpdaterProvider implements MongoUpdaterProvider { Update1_0_0_Final.class, Update1_1_0_Beta1.class, Update1_2_0_Beta1.class, - Update1_2_0_CR1.class + Update1_2_0_CR1.class, + Update1_3_0_Beta1.class }; @Override diff --git a/connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update.java b/connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update.java index f05e79cc95..cc7a2d2855 100644 --- a/connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update.java +++ b/connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update.java @@ -53,6 +53,10 @@ public abstract class Update { log.debugv("Deleted entries from {0}", collection); } + protected void removeField(String collection, String field) { + db.getCollection(collection).update(new BasicDBObject(), new BasicDBObject("$unset" , new BasicDBObject(field, 1)), false, true); + } + protected void renameCollection(String collection, String newName) { db.getCollection(collection).rename(newName); } diff --git a/connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update1_3_0_Beta1.java b/connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update1_3_0_Beta1.java new file mode 100644 index 0000000000..f834d22b1d --- /dev/null +++ b/connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update1_3_0_Beta1.java @@ -0,0 +1,59 @@ +package org.keycloak.connections.mongo.updater.impl.updates; + +import com.mongodb.BasicDBList; +import com.mongodb.BasicDBObject; +import com.mongodb.DBCollection; +import com.mongodb.DBCursor; +import org.keycloak.models.KeycloakSession; +import org.keycloak.representations.idm.IdentityProviderRepresentation; + +/** + * @author Marek Posolda + */ +public class Update1_3_0_Beta1 extends Update { + + @Override + public String getId() { + return "1.3.0.Beta1"; + } + + @Override + public void update(KeycloakSession session) { + deleteEntries("clientSessions"); + deleteEntries("sessions"); + + removeField("realms", "passwordCredentialGrantAllowed"); + + updateIdentityProviders(); + } + + private void updateIdentityProviders() { + DBCollection realms = db.getCollection("realms"); + DBCursor realmsCursor = realms.find(); + + try { + while (realmsCursor.hasNext()) { + BasicDBObject realm = (BasicDBObject) realmsCursor.next(); + + BasicDBList identityProviders = (BasicDBList) realm.get("identityProviders"); + if (identityProviders != null) { + for (Object ipObj : identityProviders) { + BasicDBObject identityProvider = (BasicDBObject) ipObj; + + boolean updateProfileFirstLogin = identityProvider.getBoolean("updateProfileFirstLogin"); + String upflMode = updateProfileFirstLogin ? IdentityProviderRepresentation.UPFLM_ON : IdentityProviderRepresentation.UPFLM_OFF; + identityProvider.put("updateProfileFirstLoginMode", upflMode); + identityProvider.removeField("updateProfileFirstLogin"); + + identityProvider.put("trustEmail", false); + } + } + + realms.save(realm); + } + } finally { + realmsCursor.close(); + } + } + +} diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java index ed6844c3ed..dab99e081d 100755 --- a/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java +++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java @@ -43,7 +43,10 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro "org.keycloak.models.entities.ProtocolMapperEntity", "org.keycloak.models.entities.IdentityProviderMapperEntity", "org.keycloak.models.mongo.keycloak.entities.MongoUserConsentEntity", - "org.keycloak.models.mongo.keycloak.entities.MongoMigrationModelEntity" + "org.keycloak.models.mongo.keycloak.entities.MongoMigrationModelEntity", + "org.keycloak.models.entities.AuthenticationExecutionEntity", + "org.keycloak.models.entities.AuthenticationFlowEntity", + "org.keycloak.models.entities.AuthenticatorEntity", }; private static final Logger logger = Logger.getLogger(DefaultMongoConnectionFactoryProvider.class); diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/MongoConnectionSpi.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/MongoConnectionSpi.java index f391dfd651..9c5c53274f 100644 --- a/connections/mongo/src/main/java/org/keycloak/connections/mongo/MongoConnectionSpi.java +++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/MongoConnectionSpi.java @@ -10,7 +10,7 @@ import org.keycloak.provider.Spi; public class MongoConnectionSpi implements Spi { @Override - public boolean isPrivate() { + public boolean isInternal() { return true; } diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/MongoUpdaterSpi.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/MongoUpdaterSpi.java index 830aaa339c..ed92458d9e 100644 --- a/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/MongoUpdaterSpi.java +++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/MongoUpdaterSpi.java @@ -10,7 +10,7 @@ import org.keycloak.provider.Spi; public class MongoUpdaterSpi implements Spi { @Override - public boolean isPrivate() { + public boolean isInternal() { return true; } diff --git a/core/src/main/java/org/keycloak/representations/idm/IdentityProviderRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/IdentityProviderRepresentation.java index c456355d9b..1e74002bc8 100755 --- a/core/src/main/java/org/keycloak/representations/idm/IdentityProviderRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/IdentityProviderRepresentation.java @@ -29,7 +29,26 @@ public class IdentityProviderRepresentation { protected String internalId; protected String providerId; protected boolean enabled = true; - protected boolean updateProfileFirstLogin = true; + + public static final String UPFLM_ON = "on"; + public static final String UPFLM_MISSING = "missing"; + public static final String UPFLM_OFF = "off"; + + /** + * Mode of profile update after first login when user is created over this identity provider. Possible values: + *
    + *
  • on - update profile page is presented for all users + *
  • missing - update profile page is presented for users with missing some of mandatory user profile fields + *
  • off - update profile page is newer shown after first login + *
+ * + * @see #UPFLM_ON + * @see #UPFLM_MISSING + * @see #UPFLM_OFF + */ + protected String updateProfileFirstLoginMode = UPFLM_ON; + + protected boolean trustEmail; protected boolean storeToken; protected boolean addReadTokenRoleOnCreate; protected boolean authenticateByDefault; @@ -75,12 +94,29 @@ public class IdentityProviderRepresentation { this.enabled = enabled; } - public boolean isUpdateProfileFirstLogin() { - return this.updateProfileFirstLogin; + /** + * + * Deprecated because replaced by {@link #updateProfileFirstLoginMode}. Kept here to allow import of old realms. + * + * @deprecated {@link #setUpdateProfileFirstLoginMode(String)} + */ + @Deprecated + public void setUpdateProfileFirstLogin(boolean updateProfileFirstLogin) { + this.updateProfileFirstLoginMode = updateProfileFirstLogin ? UPFLM_ON : UPFLM_OFF; } - public void setUpdateProfileFirstLogin(boolean updateProfileFirstLogin) { - this.updateProfileFirstLogin = updateProfileFirstLogin; + /** + * @return see {@link #updateProfileFirstLoginMode} + */ + public String getUpdateProfileFirstLoginMode() { + return updateProfileFirstLoginMode; + } + + /** + * @param updateProfileFirstLoginMode see {@link #updateProfileFirstLoginMode} + */ + public void setUpdateProfileFirstLoginMode(String updateProfileFirstLoginMode) { + this.updateProfileFirstLoginMode = updateProfileFirstLoginMode; } public boolean isAuthenticateByDefault() { @@ -106,4 +142,13 @@ public class IdentityProviderRepresentation { public void setAddReadTokenRoleOnCreate(boolean addReadTokenRoleOnCreate) { this.addReadTokenRoleOnCreate = addReadTokenRoleOnCreate; } + + public boolean isTrustEmail() { + return trustEmail; + } + + public void setTrustEmail(boolean trustEmail) { + this.trustEmail = trustEmail; + } + } diff --git a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java index 6ff027ca7d..dabc8dbf6d 100755 --- a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java @@ -18,12 +18,14 @@ public class RealmRepresentation { protected Integer accessCodeLifespanLogin; protected Boolean enabled; protected String sslRequired; + @Deprecated protected Boolean passwordCredentialGrantAllowed; protected Boolean registrationAllowed; protected Boolean registrationEmailAsUsername; protected Boolean rememberMe; protected Boolean verifyEmail; protected Boolean resetPasswordAllowed; + protected Boolean editUsernameAllowed; protected Boolean userCacheEnabled; protected Boolean realmCacheEnabled; @@ -268,10 +270,6 @@ public class RealmRepresentation { return passwordCredentialGrantAllowed; } - public void setPasswordCredentialGrantAllowed(Boolean passwordCredentialGrantAllowed) { - this.passwordCredentialGrantAllowed = passwordCredentialGrantAllowed; - } - public Boolean isRegistrationAllowed() { return registrationAllowed; } @@ -328,6 +326,14 @@ public class RealmRepresentation { this.resetPasswordAllowed = resetPassword; } + public Boolean isEditUsernameAllowed() { + return editUsernameAllowed; + } + + public void setEditUsernameAllowed(Boolean editUsernameAllowed) { + this.editUsernameAllowed = editUsernameAllowed; + } + @Deprecated public Boolean isSocial() { return social; diff --git a/core/src/main/java/org/keycloak/util/HtmlUtils.java b/core/src/main/java/org/keycloak/util/HtmlUtils.java old mode 100644 new mode 100755 index 7da97b7e88..2387482df4 --- a/core/src/main/java/org/keycloak/util/HtmlUtils.java +++ b/core/src/main/java/org/keycloak/util/HtmlUtils.java @@ -34,7 +34,17 @@ public class HtmlUtils { for (int i = 0; i < value.length(); i++) { char chr = value.charAt(i); - if (chr != '\'' && chr != '"' && chr != '<' && chr != '>' && chr != '/') { + if (chr == '<') { + escaped.append("<"); + } else if (chr == '>') { + escaped.append(">"); + } else if (chr == '"') { + escaped.append("""); + } else if (chr == '\'') { + escaped.append("'"); + } else if (chr == '&') { + escaped.append("&"); + } else { escaped.append(chr); } } diff --git a/distribution/demo-dist/assembly.xml b/distribution/demo-dist/assembly.xml index f00bfea444..5a6be78a2a 100755 --- a/distribution/demo-dist/assembly.xml +++ b/distribution/demo-dist/assembly.xml @@ -14,7 +14,6 @@ keycloak **/*.sh - standalone/configuration/standalone-keycloak.xml @@ -25,6 +24,20 @@ 0755 + + ${project.build.directory}/unpacked/keycloak-server-overlay-${project.version} + keycloak + + standalone/configuration/standalone-keycloak.xml + + + + ${project.build.directory}/unpacked/keycloak-wf9-adapter-${project.version} + keycloak + + standalone/configuration/standalone-keycloak.xml + + ${project.build.directory}/unpacked/keycloak-docs-${project.version} docs @@ -34,5 +47,11 @@ examples + + + ${project.build.directory}/unpacked/standalone.xml + keycloak/standalone/configuration + + diff --git a/distribution/demo-dist/pom.xml b/distribution/demo-dist/pom.xml index 37e4bdc727..e5394f1f84 100755 --- a/distribution/demo-dist/pom.xml +++ b/distribution/demo-dist/pom.xml @@ -16,7 +16,12 @@ org.keycloak - keycloak-server-dist + keycloak-server-overlay + zip + + + org.keycloak + keycloak-wf9-adapter-dist zip @@ -63,7 +68,7 @@ - unpack-server-overlay + unpack-server prepare-package unpack @@ -74,7 +79,24 @@ org.keycloak keycloak-server-overlay zip - ${project.build.directory}/unpacked/wildfly-${wildfly.version} + ${project.build.directory}/unpacked/keycloak-server-overlay-${project.version} + + + + + + unpack-adapter + prepare-package + + unpack + + + + + org.keycloak + keycloak-wf9-adapter-dist + zip + ${project.build.directory}/unpacked/keycloak-wf9-adapter-${project.version} @@ -134,7 +156,7 @@ standalone.xml - ${project.build.directory}/unpacked/wildfly-${wildfly.version}/standalone/configuration + ${project.build.directory}/unpacked/ diff --git a/distribution/demo-dist/src/main/keycloak-server.json b/distribution/demo-dist/src/main/keycloak-server.json deleted file mode 100644 index 9f0d03ea5d..0000000000 --- a/distribution/demo-dist/src/main/keycloak-server.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "admin": { - "realm": "master" - }, - - "eventsStore": { - "provider": "jpa", - "jpa": { - "exclude-events": [ "REFRESH_TOKEN" ] - } - }, - - "realm": { - "provider": "jpa" - }, - - "user": { - "provider": "jpa" - }, - - "userSessions": { - "provider" : "mem" - }, - - "realmCache": { - "provider": "mem" - }, - - "userCache": { - "provider": "mem", - "mem": { - "maxSize": 20000 - } - }, - - "timer": { - "provider": "basic" - }, - - "theme": { - "default": "keycloak", - "staticMaxAge": 2592000, - "cacheTemplates": true, - "cacheThemes": true, - "folder": { - "dir": "${jboss.server.config.dir}/themes" - } - }, - - "login": { - "provider": "freemarker" - }, - - "account": { - "provider": "freemarker" - }, - - "email": { - "provider": "freemarker" - }, - - "scheduled": { - "interval": 900 - }, - - "connectionsJpa": { - "default": { - "dataSource": "java:jboss/datasources/KeycloakDS", - "databaseSchema": "update" - } - } -} \ No newline at end of file diff --git a/distribution/demo-dist/src/main/providers/README.txt b/distribution/demo-dist/src/main/providers/README.txt deleted file mode 100644 index a6d523b43f..0000000000 --- a/distribution/demo-dist/src/main/providers/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -Any provider implementation jars and libraries in this folder will be loaded by Keycloak. See the providers -section in the documentation for more details. \ No newline at end of file diff --git a/distribution/demo-dist/src/main/themes/README.txt b/distribution/demo-dist/src/main/themes/README.txt deleted file mode 100644 index 705b73ac69..0000000000 --- a/distribution/demo-dist/src/main/themes/README.txt +++ /dev/null @@ -1,3 +0,0 @@ -Themes to configure the look and feel of login pages and account management console. It's not recommended to -modify existing the built-in themes, instead you should create a new theme that extends a built-in theme. See the theme -section in the documentation for more details. \ No newline at end of file diff --git a/distribution/demo-dist/src/main/xslt/standalone.xsl b/distribution/demo-dist/src/main/xslt/standalone.xsl index 5de72afe94..bc0233a99e 100755 --- a/distribution/demo-dist/src/main/xslt/standalone.xsl +++ b/distribution/demo-dist/src/main/xslt/standalone.xsl @@ -39,11 +39,9 @@ - - true - auth - + auth + diff --git a/distribution/feature-packs/server-feature-pack/pom.xml b/distribution/feature-packs/server-feature-pack/pom.xml index a73742739c..9925f5f337 100644 --- a/distribution/feature-packs/server-feature-pack/pom.xml +++ b/distribution/feature-packs/server-feature-pack/pom.xml @@ -126,7 +126,7 @@ ${project.version} war true - ${project.build.directory}/${project.build.finalName}/modules/system/layers/base/org/keycloak/keycloak-server-subsystem/main/auth-server + ${project.build.directory}/${project.build.finalName}/modules/system/layers/base/org/keycloak/keycloak-server-subsystem/main/server-war diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/de/idyl/winzipaes/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/de/idyl/winzipaes/main/module.xml index 10f1103cfd..14d7dffd6d 100644 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/de/idyl/winzipaes/main/module.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/de/idyl/winzipaes/main/module.xml @@ -4,7 +4,7 @@ - + diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-server-subsystem/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-server-subsystem/main/module.xml index 5233767d0d..0d0c336440 100644 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-server-subsystem/main/module.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-server-subsystem/main/module.xml @@ -25,7 +25,7 @@ - + diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/sun/jdk/jgss/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/sun/jdk/jgss/main/module.xml new file mode 100644 index 0000000000..6df03ff4ba --- /dev/null +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/sun/jdk/jgss/main/module.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/distribution/modules/build.xml b/distribution/modules/build.xml index d97c9f928f..fb8971d524 100755 --- a/distribution/modules/build.xml +++ b/distribution/modules/build.xml @@ -46,11 +46,6 @@ - - - - - @@ -71,10 +66,6 @@ - - - - @@ -315,40 +306,9 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/distribution/modules/pom.xml b/distribution/modules/pom.xml index b073f57520..c4fffdbda4 100755 --- a/distribution/modules/pom.xml +++ b/distribution/modules/pom.xml @@ -34,26 +34,6 @@ org.keycloak keycloak-core-jaxrs - - org.keycloak - keycloak-adapter-core - - - org.keycloak - keycloak-jboss-adapter-core - - - org.keycloak - keycloak-as7-adapter - - - org.keycloak - keycloak-undertow-adapter - - - org.keycloak - keycloak-wildfly-adapter - org.keycloak keycloak-wildfly-server-subsystem @@ -64,18 +44,6 @@ keycloak-server war - - org.keycloak - keycloak-as7-subsystem - - - org.apache.httpcomponents - httpmime - - - org.apache.httpcomponents - httpcore - diff --git a/distribution/modules/src/main/resources/modules/org/apache/httpcomponents/4.3/module.xml b/distribution/modules/src/main/resources/modules/org/apache/httpcomponents/4.3/module.xml deleted file mode 100644 index a3e65f8dd0..0000000000 --- a/distribution/modules/src/main/resources/modules/org/apache/httpcomponents/4.3/module.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/distribution/modules/src/main/resources/modules/org/bouncycastle/main/module.xml b/distribution/modules/src/main/resources/modules/org/bouncycastle/main/module.xml deleted file mode 100644 index d8fcf474cb..0000000000 --- a/distribution/modules/src/main/resources/modules/org/bouncycastle/main/module.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-adapter-core/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-adapter-core/main/module.xml deleted file mode 100755 index 70a20279ae..0000000000 --- a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-adapter-core/main/module.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-adapter-subsystem/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-adapter-subsystem/main/module.xml deleted file mode 100755 index 3408a92aa3..0000000000 --- a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-adapter-subsystem/main/module.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-as7-adapter/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-as7-adapter/main/module.xml deleted file mode 100755 index 4a9ef50813..0000000000 --- a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-as7-adapter/main/module.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-as7-subsystem/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-as7-subsystem/main/module.xml deleted file mode 100755 index 50f423478d..0000000000 --- a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-as7-subsystem/main/module.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-connections-http-client/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-connections-http-client/main/module.xml index 1d35246514..7739aa320a 100755 --- a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-connections-http-client/main/module.xml +++ b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-connections-http-client/main/module.xml @@ -14,7 +14,7 @@ - + diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml deleted file mode 100755 index b84e377136..0000000000 --- a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-saml-protocol/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-saml-protocol/main/module.xml index 655a8c0c91..45512ac392 100755 --- a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-saml-protocol/main/module.xml +++ b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-saml-protocol/main/module.xml @@ -16,7 +16,7 @@ - + diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-server-subsystem/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-server-subsystem/main/module.xml index e877fe41ca..21f917ead8 100755 --- a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-server-subsystem/main/module.xml +++ b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-server-subsystem/main/module.xml @@ -46,7 +46,6 @@ - diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-server/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-server/main/module.xml index 9a7229ff8c..fd8e190fb3 100755 --- a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-server/main/module.xml +++ b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-server/main/module.xml @@ -9,10 +9,6 @@ - - - - @@ -36,7 +32,6 @@ - @@ -61,8 +56,6 @@ - - diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-services/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-services/main/module.xml index 55a5518dad..0ee8056eaf 100755 --- a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-services/main/module.xml +++ b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-services/main/module.xml @@ -36,7 +36,6 @@ - @@ -50,7 +49,6 @@ - @@ -78,7 +76,7 @@ - + diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-undertow-adapter/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-undertow-adapter/main/module.xml deleted file mode 100755 index bd3364249a..0000000000 --- a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-undertow-adapter/main/module.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-wildfly-adapter/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-wildfly-adapter/main/module.xml deleted file mode 100755 index c37e3adad4..0000000000 --- a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-wildfly-adapter/main/module.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-wildfly-extensions/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-wildfly-extensions/main/module.xml deleted file mode 100755 index 01af076596..0000000000 --- a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-wildfly-extensions/main/module.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/distribution/pom.xml b/distribution/pom.xml index 3b7a66f869..68f19b540e 100755 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -28,13 +28,13 @@ adapters - + demo-dist docs-dist examples-dist modules proxy-dist server-dist - + server-overlay src-dist subsystem-war feature-packs diff --git a/distribution/server-overlay/assembly.xml b/distribution/server-overlay/assembly.xml index f8424b1d93..6efb409247 100755 --- a/distribution/server-overlay/assembly.xml +++ b/distribution/server-overlay/assembly.xml @@ -10,20 +10,38 @@ - ${project.build.directory}/unpacked/modules - modules + ${project.build.directory}/unpacked/keycloak-${project.version}/modules/system/layers/base + modules/system/layers/base + + com/google/zxing/** + de/idyl/winzipaes/** + net/iharder/** + org/freemarker/** + org/keycloak/** + org/liquibase/** + org/mongodb/** + org/twitter4j/** + sun/jdk/jgss/** + - ${project.build.directory}/unpacked/content + ${project.build.directory}/unpacked/keycloak-${project.version}/content - ../../forms/common-themes/src/main/resources/theme + ${project.build.directory}/unpacked/keycloak-${project.version}/standalone/configuration/themes standalone/configuration/themes **/** + + ${project.build.directory}/unpacked/keycloak-${project.version}/standalone/configuration/providers + standalone/configuration/providers + + **/** + + ../../ @@ -31,25 +49,19 @@ + - ${project.build.directory}/unpacked/wildfly-${wildfly.version}/standalone/configuration/standalone.xml + ${project.build.directory}/unpacked/keycloak-${project.version}/standalone/configuration/standalone.xml standalone/configuration standalone-keycloak.xml - src/main/keycloak-server.json + ${project.build.directory}/unpacked/keycloak-${project.version}/standalone/configuration/keycloak-server.json standalone/configuration - - src/main/themes/README.txt - standalone/configuration/themes - - - src/main/providers/README.txt - standalone/configuration/providers - + diff --git a/distribution/server-overlay/pom.xml b/distribution/server-overlay/pom.xml index a066b2c095..7ab8862188 100755 --- a/distribution/server-overlay/pom.xml +++ b/distribution/server-overlay/pom.xml @@ -10,49 +10,27 @@ keycloak-server-overlay pom - Keycloak Server Overlay + Keycloak Server Overlay Distribution org.keycloak - keycloak-jboss-modules - zip - - - org.wildfly - wildfly-dist + keycloak-server-dist zip keycloak-overlay-${project.version} + org.apache.maven.plugins maven-dependency-plugin - unpack-standalone-xml - prepare-package - - unpack - - - - - org.wildfly - wildfly-dist - zip - ${project.build.directory}/unpacked - - - */standalone/configuration/standalone.xml - - - - unpack-module + unpack-server-dist prepare-package unpack @@ -61,9 +39,9 @@ org.keycloak - keycloak-jboss-modules + keycloak-server-dist zip - ${project.build.directory}/unpacked/modules + ${project.build.directory}/unpacked @@ -71,32 +49,7 @@ - org.codehaus.mojo - xml-maven-plugin - 1.0 - - - generate-resources - package - - transform - - - - - ${project.build.directory}/unpacked/wildfly-${wildfly.version}/standalone/configuration - src/main/xslt/standalone.xsl - - standalone.xml - - ${project.build.directory}/unpacked/wildfly-${wildfly.version}/standalone/configuration - - - - - - - + org.apache.maven.plugins maven-assembly-plugin @@ -109,9 +62,11 @@ assembly.xml - target - target/assembly/work + true + ${project.build.finalName} false + ${project.build.directory} + ${project.build.directory}/assembly/work gnu diff --git a/distribution/server-overlay/src/main/keycloak-server.json b/distribution/server-overlay/src/main/keycloak-server.json deleted file mode 100644 index 9f0d03ea5d..0000000000 --- a/distribution/server-overlay/src/main/keycloak-server.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "admin": { - "realm": "master" - }, - - "eventsStore": { - "provider": "jpa", - "jpa": { - "exclude-events": [ "REFRESH_TOKEN" ] - } - }, - - "realm": { - "provider": "jpa" - }, - - "user": { - "provider": "jpa" - }, - - "userSessions": { - "provider" : "mem" - }, - - "realmCache": { - "provider": "mem" - }, - - "userCache": { - "provider": "mem", - "mem": { - "maxSize": 20000 - } - }, - - "timer": { - "provider": "basic" - }, - - "theme": { - "default": "keycloak", - "staticMaxAge": 2592000, - "cacheTemplates": true, - "cacheThemes": true, - "folder": { - "dir": "${jboss.server.config.dir}/themes" - } - }, - - "login": { - "provider": "freemarker" - }, - - "account": { - "provider": "freemarker" - }, - - "email": { - "provider": "freemarker" - }, - - "scheduled": { - "interval": 900 - }, - - "connectionsJpa": { - "default": { - "dataSource": "java:jboss/datasources/KeycloakDS", - "databaseSchema": "update" - } - } -} \ No newline at end of file diff --git a/distribution/server-overlay/src/main/providers/README.txt b/distribution/server-overlay/src/main/providers/README.txt deleted file mode 100644 index a6d523b43f..0000000000 --- a/distribution/server-overlay/src/main/providers/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -Any provider implementation jars and libraries in this folder will be loaded by Keycloak. See the providers -section in the documentation for more details. \ No newline at end of file diff --git a/distribution/server-overlay/src/main/themes/README.txt b/distribution/server-overlay/src/main/themes/README.txt deleted file mode 100644 index 705b73ac69..0000000000 --- a/distribution/server-overlay/src/main/themes/README.txt +++ /dev/null @@ -1,3 +0,0 @@ -Themes to configure the look and feel of login pages and account management console. It's not recommended to -modify existing the built-in themes, instead you should create a new theme that extends a built-in theme. See the theme -section in the documentation for more details. \ No newline at end of file diff --git a/distribution/server-overlay/src/main/xslt/standalone.xsl b/distribution/server-overlay/src/main/xslt/standalone.xsl deleted file mode 100755 index dd17b23830..0000000000 --- a/distribution/server-overlay/src/main/xslt/standalone.xsl +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - - - - - - - jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE - h2 - - sa - sa - - - - - - - - - - - - true - auth - - - - - - - - - - - - \ No newline at end of file diff --git a/docbook/reference/en/en-US/master.xml b/docbook/reference/en/en-US/master.xml index 218a1aa7a5..d379ac8151 100755 --- a/docbook/reference/en/en-US/master.xml +++ b/docbook/reference/en/en-US/master.xml @@ -35,6 +35,7 @@ + @@ -126,6 +127,7 @@ This one is short &UserFederation; &Kerberos; &ExportImport; + &AdminRecovery; &ServerCache; &SAML; &SecurityVulnerabilities; diff --git a/docbook/reference/en/en-US/modules/MigrationFromOlderVersions.xml b/docbook/reference/en/en-US/modules/MigrationFromOlderVersions.xml index ada2a89900..68e48b4a1e 100755 --- a/docbook/reference/en/en-US/modules/MigrationFromOlderVersions.xml +++ b/docbook/reference/en/en-US/modules/MigrationFromOlderVersions.xml @@ -79,6 +79,30 @@
Version specific migration +
+ Migrating to 1.3.0.Final + + Direct Grant API always enabled + + In the past Direct Grant API (or Resource Owner Password Credentials) was disabled by default and + there was an option on a realm to enable it. The Direct Grant API is now always enabled and the + option to enable/disable for a realm is removed. + + + + Database changed + + There are again few database changes. Remember to backup your database prior to upgrading. + + + + UserFederationProvider changed + + There are few minor changes in UserFederationProvider interface. You may need to sync your implementation when upgrade + to newer version and upgrade few methods, which has changed signature. Changes are really minor, but were needed to improve performance of federation. + + +
Migrating from 1.2.0.Beta1 to 1.2.0.RC1 diff --git a/docbook/reference/en/en-US/modules/admin-recovery.xml b/docbook/reference/en/en-US/modules/admin-recovery.xml new file mode 100755 index 0000000000..941284805f --- /dev/null +++ b/docbook/reference/en/en-US/modules/admin-recovery.xml @@ -0,0 +1,15 @@ + + Recovering the Master Admin User + + It is possible for the "admin" user in the master realm to become inoperable. This may be because it was + accidentally deleted, its role mappings were removed, or the password was simply forgotten. + + + To recover the master admin user, just start the server with the following system properties: + + Then you can log in to the master admin account with your temporary password. You will then be + prompted to immediately change this password. + + \ No newline at end of file diff --git a/docbook/reference/en/en-US/modules/direct-access.xml b/docbook/reference/en/en-US/modules/direct-access.xml index b21f82b15a..e7a189bb77 100755 --- a/docbook/reference/en/en-US/modules/direct-access.xml +++ b/docbook/reference/en/en-US/modules/direct-access.xml @@ -3,8 +3,7 @@ Keycloak allows you to make direct REST invocations to obtain an access token. (See Resource Owner Password Credentials Grant - from OAuth 2.0 spec). To use it, Direct Access Grants must be allowed by your realm. This is a configuration switch - in the admin console under Settings->General, specifically the "Direct Grant API" switch. You must also have + from OAuth 2.0 spec). To use it you must also have registered a valid Client to use as the "client_id" for this grant request. @@ -12,7 +11,9 @@ It is highly recommended that you do not use Direct Access Grants to write your own login pages for your application. You will lose a lot of features that Keycloak has if you do this. Specifically all the account management, remember me, lost password, account reset features of Keycloak. Instead, if you want to tailor the look and feel of Keycloak login - pages, you should create your own theme. + pages, you should create your own theme. There are also security implications + to using Direct Access Grants compared to the redirect based flows as you are exposing plain text passwords + to applications directly. It is even highly recommended that you use the browser to log in for native mobile applications! Android diff --git a/docbook/reference/en/en-US/modules/identity-broker.xml b/docbook/reference/en/en-US/modules/identity-broker.xml index 6eb5320116..75df2c7572 100755 --- a/docbook/reference/en/en-US/modules/identity-broker.xml +++ b/docbook/reference/en/en-US/modules/identity-broker.xml @@ -299,12 +299,24 @@ Allows you to force users to update their profile right after the authentication finishes and - before the account is actually created in Keycloak. When enabled, users will be presented with the + before the account is actually created in Keycloak. When "On", users will be always presented with the update profile page asking for additional information in order to federate their identities. - If disabled, the account will be created with the minimal information obtained from the identity provider + When "On missing info", users will be presented with the update profile page only if some + mandatory information (email, first name, last name) is not provided by identity provider. + If "Off", the account will be created with the minimal information obtained from the identity provider during the authentication process. + + + Trust email + + + Allows you to trust email address returned from the social provider. If enabled then email address returned by the provider + is marked as 'verified' in the Keycloak user profile. This means that email verification step is skipped even + if "Verify email" feature is enabled in realm settings. + + GUI order diff --git a/docbook/reference/en/en-US/modules/user-federation.xml b/docbook/reference/en/en-US/modules/user-federation.xml index a8e6c17358..c8e9856a3c 100755 --- a/docbook/reference/en/en-US/modules/user-federation.xml +++ b/docbook/reference/en/en-US/modules/user-federation.xml @@ -24,7 +24,8 @@
LDAP and Active Directory Plugin - Keycloak comes with a built-in LDAP/AD plugin. Currently it is set up only to import username, email, first and last name. + Keycloak comes with a built-in LDAP/AD plugin. By default, it is set up only to import username, email, first and last name, but you are free + to configure mappers and add more attributes or delete default ones. It supports password validation via LDAP/AD protocols and different user metadata synchronization modes. To configure a federated LDAP store go to the admin console. Click on the Users menu option to get you to the user management page. Then click on the Federation submenu option. When @@ -41,7 +42,7 @@ READONLY - Username, email, first and last name will be unchangable. Keycloak will show an error + Username, email, first and last name and other mapped attributes will be unchangeable. Keycloak will show an error anytime anybody tries to update these fields. Also, password updates will not be supported. @@ -50,7 +51,7 @@ WRITABLE - Username, email, first and last name, and passwords can all be updated and will + Username, email, first and last name, other mapped attributes and passwords can all be updated and will be synchronized automatically with your LDAP store. @@ -158,6 +159,56 @@ In admin console, you can trigger sync directly or you can enable periodic changed or full sync.
+
+ LDAP/Federation mappers + + LDAP mappers are listeners, which are triggered by LDAP Federation provider at various points and provide + another extension point to LDAP integration. They are triggered during import LDAP user into Keycloak, registration Keycloak user back to LDAP or when querying LDAP user from Keycloak. + When you create LDAP Federation provider, Keycloak will automatically provide set of builtin mappers for this provider. + You are free to change this set and create new mapper or update/delete existing ones. + + + By default, we have those implementation of LDAP federation mapper: + + + User Attribute Mapper + + + This allows to specify which LDAP attribute is mapped to which attribute of Keycloak User. So for example you can configure + that LDAP attribute mail is supposed to be mapped to the UserModel attribute email in Keycloak database. + For this mapper implementation, there is always one-to-one mapping (one LDAP attribute mapped to one Keycloak UserModel attribute) + + + + + FullName Mapper + + + This allows to specify that fullname of user, which is saved in some LDAP attribute (usualy cn ) will be mapped to + firstName and lastname attributes of UserModel. Having cn to contain full name of user + is common case for some LDAP deployments. + + + + + Role Mapper + + + This allows to configure role mappings from LDAP into Keycloak role mappings. One Role mapper can be used to map LDAP roles + (usually groups from particular branch of LDAP tree) into roles corresponding to either realm roles or client roles of specified client. + It's not a problem to configure more Role mappers for same LDAP provider. So for example you can specify that role mappings from groups under + ou=main,dc=example,dc=org will be mapped to realm role mappings and role mappings from + groups under ou=finance,dc=example,dc=org will be mapped to client role mappings of client finance . + + + + + + By default, there is set of User Attribute mappers to map basic UserModel attributes username, first name, lastname and email to corresponding LDAP attributes. You are free to extend this and provide + more attribute mappings (For example to street, postalCode etc), delete firstName/lastname mapper and put fullName mapper instead, add role mappers etc. + Admin console provides tooltips, which should help on how to configure corresponding mappers. + +
Writing your own User Federation Provider diff --git a/events/api/src/main/java/org/keycloak/events/Details.java b/events/api/src/main/java/org/keycloak/events/Details.java index cee7475ec6..4a0a1ad007 100755 --- a/events/api/src/main/java/org/keycloak/events/Details.java +++ b/events/api/src/main/java/org/keycloak/events/Details.java @@ -24,5 +24,12 @@ public interface Details { String NODE_HOST = "node_host"; String REASON = "reason"; String REVOKED_CLIENT = "revoked_client"; + String CLIENT_SESSION_STATE = "client_session_state"; + String CLIENT_SESSION_HOST = "client_session_host"; + + String CONSENT = "consent"; + String CONSENT_VALUE_NO_CONSENT_REQUIRED = "no_consent_required"; // No consent is required by client + String CONSENT_VALUE_CONSENT_GRANTED = "consent_granted"; // Consent granted by user + String CONSENT_VALUE_PERSISTED_CONSENT = "persistent_consent"; // Persistent consent used (was already granted by user before) } diff --git a/events/api/src/main/java/org/keycloak/events/EventListenerSpi.java b/events/api/src/main/java/org/keycloak/events/EventListenerSpi.java index a16ad46bde..a3eedd17f7 100644 --- a/events/api/src/main/java/org/keycloak/events/EventListenerSpi.java +++ b/events/api/src/main/java/org/keycloak/events/EventListenerSpi.java @@ -10,7 +10,7 @@ import org.keycloak.provider.Spi; public class EventListenerSpi implements Spi { @Override - public boolean isPrivate() { + public boolean isInternal() { return false; } diff --git a/events/api/src/main/java/org/keycloak/events/EventStoreSpi.java b/events/api/src/main/java/org/keycloak/events/EventStoreSpi.java index af5f59f018..256b7e46ca 100644 --- a/events/api/src/main/java/org/keycloak/events/EventStoreSpi.java +++ b/events/api/src/main/java/org/keycloak/events/EventStoreSpi.java @@ -10,7 +10,7 @@ import org.keycloak.provider.Spi; public class EventStoreSpi implements Spi { @Override - public boolean isPrivate() { + public boolean isInternal() { return true; } diff --git a/events/mongo/src/main/java/org/keycloak/events/mongo/MongoAdminEventQuery.java b/events/mongo/src/main/java/org/keycloak/events/mongo/MongoAdminEventQuery.java index 4fdb9221ed..612294e09e 100644 --- a/events/mongo/src/main/java/org/keycloak/events/mongo/MongoAdminEventQuery.java +++ b/events/mongo/src/main/java/org/keycloak/events/mongo/MongoAdminEventQuery.java @@ -76,13 +76,17 @@ public class MongoAdminEventQuery implements AdminEventQuery{ @Override public AdminEventQuery fromTime(Date fromTime) { - query.put("time", BasicDBObjectBuilder.start("$gte", fromTime.getTime()).get()); + BasicDBObject time = query.containsField("time") ? (BasicDBObject) query.get("time") : new BasicDBObject(); + time.append("$gte", fromTime.getTime()); + query.put("time", time); return this; } @Override public AdminEventQuery toTime(Date toTime) { - query.put("time", BasicDBObjectBuilder.start("$lte", toTime.getTime()).get()); + BasicDBObject time = query.containsField("time") ? (BasicDBObject) query.get("time") : new BasicDBObject(); + time.append("$lte", toTime.getTime()); + query.put("time", time); return this; } diff --git a/examples/admin-client/example-realm.json b/examples/admin-client/example-realm.json index 3ce2eaa5ed..5676c557e9 100755 --- a/examples/admin-client/example-realm.json +++ b/examples/admin-client/example-realm.json @@ -3,7 +3,6 @@ "enabled": true, "sslRequired": "external", "registrationAllowed": true, - "passwordCredentialGrantAllowed": true, "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=", "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "requiredCredentials": [ "password" ], diff --git a/examples/basic-auth/basicauthrealm.json b/examples/basic-auth/basicauthrealm.json index b8b2acd20a..b17e7d80e7 100644 --- a/examples/basic-auth/basicauthrealm.json +++ b/examples/basic-auth/basicauthrealm.json @@ -6,7 +6,6 @@ "accessCodeLifespanUserAction": 300, "ssoSessionIdleTimeout": 600, "ssoSessionMaxLifespan": 36000, - "passwordCredentialGrantAllowed": true, "sslRequired": "external", "registrationAllowed": false, "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=", diff --git a/examples/broker/twitter-authentication/twitter-identity-provider-realm.json b/examples/broker/twitter-authentication/twitter-identity-provider-realm.json index a450eadfa3..691fd61f8f 100644 --- a/examples/broker/twitter-authentication/twitter-identity-provider-realm.json +++ b/examples/broker/twitter-authentication/twitter-identity-provider-realm.json @@ -2,7 +2,6 @@ "realm": "twitter-identity-provider-realm", "enabled": true, "sslRequired": "external", - "passwordCredentialGrantAllowed": true, "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=", "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "defaultRoles": [ "user" ], diff --git a/examples/demo-template/customer-app/src/main/webapp/customers/view.jsp b/examples/demo-template/customer-app/src/main/webapp/customers/view.jsp index 04a54bb1e6..440a9e9da2 100755 --- a/examples/demo-template/customer-app/src/main/webapp/customers/view.jsp +++ b/examples/demo-template/customer-app/src/main/webapp/customers/view.jsp @@ -33,7 +33,7 @@ Servlet User Principal <%=request.getUserPrincipal().getName()%> try { list = CustomerDatabaseClient.getCustomers(request); } catch (CustomerDatabaseClient.Failure failure) { - out.println("There was a failure processing request. You either didn't configure Keycloak properly, or maybe" + + out.println("There was a failure processing request. You either didn't configure Keycloak properly, or maybe " + "you just forgot to secure the database service?"); out.println("Status from database service invocation was: " + failure.getStatus()); return; diff --git a/examples/demo-template/testrealm.json b/examples/demo-template/testrealm.json index 92643aab31..9b83dfa6c5 100755 --- a/examples/demo-template/testrealm.json +++ b/examples/demo-template/testrealm.json @@ -6,7 +6,6 @@ "accessCodeLifespanUserAction": 300, "ssoSessionIdleTimeout": 600, "ssoSessionMaxLifespan": 36000, - "passwordCredentialGrantAllowed": true, "sslRequired": "external", "registrationAllowed": false, "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=", diff --git a/examples/fuse/testrealm.json b/examples/fuse/testrealm.json index f1b21ee728..ed112bc874 100644 --- a/examples/fuse/testrealm.json +++ b/examples/fuse/testrealm.json @@ -6,7 +6,6 @@ "accessCodeLifespanUserAction": 300, "ssoSessionIdleTimeout": 600, "ssoSessionMaxLifespan": 36000, - "passwordCredentialGrantAllowed": true, "sslRequired": "external", "registrationAllowed": false, "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=", diff --git a/examples/multi-tenant/tenant1-realm.json b/examples/multi-tenant/tenant1-realm.json index 8140b5127f..9dcaa00f25 100644 --- a/examples/multi-tenant/tenant1-realm.json +++ b/examples/multi-tenant/tenant1-realm.json @@ -7,7 +7,6 @@ "accessCodeLifespanUserAction": 6000, "sslRequired": "external", "registrationAllowed": false, - "passwordCredentialGrantAllowed": true, "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=", "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "requiredCredentials": [ "password" ], diff --git a/examples/multi-tenant/tenant2-realm.json b/examples/multi-tenant/tenant2-realm.json index 2cd0e618cf..643d5506c2 100644 --- a/examples/multi-tenant/tenant2-realm.json +++ b/examples/multi-tenant/tenant2-realm.json @@ -7,7 +7,6 @@ "accessCodeLifespanUserAction": 6000, "sslRequired": "external", "registrationAllowed": false, - "passwordCredentialGrantAllowed": true, "updateProfileOnInitialSocialLogin": false, "privateKey": "MIICXQIBAAKBgQDA0oJjgPQJhnVhOo51KauQGfLLreMFu64OJdKXRnfvAQJQTuKNwc5JrR63l/byyW1B6FgclABF818TtLvMCAkn4EuFwQZCZhg3x3+lFGiB/IzC6UAt4Bi0JQrTbdh83/U97GIPegvaDqiqEiQESEkbCZWxM6sh/34hQaAhCaFpMwIDAQABAoGADwFSvEOQuh0IjWRtKZjwjOo4BrmlbRDJ3rf6x2LoemTttSouXzGxx/H87fSZdxNNuU9HbBHoY4ko4POzmZEWhS0gV6UjM7VArc4YjID6Hh2tfU9vCbuuKZrRs7RjxL70b51WxycKc49PQ4JiR3g04punrpq2UzToPrm66zI+ICECQQD2Jauo6cXXoxHR0QychQf4dityZwFXUoR/8oI/YFiu9XwcWgSMwrFKUdWWNKYmrIRNqCBzrGyeiGdaAjsw41T3AkEAyIpn+XL7bek/uLno5/7ULauf2dFI6MEaHJixQJD7S6Tfo/CGuDK93H4K0GAdjgR0LA0tCnB09yyPCd5NmAYKpQJBAO7+BH4s/PsyScr+vs/6GpMTqXuap6KxbBUO0YfXdEPr9mVQwboqDxmp+0esNua1+n+sDlZBw/TpW+/42p/NGmECQF0sOQyjyH+TfGCmN7j6I7ioYZeA7h/9/9TDeK8n7SmDC8kOanlQUfgMs5eG4JRoK1WANaoA/8cLc9XA7EoynGUCQQDx/Gjg6qyWheVujxjKufH1XkqDNiQHClDRM1ntChCmGq/RmpVmce+mYeOYZ9eofv7UJUCBdamllRlB+056Ld2h", "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDA0oJjgPQJhnVhOo51KauQGfLLreMFu64OJdKXRnfvAQJQTuKNwc5JrR63l/byyW1B6FgclABF818TtLvMCAkn4EuFwQZCZhg3x3+lFGiB/IzC6UAt4Bi0JQrTbdh83/U97GIPegvaDqiqEiQESEkbCZWxM6sh/34hQaAhCaFpMwIDAQAB", diff --git a/examples/saml/testsaml.json b/examples/saml/testsaml.json index 7cf4a8ef52..4d9e7d4c19 100755 --- a/examples/saml/testsaml.json +++ b/examples/saml/testsaml.json @@ -3,7 +3,6 @@ "realm": "saml-demo", "enabled": true, "sslRequired": "external", - "passwordCredentialGrantAllowed": true, "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=", "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "requiredCredentials": [ "password" ], diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportSpi.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportSpi.java index 208cc6aa61..bcc91b4cfe 100644 --- a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportSpi.java +++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportSpi.java @@ -10,7 +10,7 @@ import org.keycloak.provider.Spi; public class ExportSpi implements Spi { @Override - public boolean isPrivate() { + public boolean isInternal() { return true; } diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ImportSpi.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ImportSpi.java index 90cb6ac79e..f6f5968966 100644 --- a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ImportSpi.java +++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ImportSpi.java @@ -10,7 +10,7 @@ import org.keycloak.provider.Spi; public class ImportSpi implements Spi { @Override - public boolean isPrivate() { + public boolean isInternal() { return true; } diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPConfig.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPConfig.java index 983de98ee3..3317b9d1d0 100644 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPConfig.java +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPConfig.java @@ -1,8 +1,6 @@ package org.keycloak.federation.ldap; -import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Properties; @@ -50,7 +48,14 @@ public class LDAPConfig { } public String getUsersDn() { - return config.get(LDAPConstants.USERS_DN); + String usersDn = config.get(LDAPConstants.USERS_DN); + + if (usersDn == null) { + // Just for the backwards compatibility 1.2 -> 1.3 . Should be removed later. + usersDn = config.get("userDnSuffix"); + } + + return usersDn; } public Collection getUserObjectClasses() { @@ -103,31 +108,13 @@ public class LDAPConfig { if (uuidAttrName == null) { // Differences of unique attribute among various vendors String vendor = getVendor(); - if (vendor != null) { - switch (vendor) { - case LDAPConstants.VENDOR_RHDS: - uuidAttrName = "nsuniqueid"; - break; - case LDAPConstants.VENDOR_TIVOLI: - uuidAttrName = "uniqueidentifier"; - break; - case LDAPConstants.VENDOR_NOVELL_EDIRECTORY: - uuidAttrName = "guid"; - break; - case LDAPConstants.VENDOR_ACTIVE_DIRECTORY: - uuidAttrName = LDAPConstants.OBJECT_GUID; - } - } - - if (uuidAttrName == null) { - uuidAttrName = LDAPConstants.ENTRY_UUID; - } + uuidAttrName = LDAPConstants.getUuidAttributeName(vendor); } return uuidAttrName; } - // TODO: Remove and use mapper instead + // TODO: Remove and use mapper instead? public boolean isUserAccountControlsAfterPasswordUpdate() { String userAccountCtrls = config.get(LDAPConstants.USER_ACCOUNT_CONTROLS_AFTER_PASSWORD_UPDATE); return userAccountCtrls==null ? false : Boolean.parseBoolean(userAccountCtrls); @@ -150,6 +137,12 @@ public class LDAPConfig { String rdn = config.get(LDAPConstants.RDN_LDAP_ATTRIBUTE); if (rdn == null) { rdn = getUsernameLdapAttribute(); + + if (rdn.equalsIgnoreCase(LDAPConstants.SAM_ACCOUNT_NAME)) { + // Just for the backwards compatibility 1.2 -> 1.3 . Should be removed later. + rdn = LDAPConstants.CN; + } + } return rdn; } diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java index 4524d485d6..7ad05d34d7 100755 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java @@ -308,8 +308,7 @@ public class LDAPFederationProvider implements UserFederationProvider { @Override public void preRemove(RealmModel realm, RoleModel role) { - // complete I don't think we have to do anything here - // TODO: requires implementation... Maybe mappers callback to ensure role deletion propagated to LDAP by RoleLDAPFederationMapper + // TODO: Maybe mappers callback to ensure role deletion propagated to LDAP by RoleLDAPFederationMapper? } public boolean validPassword(RealmModel realm, UserModel user, String password) { diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java index c020f5139b..876a96d5fc 100755 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java @@ -41,7 +41,7 @@ import java.util.Set; */ public class LDAPFederationProviderFactory extends UserFederationEventAwareProviderFactory { private static final Logger logger = Logger.getLogger(LDAPFederationProviderFactory.class); - public static final String PROVIDER_NAME = "ldap"; + public static final String PROVIDER_NAME = LDAPConstants.LDAP_PROVIDER; private LDAPIdentityStoreRegistry ldapStoreRegistry; @@ -79,7 +79,7 @@ public class LDAPFederationProviderFactory extends UserFederationEventAwareProvi // Best effort to create appropriate mappers according to our LDAP config @Override - protected void onProviderModelCreated(RealmModel realm, UserFederationProviderModel newProviderModel) { + public void onProviderModelCreated(RealmModel realm, UserFederationProviderModel newProviderModel) { LDAPConfig ldapConfig = new LDAPConfig(newProviderModel.getConfig()); boolean activeDirectory = ldapConfig.isActiveDirectory(); @@ -94,12 +94,42 @@ public class LDAPFederationProviderFactory extends UserFederationEventAwareProvi UserAttributeLDAPFederationMapper.READ_ONLY, readOnly); realm.addUserFederationMapper(mapperModel); - // For AD deployments with sAMAccountName is probably more common to map "cn" to full name of user - if (activeDirectory && usernameLdapAttribute.equalsIgnoreCase(LDAPConstants.SAM_ACCOUNT_NAME)) { - mapperModel = KeycloakModelUtils.createUserFederationMapperModel("full name", newProviderModel.getId(), FullNameLDAPFederationMapperFactory.PROVIDER_ID, - FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE, LDAPConstants.CN, - UserAttributeLDAPFederationMapper.READ_ONLY, readOnly); - realm.addUserFederationMapper(mapperModel); + // CN is typically used as RDN for Active Directory deployments + if (ldapConfig.getRdnLdapAttribute().equalsIgnoreCase(LDAPConstants.CN)) { + + if (usernameLdapAttribute.equalsIgnoreCase(LDAPConstants.CN)) { + + // For AD deployments with "cn" as username, we will map "givenName" to first name + mapperModel = KeycloakModelUtils.createUserFederationMapperModel("first name", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID, + UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.FIRST_NAME, + UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.GIVENNAME, + UserAttributeLDAPFederationMapper.READ_ONLY, readOnly); + realm.addUserFederationMapper(mapperModel); + + } else { + if (editMode == UserFederationProvider.EditMode.WRITABLE) { + + // For AD deployments with "sAMAccountName" as username and writable, we need to map "cn" as username as well (this is needed so we can register new users from KC into LDAP) and we will map "givenName" to first name. + mapperModel = KeycloakModelUtils.createUserFederationMapperModel("first name", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID, + UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.FIRST_NAME, + UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.GIVENNAME, + UserAttributeLDAPFederationMapper.READ_ONLY, readOnly); + realm.addUserFederationMapper(mapperModel); + + mapperModel = KeycloakModelUtils.createUserFederationMapperModel("username-cn", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID, + UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.USERNAME, + UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.CN, + UserAttributeLDAPFederationMapper.READ_ONLY, readOnly); + realm.addUserFederationMapper(mapperModel); + } else { + + // For read-only LDAP, we map "cn" as full name + mapperModel = KeycloakModelUtils.createUserFederationMapperModel("full name", newProviderModel.getId(), FullNameLDAPFederationMapperFactory.PROVIDER_ID, + FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE, LDAPConstants.CN, + UserAttributeLDAPFederationMapper.READ_ONLY, readOnly); + realm.addUserFederationMapper(mapperModel); + } + } } else { mapperModel = KeycloakModelUtils.createUserFederationMapperModel("first name", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID, UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.FIRST_NAME, diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPIdentityStoreRegistry.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPIdentityStoreRegistry.java index 97f347b80c..c9d7bb25df 100644 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPIdentityStoreRegistry.java +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPIdentityStoreRegistry.java @@ -24,7 +24,7 @@ public class LDAPIdentityStoreRegistry { // Ldap config might have changed for the realm. In this case, we must re-initialize Map config = model.getConfig(); if (context == null || !config.equals(context.config)) { - logLDAPConfig(model.getId(), config); + logLDAPConfig(model.getDisplayName(), config); LDAPIdentityStore store = createLdapIdentityStore(config); context = new LDAPIdentityStoreContext(config, store); @@ -34,10 +34,10 @@ public class LDAPIdentityStoreRegistry { } // Don't log LDAP password - private void logLDAPConfig(String fedProviderId, Map ldapConfig) { + private void logLDAPConfig(String fedProviderDisplayName, Map ldapConfig) { Map copy = new HashMap(ldapConfig); copy.remove(LDAPConstants.BIND_CREDENTIAL); - logger.infof("Creating new LDAP based partition manager for the Federation provider: " + fedProviderId + ", LDAP Configuration: " + copy); + logger.infof("Creating new LDAP based partition manager for the Federation provider: " + fedProviderDisplayName + ", LDAP Configuration: " + copy); } /** @@ -55,23 +55,6 @@ public class LDAPIdentityStoreRegistry { checkSystemProperty("com.sun.jndi.ldap.connect.pool.protocol", "plain"); checkSystemProperty("com.sun.jndi.ldap.connect.pool.debug", "off"); - /*String ldapLoginNameMapping = ldapConfig.get(LDAPConstants.USERNAME_LDAP_ATTRIBUTE); - if (ldapLoginNameMapping == null) { - ldapLoginNameMapping = activeDirectory ? LDAPConstants.CN : LDAPConstants.UID; - } - - String ldapFirstNameMapping = activeDirectory ? "givenName" : LDAPConstants.CN; - String createTimestampMapping = activeDirectory ? "whenCreated" : LDAPConstants.CREATE_TIMESTAMP; - String modifyTimestampMapping = activeDirectory ? "whenChanged" : LDAPConstants.MODIFY_TIMESTAMP; - String[] userObjectClasses = getUserObjectClasses(ldapConfig); */ - - -/* if (activeDirectory && ldapLoginNameMapping.equals("sAMAccountName")) { - ldapUserMappingConfig.setBindingDnPropertyName("fullName"); - ldapUserMappingConfig.addAttributeMapping("fullName", LDAPConstants.CN); - logger.infof("Using 'cn' attribute for DN of user and 'sAMAccountName' for username"); - } */ - return new LDAPIdentityStore(cfg); } diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPUtils.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPUtils.java index c350818e96..396d997dbe 100755 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPUtils.java +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPUtils.java @@ -45,16 +45,6 @@ public class LDAPUtils { return ldapUser; } - public static void removeAllUsers(LDAPFederationProvider ldapProvider, RealmModel realm) { - LDAPIdentityStore ldapStore = ldapProvider.getLdapIdentityStore(); - LDAPIdentityQuery ldapQuery = LDAPUtils.createQueryForUserSearch(ldapProvider, realm); - List allUsers = ldapQuery.getResultList(); - - for (LDAPObject ldapUser : allUsers) { - ldapStore.remove(ldapUser); - } - } - public static LDAPIdentityQuery createQueryForUserSearch(LDAPFederationProvider ldapProvider, RealmModel realm) { LDAPIdentityQuery ldapQuery = new LDAPIdentityQuery(ldapProvider); LDAPConfig config = ldapProvider.getLdapIdentityStore().getConfig(); diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/OrCondition.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/OrCondition.java index 436355b27f..d898ffd23a 100644 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/OrCondition.java +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/OrCondition.java @@ -1,7 +1,5 @@ package org.keycloak.federation.ldap.idm.query.internal; -import java.util.List; - import org.keycloak.federation.ldap.idm.query.Condition; import org.keycloak.federation.ldap.idm.query.QueryParameter; diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPIdentityStore.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPIdentityStore.java index 7ba169204e..3dbfd0a399 100644 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPIdentityStore.java +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPIdentityStore.java @@ -71,11 +71,12 @@ public class LDAPIdentityStore implements IdentityStore { public void add(LDAPObject ldapObject) { // id will be assigned by the ldap server if (ldapObject.getUuid() != null) { - throw new IllegalStateException("Can't add object with already assigned uuid"); + throw new ModelException("Can't add object with already assigned uuid"); } String entryDN = ldapObject.getDn().toString(); - this.operationManager.createSubContext(entryDN, extractAttributes(ldapObject, true)); + BasicAttributes ldapAttributes = extractAttributes(ldapObject, true); + this.operationManager.createSubContext(entryDN, ldapAttributes); ldapObject.setUuid(getEntryIdentifier(ldapObject)); if (logger.isTraceEnabled()) { @@ -108,20 +109,20 @@ public class LDAPIdentityStore implements IdentityStore { @Override public List fetchQueryResults(LDAPIdentityQuery identityQuery) { + if (identityQuery.getSorting() != null && !identityQuery.getSorting().isEmpty()) { + throw new ModelException("LDAP Identity Store does not yet support sorted queries."); + } + List results = new ArrayList<>(); try { - if (identityQuery.getSorting() != null && !identityQuery.getSorting().isEmpty()) { - throw new ModelException("LDAP Identity Store does not yet support sorted queries."); - } - String baseDN = identityQuery.getSearchDn(); for (Condition condition : identityQuery.getConditions()) { // Check if we are searching by ID String uuidAttrName = getConfig().getUuidLDAPAttributeName(); - if (condition.getParameter() != null && condition.getParameter().getName().equals(uuidAttrName)) { + if (condition.getParameter() != null && condition.getParameter().getName().equalsIgnoreCase(uuidAttrName)) { if (EqualCondition.class.isInstance(condition)) { EqualCondition equalCondition = (EqualCondition) condition; SearchResult search = this.operationManager @@ -147,7 +148,7 @@ public class LDAPIdentityStore implements IdentityStore { } for (SearchResult result : search) { - if (!result.getNameInNamespace().equals(baseDN)) { + if (!result.getNameInNamespace().equalsIgnoreCase(baseDN)) { results.add(populateAttributedType(result, identityQuery.getReturningReadOnlyLdapAttributes())); } } @@ -180,8 +181,8 @@ public class LDAPIdentityStore implements IdentityStore { public boolean validatePassword(LDAPObject user, String password) { String userDN = user.getDn().toString(); - if (logger.isDebugEnabled()) { - logger.debugf("Using DN [%s] for authentication of user", userDN); + if (logger.isTraceEnabled()) { + logger.tracef("Using DN [%s] for authentication of user", userDN); } if (operationManager.authenticate(userDN, password)) { @@ -258,7 +259,9 @@ public class LDAPIdentityStore implements IdentityStore { filter.append(getObjectClassesFilter(identityQuery.getObjectClasses())); filter.append(")"); - logger.infof("Using filter for LDAP search: %s", filter); + if (logger.isTraceEnabled()) { + logger.tracef("Using filter for LDAP search: %s . Searching in DN: %s", filter, identityQuery.getSearchDn()); + } return filter; } @@ -278,7 +281,7 @@ public class LDAPIdentityStore implements IdentityStore { QueryParameter queryParameter = condition.getParameter(); - if (!getConfig().getUuidLDAPAttributeName().equals(queryParameter.getName())) { + if (!getConfig().getUuidLDAPAttributeName().equalsIgnoreCase(queryParameter.getName())) { String attributeName = queryParameter.getName(); if (attributeName != null) { @@ -377,10 +380,6 @@ public class LDAPIdentityStore implements IdentityStore { ldapObject.setDn(dn); ldapObject.setRdnAttributeName(dn.getFirstRdnAttrName()); - if (logger.isTraceEnabled()) { - logger.tracef("Populating LDAP Object from DN [%s]", entryDN); - } - NamingEnumeration ldapAttributes = attributes.getAll(); // Exact name of attributes might be different @@ -400,7 +399,7 @@ public class LDAPIdentityStore implements IdentityStore { String ldapAttributeName = ldapAttribute.getID(); - if (ldapAttributeName.toLowerCase().equals(getConfig().getUuidLDAPAttributeName().toLowerCase())) { + if (ldapAttributeName.equalsIgnoreCase(getConfig().getUuidLDAPAttributeName())) { Object uuidValue = ldapAttribute.get(); ldapObject.setUuid(this.operationManager.decodeEntryUUID(uuidValue)); } else { @@ -411,12 +410,9 @@ public class LDAPIdentityStore implements IdentityStore { attrValues.add(attrVal); } - if (ldapAttributeName.toLowerCase().equals(LDAPConstants.OBJECT_CLASS)) { + if (ldapAttributeName.equalsIgnoreCase(LDAPConstants.OBJECT_CLASS)) { ldapObject.setObjectClasses(attrValues); } else { - if (logger.isTraceEnabled()) { - logger.tracef("Populating ldap attribute [%s] with value [%s] for DN [%s].", ldapAttributeName, attrValues.toString(), entryDN); - } if (attrValues.size() == 1) { ldapObject.setAttribute(ldapAttributeName, attrValues.iterator().next()); } else { @@ -430,6 +426,9 @@ public class LDAPIdentityStore implements IdentityStore { } } + if (logger.isTraceEnabled()) { + logger.tracef("Found ldap object [%s] and populated with the attributes [%s]. Read-only attributes are [%s]", ldapObject.getDn().toString(), ldapObject.getAttributes(), ldapObject.getReadOnlyAttributeNames()); + } return ldapObject; } catch (Exception e) { @@ -444,7 +443,7 @@ public class LDAPIdentityStore implements IdentityStore { for (Map.Entry attrEntry : ldapObject.getAttributes().entrySet()) { String attrName = attrEntry.getKey(); Object attrValue = attrEntry.getValue(); - if (!ldapObject.getReadOnlyAttributeNames().contains(attrName) && (isCreate || !ldapObject.getRdnAttributeName().equals(attrName))) { + if (!ldapObject.getReadOnlyAttributeNames().contains(attrName) && (isCreate || !ldapObject.getRdnAttributeName().equalsIgnoreCase(attrName))) { if (String.class.isInstance(attrValue)) { if (attrValue.toString().trim().length() == 0) { @@ -461,7 +460,7 @@ public class LDAPIdentityStore implements IdentityStore { } else if (attrValue == null || attrValue.toString().trim().length() == 0) { entryAttributes.put(attrName, LDAPConstants.EMPTY_ATTRIBUTE_VALUE); } else { - throw new IllegalArgumentException("Unexpected type of value of argument " + attrName + ". Value is " + attrValue); + throw new ModelException("Unexpected type of value of argument " + attrName + ". Value is " + attrValue); } } } @@ -473,10 +472,10 @@ public class LDAPIdentityStore implements IdentityStore { for (String objectClassValue : ldapObject.getObjectClasses()) { objectClassAttribute.add(objectClassValue); - if (objectClassValue.equals(LDAPConstants.GROUP_OF_NAMES) - || objectClassValue.equals(LDAPConstants.GROUP_OF_ENTRIES) - || objectClassValue.equals(LDAPConstants.GROUP_OF_UNIQUE_NAMES)) { - entryAttributes.put(LDAPConstants.MEMBER, LDAPConstants.EMPTY_ATTRIBUTE_VALUE); + if (objectClassValue.equalsIgnoreCase(LDAPConstants.GROUP_OF_NAMES) + || objectClassValue.equalsIgnoreCase(LDAPConstants.GROUP_OF_ENTRIES) + || objectClassValue.equalsIgnoreCase(LDAPConstants.GROUP_OF_UNIQUE_NAMES)) { + entryAttributes.put(LDAPConstants.MEMBER, LDAPConstants.EMPTY_MEMBER_ATTRIBUTE_VALUE); } } diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPOperationManager.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPOperationManager.java index c097e4e1a8..8d934f3a11 100644 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPOperationManager.java +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPOperationManager.java @@ -128,8 +128,8 @@ public class LDAPOperationManager { execute(new LdapOperation() { @Override public SearchResult execute(LdapContext context) throws NamingException { - if (logger.isDebugEnabled()) { - logger.debugf("Removing entry with DN [%s]", entryDn); + if (logger.isTraceEnabled()) { + logger.tracef("Removing entry with DN [%s]", entryDn); } destroySubcontext(context, entryDn); return null; @@ -357,8 +357,8 @@ public class LDAPOperationManager { public void modifyAttributes(final String dn, final ModificationItem[] mods) { try { - if (logger.isDebugEnabled()) { - logger.debugf("Modifying attributes for entry [%s]: [", dn); + if (logger.isTraceEnabled()) { + logger.tracef("Modifying attributes for entry [%s]: [", dn); for (ModificationItem item : mods) { Object values; @@ -369,10 +369,10 @@ public class LDAPOperationManager { values = "No values"; } - logger.debugf(" Op [%s]: %s = %s", item.getModificationOp(), item.getAttribute().getID(), values); + logger.tracef(" Op [%s]: %s = %s", item.getModificationOp(), item.getAttribute().getID(), values); } - logger.debugf("]"); + logger.tracef("]"); } execute(new LdapOperation() { @@ -389,18 +389,18 @@ public class LDAPOperationManager { public void createSubContext(final String name, final Attributes attributes) { try { - if (logger.isDebugEnabled()) { - logger.debugf("Creating entry [%s] with attributes: [", name); + if (logger.isTraceEnabled()) { + logger.tracef("Creating entry [%s] with attributes: [", name); NamingEnumeration all = attributes.getAll(); while (all.hasMore()) { Attribute attribute = all.next(); - logger.debugf(" %s = %s", attribute.getID(), attribute.get()); + logger.tracef(" %s = %s", attribute.getID(), attribute.get()); } - logger.debugf("]"); + logger.tracef("]"); } execute(new LdapOperation() { @@ -513,7 +513,6 @@ public class LDAPOperationManager { context = createLdapContext(); return operation.execute(context); } catch (NamingException ne) { - logger.error("Could not create Ldap context or operation execution error.", ne); throw ne; } finally { if (context != null) { diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapper.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapper.java index 109b0b0775..7466778b5b 100644 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapper.java +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapper.java @@ -1,24 +1,19 @@ package org.keycloak.federation.ldap.mappers; import java.util.HashSet; -import java.util.List; import java.util.Set; import org.keycloak.federation.ldap.LDAPFederationProvider; -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.EqualCondition; import org.keycloak.federation.ldap.idm.query.internal.LDAPIdentityQuery; -import org.keycloak.mappers.UserFederationMapper; -import org.keycloak.models.KeycloakSession; import org.keycloak.models.LDAPConstants; import org.keycloak.models.RealmModel; import org.keycloak.models.UserFederationMapperModel; import org.keycloak.models.UserFederationProvider; import org.keycloak.models.UserModel; -import org.keycloak.provider.ProviderConfigProperty; /** * Mapper useful for the LDAP deployments when some attribute (usually CN) is mapped to full name of user @@ -35,7 +30,7 @@ public class FullNameLDAPFederationMapper extends AbstractLDAPFederationMapper { String ldapFullNameAttrName = getLdapFullNameAttrName(mapperModel); String fullName = ldapUser.getAttributeAsString(ldapFullNameAttrName); fullName = fullName.trim(); - if (fullName != null) { + if (fullName != null && !fullName.trim().isEmpty()) { int lastSpaceIndex = fullName.lastIndexOf(" "); if (lastSpaceIndex == -1) { user.setLastName(fullName); @@ -130,7 +125,7 @@ public class FullNameLDAPFederationMapper extends AbstractLDAPFederationMapper { fullName = firstNameCondition.getValue() + " " + lastNameCondition.getValue(); } else if (firstNameCondition != null) { fullName = (String) firstNameCondition.getValue(); - } else if (firstNameCondition != null) { + } else if (lastNameCondition != null) { fullName = (String) lastNameCondition.getValue(); } else { return; diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapper.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapper.java index 763ca3c172..165309c1e7 100644 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapper.java +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapper.java @@ -1,14 +1,11 @@ package org.keycloak.federation.ldap.mappers; -import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.TreeSet; -import javax.naming.directory.SearchControls; - import org.jboss.logging.Logger; import org.keycloak.federation.ldap.LDAPFederationProvider; import org.keycloak.federation.ldap.idm.model.LDAPDn; @@ -82,8 +79,7 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper { RoleContainerModel roleContainer = getTargetRoleContainer(mapperModel, realm); RoleModel role = roleContainer.getRole(roleName); - // TODO: debug - logger.infof("Granting role [%s] to user [%s] during import from LDAP", roleName, user.getUsername()); + logger.debugf("Granting role [%s] to user [%s] during import from LDAP", roleName, user.getUsername()); user.grantRole(role); } } @@ -97,6 +93,8 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper { // Sync roles from LDAP tree and create them in local Keycloak DB (if they don't exist here yet) protected void syncRolesFromLDAP(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, RealmModel realm) { if (!rolesSyncedModels.contains(mapperModel.getId())) { + logger.debugf("Syncing roles from LDAP into Keycloak DB. Mapper is [%s], LDAP provider is [%s]", mapperModel.getName(), ldapProvider.getModel().getDisplayName()); + LDAPIdentityQuery ldapQuery = createRoleQuery(mapperModel, ldapProvider); // Send query @@ -108,7 +106,6 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper { String roleName = ldapRole.getAttributeAsString(rolesRdnAttr); if (roleContainer.getRole(roleName) == null) { - // TODO: rather change to debug logger.infof("Syncing role [%s] from LDAP to keycloak DB", roleName); roleContainer.addRole(roleName); } @@ -127,7 +124,7 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper { String rolesDn = getRolesDn(mapperModel); ldapQuery.setSearchDn(rolesDn); - Collection roleObjectClasses = getRoleObjectClasses(mapperModel); + Collection roleObjectClasses = getRoleObjectClasses(mapperModel, ldapProvider); ldapQuery.addObjectClasses(roleObjectClasses); String rolesRdnAttr = getRoleNameLdapAttribute(mapperModel); @@ -145,11 +142,11 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper { } else { String clientId = mapperModel.getConfig().get(CLIENT_ID); if (clientId == null) { - throw new IllegalStateException("Using client roles mapping is requested, but parameter client.id not found!"); + throw new ModelException("Using client roles mapping is requested, but parameter client.id not found!"); } ClientModel client = realm.getClientByClientId(clientId); if (client == null) { - throw new IllegalStateException("Can't found requested client with clientId: " + clientId); + throw new ModelException("Can't found requested client with clientId: " + clientId); } return client; } @@ -158,7 +155,7 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper { protected String getRolesDn(UserFederationMapperModel mapperModel) { String rolesDn = mapperModel.getConfig().get(ROLES_DN); if (rolesDn == null) { - throw new IllegalStateException("Roles DN is null! Check your configuration"); + throw new ModelException("Roles DN is null! Check your configuration"); } return rolesDn; } @@ -173,10 +170,11 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper { return membershipAttrName!=null ? membershipAttrName : LDAPConstants.MEMBER; } - protected Collection getRoleObjectClasses(UserFederationMapperModel mapperModel) { + protected Collection getRoleObjectClasses(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider) { String objectClasses = mapperModel.getConfig().get(ROLE_OBJECT_CLASSES); if (objectClasses == null) { - objectClasses = "groupOfNames"; + // For Active directory, the default is 'group' . For other servers 'groupOfNames' + objectClasses = ldapProvider.getLdapIdentityStore().getConfig().isActiveDirectory() ? LDAPConstants.GROUP : LDAPConstants.GROUP_OF_NAMES; } String[] objClasses = objectClasses.split(","); @@ -192,26 +190,25 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper { private Mode getMode(UserFederationMapperModel mapperModel) { String modeString = mapperModel.getConfig().get(MODE); - if (modeString == null || modeString.trim().length() == 0) { - return Mode.LDAP_ONLY; + if (modeString == null || modeString.isEmpty()) { + throw new ModelException("Mode is missing! Check your configuration"); } return Enum.valueOf(Mode.class, modeString.toUpperCase()); } - protected LDAPObject createLDAPRole(UserFederationMapperModel mapperModel, String roleName, LDAPFederationProvider ldapProvider) { + public LDAPObject createLDAPRole(UserFederationMapperModel mapperModel, String roleName, LDAPFederationProvider ldapProvider) { LDAPObject ldapObject = new LDAPObject(); String roleNameAttribute = getRoleNameLdapAttribute(mapperModel); ldapObject.setRdnAttributeName(roleNameAttribute); - ldapObject.setObjectClasses(getRoleObjectClasses(mapperModel)); + ldapObject.setObjectClasses(getRoleObjectClasses(mapperModel, ldapProvider)); ldapObject.setAttribute(roleNameAttribute, roleName); LDAPDn roleDn = LDAPDn.fromString(getRolesDn(mapperModel)); roleDn.addFirst(roleNameAttribute, roleName); ldapObject.setDn(roleDn); - // TODO: debug - logger.infof("Creating role to [%s] to LDAP with DN [%s]", roleName, roleDn.toString()); + logger.infof("Creating role [%s] to LDAP with DN [%s]", roleName, roleDn.toString()); ldapProvider.getLdapIdentityStore().add(ldapObject); return ldapObject; } @@ -233,9 +230,9 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper { Set memberships = getExistingMemberships(mapperModel, ldapRole); memberships.remove(ldapUser.getDn().toString()); - // Some membership placeholder needs to be always here as "member" is mandatory attribute on some LDAP servers - if (memberships.size() == 0) { - memberships.add(LDAPConstants.EMPTY_ATTRIBUTE_VALUE); + // Some membership placeholder needs to be always here as "member" is mandatory attribute on some LDAP servers. But on active directory! (Empty membership is not allowed here) + if (memberships.size() == 0 && !ldapProvider.getLdapIdentityStore().getConfig().isActiveDirectory()) { + memberships.add(LDAPConstants.EMPTY_MEMBER_ATTRIBUTE_VALUE); } ldapRole.setAttribute(getMembershipLdapAttribute(mapperModel), memberships); @@ -280,23 +277,6 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper { return ldapQuery.getResultList(); } - protected Set getLDAPRoleMappingsConverted(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, LDAPObject ldapUser, RoleContainerModel roleContainer) { - List ldapRoles = getLDAPRoleMappings(mapperModel, ldapProvider, ldapUser); - - Set roles = new HashSet(); - String roleNameLdapAttr = getRoleNameLdapAttribute(mapperModel); - for (LDAPObject role : ldapRoles) { - String roleName = role.getAttributeAsString(roleNameLdapAttr); - RoleModel modelRole = roleContainer.getRole(roleName); - if (modelRole == null) { - // Add role to local DB - modelRole = roleContainer.addRole(roleName); - } - roles.add(modelRole); - } - return roles; - } - @Override public UserModel proxy(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, LDAPObject ldapUser, UserModel delegate, RealmModel realm) { final Mode mode = getMode(mapperModel); @@ -323,6 +303,9 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper { private final RealmModel realm; private final Mode mode; + // Avoid loading role mappings from LDAP more times per-request + private Set cachedLDAPRoleMappings; + public LDAPRoleMappingsUserDelegate(UserModel user, UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, LDAPObject ldapUser, RealmModel realm, Mode mode) { super(user); @@ -387,6 +370,7 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper { if (role.getContainer().equals(roleContainer)) { // We need to create new role mappings in LDAP + cachedLDAPRoleMappings = null; addRoleMappingInLDAP(mapperModel, role.getName(), ldapProvider, ldapUser); } else { super.grantRole(role); @@ -417,6 +401,30 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper { return modelRoleMappings; } + protected Set getLDAPRoleMappingsConverted(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, LDAPObject ldapUser, RoleContainerModel roleContainer) { + if (cachedLDAPRoleMappings != null) { + return new HashSet<>(cachedLDAPRoleMappings); + } + + List ldapRoles = getLDAPRoleMappings(mapperModel, ldapProvider, ldapUser); + + Set roles = new HashSet(); + String roleNameLdapAttr = getRoleNameLdapAttribute(mapperModel); + for (LDAPObject role : ldapRoles) { + String roleName = role.getAttributeAsString(roleNameLdapAttr); + RoleModel modelRole = roleContainer.getRole(roleName); + if (modelRole == null) { + // Add role to local DB + modelRole = roleContainer.addRole(roleName); + } + roles.add(modelRole); + } + + cachedLDAPRoleMappings = new HashSet<>(roles); + + return roles; + } + @Override public void deleteRoleMapping(RoleModel role) { RoleContainerModel roleContainer = getTargetRoleContainer(mapperModel, realm); @@ -440,6 +448,7 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper { throw new ModelException("Not possible to delete LDAP role mappings as mapper mode is READ_ONLY"); } else { // Delete ldap role mappings + cachedLDAPRoleMappings = null; deleteRoleMappingInLDAP(mapperModel, ldapProvider, ldapUser, ldapRole); } } diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapperFactory.java index b02e2d3831..2d6d24cc41 100644 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapperFactory.java +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapperFactory.java @@ -1,25 +1,33 @@ package org.keycloak.federation.ldap.mappers; import java.util.ArrayList; -import java.util.Arrays; import java.util.LinkedList; import java.util.List; -import java.util.Map; +import org.jboss.logging.Logger; +import org.keycloak.federation.ldap.LDAPFederationProvider; import org.keycloak.mappers.MapperConfigValidationException; import org.keycloak.mappers.UserFederationMapper; -import org.keycloak.models.ClientModel; import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.LDAPConstants; import org.keycloak.models.RealmModel; import org.keycloak.models.UserFederationMapperModel; +import org.keycloak.models.UserFederationProvider; +import org.keycloak.models.UserFederationProviderFactory; +import org.keycloak.models.UserFederationProviderModel; +import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.provider.ProviderConfigProperty; +import org.keycloak.provider.ProviderEvent; +import org.keycloak.provider.ProviderEventListener; /** * @author Marek Posolda */ public class RoleLDAPFederationMapperFactory extends AbstractLDAPFederationMapperFactory { + private static final Logger logger = Logger.getLogger(RoleLDAPFederationMapperFactory.class); + public static final String PROVIDER_ID = "role-ldap-mapper"; protected static final List configProperties = new ArrayList(); @@ -40,8 +48,8 @@ public class RoleLDAPFederationMapperFactory extends AbstractLDAPFederationMappe configProperties.add(membershipLDAPAttribute); ProviderConfigProperty roleObjectClasses = createConfigProperty(RoleLDAPFederationMapper.ROLE_OBJECT_CLASSES, "Role Object Classes", - "Object classes of the role object divided by comma (if more values needed). In typical LDAP deployment it could be 'groupOfNames' or 'groupOfEntries' ", - ProviderConfigProperty.STRING_TYPE, LDAPConstants.GROUP_OF_NAMES); + "Object class (or classes) of the role object. It's divided by comma if more classes needed. In typical LDAP deployment it could be 'groupOfNames' . In Active Directory it's usually 'group' ", + ProviderConfigProperty.STRING_TYPE, null); configProperties.add(roleObjectClasses); List modes = new LinkedList(); @@ -90,9 +98,46 @@ public class RoleLDAPFederationMapperFactory extends AbstractLDAPFederationMappe return PROVIDER_ID; } + // Sync roles from LDAP to Keycloak DB during creation or update of mapperModel + @Override + public void postInit(KeycloakSessionFactory factory) { + factory.register(new ProviderEventListener() { + + @Override + public void onEvent(ProviderEvent event) { + if (event instanceof RealmModel.UserFederationMapperEvent) { + RealmModel.UserFederationMapperEvent mapperEvent = (RealmModel.UserFederationMapperEvent)event; + UserFederationMapperModel mapperModel = mapperEvent.getFederationMapper(); + RealmModel realm = mapperEvent.getRealm(); + KeycloakSession session = mapperEvent.getSession(); + + if (mapperModel.getFederationMapperType().equals(PROVIDER_ID)) { + try { + String federationProviderId = mapperModel.getFederationProviderId(); + UserFederationProviderModel providerModel = KeycloakModelUtils.findUserFederationProviderById(federationProviderId, realm); + if (providerModel == null) { + throw new IllegalStateException("Can't find federation provider with ID [" + federationProviderId + "] in realm " + realm.getName()); + } + + UserFederationProviderFactory ldapFactory = (UserFederationProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(UserFederationProvider.class, providerModel.getProviderName()); + LDAPFederationProvider ldapProvider = (LDAPFederationProvider) ldapFactory.getInstance(session, providerModel); + + // Sync roles + new RoleLDAPFederationMapper().syncRolesFromLDAP(mapperModel, ldapProvider, realm); + } catch (Exception e) { + logger.warn("Exception during initial sync of roles from LDAP.", e); + } + } + } + } + + }); + } + @Override public void validateConfig(UserFederationMapperModel mapperModel) throws MapperConfigValidationException { checkMandatoryConfigAttribute(RoleLDAPFederationMapper.ROLES_DN, "LDAP Roles DN", mapperModel); + checkMandatoryConfigAttribute(RoleLDAPFederationMapper.MODE, "Mode", mapperModel); String realmMappings = mapperModel.getConfig().get(RoleLDAPFederationMapper.USE_REALM_ROLES_MAPPING); boolean useRealmMappings = Boolean.parseBoolean(realmMappings); diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapper.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapper.java index c4c30291dd..dd139b0b1f 100644 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapper.java +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapper.java @@ -1,18 +1,13 @@ package org.keycloak.federation.ldap.mappers; -import java.io.Serializable; import java.lang.reflect.Method; -import java.util.List; import java.util.Map; import org.keycloak.federation.ldap.LDAPFederationProvider; -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.mappers.UserFederationMapper; -import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.UserFederationMapperModel; import org.keycloak.models.UserFederationProvider; @@ -20,7 +15,6 @@ import org.keycloak.models.UserModel; import org.keycloak.models.utils.reflection.Property; import org.keycloak.models.utils.reflection.PropertyCriteria; import org.keycloak.models.utils.reflection.PropertyQueries; -import org.keycloak.provider.ProviderConfigProperty; /** * @author Marek Posolda @@ -55,7 +49,7 @@ public class UserAttributeLDAPFederationMapper extends AbstractLDAPFederationMap String ldapAttrName = mapperModel.getConfig().get(LDAP_ATTRIBUTE); Object ldapAttrValue = ldapUser.getAttribute(ldapAttrName); - if (ldapAttrValue != null) { + if (ldapAttrValue != null && !ldapAttrValue.toString().trim().isEmpty()) { Property userModelProperty = userModelProperties.get(userModelAttrName); if (userModelProperty != null) { @@ -124,7 +118,7 @@ public class UserAttributeLDAPFederationMapper extends AbstractLDAPFederationMap } protected void setLDAPAttribute(String modelAttrName, String value) { - if (modelAttrName.equals(userModelAttrName)) { + if (modelAttrName.equalsIgnoreCase(userModelAttrName)) { if (logger.isTraceEnabled()) { logger.tracef("Pushing user attribute to LDAP. Model attribute name: %s, LDAP attribute name: %s, Attribute value: %s", modelAttrName, ldapAttrName, value); } @@ -157,7 +151,7 @@ public class UserAttributeLDAPFederationMapper extends AbstractLDAPFederationMap // Change conditions and use ldapAttribute instead of userModel for (Condition condition : query.getConditions()) { QueryParameter param = condition.getParameter(); - if (param != null && param.getName().equals(userModelAttrName)) { + if (param != null && param.getName().equalsIgnoreCase(userModelAttrName)) { param.setName(ldapAttrName); } } diff --git a/forms/account-api/src/main/java/org/keycloak/account/AccountSpi.java b/forms/account-api/src/main/java/org/keycloak/account/AccountSpi.java index 2c9843ebc1..3d2072a652 100644 --- a/forms/account-api/src/main/java/org/keycloak/account/AccountSpi.java +++ b/forms/account-api/src/main/java/org/keycloak/account/AccountSpi.java @@ -10,7 +10,7 @@ import org.keycloak.provider.Spi; public class AccountSpi implements Spi { @Override - public boolean isPrivate() { + public boolean isInternal() { return true; } diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountBean.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountBean.java index 242225b1b4..20323705fe 100755 --- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountBean.java +++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountBean.java @@ -38,7 +38,7 @@ public class AccountBean { } public String getUsername() { - return user.getUsername(); + return profileFormData != null ? profileFormData.getFirst("username") : user.getUsername(); } public String getEmail() { diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/RealmBean.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/RealmBean.java index b0a5eb4959..05a84c9618 100755 --- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/RealmBean.java +++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/RealmBean.java @@ -46,4 +46,8 @@ public class RealmBean { return realm.getSupportedLocales(); } + public boolean isEditUsernameAllowed() { + return realm.isEditUsernameAllowed(); + } + } diff --git a/forms/common-freemarker/src/main/java/org/keycloak/freemarker/ThemeSpi.java b/forms/common-freemarker/src/main/java/org/keycloak/freemarker/ThemeSpi.java index 94db863159..9e97cb9f9d 100644 --- a/forms/common-freemarker/src/main/java/org/keycloak/freemarker/ThemeSpi.java +++ b/forms/common-freemarker/src/main/java/org/keycloak/freemarker/ThemeSpi.java @@ -10,7 +10,7 @@ import org.keycloak.provider.Spi; public class ThemeSpi implements Spi { @Override - public boolean isPrivate() { + public boolean isInternal() { return true; } diff --git a/forms/common-themes/src/main/resources/theme/base/account/account.ftl b/forms/common-themes/src/main/resources/theme/base/account/account.ftl index 922d9c522d..d2a6af16e0 100755 --- a/forms/common-themes/src/main/resources/theme/base/account/account.ftl +++ b/forms/common-themes/src/main/resources/theme/base/account/account.ftl @@ -16,11 +16,11 @@
- + <#if realm.editUsernameAllowed>*
- + disabled="disabled" value="${(account.username!'')?html}"/>
diff --git a/forms/common-themes/src/main/resources/theme/base/account/messages/messages_de.properties b/forms/common-themes/src/main/resources/theme/base/account/messages/messages_de.properties index add9daa906..ad3f45963b 100644 --- a/forms/common-themes/src/main/resources/theme/base/account/messages/messages_de.properties +++ b/forms/common-themes/src/main/resources/theme/base/account/messages/messages_de.properties @@ -80,7 +80,7 @@ totpStep1=Installieren Sie Fr totpStep2=Open the application and scan the barcode or enter the key. totpStep3=Enter the one-time code provided by the application and click Save to finish the setup. +missingUsernameMessage=Please specify username. missingFirstNameMessage=Please specify first name. invalidEmailMessage=Invalid email address. missingLastNameMessage=Please specify last name. diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js index f60e20e65f..53ad7ae531 100755 --- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js @@ -597,7 +597,7 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload if (instance && instance.alias) { } else { - $scope.identityProvider.updateProfileFirstLogin = false; + $scope.identityProvider.updateProfileFirstLoginMode = "on"; } }; @@ -645,7 +645,7 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload } else { $scope.identityProvider.config.nameIDPolicyFormat = $scope.nameIdFormats[0].format; - $scope.identityProvider.updateProfileFirstLogin = false; + $scope.identityProvider.updateProfileFirstLoginMode = "off"; } } @@ -663,7 +663,7 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload $scope.identityProvider.alias = providerFactory.id; $scope.identityProvider.providerId = providerFactory.id; $scope.identityProvider.enabled = true; - $scope.identityProvider.updateProfileFirstLogin = false; + $scope.identityProvider.updateProfileFirstLoginMode = "off"; $scope.identityProvider.authenticateByDefault = false; $scope.newIdentityProvider = true; } diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js index 256169288a..92e5db2c38 100755 --- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js @@ -198,6 +198,7 @@ module.controller('UserListCtrl', function($scope, realm, User) { module.controller('UserDetailCtrl', function($scope, realm, user, User, UserFederationInstances, $location, Dialog, Notifications) { $scope.realm = realm; $scope.create = !user.id; + $scope.editUsername = $scope.create || $scope.realm.editUsernameAllowed; if ($scope.create) { $scope.user = { enabled: true, attributes: {} } diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js index af9b3a2ad7..763d57f004 100755 --- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js @@ -559,7 +559,7 @@ function roleControl($scope, realm, role, roles, clients, $scope.realmMappings.push(role); } } - $scope.selectRealmRoles = []; + $scope.selectedRealmRoles = []; }); }; diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-default-roles.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-default-roles.html index 5a27f56a06..732aeeacbd 100755 --- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-default-roles.html +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-default-roles.html @@ -17,7 +17,7 @@