Mongo migration: admin roles, timeout, protocol mappers
This commit is contained in:
parent
82e290e06c
commit
a65bac7751
26 changed files with 513 additions and 157 deletions
47
connections/mongo-update/pom.xml
Normal file
47
connections/mongo-update/pom.xml
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.Beta1-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-connections-mongo-update</artifactId>
|
||||
<name>Keycloak Connections Mongo Update</name>
|
||||
<description/>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-connections-mongo</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-services</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mongodb</groupId>
|
||||
<artifactId>mongo-java-driver</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -1,24 +1,20 @@
|
|||
package org.keycloak.connections.mongo.updater;
|
||||
package org.keycloak.connections.mongo.updater.impl;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBCursor;
|
||||
import com.mongodb.DBObject;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.connections.mongo.updater.updates.Update;
|
||||
import org.keycloak.connections.mongo.updater.updates.Update1_0_0_Final;
|
||||
import org.keycloak.connections.mongo.updater.updates.Update1_1_0_Beta1;
|
||||
import org.keycloak.connections.mongo.updater.updates.Update1_2_0_Beta1;
|
||||
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.models.KeycloakSession;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
|
@ -36,7 +32,7 @@ public class DefaultMongoUpdaterProvider implements MongoUpdaterProvider {
|
|||
};
|
||||
|
||||
@Override
|
||||
public void update(DB db) {
|
||||
public void update(KeycloakSession session, DB db) {
|
||||
log.debug("Starting database update");
|
||||
try {
|
||||
boolean changeLogExists = db.collectionExists(CHANGE_LOG_COLLECTION);
|
||||
|
@ -81,17 +77,19 @@ public class DefaultMongoUpdaterProvider implements MongoUpdaterProvider {
|
|||
|
||||
u.setLog(log);
|
||||
u.setDb(db);
|
||||
u.update();
|
||||
u.update(session);
|
||||
|
||||
createLog(changeLog, u, ++order);
|
||||
|
||||
log.debugv("Completed updates for {0}", u.getId());
|
||||
}
|
||||
log.debug("Completed database update");
|
||||
} else {
|
||||
log.debug("Skip database update. Database is already up to date");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to update database", e);
|
||||
}
|
||||
log.debug("Completed database update");
|
||||
}
|
||||
|
||||
private void createLog(DBCollection changeLog, Update update, int orderExecuted) {
|
|
@ -1,6 +1,8 @@
|
|||
package org.keycloak.connections.mongo.updater;
|
||||
package org.keycloak.connections.mongo.updater.impl;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.connections.mongo.updater.MongoUpdaterProvider;
|
||||
import org.keycloak.connections.mongo.updater.MongoUpdaterProviderFactory;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
package org.keycloak.connections.mongo.updater.updates;
|
||||
package org.keycloak.connections.mongo.updater.impl.updates;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.DBCollection;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
|
@ -18,7 +20,7 @@ public abstract class Update {
|
|||
|
||||
public abstract String getId();
|
||||
|
||||
public abstract void update() throws ClassNotFoundException;
|
||||
public abstract void update(KeycloakSession session) throws ClassNotFoundException;
|
||||
|
||||
protected DBCollection createCollection(String name) {
|
||||
if (db.collectionExists(name)) {
|
||||
|
@ -51,6 +53,17 @@ public abstract class Update {
|
|||
log.debugv("Deleted entries from {0}", collection);
|
||||
}
|
||||
|
||||
protected String insertApplicationRole(DBCollection roles, String roleName, String applicationId) {
|
||||
BasicDBObject role = new BasicDBObject();
|
||||
String roleId = KeycloakModelUtils.generateId();
|
||||
role.append("_id", roleId);
|
||||
role.append("name", roleName);
|
||||
role.append("applicationId", applicationId);
|
||||
role.append("nameIndex", applicationId + "//" + roleName);
|
||||
roles.insert(role);
|
||||
return roleId;
|
||||
}
|
||||
|
||||
public void setLog(Logger log) {
|
||||
this.log = log;
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
package org.keycloak.connections.mongo.updater.updates;
|
||||
package org.keycloak.connections.mongo.updater.impl.updates;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBCollection;
|
||||
import org.keycloak.connections.mongo.updater.DefaultMongoUpdaterProvider;
|
||||
import org.keycloak.connections.mongo.updater.impl.DefaultMongoUpdaterProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
|
@ -15,7 +16,7 @@ public class Update1_0_0_Final extends Update {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void update() throws ClassNotFoundException {
|
||||
public void update(KeycloakSession session) throws ClassNotFoundException {
|
||||
DBCollection realmsCollection = db.getCollection("realms");
|
||||
realmsCollection.ensureIndex(new BasicDBObject("name", 1), new BasicDBObject("unique", true));
|
||||
|
|
@ -1,13 +1,12 @@
|
|||
package org.keycloak.connections.mongo.updater.updates;
|
||||
package org.keycloak.connections.mongo.updater.impl.updates;
|
||||
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBCursor;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.QueryBuilder;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
|
@ -19,7 +18,7 @@ public class Update1_1_0_Beta1 extends Update {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
public void update(KeycloakSession session) {
|
||||
deleteEntries("clientSessions");
|
||||
deleteEntries("sessions");
|
||||
|
|
@ -0,0 +1,269 @@
|
|||
package org.keycloak.connections.mongo.updater.impl.updates;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.BasicDBObjectBuilder;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBCursor;
|
||||
import com.mongodb.DBObject;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.connections.mongo.impl.types.MapMapper;
|
||||
import org.keycloak.models.AdminRoles;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.protocol.LoginProtocol;
|
||||
import org.keycloak.protocol.LoginProtocolFactory;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.services.util.MigrationUtils;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class Update1_2_0_Beta1 extends Update {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "1.2.0.Beta1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(KeycloakSession session) {
|
||||
convertSocialToIdFedRealms();
|
||||
convertSocialToIdFedUsers();
|
||||
addAccessCodeLoginTimeout();
|
||||
addNewAdminRoles();
|
||||
addDefaultProtocolMappers(session);
|
||||
}
|
||||
|
||||
|
||||
private void convertSocialToIdFedRealms() {
|
||||
DBCollection realms = db.getCollection("realms");
|
||||
DBCursor realmsCursor = realms.find();
|
||||
|
||||
try {
|
||||
while (realmsCursor.hasNext()) {
|
||||
BasicDBObject realm = (BasicDBObject) realmsCursor.next();
|
||||
boolean updateProfileOnInitialSocialLogin = realm.getBoolean("updateProfileOnInitialSocialLogin");
|
||||
BasicDBObject socialConfig = (BasicDBObject) realm.get("socialConfig");
|
||||
|
||||
BasicDBList identityProviders = (BasicDBList) realm.get("identityProviders");
|
||||
if (identityProviders == null) {
|
||||
identityProviders = new BasicDBList();
|
||||
realm.put("identityProviders", identityProviders);
|
||||
}
|
||||
|
||||
if (socialConfig != null) {
|
||||
for (Map.Entry<String, Object> entry : socialConfig.entrySet()) {
|
||||
if (entry.getKey().endsWith("###key")) {
|
||||
String socialProviderId = entry.getKey().substring(0, entry.getKey().indexOf("###"));
|
||||
String clientId = (String) entry.getValue();
|
||||
String clientSecret = socialConfig.getString(socialProviderId + "###secret");
|
||||
|
||||
DBObject identityProviderConfig = new BasicDBObjectBuilder()
|
||||
.add("clientId", clientId)
|
||||
.add("clientSecret", clientSecret).get();
|
||||
|
||||
DBObject identityProvider = new BasicDBObjectBuilder()
|
||||
.add("internalId", KeycloakModelUtils.generateId())
|
||||
.add("providerId", socialProviderId)
|
||||
.add("name", socialProviderId)
|
||||
.add("id", socialProviderId)
|
||||
.add("updateProfileFirstLogin", updateProfileOnInitialSocialLogin)
|
||||
.add("enabled", true)
|
||||
.add("storeToken", false)
|
||||
.add("authenticateByDefault", false)
|
||||
.add("config", identityProviderConfig).get();
|
||||
|
||||
identityProviders.add(identityProvider);
|
||||
log.debugv("Converted social provider {0} to identity provider", socialProviderId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove obsolete keys from realm
|
||||
realm.remove("social");
|
||||
realm.remove("updateProfileOnInitialSocialLogin");
|
||||
realm.remove("socialConfig");
|
||||
|
||||
// Update realm in DB now
|
||||
realms.save(realm);
|
||||
|
||||
log.debugv("Social providers of realm {0} converted to identity providers", realm.get("_id"));
|
||||
}
|
||||
} finally {
|
||||
realmsCursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void convertSocialToIdFedUsers() {
|
||||
DBCollection users = db.getCollection("users");
|
||||
DBCursor usersCursor = users.find();
|
||||
|
||||
try {
|
||||
while (usersCursor.hasNext()) {
|
||||
BasicDBObject user = (BasicDBObject) usersCursor.next();
|
||||
|
||||
BasicDBList socialLinks = (BasicDBList) user.get("socialLinks");
|
||||
if (socialLinks != null) {
|
||||
BasicDBList federatedIdentities = (BasicDBList) user.get("federatedIdentities");
|
||||
if (federatedIdentities == null) {
|
||||
federatedIdentities = new BasicDBList();
|
||||
user.put("federatedIdentities", federatedIdentities);
|
||||
}
|
||||
|
||||
for (Object socialLinkObj : socialLinks) {
|
||||
BasicDBObject socialLink = (BasicDBObject) socialLinkObj;
|
||||
BasicDBObject idFedLink = new BasicDBObject();
|
||||
idFedLink.put("userName", socialLink.get("socialUsername"));
|
||||
idFedLink.put("userId", socialLink.get("socialUserId"));
|
||||
idFedLink.put("identityProvider", socialLink.get("socialProvider"));
|
||||
|
||||
federatedIdentities.add(idFedLink);
|
||||
}
|
||||
|
||||
// Remove obsolete keys and save user
|
||||
user.remove("socialLinks");
|
||||
users.save(user);
|
||||
|
||||
if (log.isTraceEnabled()) {
|
||||
log.tracev("Social links of user {0} converted to identity links", user.get("_id"));
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
usersCursor.close();
|
||||
}
|
||||
|
||||
log.debug("Social links of users converted to identity links");
|
||||
}
|
||||
|
||||
private void addAccessCodeLoginTimeout() {
|
||||
DBCollection realms = db.getCollection("realms");
|
||||
DBCursor realmsCursor = realms.find();
|
||||
|
||||
try {
|
||||
while (realmsCursor.hasNext()) {
|
||||
BasicDBObject realm = (BasicDBObject) realmsCursor.next();
|
||||
realm.put("accessCodeLifespanLogin", 1800);
|
||||
realms.save(realm);
|
||||
}
|
||||
} finally {
|
||||
realmsCursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void addNewAdminRoles() {
|
||||
DBCollection realms = db.getCollection("realms");
|
||||
String adminRealmName = Config.getAdminRealm();
|
||||
|
||||
DBCursor realmsCursor = realms.find();
|
||||
try {
|
||||
while (realmsCursor.hasNext()) {
|
||||
BasicDBObject realm = (BasicDBObject) realmsCursor.next();
|
||||
if (adminRealmName.equals(realm.get("name"))) {
|
||||
addNewAdminRolesToMasterRealm(realm);
|
||||
} else {
|
||||
addNewAdminRolesToRealm(realm);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
realmsCursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void addNewAdminRolesToMasterRealm(BasicDBObject adminRealm) {
|
||||
DBCollection realms = db.getCollection("realms");
|
||||
DBCollection applications = db.getCollection("applications");
|
||||
DBCollection roles = db.getCollection("roles");
|
||||
|
||||
DBCursor realmsCursor = realms.find();
|
||||
try {
|
||||
while (realmsCursor.hasNext()) {
|
||||
BasicDBObject currentRealm = (BasicDBObject) realmsCursor.next();
|
||||
String masterAdminAppName = currentRealm.getString("name") + "-realm";
|
||||
|
||||
BasicDBObject masterAdminApp = (BasicDBObject) applications.findOne(new BasicDBObject().append("realmId", adminRealm.get("_id")).append("name", masterAdminAppName));
|
||||
|
||||
String viewIdProvidersRoleId = insertApplicationRole(roles, AdminRoles.VIEW_IDENTITY_PROVIDERS, masterAdminApp.getString("_id"));
|
||||
String manageIdProvidersRoleId = insertApplicationRole(roles, AdminRoles.MANAGE_IDENTITY_PROVIDERS, masterAdminApp.getString("_id"));
|
||||
|
||||
BasicDBObject adminRole = (BasicDBObject) roles.findOne(new BasicDBObject().append("realmId", adminRealm.get("_id")).append("name", AdminRoles.ADMIN));
|
||||
BasicDBList adminCompositeRoles = (BasicDBList) adminRole.get("compositeRoleIds");
|
||||
adminCompositeRoles.add(viewIdProvidersRoleId);
|
||||
adminCompositeRoles.add(manageIdProvidersRoleId);
|
||||
roles.save(adminRole);
|
||||
|
||||
log.debugv("Added roles {0} and {1} to application {2}", AdminRoles.VIEW_IDENTITY_PROVIDERS, AdminRoles.MANAGE_IDENTITY_PROVIDERS, masterAdminAppName);
|
||||
}
|
||||
} finally {
|
||||
realmsCursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void addNewAdminRolesToRealm(BasicDBObject currentRealm) {
|
||||
DBCollection applications = db.getCollection("applications");
|
||||
DBCollection roles = db.getCollection("roles");
|
||||
|
||||
BasicDBObject adminApp = (BasicDBObject) applications.findOne(new BasicDBObject().append("realmId", currentRealm.get("_id")).append("name", "realm-management"));
|
||||
|
||||
String viewIdProvidersRoleId = insertApplicationRole(roles, AdminRoles.VIEW_IDENTITY_PROVIDERS, adminApp.getString("_id"));
|
||||
String manageIdProvidersRoleId = insertApplicationRole(roles, AdminRoles.MANAGE_IDENTITY_PROVIDERS, adminApp.getString("_id"));
|
||||
|
||||
BasicDBObject adminRole = (BasicDBObject) roles.findOne(new BasicDBObject().append("applicationId", adminApp.get("_id")).append("name", AdminRoles.REALM_ADMIN));
|
||||
BasicDBList adminCompositeRoles = (BasicDBList) adminRole.get("compositeRoleIds");
|
||||
adminCompositeRoles.add(viewIdProvidersRoleId);
|
||||
adminCompositeRoles.add(manageIdProvidersRoleId);
|
||||
|
||||
roles.save(adminRole);
|
||||
log.debugv("Added roles {0} and {1} to application realm-management of realm {2}", AdminRoles.VIEW_IDENTITY_PROVIDERS, AdminRoles.MANAGE_IDENTITY_PROVIDERS, currentRealm.get("name"));
|
||||
}
|
||||
|
||||
private void addDefaultProtocolMappers(KeycloakSession session) {
|
||||
addDefaultMappers(session, db.getCollection("applications"));
|
||||
addDefaultMappers(session, db.getCollection("oauthClients"));
|
||||
}
|
||||
|
||||
private void addDefaultMappers(KeycloakSession session, DBCollection clients) {
|
||||
DBCursor clientsCursor = clients.find();
|
||||
try {
|
||||
while (clientsCursor.hasNext()) {
|
||||
BasicDBObject currentClient = (BasicDBObject) clientsCursor.next();
|
||||
|
||||
BasicDBList dbProtocolMappers = new BasicDBList();
|
||||
currentClient.put("protocolMappers", dbProtocolMappers);
|
||||
|
||||
Object claimMask = currentClient.get("allowedClaimsMask");
|
||||
Collection<ProtocolMapperModel> clientProtocolMappers = MigrationUtils.getMappersForClaimMask(session, (Long) claimMask);
|
||||
|
||||
for (ProtocolMapperModel protocolMapper : clientProtocolMappers) {
|
||||
BasicDBObject dbMapper = new BasicDBObject();
|
||||
dbMapper.put("id", KeycloakModelUtils.generateId());
|
||||
dbMapper.put("protocol", protocolMapper.getProtocol());
|
||||
dbMapper.put("name", protocolMapper.getName());
|
||||
dbMapper.put("consentRequired", protocolMapper.isConsentRequired());
|
||||
dbMapper.put("consentText", protocolMapper.getConsentText());
|
||||
dbMapper.put("protocolMapper", protocolMapper.getProtocolMapper());
|
||||
|
||||
Map<String, String> config = protocolMapper.getConfig();
|
||||
BasicDBObject dbConfig = MapMapper.convertMap(config);
|
||||
dbMapper.put("config", dbConfig);
|
||||
|
||||
dbProtocolMappers.add(dbMapper);
|
||||
}
|
||||
|
||||
currentClient.remove("allowedClaimsMask");
|
||||
|
||||
log.debugv("Added default mappers to application {1}", currentClient.get("name"));
|
||||
clients.save(currentClient);
|
||||
}
|
||||
} finally {
|
||||
clientsCursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
org.keycloak.connections.mongo.updater.impl.DefaultMongoUpdaterProviderFactory
|
|
@ -10,7 +10,7 @@ import org.keycloak.Config;
|
|||
import org.keycloak.connections.mongo.api.MongoStore;
|
||||
import org.keycloak.connections.mongo.impl.MongoStoreImpl;
|
||||
import org.keycloak.connections.mongo.impl.context.TransactionMongoStoreInvocationContext;
|
||||
import org.keycloak.connections.mongo.updater.DefaultMongoUpdaterProvider;
|
||||
import org.keycloak.connections.mongo.updater.MongoUpdaterProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
|
||||
|
@ -51,7 +51,7 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
|
|||
|
||||
@Override
|
||||
public MongoConnectionProvider create(KeycloakSession session) {
|
||||
lazyInit();
|
||||
lazyInit(session);
|
||||
|
||||
TransactionMongoStoreInvocationContext invocationContext = new TransactionMongoStoreInvocationContext(mongoStore);
|
||||
session.getTransaction().enlist(new MongoKeycloakTransaction(invocationContext));
|
||||
|
@ -69,7 +69,7 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
|
|||
}
|
||||
|
||||
|
||||
private void lazyInit() {
|
||||
private void lazyInit(KeycloakSession session) {
|
||||
if (client == null) {
|
||||
synchronized (this) {
|
||||
if (client == null) {
|
||||
|
@ -94,7 +94,13 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
|
|||
String databaseSchema = config.get("databaseSchema");
|
||||
if (databaseSchema != null) {
|
||||
if (databaseSchema.equals("update")) {
|
||||
new DefaultMongoUpdaterProvider().update(db);
|
||||
MongoUpdaterProvider mongoUpdater = session.getProvider(MongoUpdaterProvider.class);
|
||||
|
||||
if (mongoUpdater == null) {
|
||||
throw new RuntimeException("Can't update database: Mongo updater provider not found");
|
||||
}
|
||||
|
||||
mongoUpdater.update(session, db);
|
||||
} else {
|
||||
throw new RuntimeException("Invalid value for databaseSchema: " + databaseSchema);
|
||||
}
|
||||
|
|
|
@ -25,10 +25,13 @@ public class MapMapper<T extends Map> implements Mapper<T, BasicDBObject> {
|
|||
|
||||
@Override
|
||||
public BasicDBObject convertObject(MapperContext<T, BasicDBObject> context) {
|
||||
T objectToConvert = context.getObjectToConvert();
|
||||
T mapToConvert = context.getObjectToConvert();
|
||||
return convertMap(mapToConvert);
|
||||
}
|
||||
|
||||
public static BasicDBObject convertMap(Map mapToConvert) {
|
||||
BasicDBObject dbObject = new BasicDBObject();
|
||||
Set<Map.Entry> entries = objectToConvert.entrySet();
|
||||
Set<Map.Entry> entries = mapToConvert.entrySet();
|
||||
for (Map.Entry entry : entries) {
|
||||
String key = (String)entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.keycloak.connections.mongo.updater;
|
||||
|
||||
import com.mongodb.DB;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.provider.Provider;
|
||||
|
||||
/**
|
||||
|
@ -8,6 +9,6 @@ import org.keycloak.provider.Provider;
|
|||
*/
|
||||
public interface MongoUpdaterProvider extends Provider {
|
||||
|
||||
public void update(DB db);
|
||||
public void update(KeycloakSession session, DB db);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,111 +0,0 @@
|
|||
package org.keycloak.connections.mongo.updater.updates;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.BasicDBObjectBuilder;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBCursor;
|
||||
import com.mongodb.DBObject;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class Update1_2_0_Beta1 extends Update {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "1.2.0.Beta1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
convertSocialToIdFedRealms();
|
||||
convertSocialToIdFedUsers();
|
||||
}
|
||||
|
||||
|
||||
private void convertSocialToIdFedRealms() {
|
||||
DBCollection realms = db.getCollection("realms");
|
||||
DBCursor realmsCursor = realms.find();
|
||||
while (realmsCursor.hasNext()) {
|
||||
BasicDBObject realm = (BasicDBObject) realmsCursor.next();
|
||||
boolean updateProfileOnInitialSocialLogin = realm.getBoolean("updateProfileOnInitialSocialLogin");
|
||||
BasicDBObject socialConfig = (BasicDBObject) realm.get("socialConfig");
|
||||
|
||||
BasicDBList identityProviders = (BasicDBList) realm.get("identityProviders");
|
||||
if (identityProviders == null) {
|
||||
identityProviders = new BasicDBList();
|
||||
realm.put("identityProviders", identityProviders);
|
||||
}
|
||||
|
||||
if (socialConfig != null) {
|
||||
for (Map.Entry<String, Object> entry : socialConfig.entrySet()) {
|
||||
if (entry.getKey().endsWith("###key")) {
|
||||
String socialProviderId = entry.getKey().substring(0, entry.getKey().indexOf("###"));
|
||||
String clientId = (String) entry.getValue();
|
||||
String clientSecret = socialConfig.getString(socialProviderId + "###secret");
|
||||
|
||||
DBObject identityProviderConfig = new BasicDBObjectBuilder()
|
||||
.add("clientId", clientId)
|
||||
.add("clientSecret", clientSecret).get();
|
||||
|
||||
DBObject identityProvider = new BasicDBObjectBuilder()
|
||||
.add("internalId", KeycloakModelUtils.generateId())
|
||||
.add("providerId", socialProviderId)
|
||||
.add("name", socialProviderId)
|
||||
.add("id", socialProviderId)
|
||||
.add("updateProfileFirstLogin", updateProfileOnInitialSocialLogin)
|
||||
.add("enabled", true)
|
||||
.add("storeToken", false)
|
||||
.add("authenticateByDefault", false)
|
||||
.add("config", identityProviderConfig).get();
|
||||
|
||||
identityProviders.add(identityProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove obsolete keys from realm
|
||||
realm.remove("social");
|
||||
realm.remove("updateProfileOnInitialSocialLogin");
|
||||
realm.remove("socialConfig");
|
||||
|
||||
// Update realm in DB now
|
||||
realms.save(realm);
|
||||
}
|
||||
}
|
||||
|
||||
private void convertSocialToIdFedUsers() {
|
||||
DBCollection users = db.getCollection("users");
|
||||
DBCursor usersCursor = users.find();
|
||||
while (usersCursor.hasNext()) {
|
||||
BasicDBObject user = (BasicDBObject) usersCursor.next();
|
||||
|
||||
BasicDBList socialLinks = (BasicDBList) user.get("socialLinks");
|
||||
if (socialLinks != null) {
|
||||
BasicDBList federatedIdentities = (BasicDBList) user.get("federatedIdentities");
|
||||
if (federatedIdentities == null) {
|
||||
federatedIdentities = new BasicDBList();
|
||||
user.put("federatedIdentities", federatedIdentities);
|
||||
}
|
||||
|
||||
for (Object socialLinkObj : socialLinks) {
|
||||
BasicDBObject socialLink = (BasicDBObject) socialLinkObj;
|
||||
BasicDBObject idFedLink = new BasicDBObject();
|
||||
idFedLink.put("userName", socialLink.get("socialUsername"));
|
||||
idFedLink.put("userId", socialLink.get("socialUserId"));
|
||||
idFedLink.put("identityProvider", socialLink.get("socialProvider"));
|
||||
|
||||
federatedIdentities.add(idFedLink);
|
||||
}
|
||||
|
||||
// Remove obsolete keys and save user
|
||||
user.remove("socialLinks");
|
||||
users.save(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1 +1,2 @@
|
|||
org.keycloak.connections.mongo.MongoConnectionSpi
|
||||
org.keycloak.connections.mongo.MongoConnectionSpi
|
||||
org.keycloak.connections.mongo.updater.MongoUpdaterSpi
|
|
@ -18,6 +18,7 @@
|
|||
<module>infinispan</module>
|
||||
<module>mongo</module>
|
||||
<module>file</module>
|
||||
<module>mongo-update</module>
|
||||
</modules>
|
||||
|
||||
<build>
|
||||
|
|
5
dependencies/server-all/pom.xml
vendored
5
dependencies/server-all/pom.xml
vendored
|
@ -180,6 +180,11 @@
|
|||
<artifactId>keycloak-connections-mongo</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-connections-mongo-update</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-mongo</artifactId>
|
||||
|
|
|
@ -269,6 +269,10 @@
|
|||
<maven-resource group="org.keycloak" artifact="keycloak-connections-mongo"/>
|
||||
</module-def>
|
||||
|
||||
<module-def name="org.keycloak.keycloak-connections-mongo-update">
|
||||
<maven-resource group="org.keycloak" artifact="keycloak-connections-mongo-update"/>
|
||||
</module-def>
|
||||
|
||||
<module-def name="org.keycloak.keycloak-model-mongo">
|
||||
<maven-resource group="org.keycloak" artifact="keycloak-model-mongo"/>
|
||||
</module-def>
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
|
||||
|
||||
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-connections-mongo-update">
|
||||
<resources>
|
||||
<!-- Insert resources here -->
|
||||
</resources>
|
||||
<dependencies>
|
||||
<module name="org.keycloak.keycloak-core"/>
|
||||
<module name="org.keycloak.keycloak-model-api"/>
|
||||
<module name="org.keycloak.keycloak-connections-mongo"/>
|
||||
<module name="org.keycloak.keycloak-services"/>
|
||||
<module name="org.mongodb.mongo-java-driver"/>
|
||||
<module name="org.jboss.logging"/>
|
||||
<module name="javax.api"/>
|
||||
</dependencies>
|
||||
|
||||
</module>
|
|
@ -16,6 +16,7 @@
|
|||
<module name="org.keycloak.keycloak-connections-jpa" services="import"/>
|
||||
<module name="org.keycloak.keycloak-connections-jpa-liquibase" services="import"/>
|
||||
<module name="org.keycloak.keycloak-connections-mongo" services="import"/>
|
||||
<module name="org.keycloak.keycloak-connections-mongo-update" services="import"/>
|
||||
<module name="org.keycloak.keycloak-connections-file" services="import"/>
|
||||
<module name="org.keycloak.keycloak-core" services="import"/>
|
||||
<module name="org.keycloak.keycloak-core-jaxrs" services="import"/>
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
<module name="org.keycloak.keycloak-connections-jpa" services="import"/>
|
||||
<module name="org.keycloak.keycloak-connections-jpa-liquibase" services="import"/>
|
||||
<module name="org.keycloak.keycloak-connections-mongo" services="import"/>
|
||||
<module name="org.keycloak.keycloak-connections-mongo-update" services="import"/>
|
||||
<module name="org.keycloak.keycloak-connections-file" services="import"/>
|
||||
<module name="org.keycloak.keycloak-core" services="import"/>
|
||||
<module name="org.keycloak.keycloak-core-jaxrs" services="import"/>
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
<module name="org.keycloak.keycloak-connections-jpa" services="import" meta-inf="import"/>
|
||||
<module name="org.keycloak.keycloak-connections-jpa-liquibase" services="import"/>
|
||||
<module name="org.keycloak.keycloak-connections-mongo" services="import"/>
|
||||
<module name="org.keycloak.keycloak-connections-mongo-update" services="import"/>
|
||||
<module name="org.keycloak.keycloak-connections-file" services="import"/>
|
||||
<module name="org.keycloak.keycloak-core" services="import"/>
|
||||
<module name="org.keycloak.keycloak-core-jaxrs" services="import"/>
|
||||
|
|
|
@ -49,4 +49,6 @@ public class ClaimMask {
|
|||
return (mask & PHONE) > 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -5,14 +5,11 @@ import org.jboss.resteasy.client.ClientRequest;
|
|||
import org.jboss.resteasy.client.ClientResponse;
|
||||
import org.jboss.resteasy.client.core.executors.ApacheHttpClient4Executor;
|
||||
import org.keycloak.models.ApplicationModel;
|
||||
import org.keycloak.models.ClaimMask;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.protocol.LoginProtocol;
|
||||
|
|
|
@ -51,6 +51,11 @@ public class SamlProtocolFactory extends AbstractLoginProtocolFactory {
|
|||
return builtins;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProtocolMapperModel> getDefaultBuiltinMappers() {
|
||||
return defaultBuiltins;
|
||||
}
|
||||
|
||||
static List<ProtocolMapperModel> builtins = new ArrayList<>();
|
||||
static List<ProtocolMapperModel> defaultBuiltins = new ArrayList<>();
|
||||
|
||||
|
|
|
@ -19,5 +19,12 @@ public interface LoginProtocolFactory extends ProviderFactory<LoginProtocol> {
|
|||
* @return
|
||||
*/
|
||||
List<ProtocolMapperModel> getBuiltinMappers();
|
||||
|
||||
/**
|
||||
* List of mappers, which are added to new clients by default
|
||||
* @return
|
||||
*/
|
||||
List<ProtocolMapperModel> getDefaultBuiltinMappers();
|
||||
|
||||
Object createProtocolEndpoint(RealmModel realm, EventBuilder event, AuthenticationManager authManager);
|
||||
}
|
||||
|
|
|
@ -25,6 +25,14 @@ import java.util.Map;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
|
||||
|
||||
public static final String USERNAME = "username";
|
||||
public static final String EMAIL = "email";
|
||||
public static final String EMAIL_VERIFIED = "email verified";
|
||||
public static final String GIVEN_NAME = "given name";
|
||||
public static final String FAMILY_NAME = "family name";
|
||||
public static final String FULL_NAME = "full name";
|
||||
|
||||
@Override
|
||||
public LoginProtocol create(KeycloakSession session) {
|
||||
return new OIDCLoginProtocol().setSession(session);
|
||||
|
@ -35,41 +43,46 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
|
|||
return builtins;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProtocolMapperModel> getDefaultBuiltinMappers() {
|
||||
return defaultBuiltins;
|
||||
}
|
||||
|
||||
static List<ProtocolMapperModel> builtins = new ArrayList<>();
|
||||
static List<ProtocolMapperModel> defaultBuiltins = new ArrayList<>();
|
||||
|
||||
static {
|
||||
|
||||
ProtocolMapperModel model;
|
||||
model = UserPropertyMapper.createClaimMapper("username",
|
||||
model = UserPropertyMapper.createClaimMapper(USERNAME,
|
||||
"username",
|
||||
"preferred_username", "String",
|
||||
true, "username",
|
||||
true, USERNAME,
|
||||
true, true);
|
||||
builtins.add(model);
|
||||
defaultBuiltins.add(model);
|
||||
model = UserPropertyMapper.createClaimMapper("email",
|
||||
model = UserPropertyMapper.createClaimMapper(EMAIL,
|
||||
"email",
|
||||
"email", "String",
|
||||
true, "email",
|
||||
true, EMAIL,
|
||||
true, true);
|
||||
builtins.add(model);
|
||||
defaultBuiltins.add(model);
|
||||
model = UserPropertyMapper.createClaimMapper("given name",
|
||||
model = UserPropertyMapper.createClaimMapper(GIVEN_NAME,
|
||||
"firstName",
|
||||
"given_name", "String",
|
||||
true, "given name",
|
||||
true, GIVEN_NAME,
|
||||
true, true);
|
||||
builtins.add(model);
|
||||
defaultBuiltins.add(model);
|
||||
model = UserPropertyMapper.createClaimMapper("family name",
|
||||
model = UserPropertyMapper.createClaimMapper(FAMILY_NAME,
|
||||
"lastName",
|
||||
"family_name", "String",
|
||||
true, "family name",
|
||||
true, FAMILY_NAME,
|
||||
true, true);
|
||||
builtins.add(model);
|
||||
defaultBuiltins.add(model);
|
||||
model = UserPropertyMapper.createClaimMapper("email verified",
|
||||
model = UserPropertyMapper.createClaimMapper(EMAIL_VERIFIED,
|
||||
"emailVerified",
|
||||
"email_verified", "boolean",
|
||||
false, null,
|
||||
|
@ -77,11 +90,11 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
|
|||
builtins.add(model);
|
||||
|
||||
ProtocolMapperModel fullName = new ProtocolMapperModel();
|
||||
fullName.setName("full name");
|
||||
fullName.setName(FULL_NAME);
|
||||
fullName.setProtocolMapper(FullNameMapper.PROVIDER_ID);
|
||||
fullName.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||
fullName.setConsentRequired(true);
|
||||
fullName.setConsentText("full name");
|
||||
fullName.setConsentText(FULL_NAME);
|
||||
Map<String, String> config = new HashMap<String, String>();
|
||||
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
|
||||
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
package org.keycloak.services.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.keycloak.models.ClaimMask;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.protocol.LoginProtocol;
|
||||
import org.keycloak.protocol.LoginProtocolFactory;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
/**
|
||||
* Various common utils needed for migration from older version to newer
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class MigrationUtils {
|
||||
|
||||
private MigrationUtils() {}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param session
|
||||
* @param claimMask mask used on ClientModel in 1.1.0
|
||||
* @return set of 1.2.0.Beta1 protocol mappers corresponding to given claimMask
|
||||
*/
|
||||
public static Collection<ProtocolMapperModel> getMappersForClaimMask(KeycloakSession session, Long claimMask) {
|
||||
Map<String, ProtocolMapperModel> allMappers = getAllDefaultMappers(session);
|
||||
|
||||
if (claimMask == null) {
|
||||
return allMappers.values();
|
||||
}
|
||||
|
||||
if (!ClaimMask.hasUsername(claimMask)) {
|
||||
allMappers.remove(OIDCLoginProtocolFactory.USERNAME);
|
||||
}
|
||||
if (!ClaimMask.hasEmail(claimMask)) {
|
||||
allMappers.remove(OIDCLoginProtocolFactory.EMAIL);
|
||||
}
|
||||
if (!ClaimMask.hasName(claimMask)) {
|
||||
allMappers.remove(OIDCLoginProtocolFactory.FAMILY_NAME);
|
||||
allMappers.remove(OIDCLoginProtocolFactory.FULL_NAME);
|
||||
allMappers.remove(OIDCLoginProtocolFactory.GIVEN_NAME);
|
||||
}
|
||||
|
||||
return allMappers.values();
|
||||
}
|
||||
|
||||
private static Map<String, ProtocolMapperModel> getAllDefaultMappers(KeycloakSession session) {
|
||||
Map<String, ProtocolMapperModel> allMappers = new HashMap<String, ProtocolMapperModel>();
|
||||
|
||||
List<ProviderFactory> loginProtocolFactories = session.getKeycloakSessionFactory().getProviderFactories(LoginProtocol.class);
|
||||
|
||||
for (ProviderFactory factory : loginProtocolFactories) {
|
||||
LoginProtocolFactory loginProtocolFactory = (LoginProtocolFactory) factory;
|
||||
List<ProtocolMapperModel> currentMappers = loginProtocolFactory.getDefaultBuiltinMappers();
|
||||
|
||||
for (ProtocolMapperModel protocolMapper : currentMappers) {
|
||||
allMappers.put(protocolMapper.getName(), protocolMapper);
|
||||
}
|
||||
}
|
||||
|
||||
return allMappers;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue