model migration
This commit is contained in:
parent
f17863041a
commit
4166393396
29 changed files with 561 additions and 31 deletions
|
@ -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
|
||||
|
|
10
connections/file/src/main/java/org/keycloak/connections/file/InMemoryModel.java
Normal file → Executable file
10
connections/file/src/main/java/org/keycloak/connections/file/InMemoryModel.java
Normal file → Executable file
|
@ -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);
|
||||
}
|
||||
|
|
30
connections/file/src/main/java/org/keycloak/connections/file/Model.java
Executable file
30
connections/file/src/main/java/org/keycloak/connections/file/Model.java
Executable file
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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"/>
|
||||
|
|
|
@ -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>
|
||||
|
|
18
model/api/src/main/java/org/keycloak/migration/MigrationModel.java
Executable file
18
model/api/src/main/java/org/keycloak/migration/MigrationModel.java
Executable file
|
@ -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);
|
||||
}
|
31
model/api/src/main/java/org/keycloak/migration/MigrationModelManager.java
Executable file
31
model/api/src/main/java/org/keycloak/migration/MigrationModelManager.java
Executable file
|
@ -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);
|
||||
|
||||
|
||||
}
|
||||
}
|
69
model/api/src/main/java/org/keycloak/migration/ModelVersion.java
Executable file
69
model/api/src/main/java/org/keycloak/migration/ModelVersion.java
Executable file
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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};
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.keycloak.models;
|
||||
|
||||
import org.keycloak.migration.MigrationModel;
|
||||
import org.keycloak.provider.Provider;
|
||||
|
||||
import java.util.Set;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
45
model/api/src/test/java/org/keycloak/models/MigrationVersionTest.java
Executable file
45
model/api/src/test/java/org/keycloak/models/MigrationVersionTest.java
Executable file
|
@ -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));
|
||||
|
||||
}
|
||||
}
|
7
model/file/src/main/java/org/keycloak/models/file/FileRealmProvider.java
Normal file → Executable file
7
model/file/src/main/java/org/keycloak/models/file/FileRealmProvider.java
Normal file → Executable file
|
@ -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;
|
||||
|
||||
|
@ -54,6 +56,11 @@ public class FileRealmProvider implements RealmProvider {
|
|||
fcProvider.sessionClosed(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MigrationModel getMigrationModel() {
|
||||
return new MigrationModelAdapter(inMemoryModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel createRealm(String name) {
|
||||
return createRealm(KeycloakModelUtils.generateId(), name);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
@ -45,6 +46,12 @@ public class DefaultCacheRealmProvider implements CacheRealmProvider {
|
|||
session.getTransaction().enlistAfterCompletion(getTransaction());
|
||||
}
|
||||
|
||||
@Override
|
||||
public MigrationModel getMigrationModel() {
|
||||
return getDelegate().getMigrationModel();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return cache.isEnabled();
|
||||
|
|
|
@ -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;
|
||||
|
@ -49,6 +50,11 @@ public class NoCacheRealmProvider implements CacheRealmProvider {
|
|||
public void registerRoleInvalidation(String id) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MigrationModel getMigrationModel() {
|
||||
return getDelegate().getMigrationModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel createRealm(String name) {
|
||||
return getDelegate().createRealm(name);
|
||||
|
|
|
@ -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;
|
||||
|
@ -30,6 +31,11 @@ public class JpaRealmProvider implements RealmProvider {
|
|||
this.em = em;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MigrationModel getMigrationModel() {
|
||||
return new MigrationModelAdapter(em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel createRealm(String name) {
|
||||
return createRealm(KeycloakModelUtils.generateId(), name);
|
||||
|
|
42
model/jpa/src/main/java/org/keycloak/models/jpa/MigrationModelAdapter.java
Executable file
42
model/jpa/src/main/java/org/keycloak/models/jpa/MigrationModelAdapter.java
Executable file
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
@ -36,6 +38,16 @@ public class MongoRealmProvider implements RealmProvider {
|
|||
// TODO
|
||||
}
|
||||
|
||||
@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);
|
||||
|
|
|
@ -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) {
|
||||
|
||||
}
|
||||
}
|
|
@ -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+"}");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -168,6 +170,15 @@ public class AccountTest {
|
|||
Thread.sleep(100000000);
|
||||
}
|
||||
|
||||
@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");
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue