keycloak-uncached
Changes
connections/file/src/main/java/org/keycloak/connections/file/DefaultFileConnectionProviderFactory.java 23(+21 -2)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheRealmProvider.java 7(+7 -0)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheRealmProvider.java 6(+6 -0)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MigrationModelAdapter.java 57(+57 -0)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoRealmProvider.java 12(+12 -0)
Details
diff --git a/connections/file/src/main/java/org/keycloak/connections/file/DefaultFileConnectionProviderFactory.java b/connections/file/src/main/java/org/keycloak/connections/file/DefaultFileConnectionProviderFactory.java
index 0c9b5fe..26c14de 100755
--- a/connections/file/src/main/java/org/keycloak/connections/file/DefaultFileConnectionProviderFactory.java
+++ b/connections/file/src/main/java/org/keycloak/connections/file/DefaultFileConnectionProviderFactory.java
@@ -24,6 +24,8 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+
+import org.codehaus.jackson.JsonToken;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.exportimport.Strategy;
@@ -84,7 +86,22 @@ public class DefaultFileConnectionProviderFactory implements FileConnectionProvi
FileInputStream fis = null;
try {
fis = new FileInputStream(kcdata);
+ Model model = JsonSerialization.readValue(fis, Model.class);
ImportUtils.importFromStream(session, JsonSerialization.mapper, fis, Strategy.IGNORE_EXISTING);
+ session.realms().getMigrationModel().setStoredVersion(model.getModelVersion());
+
+ List<RealmRepresentation> realmReps = new ArrayList<RealmRepresentation>();
+ for (RealmRepresentation realmRep : model.getRealms()) {
+ if (Config.getAdminRealm().equals(realmRep.getRealm())) {
+ realmReps.add(0, realmRep);
+ } else {
+ realmReps.add(realmRep);
+ }
+ }
+ for (RealmRepresentation realmRep : realmReps) {
+ ImportUtils.importRealm(session, realmRep, Strategy.IGNORE_EXISTING);
+ }
+
} catch (IOException ioe) {
logger.error("Unable to read model file " + kcdata.getAbsolutePath(), ioe);
} finally {
@@ -128,8 +145,10 @@ public class DefaultFileConnectionProviderFactory implements FileConnectionProvi
for (RealmModel realm : realms) {
reps.add(ExportUtils.exportRealm(session, realm, true));
}
-
- JsonSerialization.prettyMapper.writeValue(outStream, reps);
+ Model model = new Model();
+ model.setRealms(reps);
+ model.setModelVersion(session.realms().getMigrationModel().getStoredVersion());
+ JsonSerialization.prettyMapper.writeValue(outStream, model);
}
@Override
diff --git a/connections/file/src/main/java/org/keycloak/connections/file/InMemoryModel.java b/connections/file/src/main/java/org/keycloak/connections/file/InMemoryModel.java
old mode 100644
new mode 100755
index 8fddce6..2476b44
--- a/connections/file/src/main/java/org/keycloak/connections/file/InMemoryModel.java
+++ b/connections/file/src/main/java/org/keycloak/connections/file/InMemoryModel.java
@@ -37,6 +37,8 @@ public class InMemoryModel {
// realmId, userId, userModel
private final Map<String, Map<String,UserModel>> allUsers = new HashMap<String, Map<String,UserModel>>();
+ private String modelVersion;
+
public InMemoryModel() {
}
@@ -45,6 +47,14 @@ public class InMemoryModel {
allUsers.put(id, new HashMap<String, UserModel>());
}
+ public String getModelVersion() {
+ return modelVersion;
+ }
+
+ public void setModelVersion(String modelVersion) {
+ this.modelVersion = modelVersion;
+ }
+
public RealmModel getRealm(String id) {
return allRealms.get(id);
}
diff --git a/connections/file/src/main/java/org/keycloak/connections/file/Model.java b/connections/file/src/main/java/org/keycloak/connections/file/Model.java
new file mode 100755
index 0000000..7350c38
--- /dev/null
+++ b/connections/file/src/main/java/org/keycloak/connections/file/Model.java
@@ -0,0 +1,30 @@
+package org.keycloak.connections.file;
+
+import org.keycloak.representations.idm.RealmRepresentation;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class Model {
+ private String modelVersion;
+ private List<RealmRepresentation> realms;
+
+ public String getModelVersion() {
+ return modelVersion;
+ }
+
+ public void setModelVersion(String modelVersion) {
+ this.modelVersion = modelVersion;
+ }
+
+ public List<RealmRepresentation> getRealms() {
+ return realms;
+ }
+
+ public void setRealms(List<RealmRepresentation> realms) {
+ this.realms = realms;
+ }
+}
diff --git a/connections/jpa/src/main/resources/META-INF/persistence.xml b/connections/jpa/src/main/resources/META-INF/persistence.xml
index 83eadb5..4dbad93 100755
--- a/connections/jpa/src/main/resources/META-INF/persistence.xml
+++ b/connections/jpa/src/main/resources/META-INF/persistence.xml
@@ -11,6 +11,7 @@
<class>org.keycloak.models.jpa.entities.UserFederationProviderEntity</class>
<class>org.keycloak.models.jpa.entities.RoleEntity</class>
<class>org.keycloak.models.jpa.entities.FederatedIdentityEntity</class>
+ <class>org.keycloak.models.jpa.entities.MigrationModelEntity</class>
<class>org.keycloak.models.jpa.entities.UserEntity</class>
<class>org.keycloak.models.jpa.entities.UserRequiredActionEntity</class>
<class>org.keycloak.models.jpa.entities.UserAttributeEntity</class>
diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.RC1.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.RC1.xml
index 709d50b..8d09ba5 100755
--- a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.RC1.xml
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.RC1.xml
@@ -5,6 +5,14 @@
<delete tableName="CLIENT_SESSION_NOTE"/>
<delete tableName="CLIENT_SESSION"/>
<delete tableName="USER_SESSION"/>
+ <createTable tableName="MIGRATION_MODEL">
+ <column name="ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="VERSION" type="VARCHAR(36)">
+ <constraints nullable="true"/>
+ </column>
+ </createTable>
<createTable tableName="IDENTITY_PROVIDER_MAPPER">
<column name="ID" type="VARCHAR(36)">
@@ -70,6 +78,7 @@
<constraints nullable="false"/>
</column>
</createTable>
+ <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_MIGMOD" tableName="MIGRATION_MODEL"/>
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_IDPM" tableName="IDENTITY_PROVIDER_MAPPER"/>
<addPrimaryKey columnNames="IDP_MAPPER_ID, NAME" constraintName="CONSTRAINT_IDPMConfig" tableName="IDP_MAPPER_CONFIG"/>
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_GRNTCSNT_PM" tableName="USER_CONSENT"/>
diff --git a/model/api/src/main/java/org/keycloak/migration/MigrationModel.java b/model/api/src/main/java/org/keycloak/migration/MigrationModel.java
new file mode 100755
index 0000000..936fbcf
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/migration/MigrationModel.java
@@ -0,0 +1,18 @@
+package org.keycloak.migration;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface MigrationModel {
+ /**
+ * Must have the form of major.minor.micro as the version is parsed and numbers are compared
+ */
+ public static final String LATEST_VERSION = "1.2.0.CR1";
+
+ String getStoredVersion();
+ void setStoredVersion(String version);
+}
diff --git a/model/api/src/main/java/org/keycloak/migration/MigrationModelManager.java b/model/api/src/main/java/org/keycloak/migration/MigrationModelManager.java
new file mode 100755
index 0000000..c749676
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/migration/MigrationModelManager.java
@@ -0,0 +1,31 @@
+package org.keycloak.migration;
+
+import org.jboss.logging.Logger;
+import org.keycloak.migration.migrators.MigrationTo1_2_0_RC1;
+import org.keycloak.models.KeycloakSession;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class MigrationModelManager {
+ private static Logger logger = Logger.getLogger(MigrationModelManager.class);
+
+ public static void migrate(KeycloakSession session) {
+ MigrationModel model = session.realms().getMigrationModel();
+ String storedVersion = model.getStoredVersion();
+ if (MigrationModel.LATEST_VERSION.equals(storedVersion)) return;
+ ModelVersion stored = null;
+ if (storedVersion == null) stored = new ModelVersion(0, 0, 0);
+ else stored = new ModelVersion(storedVersion);
+
+ if (stored.lessThan(MigrationTo1_2_0_RC1.VERSION)) {
+ logger.info("Migrating older model to 1.2.0.RC1 updates");
+ new MigrationTo1_2_0_RC1().migrate(session);
+ }
+
+ model.setStoredVersion(MigrationModel.LATEST_VERSION);
+
+
+ }
+}
diff --git a/model/api/src/main/java/org/keycloak/migration/migrators/MigrationTo1_2_0_RC1.java b/model/api/src/main/java/org/keycloak/migration/migrators/MigrationTo1_2_0_RC1.java
new file mode 100755
index 0000000..5c483f9
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/migration/migrators/MigrationTo1_2_0_RC1.java
@@ -0,0 +1,38 @@
+package org.keycloak.migration.migrators;
+
+import org.keycloak.migration.ModelVersion;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.Constants;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class MigrationTo1_2_0_RC1 {
+ public static final ModelVersion VERSION = new ModelVersion("1.2.0.RC1");
+
+ public void setupBrokerService(RealmModel realm) {
+ ClientModel client = realm.getClientNameMap().get(Constants.BROKER_SERVICE_CLIENT_ID);
+ if (client == null) {
+ client = KeycloakModelUtils.createClient(realm, Constants.BROKER_SERVICE_CLIENT_ID);
+ client.setEnabled(true);
+ client.setFullScopeAllowed(false);
+
+ for (String role : Constants.BROKER_SERVICE_ROLES) {
+ client.addRole(role).setDescription("${role_"+role+"}");
+ }
+ }
+ }
+ public void migrate(KeycloakSession session) {
+ List<RealmModel> realms = session.realms().getRealms();
+ for (RealmModel realm : realms) {
+ setupBrokerService(realm);
+ }
+
+ }
+}
diff --git a/model/api/src/main/java/org/keycloak/migration/ModelVersion.java b/model/api/src/main/java/org/keycloak/migration/ModelVersion.java
new file mode 100755
index 0000000..1dfcd14
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/migration/ModelVersion.java
@@ -0,0 +1,69 @@
+package org.keycloak.migration;
+
+import org.jboss.logging.Logger;
+
+/**
+* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+* @version $Revision: 1 $
+*/
+public class ModelVersion {
+ private static Logger logger = Logger.getLogger(ModelVersion.class);
+ int major;
+ int minor;
+ int micro;
+ String qualifier;
+
+ public ModelVersion(int major, int minor, int micro) {
+ this.major = major;
+ this.minor = minor;
+ this.micro = micro;
+ }
+
+ public ModelVersion(String version) {
+ String[] split = version.split("\\.");
+ try {
+ if (split.length > 0) {
+ major = Integer.parseInt(split[0]);
+ }
+ if (split.length > 1) {
+ minor = Integer.parseInt(split[1]);
+ }
+ if (split.length > 2) {
+ micro = Integer.parseInt(split[2]);
+ }
+ if (split.length > 3) {
+ qualifier = split[3];
+ }
+ } catch (NumberFormatException e) {
+ logger.warn("failed to parse version: " + version, e);
+ }
+ }
+
+ public int getMajor() {
+ return major;
+ }
+
+ public int getMinor() {
+ return minor;
+ }
+
+ public int getMicro() {
+ return micro;
+ }
+
+ public String getQualifier() {
+ return qualifier;
+ }
+
+ public boolean lessThan(ModelVersion version) {
+ if (major < version.major) return true;
+ if (minor < version.minor) return true;
+ if (micro < version.micro) return true;
+ if (qualifier == version.qualifier) return false;
+ if (qualifier == null) return false;
+ if (version.qualifier == null) return true;
+ int comp = qualifier.compareTo(version.qualifier);
+ if (comp < 0) return true;
+ return false;
+ }
+}
diff --git a/model/api/src/main/java/org/keycloak/models/Constants.java b/model/api/src/main/java/org/keycloak/models/Constants.java
index 08cf4ee..60d71c7 100755
--- a/model/api/src/main/java/org/keycloak/models/Constants.java
+++ b/model/api/src/main/java/org/keycloak/models/Constants.java
@@ -12,4 +12,6 @@ public interface Constants {
String INSTALLED_APP_URN = "urn:ietf:wg:oauth:2.0:oob";
String INSTALLED_APP_URL = "http://localhost";
+ String READ_TOKEN_ROLE = "READ_TOKEN";
+ String[] BROKER_SERVICE_ROLES = {READ_TOKEN_ROLE};
}
diff --git a/model/api/src/main/java/org/keycloak/models/KeycloakSession.java b/model/api/src/main/java/org/keycloak/models/KeycloakSession.java
index b6f35e6..b8ce57b 100755
--- a/model/api/src/main/java/org/keycloak/models/KeycloakSession.java
+++ b/model/api/src/main/java/org/keycloak/models/KeycloakSession.java
@@ -1,5 +1,6 @@
package org.keycloak.models;
+import org.keycloak.migration.MigrationModel;
import org.keycloak.provider.Provider;
import java.util.Set;
diff --git a/model/api/src/main/java/org/keycloak/models/RealmProvider.java b/model/api/src/main/java/org/keycloak/models/RealmProvider.java
index 17de99d..4556002 100755
--- a/model/api/src/main/java/org/keycloak/models/RealmProvider.java
+++ b/model/api/src/main/java/org/keycloak/models/RealmProvider.java
@@ -1,5 +1,6 @@
package org.keycloak.models;
+import org.keycloak.migration.MigrationModel;
import org.keycloak.provider.Provider;
import java.util.List;
@@ -11,7 +12,7 @@ import java.util.List;
public interface RealmProvider extends Provider {
// Note: The reason there are so many query methods here is for layering a cache on top of an persistent KeycloakSession
-
+ MigrationModel getMigrationModel();
RealmModel createRealm(String name);
RealmModel createRealm(String id, String name);
RealmModel getRealm(String id);
diff --git a/model/api/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/model/api/src/main/resources/META-INF/services/org.keycloak.provider.Spi
index 64dd861..9b36900 100755
--- a/model/api/src/main/resources/META-INF/services/org.keycloak.provider.Spi
+++ b/model/api/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -1,5 +1,5 @@
-org.keycloak.models.UserFederationSpi
-org.keycloak.models.RealmSpi
-org.keycloak.models.UserSessionSpi
-org.keycloak.models.UserSpi
+org.keycloak.models.UserFederationSpi
+org.keycloak.models.RealmSpi
+org.keycloak.models.UserSessionSpi
+org.keycloak.models.UserSpi
org.keycloak.migration.MigrationSpi
\ No newline at end of file
diff --git a/model/api/src/test/java/org/keycloak/models/MigrationVersionTest.java b/model/api/src/test/java/org/keycloak/models/MigrationVersionTest.java
new file mode 100755
index 0000000..c706d8a
--- /dev/null
+++ b/model/api/src/test/java/org/keycloak/models/MigrationVersionTest.java
@@ -0,0 +1,45 @@
+package org.keycloak.models;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.keycloak.migration.ModelVersion;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class MigrationVersionTest {
+
+ @Test
+ public void testVersion() {
+ ModelVersion version_100Beta1 = new ModelVersion("1.0.0.Beta1");
+ Assert.assertEquals(version_100Beta1.getMajor(), 1);
+ Assert.assertEquals(version_100Beta1.getMinor(), 0);
+ Assert.assertEquals(version_100Beta1.getMicro(), 0);
+ ModelVersion version_100RC1 = new ModelVersion("1.0.0.RC1");
+ ModelVersion version_100 = new ModelVersion("1.0.0");
+ ModelVersion version_110Beta1 = new ModelVersion("1.1.0.Beta1");
+ ModelVersion version_110RC1 = new ModelVersion("1.1.0.RC1");
+ ModelVersion version_110 = new ModelVersion("1.1.0");
+ ModelVersion version_111Beta1 = new ModelVersion("1.1.1.Beta1");
+ ModelVersion version_111RC1 = new ModelVersion("1.1.1.RC1");
+ ModelVersion version_111 = new ModelVersion("1.1.1");
+ ModelVersion version_211Beta1 = new ModelVersion("2.1.1.Beta1");
+ ModelVersion version_211RC1 = new ModelVersion("2.1.1.RC1");
+ Assert.assertEquals(version_211RC1.getMajor(), 2);
+ Assert.assertEquals(version_211RC1.getMinor(), 1);
+ Assert.assertEquals(version_211RC1.getMicro(), 1);
+ Assert.assertEquals(version_211RC1.getQualifier(), "RC1");
+ ModelVersion version_211 = new ModelVersion("2.1.1");
+
+ Assert.assertFalse(version_100Beta1.lessThan(version_100Beta1));
+ Assert.assertTrue(version_100Beta1.lessThan(version_100RC1));
+ Assert.assertTrue(version_100Beta1.lessThan(version_100));
+ Assert.assertTrue(version_100Beta1.lessThan(version_110Beta1));
+ Assert.assertTrue(version_100Beta1.lessThan(version_110RC1));
+ Assert.assertTrue(version_100Beta1.lessThan(version_110));
+
+ Assert.assertFalse(version_211.lessThan(version_110RC1));
+
+ }
+}
diff --git a/model/file/src/main/java/org/keycloak/models/file/adapter/MigrationModelAdapter.java b/model/file/src/main/java/org/keycloak/models/file/adapter/MigrationModelAdapter.java
new file mode 100755
index 0000000..f92718c
--- /dev/null
+++ b/model/file/src/main/java/org/keycloak/models/file/adapter/MigrationModelAdapter.java
@@ -0,0 +1,26 @@
+package org.keycloak.models.file.adapter;
+
+import org.keycloak.connections.file.InMemoryModel;
+import org.keycloak.migration.MigrationModel;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class MigrationModelAdapter implements MigrationModel {
+ protected InMemoryModel em;
+
+ public MigrationModelAdapter(InMemoryModel em) {
+ this.em = em;
+ }
+
+ @Override
+ public String getStoredVersion() {
+ return em.getModelVersion();
+ }
+
+ @Override
+ public void setStoredVersion(String version) {
+ em.setModelVersion(version);
+ }
+}
diff --git a/model/file/src/main/java/org/keycloak/models/file/FileRealmProvider.java b/model/file/src/main/java/org/keycloak/models/file/FileRealmProvider.java
old mode 100644
new mode 100755
index 3f23b5c..593aa88
--- a/model/file/src/main/java/org/keycloak/models/file/FileRealmProvider.java
+++ b/model/file/src/main/java/org/keycloak/models/file/FileRealmProvider.java
@@ -18,6 +18,7 @@ package org.keycloak.models.file;
import org.keycloak.connections.file.FileConnectionProvider;
import org.keycloak.connections.file.InMemoryModel;
+import org.keycloak.migration.MigrationModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
@@ -25,6 +26,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RealmProvider;
import org.keycloak.models.RoleModel;
import org.keycloak.models.entities.RealmEntity;
+import org.keycloak.models.file.adapter.MigrationModelAdapter;
import org.keycloak.models.file.adapter.RealmAdapter;
import org.keycloak.models.utils.KeycloakModelUtils;
@@ -55,6 +57,11 @@ public class FileRealmProvider implements RealmProvider {
}
@Override
+ public MigrationModel getMigrationModel() {
+ return new MigrationModelAdapter(inMemoryModel);
+ }
+
+ @Override
public RealmModel createRealm(String name) {
return createRealm(KeycloakModelUtils.generateId(), name);
}
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheRealmProvider.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheRealmProvider.java
index 13f6924..469973f 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheRealmProvider.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheRealmProvider.java
@@ -1,5 +1,6 @@
package org.keycloak.models.cache;
+import org.keycloak.migration.MigrationModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakTransaction;
@@ -46,6 +47,12 @@ public class DefaultCacheRealmProvider implements CacheRealmProvider {
}
@Override
+ public MigrationModel getMigrationModel() {
+ return getDelegate().getMigrationModel();
+ }
+
+
+ @Override
public boolean isEnabled() {
return cache.isEnabled();
}
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheRealmProvider.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheRealmProvider.java
index 12c28f6..cb8a8a3 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheRealmProvider.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheRealmProvider.java
@@ -1,5 +1,6 @@
package org.keycloak.models.cache;
+import org.keycloak.migration.MigrationModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
@@ -50,6 +51,11 @@ public class NoCacheRealmProvider implements CacheRealmProvider {
}
@Override
+ public MigrationModel getMigrationModel() {
+ return getDelegate().getMigrationModel();
+ }
+
+ @Override
public RealmModel createRealm(String name) {
return getDelegate().createRealm(name);
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/MigrationModelEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/MigrationModelEntity.java
new file mode 100755
index 0000000..a29873e
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/MigrationModelEntity.java
@@ -0,0 +1,43 @@
+package org.keycloak.models.jpa.entities;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Table(name="MIGRATION_MODEL")
+@Entity
+public class MigrationModelEntity {
+ public static final String SINGLETON_ID = "SINGLETON";
+ @Id
+ @Column(name="ID", length = 36)
+ private String id;
+
+ @Column(name="VERSION", length = 36)
+ protected String version;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
index 1be423a..758531a 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
@@ -1,5 +1,6 @@
package org.keycloak.models.jpa;
+import org.keycloak.migration.MigrationModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
@@ -31,6 +32,11 @@ public class JpaRealmProvider implements RealmProvider {
}
@Override
+ public MigrationModel getMigrationModel() {
+ return new MigrationModelAdapter(em);
+ }
+
+ @Override
public RealmModel createRealm(String name) {
return createRealm(KeycloakModelUtils.generateId(), name);
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/MigrationModelAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/MigrationModelAdapter.java
new file mode 100755
index 0000000..034e141
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/MigrationModelAdapter.java
@@ -0,0 +1,42 @@
+package org.keycloak.models.jpa;
+
+import org.keycloak.migration.MigrationModel;
+import org.keycloak.models.jpa.entities.MigrationModelEntity;
+import org.keycloak.util.Time;
+
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class MigrationModelAdapter implements MigrationModel {
+ protected EntityManager em;
+
+ public MigrationModelAdapter(EntityManager em) {
+ this.em = em;
+ }
+
+ @Override
+ public String getStoredVersion() {
+ MigrationModelEntity entity = em.find(MigrationModelEntity.class, MigrationModelEntity.SINGLETON_ID);
+ if (entity == null) return null;
+ return entity.getVersion();
+ }
+
+ @Override
+ public void setStoredVersion(String version) {
+ MigrationModelEntity entity = em.find(MigrationModelEntity.class, MigrationModelEntity.SINGLETON_ID);
+ if (entity == null) {
+ entity = new MigrationModelEntity();
+ entity.setId(MigrationModelEntity.SINGLETON_ID);
+ entity.setVersion(version);
+ em.persist(entity);
+ } else {
+ entity.setVersion(version);
+ em.flush();
+ }
+ }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MigrationModelAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MigrationModelAdapter.java
new file mode 100755
index 0000000..3e06af6
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MigrationModelAdapter.java
@@ -0,0 +1,57 @@
+package org.keycloak.models.mongo.keycloak.adapters;
+
+import com.mongodb.DBObject;
+import com.mongodb.QueryBuilder;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.migration.MigrationModel;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ProtocolMapperModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.entities.ProtocolMapperEntity;
+import org.keycloak.models.mongo.keycloak.entities.MongoClientEntity;
+import org.keycloak.models.mongo.keycloak.entities.MongoMigrationModelEntity;
+import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
+import org.keycloak.models.mongo.utils.MongoModelUtils;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class MigrationModelAdapter extends AbstractMongoAdapter<MongoMigrationModelEntity> implements MigrationModel {
+
+ protected final MongoMigrationModelEntity entity;
+
+ public MigrationModelAdapter(KeycloakSession session, MongoMigrationModelEntity entity, MongoStoreInvocationContext invContext) {
+ super(invContext);
+ this.entity = entity;
+ }
+
+ @Override
+ public MongoMigrationModelEntity getMongoEntity() {
+ return entity;
+ }
+
+ @Override
+ public String getStoredVersion() {
+ return getMongoEntity().getVersion();
+ }
+
+ @Override
+ public void setStoredVersion(String version) {
+ getMongoEntity().setVersion(version);
+ updateMongoEntity();
+
+ }
+
+
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoRealmProvider.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoRealmProvider.java
index c8bdb38..1de862c 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoRealmProvider.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoRealmProvider.java
@@ -5,12 +5,14 @@ import com.mongodb.DBObject;
import com.mongodb.QueryBuilder;
import org.keycloak.connections.mongo.api.MongoStore;
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.migration.MigrationModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RealmProvider;
import org.keycloak.models.RoleModel;
import org.keycloak.models.mongo.keycloak.entities.MongoClientEntity;
+import org.keycloak.models.mongo.keycloak.entities.MongoMigrationModelEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
import org.keycloak.models.utils.KeycloakModelUtils;
@@ -37,6 +39,16 @@ public class MongoRealmProvider implements RealmProvider {
}
@Override
+ public MigrationModel getMigrationModel() {
+ MongoMigrationModelEntity entity = getMongoStore().loadEntity(MongoMigrationModelEntity.class, MongoMigrationModelEntity.MIGRATION_MODEL_ID, invocationContext);
+ if (entity == null) {
+ entity = new MongoMigrationModelEntity();
+ getMongoStore().insertEntity(entity, invocationContext);
+ }
+ return new MigrationModelAdapter(session, entity, invocationContext);
+ }
+
+ @Override
public RealmModel createRealm(String name) {
return createRealm(KeycloakModelUtils.generateId(), name);
}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoMigrationModelEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoMigrationModelEntity.java
new file mode 100755
index 0000000..6acc40c
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoMigrationModelEntity.java
@@ -0,0 +1,38 @@
+package org.keycloak.models.mongo.keycloak.entities;
+
+import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class MongoMigrationModelEntity implements MongoIdentifiableEntity {
+ public static final String MIGRATION_MODEL_ID = "VERSION";
+ private String id = MIGRATION_MODEL_ID;
+ private String version;
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ @Override
+ public String getId() {
+ return id;
+ }
+
+ @Override
+ public void setId(String id) {
+ this.id = id;
+
+ }
+
+ @Override
+ public void afterRemove(MongoStoreInvocationContext invocationContext) {
+
+ }
+}
diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
index 3d56a2a..4ff3ee1 100755
--- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
@@ -21,7 +21,6 @@ import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
-import org.keycloak.services.resources.IdentityBrokerService;
import org.keycloak.timer.TimerProvider;
import java.util.Collections;
@@ -226,7 +225,7 @@ public class RealmManager {
client.setEnabled(true);
client.setFullScopeAllowed(false);
- for (String role : IdentityBrokerService.ROLES) {
+ for (String role : Constants.BROKER_SERVICE_ROLES) {
client.addRole(role).setDescription("${role_"+role+"}");
}
}
diff --git a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
index c10ef1e..a8119ce 100755
--- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
+++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
@@ -44,7 +44,6 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
-import org.keycloak.protocol.ProtocolMapper;
import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.representations.AccessToken;
@@ -60,17 +59,13 @@ import org.keycloak.services.validation.Validation;
import org.keycloak.social.SocialIdentityProvider;
import org.keycloak.util.ObjectUtil;
-import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.OPTIONS;
-import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriBuilder;
@@ -95,8 +90,6 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
private static final Logger LOGGER = Logger.getLogger(IdentityBrokerService.class);
public static final String BROKER_PROVIDER_ID = "BROKER_PROVIDER_ID";
- public static final String READ_TOKEN_ROLE = "READ_TOKEN";
- public static final String[] ROLES = {READ_TOKEN_ROLE};
private final RealmModel realmModel;
@@ -207,7 +200,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
}
Map<String, AccessToken.Access> resourceAccess = token.getResourceAccess();
AccessToken.Access brokerRoles = resourceAccess == null ? null : resourceAccess.get(Constants.BROKER_SERVICE_CLIENT_ID);
- if (brokerRoles == null || !brokerRoles.isUserInRole(READ_TOKEN_ROLE)) {
+ if (brokerRoles == null || !brokerRoles.isUserInRole(Constants.READ_TOKEN_ROLE)) {
return corsResponse(forbidden("Client [" + audience + "] not authorized to retrieve tokens from identity provider [" + providerId + "]."), clientModel);
}
@@ -536,7 +529,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
if (context.getIdpConfig().isAddReadTokenRoleOnCreate()) {
- RoleModel readTokenRole = realmModel.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(READ_TOKEN_ROLE);
+ RoleModel readTokenRole = realmModel.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(Constants.READ_TOKEN_ROLE);
federatedUser.grantRole(readTokenRole);
}
diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
index 11b59c8..d3586bc 100755
--- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
+++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
@@ -8,6 +8,7 @@ import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.Config;
import org.keycloak.SkeletonKeyContextResolver;
import org.keycloak.exportimport.ExportImportManager;
+import org.keycloak.migration.MigrationModelManager;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
@@ -84,9 +85,26 @@ public class KeycloakApplication extends Application {
setupDefaultRealm(context.getContextPath());
importRealms(context);
+ migrateModel();
+
+
setupScheduledTasks(sessionFactory);
}
+ protected void migrateModel() {
+ KeycloakSession session = sessionFactory.create();
+ try {
+ session.getTransaction().begin();
+ MigrationModelManager.migrate(session);
+ session.getTransaction().commit();
+ } catch (Exception e) {
+ session.getTransaction().rollback();
+ log.error("Failed to migrate datamodel", e);
+ } finally {
+ session.close();
+ }
+ }
+
public String getContextPath() {
return contextPath;
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
index 7672df1..b8cf2a8 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
@@ -32,7 +32,9 @@ import org.keycloak.account.freemarker.model.ApplicationsBean;
import org.keycloak.events.Details;
import org.keycloak.events.Event;
import org.keycloak.events.EventType;
+import org.keycloak.migration.MigrationModel;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel;
@@ -169,6 +171,15 @@ public class AccountTest {
}
@Test
+ public void testMigrationModel() {
+ KeycloakSession keycloakSession = keycloakRule.startSession();
+ Assert.assertEquals(keycloakSession.realms().getMigrationModel().getStoredVersion(), MigrationModel.LATEST_VERSION);
+ keycloakSession.close();
+ }
+
+
+
+ @Test
public void returnToAppFromQueryParam() {
driver.navigate().to(AccountUpdateProfilePage.PATH + "?referrer=test-app");
loginPage.login("test-user@localhost", "password");
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
index 6b294e2..72e743b 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
@@ -17,11 +17,6 @@
*/
package org.keycloak.testsuite.broker;
-import org.apache.commons.io.IOUtils;
-import org.apache.http.HttpResponse;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.impl.client.DefaultHttpClient;
import org.codehaus.jackson.map.ObjectMapper;
import org.junit.After;
import org.junit.Assert;
@@ -29,7 +24,6 @@ import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
-import org.keycloak.OAuth2Constants;
import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants;
import org.keycloak.models.FederatedIdentityModel;
@@ -41,9 +35,7 @@ import org.keycloak.models.UserModel;
import org.keycloak.models.UserModel.RequiredAction;
import org.keycloak.representations.IDToken;
import org.keycloak.services.Urls;
-import org.keycloak.services.resources.IdentityBrokerService;
import org.keycloak.testsuite.OAuthClient;
-import org.keycloak.testsuite.OAuthClient.AccessTokenResponse;
import org.keycloak.testsuite.broker.util.UserSessionStatusServlet.UserSessionStatus;
import org.keycloak.testsuite.pages.AccountFederatedIdentityPage;
import org.keycloak.testsuite.pages.AccountPasswordPage;
@@ -68,7 +60,6 @@ import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriBuilder;
import java.io.IOException;
import java.net.URI;
-import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -423,7 +414,7 @@ public abstract class AbstractIdentityProviderTest {
protected void configureClientRetrieveToken(String clientId) {
RealmModel realm = getRealm();
- RoleModel readTokenRole = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(IdentityBrokerService.READ_TOKEN_ROLE);
+ RoleModel readTokenRole = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(Constants.READ_TOKEN_ROLE);
ClientModel client = realm.getClientByClientId(clientId);
if (!client.hasScope(readTokenRole)) client.addScopeMapping(readTokenRole);
@@ -435,7 +426,7 @@ public abstract class AbstractIdentityProviderTest {
protected void configureUserRetrieveToken(String username) {
RealmModel realm = getRealm();
UserModel user = session.users().getUserByUsername(username, realm);
- RoleModel readTokenRole = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(IdentityBrokerService.READ_TOKEN_ROLE);
+ RoleModel readTokenRole = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(Constants.READ_TOKEN_ROLE);
if (user != null && !user.hasRole(readTokenRole)) {
user.grantRole(readTokenRole);
}
@@ -446,7 +437,7 @@ public abstract class AbstractIdentityProviderTest {
protected void unconfigureClientRetrieveToken(String clientId) {
RealmModel realm = getRealm();
- RoleModel readTokenRole = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(IdentityBrokerService.READ_TOKEN_ROLE);
+ RoleModel readTokenRole = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(Constants.READ_TOKEN_ROLE);
ClientModel client = realm.getClientByClientId(clientId);
if (client.hasScope(readTokenRole)) client.deleteScopeMapping(readTokenRole);
@@ -458,7 +449,7 @@ public abstract class AbstractIdentityProviderTest {
protected void unconfigureUserRetrieveToken(String username) {
RealmModel realm = getRealm();
UserModel user = session.users().getUserByUsername(username, realm);
- RoleModel readTokenRole = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(IdentityBrokerService.READ_TOKEN_ROLE);
+ RoleModel readTokenRole = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(Constants.READ_TOKEN_ROLE);
if (user != null && user.hasRole(readTokenRole)) {
user.deleteRoleMapping(readTokenRole);
}