Mongo migration: admin roles, timeout, protocol mappers

This commit is contained in:
mposolda 2015-03-19 10:42:30 +01:00
parent 82e290e06c
commit a65bac7751
26 changed files with 513 additions and 157 deletions

View 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>

View file

@ -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) {

View file

@ -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;

View file

@ -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;
}

View file

@ -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));

View file

@ -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");

View file

@ -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();
}
}
}

View file

@ -0,0 +1 @@
org.keycloak.connections.mongo.updater.impl.DefaultMongoUpdaterProviderFactory

View file

@ -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);
}

View file

@ -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();

View file

@ -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);
}

View file

@ -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);
}
}
}
}

View file

@ -1 +1,2 @@
org.keycloak.connections.mongo.MongoConnectionSpi
org.keycloak.connections.mongo.updater.MongoUpdaterSpi

View file

@ -18,6 +18,7 @@
<module>infinispan</module>
<module>mongo</module>
<module>file</module>
<module>mongo-update</module>
</modules>
<build>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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"/>

View file

@ -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"/>

View file

@ -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"/>

View file

@ -49,4 +49,6 @@ public class ClaimMask {
return (mask & PHONE) > 0;
}
}

View file

@ -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;

View file

@ -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<>();

View file

@ -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);
}

View file

@ -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");

View file

@ -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;
}
}