diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.RC1.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.RC1.xml index a1698dcc9d..7b8c877113 100755 --- a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.RC1.xml +++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.RC1.xml @@ -35,7 +35,7 @@ - + @@ -46,16 +46,16 @@ - - + + - - + + @@ -64,14 +64,14 @@ - - - + + + - - - + + + @@ -101,7 +101,7 @@ - + diff --git a/connections/jpa/src/main/resources/META-INF/persistence.xml b/connections/jpa/src/main/resources/META-INF/persistence.xml index fbea205511..b4d93a7da9 100755 --- a/connections/jpa/src/main/resources/META-INF/persistence.xml +++ b/connections/jpa/src/main/resources/META-INF/persistence.xml @@ -20,9 +20,9 @@ org.keycloak.models.jpa.entities.IdentityProviderMapperEntity org.keycloak.models.jpa.entities.ClientIdentityProviderMappingEntity org.keycloak.models.jpa.entities.ProtocolMapperEntity - org.keycloak.models.jpa.entities.GrantedConsentEntity - org.keycloak.models.jpa.entities.GrantedConsentRoleEntity - org.keycloak.models.jpa.entities.GrantedConsentProtocolMapperEntity + org.keycloak.models.jpa.entities.UserConsentEntity + org.keycloak.models.jpa.entities.UserConsentRoleEntity + org.keycloak.models.jpa.entities.UserConsentProtocolMapperEntity org.keycloak.models.sessions.jpa.entities.ClientSessionEntity diff --git a/connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update1_2_0_RC1.java b/connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update1_2_0_RC1.java index c980067237..8ee4db3c53 100644 --- a/connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update1_2_0_RC1.java +++ b/connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update1_2_0_RC1.java @@ -22,6 +22,7 @@ public class Update1_2_0_RC1 extends Update { db.getCollection("realms").update(new BasicDBObject(), new BasicDBObject("$rename", new BasicDBObject("adminAppId", "clientId")), false, true); + ensureIndex("userConsents", new String[]{"clientId", "userId"}, true, false); } private void convertApplicationsToClients() { diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java index 138955bc6c..0ef0069b41 100755 --- a/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java +++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java @@ -38,7 +38,8 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro "org.keycloak.models.sessions.mongo.entities.MongoUserSessionEntity", "org.keycloak.models.sessions.mongo.entities.MongoClientSessionEntity", "org.keycloak.models.entities.UserFederationProviderEntity", - "org.keycloak.models.entities.ProtocolMapperEntity" + "org.keycloak.models.entities.ProtocolMapperEntity", + "org.keycloak.models.mongo.keycloak.entities.MongoUserConsentEntity" }; private static final Logger logger = Logger.getLogger(DefaultMongoConnectionFactoryProvider.class); diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/api/MongoStore.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/api/MongoStore.java index 36f448cdff..397c307146 100755 --- a/connections/mongo/src/main/java/org/keycloak/connections/mongo/api/MongoStore.java +++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/api/MongoStore.java @@ -24,13 +24,35 @@ public interface MongoStore { */ void updateEntity(MongoIdentifiableEntity entity, MongoStoreInvocationContext context); + /** + * Bulk update of more entities of some type + * + * @param type + * @param query + * @param update + * @param context + * @return count of updated entities + */ + int updateEntities(Class type, DBObject query, DBObject update, MongoStoreInvocationContext context); T loadEntity(Class type, String id, MongoStoreInvocationContext context); T loadSingleEntity(Class type, DBObject query, MongoStoreInvocationContext context); + /** + * @param type + * @param query + * @param context + * @return query result or empty list if no results available for the query. Doesn't return null + */ List loadEntities(Class type, DBObject query, MongoStoreInvocationContext context); + /** + * @param type + * @param query + * @param context + * @return query result or empty list if no results available for the query. Doesn't return null + */ List loadEntities(Class type, DBObject query, DBObject sort, int firstResult, int maxResults, MongoStoreInvocationContext context); int countEntities(Class type, DBObject query, MongoStoreInvocationContext context); @@ -39,7 +61,16 @@ public interface MongoStore { boolean removeEntity(Class type, String id, MongoStoreInvocationContext context); - boolean removeEntities(Class type, DBObject query, MongoStoreInvocationContext context); + /** + * + * @param type + * @param query + * @param callback if true, then store will first load all entities, then call "afterRemove" for every entity. If false, the entities are removed directly without load and calling "afterRemove" callback + * false has better performance (especially if we are going to remove big number of entities) + * @param context + * @return count of removed entities + */ + int removeEntities(Class type, DBObject query, boolean callback, MongoStoreInvocationContext context); boolean pushItemToList(MongoIdentifiableEntity entity, String listPropertyName, S itemToPush, boolean skipIfAlreadyPresent, MongoStoreInvocationContext context); diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/api/context/MongoStoreInvocationContext.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/api/context/MongoStoreInvocationContext.java index c542ffe420..272f1c8caa 100644 --- a/connections/mongo/src/main/java/org/keycloak/connections/mongo/api/context/MongoStoreInvocationContext.java +++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/api/context/MongoStoreInvocationContext.java @@ -22,6 +22,8 @@ public interface MongoStoreInvocationContext { void beforeDBSearch(Class entityType); + void beforeDBBulkUpdateOrRemove(Class entityType); + void begin(); void commit(); diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/MongoStoreImpl.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/MongoStoreImpl.java index aded77e6b0..1696b65129 100755 --- a/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/MongoStoreImpl.java +++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/MongoStoreImpl.java @@ -7,6 +7,7 @@ import com.mongodb.DBCollection; import com.mongodb.DBCursor; import com.mongodb.DBObject; import com.mongodb.MongoException; +import com.mongodb.WriteResult; import org.jboss.logging.Logger; import org.keycloak.connections.mongo.api.MongoCollection; import org.keycloak.connections.mongo.api.MongoEntity; @@ -127,7 +128,7 @@ public class MongoStoreImpl implements MongoStore { throw convertException(e); } - // Treat object as created in this transaction (It is already submited to transaction) + // Treat object as created in this transaction (It is already submitted to transaction) context.addCreatedEntity(entity); } @@ -170,6 +171,16 @@ public class MongoStoreImpl implements MongoStore { context.addUpdateTask(entity, fullUpdateTask); } + @Override + public int updateEntities(Class type, DBObject query, DBObject update, MongoStoreInvocationContext context) { + context.beforeDBBulkUpdateOrRemove(type); + + DBCollection collection = getDBCollectionForType(type); + WriteResult wr = collection.update(query, update, false, true); + + logger.debugf("Updated %d collections of type %s", wr.getN(), type); + return wr.getN(); + } @Override public T loadEntity(Class type, String id, MongoStoreInvocationContext context) { @@ -275,19 +286,32 @@ public class MongoStoreImpl implements MongoStore { @Override - public boolean removeEntities(Class type, DBObject query, MongoStoreInvocationContext context) { - List foundObjects = loadEntities(type, query, context); - if (foundObjects.size() == 0) { - return false; - } else { - DBCollection dbCollection = getDBCollectionForType(type); - dbCollection.remove(query); - //logger.debug("Removed %d" + foundObjects.size() + " entities of type: " + type + ", query: " + query); + public int removeEntities(Class type, DBObject query, boolean callback, MongoStoreInvocationContext context) { + if (callback) { + List foundObjects = loadEntities(type, query, context); + if (foundObjects.size() == 0) { + return 0; + } else { + DBCollection dbCollection = getDBCollectionForType(type); + dbCollection.remove(query); - for (MongoIdentifiableEntity found : foundObjects) { - context.addRemovedEntity(found);; + logger.debugf("Removed %d entities of type: %s, query: %s", foundObjects.size(), type, query); + + for (MongoIdentifiableEntity found : foundObjects) { + context.addRemovedEntity(found);; + } + return foundObjects.size(); } - return true; + } else { + + context.beforeDBBulkUpdateOrRemove(type); + + DBCollection dbCollection = getDBCollectionForType(type); + WriteResult writeResult = dbCollection.remove(query); + int removedCount = writeResult.getN(); + + logger.debugf("Removed directly %d entities of type: %s, query: %s", removedCount, type, query); + return removedCount; } } diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/context/SimpleMongoStoreInvocationContext.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/context/SimpleMongoStoreInvocationContext.java index 0ecffc6468..868805b848 100644 --- a/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/context/SimpleMongoStoreInvocationContext.java +++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/context/SimpleMongoStoreInvocationContext.java @@ -45,6 +45,10 @@ public class SimpleMongoStoreInvocationContext implements MongoStoreInvocationCo public void beforeDBSearch(Class entityType) { } + @Override + public void beforeDBBulkUpdateOrRemove(Class entityType) { + } + @Override public void begin() { } diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/context/TransactionMongoStoreInvocationContext.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/context/TransactionMongoStoreInvocationContext.java index 39cf9eb5b9..117f07f9e0 100644 --- a/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/context/TransactionMongoStoreInvocationContext.java +++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/context/TransactionMongoStoreInvocationContext.java @@ -50,10 +50,6 @@ public class TransactionMongoStoreInvocationContext implements MongoStoreInvocat @Override public void addUpdateTask(MongoIdentifiableEntity entityToUpdate, MongoTask task) { - if (!loadedObjects.containsValue(entityToUpdate)) { - throw new IllegalStateException("Entity " + entityToUpdate + " not found in loaded objects"); - } - Set currentObjectTasks = pendingUpdateTasks.get(entityToUpdate); if (currentObjectTasks == null) { currentObjectTasks = new LinkedHashSet(); @@ -106,6 +102,24 @@ public class TransactionMongoStoreInvocationContext implements MongoStoreInvocat } } + @Override + public void beforeDBBulkUpdateOrRemove(Class entityType) { + beforeDBSearch(entityType); + Set toRemove = new HashSet(); + + for (Map.Entry entry : loadedObjects.entrySet()) { + MongoIdentifiableEntity entity = entry.getValue(); + if (entity.getClass().equals(entityType)) { + toRemove.add(entry.getKey()); + } + } + + // Now remove all loadedObjects + for (String objectId : toRemove) { + loadedObjects.remove(objectId); + } + } + @Override public void begin() { loadedObjects.clear(); diff --git a/core/src/main/java/org/keycloak/representations/idm/UserConsentRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/UserConsentRepresentation.java new file mode 100644 index 0000000000..113ba8483c --- /dev/null +++ b/core/src/main/java/org/keycloak/representations/idm/UserConsentRepresentation.java @@ -0,0 +1,28 @@ +package org.keycloak.representations.idm; + +import java.util.List; + +/** + * @author Marek Posolda + */ +public class UserConsentRepresentation { + + protected List grantedRoles; // points to roleIds + protected List grantedProtocolMappers; // points to protocolMapperIds + + public List getGrantedRoles() { + return grantedRoles; + } + + public void setGrantedRoles(List grantedRoles) { + this.grantedRoles = grantedRoles; + } + + public List getGrantedProtocolMappers() { + return grantedProtocolMappers; + } + + public void setGrantedProtocolMappers(List grantedProtocolMappers) { + this.grantedProtocolMappers = grantedProtocolMappers; + } +} diff --git a/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java index 94a90d9e45..b9716d3150 100755 --- a/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java @@ -27,6 +27,7 @@ public class UserRepresentation { protected List federatedIdentities; protected List realmRoles; protected Map> clientRoles; + protected Map clientConsents; @Deprecated protected Map> applicationRoles; @@ -176,6 +177,14 @@ public class UserRepresentation { this.clientRoles = clientRoles; } + public Map getClientConsents() { + return clientConsents; + } + + public void setClientConsents(Map clientConsents) { + this.clientConsents = clientConsents; + } + @Deprecated public Map> getApplicationRoles() { return applicationRoles; diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportUtils.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportUtils.java index c404c3d3ad..bccce2d0ff 100755 --- a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportUtils.java +++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportUtils.java @@ -8,14 +8,15 @@ import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.SerializationConfig; import org.keycloak.models.ClientModel; import org.keycloak.models.KeycloakSession; +import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleContainerModel; import org.keycloak.models.RoleModel; import org.keycloak.models.FederatedIdentityModel; +import org.keycloak.models.UserConsentModel; import org.keycloak.models.UserCredentialValueModel; import org.keycloak.models.UserModel; import org.keycloak.models.utils.ModelToRepresentation; -import org.keycloak.representations.idm.ApplicationRepresentation; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.RealmRepresentation; @@ -23,6 +24,7 @@ import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.RolesRepresentation; import org.keycloak.representations.idm.ScopeMappingRepresentation; import org.keycloak.representations.idm.FederatedIdentityRepresentation; +import org.keycloak.representations.idm.UserConsentRepresentation; import org.keycloak.representations.idm.UserRepresentation; import java.io.IOException; @@ -283,6 +285,35 @@ public class ExportUtils { userRep.setCredentials(credReps); userRep.setFederationLink(user.getFederationLink()); + // Grants + List consents = user.getConsents(); + Map consentReps = new HashMap(); + for (UserConsentModel consent : consents) { + String clientId = consent.getClient().getClientId(); + + List grantedProtocolMappers = new LinkedList(); + for (ProtocolMapperModel protocolMapper : consent.getGrantedProtocolMappers()) { + grantedProtocolMappers.add(protocolMapper.getId()); + } + + List grantedRoles = new LinkedList(); + for (RoleModel role : consent.getGrantedRoles()) { + grantedRoles.add(role.getId()); + } + + + if (grantedRoles.size() > 0 || grantedProtocolMappers.size() > 0) { + UserConsentRepresentation consentRep = new UserConsentRepresentation(); + if (grantedRoles.size() > 0) consentRep.setGrantedRoles(grantedRoles); + if (grantedProtocolMappers.size() > 0) consentRep.setGrantedProtocolMappers(grantedProtocolMappers); + consentReps.put(clientId, consentRep); + } + } + + if (consentReps.size() > 0) { + userRep.setClientConsents(consentReps); + } + return userRep; } diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccessBean.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccessBean.java index 58cea2fc0f..67eb4a68b4 100644 --- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccessBean.java +++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccessBean.java @@ -5,7 +5,7 @@ import java.util.LinkedList; import java.util.List; import org.keycloak.models.ClientModel; -import org.keycloak.models.GrantedConsentModel; +import org.keycloak.models.UserConsentModel; import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; @@ -20,14 +20,13 @@ public class AccessBean { private List clientGrants = new LinkedList(); public AccessBean(RealmModel realm, UserModel user, URI baseUri, String stateChecker) { - List grantedConsents = user.getGrantedConsents(); - for (GrantedConsentModel consent : grantedConsents) { - ClientModel client = realm.getClientById(consent.getClientId()); + List grantedConsents = user.getConsents(); + for (UserConsentModel consent : grantedConsents) { + ClientModel client = consent.getClient(); List realmRolesGranted = new LinkedList(); MultivaluedHashMap resourceRolesGranted = new MultivaluedHashMap(); - for (String roleId : consent.getGrantedRoles()) { - RoleModel role = realm.getRoleById(roleId); + for (RoleModel role : consent.getGrantedRoles()) { if (role.getContainer() instanceof RealmModel) { realmRolesGranted.add(role); } else { @@ -36,8 +35,7 @@ public class AccessBean { } List claimsGranted = new LinkedList(); - for (String protocolMapperId : consent.getGrantedProtocolMappers()) { - ProtocolMapperModel protocolMapper = client.getProtocolMapperById(protocolMapperId); + for (ProtocolMapperModel protocolMapper : consent.getGrantedProtocolMappers()) { claimsGranted.add(protocolMapper.getConsentText()); } diff --git a/model/api/src/main/java/org/keycloak/models/GrantedConsentModel.java b/model/api/src/main/java/org/keycloak/models/GrantedConsentModel.java deleted file mode 100644 index e0366559be..0000000000 --- a/model/api/src/main/java/org/keycloak/models/GrantedConsentModel.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.keycloak.models; - -import java.util.HashSet; -import java.util.Set; - -/** - * @author Marek Posolda - */ -public class GrantedConsentModel { - - private final String clientId; - private Set protocolMapperIds = new HashSet(); - private Set roleIds = new HashSet(); - - public GrantedConsentModel(String clientId) { - this.clientId = clientId; - } - - public String getClientId() { - return clientId; - } - - public void addGrantedRole(String roleId) { - roleIds.add(roleId); - } - - public Set getGrantedRoles() { - return roleIds; - } - - public boolean isRoleGranted(String roleId) { - return roleIds.contains(roleId); - } - - public void addGrantedProtocolMapper(String protocolMapperId) { - protocolMapperIds.add(protocolMapperId); - } - - public Set getGrantedProtocolMappers() { - return protocolMapperIds; - } - - public boolean isProtocolMapperGranted(String protocolMapperId) { - return protocolMapperIds.contains(protocolMapperId); - } - -} diff --git a/model/api/src/main/java/org/keycloak/models/UserConsentModel.java b/model/api/src/main/java/org/keycloak/models/UserConsentModel.java new file mode 100644 index 0000000000..11f1034500 --- /dev/null +++ b/model/api/src/main/java/org/keycloak/models/UserConsentModel.java @@ -0,0 +1,69 @@ +package org.keycloak.models; + +import java.util.HashSet; +import java.util.Set; + +/** + * @author Marek Posolda + */ +public class UserConsentModel { + + private final RealmModel realm; + private final ClientModel client; + private Set protocolMappers = new HashSet(); + private Set roles = new HashSet(); + + public UserConsentModel(RealmModel realm, String clientId) { + this.realm = realm; + this.client = realm.getClientById(clientId); + + if (client == null) { + throw new ModelException("Client with id [" + clientId + "] is not available"); + } + } + + public ClientModel getClient() { + return client; + } + + public void addGrantedRole(String roleId) { + RoleModel role = realm.getRoleById(roleId); + + // Chance that role was already deleted by other transaction and is not available anymore + if (role != null) { + roles.add(role); + } + } + + public Set getGrantedRoles() { + return roles; + } + + public boolean isRoleGranted(RoleModel role) { + for (RoleModel currentRole : roles) { + if (currentRole.getId().equals(role.getId())) return true; + } + return false; + } + + public void addGrantedProtocolMapper(String protocolMapperId) { + ProtocolMapperModel protocolMapper = client.getProtocolMapperById(protocolMapperId); + + // Chance that protocolMapper was already deleted by other transaction and is not available anymore + if (protocolMapper != null) { + protocolMappers.add(protocolMapper); + } + } + + public Set getGrantedProtocolMappers() { + return protocolMappers; + } + + public boolean isProtocolMapperGranted(ProtocolMapperModel protocolMapper) { + for (ProtocolMapperModel currentProtMapper : protocolMappers) { + if (currentProtMapper.getId().equals(protocolMapper.getId())) return true; + } + return false; + } + +} diff --git a/model/api/src/main/java/org/keycloak/models/UserModel.java b/model/api/src/main/java/org/keycloak/models/UserModel.java index 6ad716c2ff..ed5ae9ea96 100755 --- a/model/api/src/main/java/org/keycloak/models/UserModel.java +++ b/model/api/src/main/java/org/keycloak/models/UserModel.java @@ -75,11 +75,11 @@ public interface UserModel { String getFederationLink(); void setFederationLink(String link); - GrantedConsentModel addGrantedConsent(GrantedConsentModel consent); - GrantedConsentModel getGrantedConsentByClient(String clientId); - List getGrantedConsents(); - void updateGrantedConsent(GrantedConsentModel consent); - boolean revokeGrantedConsentForClient(String clientId); + void addConsent(UserConsentModel consent); + UserConsentModel getConsentByClient(String clientId); + List getConsents(); + void updateConsent(UserConsentModel consent); + boolean revokeConsentForClient(String clientId); public static enum RequiredAction { VERIFY_EMAIL, UPDATE_PROFILE, CONFIGURE_TOTP, UPDATE_PASSWORD diff --git a/model/api/src/main/java/org/keycloak/models/entities/UserConsentEntity.java b/model/api/src/main/java/org/keycloak/models/entities/UserConsentEntity.java new file mode 100644 index 0000000000..47b76c6ba2 --- /dev/null +++ b/model/api/src/main/java/org/keycloak/models/entities/UserConsentEntity.java @@ -0,0 +1,47 @@ +package org.keycloak.models.entities; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Marek Posolda + */ +public class UserConsentEntity extends AbstractIdentifiableEntity { + + private String userId; + private String clientId; + private List grantedRoles = new ArrayList(); + private List grantedProtocolMappers = new ArrayList(); + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public List getGrantedRoles() { + return grantedRoles; + } + + public void setGrantedRoles(List grantedRoles) { + this.grantedRoles = grantedRoles; + } + + public List getGrantedProtocolMappers() { + return grantedProtocolMappers; + } + + public void setGrantedProtocolMappers(List grantedProtocolMappers) { + this.grantedProtocolMappers = grantedProtocolMappers; + } +} diff --git a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java index 6fc3e1e46a..0312ff452a 100755 --- a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java +++ b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java @@ -16,6 +16,7 @@ import org.keycloak.models.PasswordPolicy; import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; +import org.keycloak.models.UserConsentModel; import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserCredentialValueModel; import org.keycloak.models.UserFederationProviderModel; @@ -34,6 +35,7 @@ import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.ScopeMappingRepresentation; import org.keycloak.representations.idm.SocialLinkRepresentation; +import org.keycloak.representations.idm.UserConsentRepresentation; import org.keycloak.representations.idm.UserFederationProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.util.UriUtils; @@ -789,6 +791,35 @@ public class RepresentationToModel { createClientRoleMappings(client, user, entry.getValue()); } } + if (userRep.getClientConsents() != null) { + for (Map.Entry entry : userRep.getClientConsents().entrySet()) { + ClientModel client = clientMap.get(entry.getKey()); + if (client == null) { + throw new RuntimeException("Unable to find client consent mappings for client: " + entry.getKey()); + } + + UserConsentModel consentModel = new UserConsentModel(newRealm, client.getId()); + + UserConsentRepresentation consentRep = entry.getValue(); + if (consentRep.getGrantedRoles() != null) { + for (String roleId : consentRep.getGrantedRoles()) { + if (newRealm.getRoleById(roleId) == null) { + throw new RuntimeException("Unable to find realm role referenced in consent mappings of user " + user.getUsername() + ". Role ID: " + roleId); + } + consentModel.addGrantedRole(roleId); + } + } + if (consentRep.getGrantedProtocolMappers() != null) { + for (String mapperId : consentRep.getGrantedProtocolMappers()) { + if (client.getProtocolMapperById(mapperId) == null) { + throw new RuntimeException("Unable to find protocol mapper referenced in consent mappings of user " + user.getUsername() + ". Protocol mapper ID: " + mapperId); + } + consentModel.addGrantedProtocolMapper(mapperId);; + } + } + user.addConsent(consentModel); + } + } return user; } diff --git a/model/api/src/main/java/org/keycloak/models/utils/UserModelDelegate.java b/model/api/src/main/java/org/keycloak/models/utils/UserModelDelegate.java index 83b6add1f0..8ac8f5b1b6 100755 --- a/model/api/src/main/java/org/keycloak/models/utils/UserModelDelegate.java +++ b/model/api/src/main/java/org/keycloak/models/utils/UserModelDelegate.java @@ -1,7 +1,7 @@ package org.keycloak.models.utils; import org.keycloak.models.ClientModel; -import org.keycloak.models.GrantedConsentModel; +import org.keycloak.models.UserConsentModel; import org.keycloak.models.RoleModel; import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserCredentialValueModel; @@ -188,27 +188,27 @@ public class UserModelDelegate implements UserModel { } @Override - public GrantedConsentModel addGrantedConsent(GrantedConsentModel consent) { - return delegate.addGrantedConsent(consent); + public void addConsent(UserConsentModel consent) { + delegate.addConsent(consent); } @Override - public GrantedConsentModel getGrantedConsentByClient(String clientId) { - return delegate.getGrantedConsentByClient(clientId); + public UserConsentModel getConsentByClient(String clientId) { + return delegate.getConsentByClient(clientId); } @Override - public List getGrantedConsents() { - return delegate.getGrantedConsents(); + public List getConsents() { + return delegate.getConsents(); } @Override - public void updateGrantedConsent(GrantedConsentModel consent) { - delegate.updateGrantedConsent(consent); + public void updateConsent(UserConsentModel consent) { + delegate.updateConsent(consent); } @Override - public boolean revokeGrantedConsentForClient(String clientId) { - return delegate.revokeGrantedConsentForClient(clientId); + public boolean revokeConsentForClient(String clientId) { + return delegate.revokeConsentForClient(clientId); } } diff --git a/model/file/src/main/java/org/keycloak/models/file/adapter/ClientAdapter.java b/model/file/src/main/java/org/keycloak/models/file/adapter/ClientAdapter.java index 18cc9c858b..34ddb06734 100755 --- a/model/file/src/main/java/org/keycloak/models/file/adapter/ClientAdapter.java +++ b/model/file/src/main/java/org/keycloak/models/file/adapter/ClientAdapter.java @@ -277,7 +277,8 @@ public class ClientAdapter implements ClientModel { throw new RuntimeException("protocol mapper name must be unique per protocol"); } ProtocolMapperEntity entity = new ProtocolMapperEntity(); - entity.setId(KeycloakModelUtils.generateId()); + String id = model.getId() != null ? model.getId() : KeycloakModelUtils.generateId(); + entity.setId(id); entity.setProtocol(model.getProtocol()); entity.setName(model.getName()); entity.setProtocolMapper(model.getProtocolMapper()); diff --git a/model/file/src/main/java/org/keycloak/models/file/adapter/UserAdapter.java b/model/file/src/main/java/org/keycloak/models/file/adapter/UserAdapter.java index 3033eea720..0937e2c46f 100755 --- a/model/file/src/main/java/org/keycloak/models/file/adapter/UserAdapter.java +++ b/model/file/src/main/java/org/keycloak/models/file/adapter/UserAdapter.java @@ -20,7 +20,7 @@ import org.keycloak.models.ClientModel; import static org.keycloak.models.utils.Pbkdf2PasswordEncoder.getSalt; -import org.keycloak.models.GrantedConsentModel; +import org.keycloak.models.UserConsentModel; import org.keycloak.models.PasswordPolicy; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; @@ -432,30 +432,29 @@ public class UserAdapter implements UserModel, Comparable { } @Override - public GrantedConsentModel addGrantedConsent(GrantedConsentModel consent) { + public void addConsent(UserConsentModel consent) { + // TODO + } + + @Override + public UserConsentModel getConsentByClient(String clientId) { // TODO return null; } @Override - public GrantedConsentModel getGrantedConsentByClient(String clientId) { + public List getConsents() { // TODO return null; } @Override - public List getGrantedConsents() { - // TODO - return null; - } - - @Override - public void updateGrantedConsent(GrantedConsentModel consent) { + public void updateConsent(UserConsentModel consent) { // TODO } @Override - public boolean revokeGrantedConsentForClient(String clientId) { + public boolean revokeConsentForClient(String clientId) { // TODO return false; } diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/UserAdapter.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/UserAdapter.java index 97b62fc6c1..10336fd592 100755 --- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/UserAdapter.java +++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/UserAdapter.java @@ -1,7 +1,7 @@ package org.keycloak.models.cache; import org.keycloak.models.ClientModel; -import org.keycloak.models.GrantedConsentModel; +import org.keycloak.models.UserConsentModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleContainerModel; @@ -277,34 +277,34 @@ public class UserAdapter implements UserModel { } @Override - public GrantedConsentModel addGrantedConsent(GrantedConsentModel consent) { + public void addConsent(UserConsentModel consent) { getDelegateForUpdate(); - return updated.addGrantedConsent(consent); + updated.addConsent(consent); } @Override - public GrantedConsentModel getGrantedConsentByClient(String clientId) { + public UserConsentModel getConsentByClient(String clientId) { // TODO: caching? getDelegateForUpdate(); - return updated.getGrantedConsentByClient(clientId); + return updated.getConsentByClient(clientId); } @Override - public List getGrantedConsents() { + public List getConsents() { // TODO: caching? getDelegateForUpdate(); - return updated.getGrantedConsents(); + return updated.getConsents(); } @Override - public void updateGrantedConsent(GrantedConsentModel consent) { + public void updateConsent(UserConsentModel consent) { getDelegateForUpdate(); - updated.updateGrantedConsent(consent); + updated.updateConsent(consent); } @Override - public boolean revokeGrantedConsentForClient(String clientId) { + public boolean revokeConsentForClient(String clientId) { getDelegateForUpdate(); - return updated.revokeGrantedConsentForClient(clientId); + return updated.revokeConsentForClient(clientId); } } diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java index 498db9a81c..f9f62b6ad5 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java @@ -378,7 +378,7 @@ public class ClientAdapter implements ClientModel { if (getProtocolMapperByName(model.getProtocol(), model.getName()) != null) { throw new RuntimeException("protocol mapper name must be unique per protocol"); } - String id = KeycloakModelUtils.generateId(); + String id = model.getId() != null ? model.getId() : KeycloakModelUtils.generateId(); ProtocolMapperEntity entity = new ProtocolMapperEntity(); entity.setId(id); entity.setName(model.getName()); diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java index 15300d013a..d0af512b05 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java @@ -88,9 +88,9 @@ public class JpaUserProvider implements UserProvider { private void removeUser(UserEntity user) { em.createNamedQuery("deleteUserRoleMappingsByUser").setParameter("user", user).executeUpdate(); em.createNamedQuery("deleteFederatedIdentityByUser").setParameter("user", user).executeUpdate(); - em.createNamedQuery("deleteGrantedConsentRolesByUser").setParameter("user", user).executeUpdate(); - em.createNamedQuery("deleteGrantedConsentProtMappersByUser").setParameter("user", user).executeUpdate(); - em.createNamedQuery("deleteGrantedConsentsByUser").setParameter("user", user).executeUpdate(); + em.createNamedQuery("deleteUserConsentRolesByUser").setParameter("user", user).executeUpdate(); + em.createNamedQuery("deleteUserConsentProtMappersByUser").setParameter("user", user).executeUpdate(); + em.createNamedQuery("deleteUserConsentsByUser").setParameter("user", user).executeUpdate(); em.remove(user); } @@ -134,11 +134,11 @@ public class JpaUserProvider implements UserProvider { @Override public void preRemove(RealmModel realm) { - int num = em.createNamedQuery("deleteGrantedConsentRolesByRealm") + int num = em.createNamedQuery("deleteUserConsentRolesByRealm") .setParameter("realmId", realm.getId()).executeUpdate(); - num = em.createNamedQuery("deleteGrantedConsentProtMappersByRealm") + num = em.createNamedQuery("deleteUserConsentProtMappersByRealm") .setParameter("realmId", realm.getId()).executeUpdate(); - num = em.createNamedQuery("deleteGrantedConsentsByRealm") + num = em.createNamedQuery("deleteUserConsentsByRealm") .setParameter("realmId", realm.getId()).executeUpdate(); num = em.createNamedQuery("deleteUserRoleMappingsByRealm") .setParameter("realmId", realm.getId()).executeUpdate(); @@ -184,20 +184,20 @@ public class JpaUserProvider implements UserProvider { @Override public void preRemove(RealmModel realm, RoleModel role) { - em.createNamedQuery("deleteGrantedConsentRolesByRole").setParameter("roleId", role.getId()).executeUpdate(); + em.createNamedQuery("deleteUserConsentRolesByRole").setParameter("roleId", role.getId()).executeUpdate(); em.createNamedQuery("deleteUserRoleMappingsByRole").setParameter("roleId", role.getId()).executeUpdate(); } @Override public void preRemove(RealmModel realm, ClientModel client) { - em.createNamedQuery("deleteGrantedConsentProtMappersByClient").setParameter("clientId", client.getId()).executeUpdate(); - em.createNamedQuery("deleteGrantedConsentRolesByClient").setParameter("clientId", client.getId()).executeUpdate(); - em.createNamedQuery("deleteGrantedConsentsByClient").setParameter("clientId", client.getId()).executeUpdate(); + em.createNamedQuery("deleteUserConsentProtMappersByClient").setParameter("clientId", client.getId()).executeUpdate(); + em.createNamedQuery("deleteUserConsentRolesByClient").setParameter("clientId", client.getId()).executeUpdate(); + em.createNamedQuery("deleteUserConsentsByClient").setParameter("clientId", client.getId()).executeUpdate(); } @Override public void preRemove(ClientModel client, ProtocolMapperModel protocolMapper) { - em.createNamedQuery("deleteGrantedConsentProtMappersByProtocolMapper") + em.createNamedQuery("deleteUserConsentProtMappersByProtocolMapper") .setParameter("protocolMapperId", protocolMapper.getId()) .executeUpdate(); } diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java index f8d935275a..737e0e3760 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java @@ -1,7 +1,8 @@ package org.keycloak.models.jpa; import org.keycloak.models.ClientModel; -import org.keycloak.models.GrantedConsentModel; +import org.keycloak.models.ProtocolMapperModel; +import org.keycloak.models.UserConsentModel; import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.ModelException; import org.keycloak.models.PasswordPolicy; @@ -12,9 +13,9 @@ import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserCredentialValueModel; import org.keycloak.models.UserModel; import org.keycloak.models.jpa.entities.CredentialEntity; -import org.keycloak.models.jpa.entities.GrantedConsentEntity; -import org.keycloak.models.jpa.entities.GrantedConsentProtocolMapperEntity; -import org.keycloak.models.jpa.entities.GrantedConsentRoleEntity; +import org.keycloak.models.jpa.entities.UserConsentEntity; +import org.keycloak.models.jpa.entities.UserConsentProtocolMapperEntity; +import org.keycloak.models.jpa.entities.UserConsentRoleEntity; import org.keycloak.models.jpa.entities.UserAttributeEntity; import org.keycloak.models.jpa.entities.UserEntity; import org.keycloak.models.jpa.entities.UserRequiredActionEntity; @@ -29,7 +30,6 @@ import javax.persistence.TypedQuery; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; -import java.util.Date; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -480,18 +480,15 @@ public class UserAdapter implements UserModel { } @Override - public GrantedConsentModel addGrantedConsent(GrantedConsentModel consent) { - String clientId = consent.getClientId(); - if (clientId == null) { - throw new ModelException("clientId needs to be filled for newly added consent!"); - } + public void addConsent(UserConsentModel consent) { + String clientId = consent.getClient().getId(); - GrantedConsentEntity consentEntity = getGrantedConsentEntity(clientId); + UserConsentEntity consentEntity = getGrantedConsentEntity(clientId); if (consentEntity != null) { throw new ModelDuplicateException("Consent already exists for client [" + clientId + "] and user [" + user.getId() + "]"); } - consentEntity = new GrantedConsentEntity(); + consentEntity = new UserConsentEntity(); consentEntity.setId(KeycloakModelUtils.generateId()); consentEntity.setUser(user); consentEntity.setClientId(clientId); @@ -499,38 +496,33 @@ public class UserAdapter implements UserModel { em.flush(); updateGrantedConsentEntity(consentEntity, consent); - - return consent; } @Override - public GrantedConsentModel getGrantedConsentByClient(String clientId) { - GrantedConsentEntity entity = getGrantedConsentEntity(clientId); + public UserConsentModel getConsentByClient(String clientId) { + UserConsentEntity entity = getGrantedConsentEntity(clientId); return toConsentModel(entity); } @Override - public List getGrantedConsents() { - TypedQuery query = em.createNamedQuery("grantedConsentsByUser", GrantedConsentEntity.class); + public List getConsents() { + TypedQuery query = em.createNamedQuery("userConsentsByUser", UserConsentEntity.class); query.setParameter("userId", getId()); - List results = query.getResultList(); + List results = query.getResultList(); - List consents = new ArrayList(); - for (GrantedConsentEntity entity : results) { - GrantedConsentModel model = toConsentModel(entity); + List consents = new ArrayList(); + for (UserConsentEntity entity : results) { + UserConsentModel model = toConsentModel(entity); consents.add(model); } return consents; } @Override - public void updateGrantedConsent(GrantedConsentModel consent) { - String clientId = consent.getClientId(); - if (clientId == null) { - throw new ModelException("clientId needs to be for newly added consent!"); - } + public void updateConsent(UserConsentModel consent) { + String clientId = consent.getClient().getId(); - GrantedConsentEntity consentEntity = getGrantedConsentEntity(clientId); + UserConsentEntity consentEntity = getGrantedConsentEntity(clientId); if (consentEntity == null) { throw new ModelException("Consent not found for client [" + clientId + "] and user [" + user.getId() + "]"); } @@ -539,8 +531,8 @@ public class UserAdapter implements UserModel { } @Override - public boolean revokeGrantedConsentForClient(String clientId) { - GrantedConsentEntity consentEntity = getGrantedConsentEntity(clientId); + public boolean revokeConsentForClient(String clientId) { + UserConsentEntity consentEntity = getGrantedConsentEntity(clientId); if (consentEntity == null) return false; em.remove(consentEntity); @@ -549,11 +541,11 @@ public class UserAdapter implements UserModel { } - private GrantedConsentEntity getGrantedConsentEntity(String clientId) { - TypedQuery query = em.createNamedQuery("grantedConsentByUserAndClient", GrantedConsentEntity.class); + private UserConsentEntity getGrantedConsentEntity(String clientId) { + TypedQuery query = em.createNamedQuery("userConsentByUserAndClient", UserConsentEntity.class); query.setParameter("userId", getId()); query.setParameter("clientId", clientId); - List results = query.getResultList(); + List results = query.getResultList(); if (results.size() > 1) { throw new ModelException("More results found for user [" + getUsername() + "] and client [" + clientId + "]"); } else if (results.size() == 1) { @@ -563,23 +555,23 @@ public class UserAdapter implements UserModel { } } - private GrantedConsentModel toConsentModel(GrantedConsentEntity entity) { + private UserConsentModel toConsentModel(UserConsentEntity entity) { if (entity == null) { return null; } - GrantedConsentModel model = new GrantedConsentModel(entity.getClientId()); + UserConsentModel model = new UserConsentModel(realm, entity.getClientId()); - Collection grantedRoleEntities = entity.getGrantedRoles(); + Collection grantedRoleEntities = entity.getGrantedRoles(); if (grantedRoleEntities != null) { - for (GrantedConsentRoleEntity grantedRole : grantedRoleEntities) { + for (UserConsentRoleEntity grantedRole : grantedRoleEntities) { model.addGrantedRole(grantedRole.getRoleId()); } } - Collection grantedProtocolMapperEntities = entity.getGrantedProtocolMappers(); + Collection grantedProtocolMapperEntities = entity.getGrantedProtocolMappers(); if (grantedProtocolMapperEntities != null) { - for (GrantedConsentProtocolMapperEntity grantedProtMapper : grantedProtocolMapperEntities) { + for (UserConsentProtocolMapperEntity grantedProtMapper : grantedProtocolMapperEntities) { model.addGrantedProtocolMapper(grantedProtMapper.getProtocolMapperId()); } } @@ -588,14 +580,14 @@ public class UserAdapter implements UserModel { } // Update roles and protocolMappers to given consentEntity from the consentModel - private void updateGrantedConsentEntity(GrantedConsentEntity consentEntity, GrantedConsentModel consentModel) { - Collection grantedProtocolMapperEntities = consentEntity.getGrantedProtocolMappers(); - Collection mappersToRemove = new HashSet(grantedProtocolMapperEntities); + private void updateGrantedConsentEntity(UserConsentEntity consentEntity, UserConsentModel consentModel) { + Collection grantedProtocolMapperEntities = consentEntity.getGrantedProtocolMappers(); + Collection mappersToRemove = new HashSet(grantedProtocolMapperEntities); - for (String protocolMapperId : consentModel.getGrantedProtocolMappers()) { - GrantedConsentProtocolMapperEntity grantedProtocolMapperEntity = new GrantedConsentProtocolMapperEntity(); - grantedProtocolMapperEntity.setGrantedConsent(consentEntity); - grantedProtocolMapperEntity.setProtocolMapperId(protocolMapperId); + for (ProtocolMapperModel protocolMapper : consentModel.getGrantedProtocolMappers()) { + UserConsentProtocolMapperEntity grantedProtocolMapperEntity = new UserConsentProtocolMapperEntity(); + grantedProtocolMapperEntity.setUserConsent(consentEntity); + grantedProtocolMapperEntity.setProtocolMapperId(protocolMapper.getId()); // Check if it's already there if (!grantedProtocolMapperEntities.contains(grantedProtocolMapperEntity)) { @@ -607,17 +599,17 @@ public class UserAdapter implements UserModel { } } // Those mappers were no longer on consentModel and will be removed - for (GrantedConsentProtocolMapperEntity toRemove : mappersToRemove) { + for (UserConsentProtocolMapperEntity toRemove : mappersToRemove) { grantedProtocolMapperEntities.remove(toRemove); em.remove(toRemove); } - Collection grantedRoleEntities = consentEntity.getGrantedRoles(); - Set rolesToRemove = new HashSet(grantedRoleEntities); - for (String roleId : consentModel.getGrantedRoles()) { - GrantedConsentRoleEntity consentRoleEntity = new GrantedConsentRoleEntity(); - consentRoleEntity.setGrantedConsent(consentEntity); - consentRoleEntity.setRoleId(roleId); + Collection grantedRoleEntities = consentEntity.getGrantedRoles(); + Set rolesToRemove = new HashSet(grantedRoleEntities); + for (RoleModel role : consentModel.getGrantedRoles()) { + UserConsentRoleEntity consentRoleEntity = new UserConsentRoleEntity(); + consentRoleEntity.setUserConsent(consentEntity); + consentRoleEntity.setRoleId(role.getId()); // Check if it's already there if (!grantedRoleEntities.contains(consentRoleEntity)) { @@ -629,7 +621,7 @@ public class UserAdapter implements UserModel { } } // Those roles were no longer on consentModel and will be removed - for (GrantedConsentRoleEntity toRemove : rolesToRemove) { + for (UserConsentRoleEntity toRemove : rolesToRemove) { grantedRoleEntities.remove(toRemove); em.remove(toRemove); } diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/GrantedConsentEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/GrantedConsentEntity.java deleted file mode 100644 index 262f37f671..0000000000 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/GrantedConsentEntity.java +++ /dev/null @@ -1,96 +0,0 @@ -package org.keycloak.models.jpa.entities; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -import javax.persistence.CascadeType; -import javax.persistence.CollectionTable; -import javax.persistence.Column; -import javax.persistence.ElementCollection; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.JoinTable; -import javax.persistence.ManyToOne; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.OneToMany; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; - -/** - * @author Marek Posolda - */ -@Entity -@Table(name="GRANTED_CONSENT", uniqueConstraints = { - @UniqueConstraint(columnNames = {"USER_ID", "CLIENT_ID"}) -}) -@NamedQueries({ - @NamedQuery(name="grantedConsentByUserAndClient", query="select consent from GrantedConsentEntity consent where consent.user.id = :userId and consent.clientId = :clientId"), - @NamedQuery(name="grantedConsentsByUser", query="select consent from GrantedConsentEntity consent where consent.user.id = :userId"), - @NamedQuery(name="deleteGrantedConsentsByRealm", query="delete from GrantedConsentEntity consent where consent.user IN (select user from UserEntity user where user.realmId = :realmId)"), - @NamedQuery(name="deleteGrantedConsentsByUser", query="delete from GrantedConsentEntity consent where consent.user = :user"), - @NamedQuery(name="deleteGrantedConsentsByClient", query="delete from GrantedConsentEntity consent where consent.clientId = :clientId"), -}) -public class GrantedConsentEntity { - - @Id - @Column(name="ID", length = 36) - protected String id; - - @ManyToOne(fetch= FetchType.LAZY) - @JoinColumn(name="USER_ID") - protected UserEntity user; - - @Column(name="CLIENT_ID") - protected String clientId; - - @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "grantedConsent") - Collection grantedRoles = new ArrayList(); - - @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "grantedConsent") - Collection grantedProtocolMappers = new ArrayList(); - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public UserEntity getUser() { - return user; - } - - public void setUser(UserEntity user) { - this.user = user; - } - - public String getClientId() { - return clientId; - } - - public void setClientId(String clientId) { - this.clientId = clientId; - } - - public Collection getGrantedRoles() { - return grantedRoles; - } - - public void setGrantedRoles(Collection grantedRoles) { - this.grantedRoles = grantedRoles; - } - - public Collection getGrantedProtocolMappers() { - return grantedProtocolMappers; - } - - public void setGrantedProtocolMappers(Collection grantedProtocolMappers) { - this.grantedProtocolMappers = grantedProtocolMappers; - } -} diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/GrantedConsentProtocolMapperEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/GrantedConsentProtocolMapperEntity.java deleted file mode 100644 index 7683dec1fe..0000000000 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/GrantedConsentProtocolMapperEntity.java +++ /dev/null @@ -1,115 +0,0 @@ -package org.keycloak.models.jpa.entities; - -import java.io.Serializable; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.Id; -import javax.persistence.IdClass; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.Table; - -/** - * @author Marek Posolda - */ -@NamedQueries({ - @NamedQuery(name="deleteGrantedConsentProtMappersByRealm", query= - "delete from GrantedConsentProtocolMapperEntity csm where csm.grantedConsent IN (select consent from GrantedConsentEntity consent where consent.user IN (select user from UserEntity user where user.realmId = :realmId))"), - @NamedQuery(name="deleteGrantedConsentProtMappersByUser", query="delete from GrantedConsentProtocolMapperEntity csm where csm.grantedConsent IN (select consent from GrantedConsentEntity consent where consent.user = :user)"), - @NamedQuery(name="deleteGrantedConsentProtMappersByProtocolMapper", query="delete from GrantedConsentProtocolMapperEntity csm where csm.protocolMapperId = :protocolMapperId)"), - @NamedQuery(name="deleteGrantedConsentProtMappersByClient", query="delete from GrantedConsentProtocolMapperEntity csm where csm.grantedConsent IN (select consent from GrantedConsentEntity consent where consent.clientId = :clientId))"), -}) -@Entity -@Table(name="GRANTED_CONSENT_PROT_MAPPER") -@IdClass(GrantedConsentProtocolMapperEntity.Key.class) -public class GrantedConsentProtocolMapperEntity { - - @Id - @ManyToOne(fetch= FetchType.LAZY) - @JoinColumn(name = "GRANTED_CONSENT_ID") - protected GrantedConsentEntity grantedConsent; - - @Id - @Column(name="PROTOCOL_MAPPER_ID") - protected String protocolMapperId; - - public GrantedConsentEntity getGrantedConsent() { - return grantedConsent; - } - - public void setGrantedConsent(GrantedConsentEntity grantedConsent) { - this.grantedConsent = grantedConsent; - } - - public String getProtocolMapperId() { - return protocolMapperId; - } - - public void setProtocolMapperId(String protocolMapperId) { - this.protocolMapperId = protocolMapperId; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - GrantedConsentProtocolMapperEntity that = (GrantedConsentProtocolMapperEntity)o; - Key myKey = new Key(this.grantedConsent, this.protocolMapperId); - Key hisKey = new Key(that.grantedConsent, that.protocolMapperId); - return myKey.equals(hisKey); - } - - @Override - public int hashCode() { - Key myKey = new Key(this.grantedConsent, this.protocolMapperId); - return myKey.hashCode(); - } - - public static class Key implements Serializable { - - protected GrantedConsentEntity grantedConsent; - - protected String protocolMapperId; - - public Key() { - } - - public Key(GrantedConsentEntity grantedConsent, String protocolMapperId) { - this.grantedConsent = grantedConsent; - this.protocolMapperId = protocolMapperId; - } - - public GrantedConsentEntity getGrantedConsent() { - return grantedConsent; - } - - public String getProtocolMapperId() { - return protocolMapperId; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Key key = (Key) o; - - if (grantedConsent != null ? !grantedConsent.getId().equals(key.grantedConsent != null ? key.grantedConsent.getId() : null) : key.grantedConsent != null) return false; - if (protocolMapperId != null ? !protocolMapperId.equals(key.protocolMapperId) : key.protocolMapperId != null) return false; - - return true; - } - - @Override - public int hashCode() { - int result = grantedConsent != null ? grantedConsent.getId().hashCode() : 0; - result = 31 * result + (protocolMapperId != null ? protocolMapperId.hashCode() : 0); - return result; - } - } -} diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/GrantedConsentRoleEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/GrantedConsentRoleEntity.java deleted file mode 100644 index ee2b3bb98d..0000000000 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/GrantedConsentRoleEntity.java +++ /dev/null @@ -1,115 +0,0 @@ -package org.keycloak.models.jpa.entities; - -import java.io.Serializable; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.Id; -import javax.persistence.IdClass; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.Table; - -/** - * @author Marek Posolda - */ -@NamedQueries({ - @NamedQuery(name="deleteGrantedConsentRolesByRealm", query="delete from GrantedConsentRoleEntity grantedRole where grantedRole.grantedConsent IN (select consent from GrantedConsentEntity consent where consent.user IN (select user from UserEntity user where user.realmId = :realmId))"), - @NamedQuery(name="deleteGrantedConsentRolesByUser", query="delete from GrantedConsentRoleEntity grantedRole where grantedRole.grantedConsent IN (select consent from GrantedConsentEntity consent where consent.user = :user)"), - @NamedQuery(name="deleteGrantedConsentRolesByRole", query="delete from GrantedConsentRoleEntity grantedRole where grantedRole.roleId = :roleId)"), - @NamedQuery(name="deleteGrantedConsentRolesByClient", query="delete from GrantedConsentRoleEntity grantedRole where grantedRole.grantedConsent IN (select consent from GrantedConsentEntity consent where consent.clientId = :clientId)"), -}) -@Entity -@Table(name="GRANTED_CONSENT_ROLE") -@IdClass(GrantedConsentRoleEntity.Key.class) -public class GrantedConsentRoleEntity { - - @Id - @ManyToOne(fetch= FetchType.LAZY) - @JoinColumn(name = "GRANTED_CONSENT_ID") - protected GrantedConsentEntity grantedConsent; - - @Id - @Column(name="ROLE_ID") - protected String roleId; - - public GrantedConsentEntity getGrantedConsent() { - return grantedConsent; - } - - public void setGrantedConsent(GrantedConsentEntity grantedConsent) { - this.grantedConsent = grantedConsent; - } - - public String getRoleId() { - return roleId; - } - - public void setRoleId(String roleId) { - this.roleId = roleId; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - GrantedConsentRoleEntity that = (GrantedConsentRoleEntity)o; - Key myKey = new Key(this.grantedConsent, this.roleId); - Key hisKey = new Key(that.grantedConsent, that.roleId); - return myKey.equals(hisKey); - } - - @Override - public int hashCode() { - Key myKey = new Key(this.grantedConsent, this.roleId); - return myKey.hashCode(); - } - - public static class Key implements Serializable { - - protected GrantedConsentEntity grantedConsent; - - protected String roleId; - - public Key() { - } - - public Key(GrantedConsentEntity grantedConsent, String roleId) { - this.grantedConsent = grantedConsent; - this.roleId = roleId; - } - - public GrantedConsentEntity getGrantedConsent() { - return grantedConsent; - } - - public String getRoleId() { - return roleId; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Key key = (Key) o; - - if (grantedConsent != null ? !grantedConsent.getId().equals(key.grantedConsent != null ? key.grantedConsent.getId() : null) : key.grantedConsent != null) return false; - if (roleId != null ? !roleId.equals(key.roleId) : key.roleId != null) return false; - - return true; - } - - @Override - public int hashCode() { - int result = grantedConsent != null ? grantedConsent.getId().hashCode() : 0; - result = 31 * result + (roleId != null ? roleId.hashCode() : 0); - return result; - } - } - -} diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentEntity.java new file mode 100644 index 0000000000..9df40690b8 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentEntity.java @@ -0,0 +1,91 @@ +package org.keycloak.models.jpa.entities; + +import java.util.ArrayList; +import java.util.Collection; + +import javax.persistence.CascadeType; +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.OneToMany; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; + +/** + * @author Marek Posolda + */ +@Entity +@Table(name="USER_CONSENT", uniqueConstraints = { + @UniqueConstraint(columnNames = {"USER_ID", "CLIENT_ID"}) +}) +@NamedQueries({ + @NamedQuery(name="userConsentByUserAndClient", query="select consent from UserConsentEntity consent where consent.user.id = :userId and consent.clientId = :clientId"), + @NamedQuery(name="userConsentsByUser", query="select consent from UserConsentEntity consent where consent.user.id = :userId"), + @NamedQuery(name="deleteUserConsentsByRealm", query="delete from UserConsentEntity consent where consent.user IN (select user from UserEntity user where user.realmId = :realmId)"), + @NamedQuery(name="deleteUserConsentsByUser", query="delete from UserConsentEntity consent where consent.user = :user"), + @NamedQuery(name="deleteUserConsentsByClient", query="delete from UserConsentEntity consent where consent.clientId = :clientId"), +}) +public class UserConsentEntity { + + @Id + @Column(name="ID", length = 36) + protected String id; + + @ManyToOne(fetch= FetchType.LAZY) + @JoinColumn(name="USER_ID") + protected UserEntity user; + + @Column(name="CLIENT_ID") + protected String clientId; + + @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "userConsent") + Collection grantedRoles = new ArrayList(); + + @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "userConsent") + Collection grantedProtocolMappers = new ArrayList(); + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public UserEntity getUser() { + return user; + } + + public void setUser(UserEntity user) { + this.user = user; + } + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public Collection getGrantedRoles() { + return grantedRoles; + } + + public void setGrantedRoles(Collection grantedRoles) { + this.grantedRoles = grantedRoles; + } + + public Collection getGrantedProtocolMappers() { + return grantedProtocolMappers; + } + + public void setGrantedProtocolMappers(Collection grantedProtocolMappers) { + this.grantedProtocolMappers = grantedProtocolMappers; + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentProtocolMapperEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentProtocolMapperEntity.java new file mode 100644 index 0000000000..16814d2e89 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentProtocolMapperEntity.java @@ -0,0 +1,115 @@ +package org.keycloak.models.jpa.entities; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.IdClass; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; +import javax.persistence.Table; + +/** + * @author Marek Posolda + */ +@NamedQueries({ + @NamedQuery(name="deleteUserConsentProtMappersByRealm", query= + "delete from UserConsentProtocolMapperEntity csm where csm.userConsent IN (select consent from UserConsentEntity consent where consent.user IN (select user from UserEntity user where user.realmId = :realmId))"), + @NamedQuery(name="deleteUserConsentProtMappersByUser", query="delete from UserConsentProtocolMapperEntity csm where csm.userConsent IN (select consent from UserConsentEntity consent where consent.user = :user)"), + @NamedQuery(name="deleteUserConsentProtMappersByProtocolMapper", query="delete from UserConsentProtocolMapperEntity csm where csm.protocolMapperId = :protocolMapperId)"), + @NamedQuery(name="deleteUserConsentProtMappersByClient", query="delete from UserConsentProtocolMapperEntity csm where csm.userConsent IN (select consent from UserConsentEntity consent where consent.clientId = :clientId))"), +}) +@Entity +@Table(name="USER_CONSENT_PROT_MAPPER") +@IdClass(UserConsentProtocolMapperEntity.Key.class) +public class UserConsentProtocolMapperEntity { + + @Id + @ManyToOne(fetch= FetchType.LAZY) + @JoinColumn(name = "USER_CONSENT_ID") + protected UserConsentEntity userConsent; + + @Id + @Column(name="PROTOCOL_MAPPER_ID") + protected String protocolMapperId; + + public UserConsentEntity getUserConsent() { + return userConsent; + } + + public void setUserConsent(UserConsentEntity userConsent) { + this.userConsent = userConsent; + } + + public String getProtocolMapperId() { + return protocolMapperId; + } + + public void setProtocolMapperId(String protocolMapperId) { + this.protocolMapperId = protocolMapperId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + UserConsentProtocolMapperEntity that = (UserConsentProtocolMapperEntity)o; + Key myKey = new Key(this.userConsent, this.protocolMapperId); + Key hisKey = new Key(that.userConsent, that.protocolMapperId); + return myKey.equals(hisKey); + } + + @Override + public int hashCode() { + Key myKey = new Key(this.userConsent, this.protocolMapperId); + return myKey.hashCode(); + } + + public static class Key implements Serializable { + + protected UserConsentEntity userConsent; + + protected String protocolMapperId; + + public Key() { + } + + public Key(UserConsentEntity userConsent, String protocolMapperId) { + this.userConsent = userConsent; + this.protocolMapperId = protocolMapperId; + } + + public UserConsentEntity getUserConsent() { + return userConsent; + } + + public String getProtocolMapperId() { + return protocolMapperId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Key key = (Key) o; + + if (userConsent != null ? !userConsent.getId().equals(key.userConsent != null ? key.userConsent.getId() : null) : key.userConsent != null) return false; + if (protocolMapperId != null ? !protocolMapperId.equals(key.protocolMapperId) : key.protocolMapperId != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = userConsent != null ? userConsent.getId().hashCode() : 0; + result = 31 * result + (protocolMapperId != null ? protocolMapperId.hashCode() : 0); + return result; + } + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentRoleEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentRoleEntity.java new file mode 100644 index 0000000000..b8d1a8de51 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentRoleEntity.java @@ -0,0 +1,115 @@ +package org.keycloak.models.jpa.entities; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.IdClass; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; +import javax.persistence.Table; + +/** + * @author Marek Posolda + */ +@NamedQueries({ + @NamedQuery(name="deleteUserConsentRolesByRealm", query="delete from UserConsentRoleEntity grantedRole where grantedRole.userConsent IN (select consent from UserConsentEntity consent where consent.user IN (select user from UserEntity user where user.realmId = :realmId))"), + @NamedQuery(name="deleteUserConsentRolesByUser", query="delete from UserConsentRoleEntity grantedRole where grantedRole.userConsent IN (select consent from UserConsentEntity consent where consent.user = :user)"), + @NamedQuery(name="deleteUserConsentRolesByRole", query="delete from UserConsentRoleEntity grantedRole where grantedRole.roleId = :roleId)"), + @NamedQuery(name="deleteUserConsentRolesByClient", query="delete from UserConsentRoleEntity grantedRole where grantedRole.userConsent IN (select consent from UserConsentEntity consent where consent.clientId = :clientId)"), +}) +@Entity +@Table(name="USER_CONSENT_ROLE") +@IdClass(UserConsentRoleEntity.Key.class) +public class UserConsentRoleEntity { + + @Id + @ManyToOne(fetch= FetchType.LAZY) + @JoinColumn(name = "USER_CONSENT_ID") + protected UserConsentEntity userConsent; + + @Id + @Column(name="ROLE_ID") + protected String roleId; + + public UserConsentEntity getUserConsent() { + return userConsent; + } + + public void setUserConsent(UserConsentEntity userConsent) { + this.userConsent = userConsent; + } + + public String getRoleId() { + return roleId; + } + + public void setRoleId(String roleId) { + this.roleId = roleId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + UserConsentRoleEntity that = (UserConsentRoleEntity)o; + Key myKey = new Key(this.userConsent, this.roleId); + Key hisKey = new Key(that.userConsent, that.roleId); + return myKey.equals(hisKey); + } + + @Override + public int hashCode() { + Key myKey = new Key(this.userConsent, this.roleId); + return myKey.hashCode(); + } + + public static class Key implements Serializable { + + protected UserConsentEntity userConsent; + + protected String roleId; + + public Key() { + } + + public Key(UserConsentEntity userConsent, String roleId) { + this.userConsent = userConsent; + this.roleId = roleId; + } + + public UserConsentEntity getUserConsent() { + return userConsent; + } + + public String getRoleId() { + return roleId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Key key = (Key) o; + + if (userConsent != null ? !userConsent.getId().equals(key.userConsent != null ? key.userConsent.getId() : null) : key.userConsent != null) return false; + if (roleId != null ? !roleId.equals(key.roleId) : key.roleId != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = userConsent != null ? userConsent.getId().hashCode() : 0; + result = 31 * result + (roleId != null ? roleId.hashCode() : 0); + return result; + } + } + +} diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java index 8e07bc9af2..2cb2c79a3e 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java @@ -305,7 +305,8 @@ public class ClientAdapter extends AbstractMongoAdapter imple throw new RuntimeException("protocol mapper name must be unique per protocol"); } ProtocolMapperEntity entity = new ProtocolMapperEntity(); - entity.setId(KeycloakModelUtils.generateId()); + String id = model.getId() != null ? model.getId() : KeycloakModelUtils.generateId(); + entity.setId(id); entity.setProtocol(model.getProtocol()); entity.setName(model.getName()); entity.setProtocolMapper(model.getProtocolMapper()); diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java index f71c79779b..83bc85ccba 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java @@ -17,6 +17,7 @@ import org.keycloak.models.UserFederationProviderModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserProvider; import org.keycloak.models.entities.FederatedIdentityEntity; +import org.keycloak.models.mongo.keycloak.entities.MongoUserConsentEntity; import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity; import org.keycloak.models.utils.CredentialValidation; @@ -43,7 +44,6 @@ public class MongoUserProvider implements UserProvider { @Override public void close() { - // TODO } @Override @@ -274,11 +274,7 @@ public class MongoUserProvider implements UserProvider { @Override public boolean removeUser(RealmModel realm, UserModel user) { - DBObject query = new QueryBuilder() - .and("_id").is(user.getId()) - .and("realmId").is(realm.getId()) - .get(); - return getMongoStore().removeEntities(MongoUserEntity.class, query, invocationContext); + return getMongoStore().removeEntity(MongoUserEntity.class, user.getId(), invocationContext); } @@ -339,32 +335,60 @@ public class MongoUserProvider implements UserProvider { DBObject query = new QueryBuilder() .and("realmId").is(realm.getId()) .get(); - getMongoStore().removeEntities(MongoUserEntity.class, query, invocationContext); + getMongoStore().removeEntities(MongoUserEntity.class, query, true, invocationContext); } @Override public void preRemove(RealmModel realm, UserFederationProviderModel link) { + // Remove all users linked with federationProvider and their consents DBObject query = new QueryBuilder() .and("realmId").is(realm.getId()) .and("federationLink").is(link.getId()) .get(); - getMongoStore().removeEntities(MongoUserEntity.class, query, invocationContext); + getMongoStore().removeEntities(MongoUserEntity.class, query, true, invocationContext); } @Override public void preRemove(RealmModel realm, ClientModel client) { - // TODO + // Remove all role mappings and consents mapped to all roles of this client + for (RoleModel role : client.getRoles()) { + preRemove(realm, role); + } + + // Finally remove all consents of this client + DBObject query = new QueryBuilder() + .and("clientId").is(client.getId()) + .get(); + getMongoStore().removeEntities(MongoUserConsentEntity.class, query, false, invocationContext); } @Override public void preRemove(ClientModel client, ProtocolMapperModel protocolMapper) { - // TODO + // Remove this protocol mapper from all consents, which has it + DBObject query = new QueryBuilder() + .and("grantedProtocolMappers").is(protocolMapper.getId()) + .get(); + DBObject pull = new BasicDBObject("$pull", query); + getMongoStore().updateEntities(MongoUserEntity.class, query, pull, invocationContext); } @Override public void preRemove(RealmModel realm, RoleModel role) { - // todo not sure what to do for this + // Remove this role from all users, which has it + DBObject query = new QueryBuilder() + .and("roleIds").is(role.getId()) + .get(); + + DBObject pull = new BasicDBObject("$pull", query); + getMongoStore().updateEntities(MongoUserEntity.class, query, pull, invocationContext); + + // Remove this role from all consents, which has it + query = new QueryBuilder() + .and("grantedRoles").is(role.getId()) + .get(); + pull = new BasicDBObject("$pull", query); + getMongoStore().updateEntities(MongoUserConsentEntity.class, query, pull, invocationContext); } @Override diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java index 5fa3e52fa5..b0547930c3 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java @@ -2,10 +2,15 @@ package org.keycloak.models.mongo.keycloak.adapters; import static org.keycloak.models.utils.Pbkdf2PasswordEncoder.getSalt; +import com.mongodb.DBObject; +import com.mongodb.QueryBuilder; import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext; import org.keycloak.models.ClientModel; -import org.keycloak.models.GrantedConsentModel; +import org.keycloak.models.ProtocolMapperModel; +import org.keycloak.models.UserConsentModel; import org.keycloak.models.KeycloakSession; +import org.keycloak.models.ModelDuplicateException; +import org.keycloak.models.ModelException; import org.keycloak.models.PasswordPolicy; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; @@ -13,7 +18,9 @@ import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserCredentialValueModel; import org.keycloak.models.UserModel; import org.keycloak.models.entities.CredentialEntity; +import org.keycloak.models.entities.UserConsentEntity; import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity; +import org.keycloak.models.mongo.keycloak.entities.MongoUserConsentEntity; import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity; import org.keycloak.models.mongo.utils.MongoModelUtils; import org.keycloak.models.utils.Pbkdf2PasswordEncoder; @@ -22,9 +29,9 @@ import org.keycloak.util.Time; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; -import java.util.Date; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -360,30 +367,18 @@ public class UserAdapter extends AbstractMongoAdapter implement @Override public Set getRoleMappings() { - Set result = new HashSet(); - List roles = MongoModelUtils.getAllRolesOfUser(this, invocationContext); - - for (MongoRoleEntity role : roles) { - if (realm.getId().equals(role.getRealmId())) { - result.add(new RoleAdapter(session, realm, role, realm, invocationContext)); - } else { - // Likely applicationRole, but we don't have this application yet - result.add(new RoleAdapter(session, realm, role, invocationContext)); - } - } - return result; + List roles = MongoModelUtils.getAllRolesOfUser(realm, this); + return new HashSet(roles); } @Override public Set getRealmRoleMappings() { Set allRoles = getRoleMappings(); - // Filter to retrieve just realm roles TODO: Maybe improve to avoid filter programmatically... Maybe have separate fields for realmRoles and appRoles on user? + // Filter to retrieve just realm roles Set realmRoles = new HashSet(); for (RoleModel role : allRoles) { - MongoRoleEntity roleEntity = ((RoleAdapter) role).getRole(); - - if (realm.getId().equals(roleEntity.getRealmId())) { + if (role.getContainer() instanceof RealmModel) { realmRoles.add(role); } } @@ -400,11 +395,11 @@ public class UserAdapter extends AbstractMongoAdapter implement @Override public Set getClientRoleMappings(ClientModel app) { Set result = new HashSet(); - List roles = MongoModelUtils.getAllRolesOfUser(this, invocationContext); + List roles = MongoModelUtils.getAllRolesOfUser(realm, this); - for (MongoRoleEntity role : roles) { - if (app.getId().equals(role.getClientId())) { - result.add(new RoleAdapter(session, realm, role, app, invocationContext)); + for (RoleModel role : roles) { + if (app.equals(role.getContainer())) { + result.add(role); } } return result; @@ -422,32 +417,96 @@ public class UserAdapter extends AbstractMongoAdapter implement } @Override - public GrantedConsentModel addGrantedConsent(GrantedConsentModel consent) { - // TODO - return null; + public void addConsent(UserConsentModel consent) { + String clientId = consent.getClient().getId(); + if (getConsentEntityByClientId(clientId) != null) { + throw new ModelDuplicateException("Consent already exists for client [" + clientId + "] and user [" + user.getId() + "]"); + } + + MongoUserConsentEntity consentEntity = new MongoUserConsentEntity(); + consentEntity.setUserId(getId()); + consentEntity.setClientId(clientId); + fillEntityFromModel(consent, consentEntity); + getMongoStore().insertEntity(consentEntity, invocationContext); } @Override - public GrantedConsentModel getGrantedConsentByClient(String clientId) { - // TODO - return null; + public UserConsentModel getConsentByClient(String clientId) { + UserConsentEntity consentEntity = getConsentEntityByClientId(clientId); + return consentEntity!=null ? toConsentModel(consentEntity) : null; } @Override - public List getGrantedConsents() { - // TODO - return null; + public List getConsents() { + List result = new ArrayList(); + + DBObject query = new QueryBuilder() + .and("userId").is(getId()) + .get(); + List grantedConsents = getMongoStore().loadEntities(MongoUserConsentEntity.class, query, invocationContext); + + for (UserConsentEntity consentEntity : grantedConsents) { + UserConsentModel model = toConsentModel(consentEntity); + result.add(model); + } + + return result; + } + + private MongoUserConsentEntity getConsentEntityByClientId(String clientId) { + DBObject query = new QueryBuilder() + .and("userId").is(getId()) + .and("clientId").is(clientId) + .get(); + return getMongoStore().loadSingleEntity(MongoUserConsentEntity.class, query, invocationContext); + } + + private UserConsentModel toConsentModel(UserConsentEntity entity) { + UserConsentModel model = new UserConsentModel(realm, entity.getClientId()); + for (String roleId : entity.getGrantedRoles()) { + model.addGrantedRole(roleId); + } + for (String protMapperId : entity.getGrantedProtocolMappers()) { + model.addGrantedProtocolMapper(protMapperId); + } + return model; + } + + // Fill roles and protocolMappers to entity + private void fillEntityFromModel(UserConsentModel consent, MongoUserConsentEntity consentEntity) { + List roleIds = new LinkedList(); + for (RoleModel role : consent.getGrantedRoles()) { + roleIds.add(role.getId()); + } + consentEntity.setGrantedRoles(roleIds); + + List protMapperIds = new LinkedList(); + for (ProtocolMapperModel protMapperModel : consent.getGrantedProtocolMappers()) { + protMapperIds.add(protMapperModel.getId()); + } + consentEntity.setGrantedProtocolMappers(protMapperIds); } @Override - public void updateGrantedConsent(GrantedConsentModel consent) { - // TODO + public void updateConsent(UserConsentModel consent) { + String clientId = consent.getClient().getId(); + MongoUserConsentEntity consentEntity = getConsentEntityByClientId(clientId); + if (consentEntity == null) { + throw new ModelException("Consent not found for client [" + clientId + "] and user [" + user.getId() + "]"); + } else { + fillEntityFromModel(consent, consentEntity); + getMongoStore().updateEntity(consentEntity, invocationContext); + } } @Override - public boolean revokeGrantedConsentForClient(String clientId) { - // TODO - return false; + public boolean revokeConsentForClient(String clientId) { + MongoUserConsentEntity entity = getConsentEntityByClientId(clientId); + if (entity == null) { + return false; + } + + return getMongoStore().removeEntity(entity, invocationContext); } @Override diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoClientEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoClientEntity.java index cc97d5d992..8c1271563c 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoClientEntity.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoClientEntity.java @@ -19,6 +19,6 @@ public class MongoClientEntity extends ClientEntity implements MongoIdentifiable DBObject query = new QueryBuilder() .and("clientId").is(getId()) .get(); - context.getMongoStore().removeEntities(MongoRoleEntity.class, query, context); + context.getMongoStore().removeEntities(MongoRoleEntity.class, query, true, context); } } diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRealmEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRealmEntity.java index 22097514fb..a64c2b5e67 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRealmEntity.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRealmEntity.java @@ -19,13 +19,10 @@ public class MongoRealmEntity extends RealmEntity implements MongoIdentifiableEn .and("realmId").is(getId()) .get(); - // Remove all users of this realm - context.getMongoStore().removeEntities(MongoUserEntity.class, query, context); - // Remove all roles of this realm - context.getMongoStore().removeEntities(MongoRoleEntity.class, query, context); + context.getMongoStore().removeEntities(MongoRoleEntity.class, query, true, context); - // Remove all applications of this realm - context.getMongoStore().removeEntities(MongoClientEntity.class, query, context); + // Remove all clients of this realm + context.getMongoStore().removeEntities(MongoClientEntity.class, query, true, context); } } diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRoleEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRoleEntity.java index dc820c25f8..20210691d8 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRoleEntity.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRoleEntity.java @@ -41,26 +41,15 @@ public class MongoRoleEntity extends RoleEntity implements MongoIdentifiableEnti public void afterRemove(MongoStoreInvocationContext invContext) { MongoStore mongoStore = invContext.getMongoStore(); - // Remove this role from all users, which has it + // Remove this scope from all clients, which has it DBObject query = new QueryBuilder() - .and("roleIds").is(getId()) - .get(); - - List users = mongoStore.loadEntities(MongoUserEntity.class, query, invContext); - for (MongoUserEntity user : users) { - //logger.info("Removing role " + getName() + " from user " + user.getUsername()); - mongoStore.pullItemFromList(user, "roleIds", getId(), invContext); - } - - // Remove this scope from all users, which has it - query = new QueryBuilder() .and("scopeIds").is(getId()) .get(); - users = mongoStore.loadEntities(MongoUserEntity.class, query, invContext); - for (MongoUserEntity user : users) { + List clients = mongoStore.loadEntities(MongoClientEntity.class, query, invContext); + for (MongoClientEntity client : clients) { //logger.info("Removing scope " + getName() + " from user " + user.getUsername()); - mongoStore.pullItemFromList(user, "scopeIds", getId(), invContext); + mongoStore.pullItemFromList(client, "scopeIds", getId(), invContext); } // Remove defaultRoles from realm diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoUserConsentEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoUserConsentEntity.java new file mode 100644 index 0000000000..16e0026831 --- /dev/null +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoUserConsentEntity.java @@ -0,0 +1,17 @@ +package org.keycloak.models.mongo.keycloak.entities; + +import org.keycloak.connections.mongo.api.MongoCollection; +import org.keycloak.connections.mongo.api.MongoIdentifiableEntity; +import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext; +import org.keycloak.models.entities.UserConsentEntity; + +/** + * @author Marek Posolda + */ +@MongoCollection(collectionName = "userConsents") +public class MongoUserConsentEntity extends UserConsentEntity implements MongoIdentifiableEntity { + + @Override + public void afterRemove(MongoStoreInvocationContext invocationContext) { + } +} diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoUserEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoUserEntity.java index c9f317e50d..d1700d88ad 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoUserEntity.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoUserEntity.java @@ -1,5 +1,7 @@ package org.keycloak.models.mongo.keycloak.entities; +import com.mongodb.DBObject; +import com.mongodb.QueryBuilder; import org.keycloak.connections.mongo.api.MongoCollection; import org.keycloak.connections.mongo.api.MongoIdentifiableEntity; import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext; @@ -19,6 +21,12 @@ public class MongoUserEntity extends UserEntity implements MongoIdentifiableEnti } @Override - public void afterRemove(MongoStoreInvocationContext invocationContext) { + public void afterRemove(MongoStoreInvocationContext context) { + // Remove all consents of this user + DBObject query = new QueryBuilder() + .and("userId").is(getId()) + .get(); + + context.getMongoStore().removeEntities(MongoUserConsentEntity.class, query, true, context); } } diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/utils/MongoModelUtils.java b/model/mongo/src/main/java/org/keycloak/models/mongo/utils/MongoModelUtils.java index 48828deecb..02923cfc4f 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/utils/MongoModelUtils.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/utils/MongoModelUtils.java @@ -4,6 +4,8 @@ import com.mongodb.DBObject; import com.mongodb.QueryBuilder; import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext; import org.keycloak.models.ClientModel; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleModel; import org.keycloak.models.UserModel; import org.keycloak.models.entities.ClientEntity; import org.keycloak.models.mongo.keycloak.adapters.ClientAdapter; @@ -12,6 +14,7 @@ import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity; import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity; import java.util.Collections; +import java.util.LinkedList; import java.util.List; /** @@ -20,18 +23,22 @@ import java.util.List; public class MongoModelUtils { // Get everything including both application and realm roles - public static List getAllRolesOfUser(UserModel user, MongoStoreInvocationContext invContext) { + public static List getAllRolesOfUser(RealmModel realm, UserModel user) { MongoUserEntity userEntity = ((UserAdapter)user).getUser(); List roleIds = userEntity.getRoleIds(); if (roleIds == null || roleIds.isEmpty()) { - return Collections.EMPTY_LIST; + return Collections.emptyList(); } - DBObject query = new QueryBuilder() - .and("_id").in(roleIds) - .get(); - return invContext.getMongoStore().loadEntities(MongoRoleEntity.class, query, invContext); + List roles = new LinkedList(); + for (String roleId : roleIds) { + RoleModel role = realm.getRoleById(roleId); + if (role != null) { + roles.add(role); + } + } + return roles; } // Get everything including both application and realm scopes @@ -40,7 +47,7 @@ public class MongoModelUtils { List scopeIds = scopedEntity.getScopeIds(); if (scopeIds == null || scopeIds.isEmpty()) { - return Collections.EMPTY_LIST; + return Collections.emptyList(); } DBObject query = new QueryBuilder() diff --git a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/MongoUserSessionProvider.java b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/MongoUserSessionProvider.java index 0bc584940f..848fdeb267 100755 --- a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/MongoUserSessionProvider.java +++ b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/MongoUserSessionProvider.java @@ -194,18 +194,14 @@ public class MongoUserSessionProvider implements UserSessionProvider { @Override public void removeUserSessions(RealmModel realm, UserModel user) { DBObject query = new BasicDBObject("user", user.getId()); - mongoStore.removeEntities(MongoUserSessionEntity.class, query, invocationContext); + mongoStore.removeEntities(MongoUserSessionEntity.class, query, true, invocationContext); } @Override public void removeUserSessions(RealmModel realm) { DBObject query = new BasicDBObject("realmId", realm.getId()); - mongoStore.removeEntities(MongoUserSessionEntity.class, query, invocationContext); - query = new QueryBuilder() - .and("realmId").is(realm.getId()) - .get(); - - mongoStore.removeEntities(MongoClientSessionEntity.class, query, invocationContext); + mongoStore.removeEntities(MongoUserSessionEntity.class, query, false, invocationContext); + mongoStore.removeEntities(MongoClientSessionEntity.class, query, false, invocationContext); } @Override @@ -216,20 +212,20 @@ public class MongoUserSessionProvider implements UserSessionProvider { .and("started").lessThan(currentTime - realm.getSsoSessionMaxLifespan()) .get(); - mongoStore.removeEntities(MongoUserSessionEntity.class, query, invocationContext); + mongoStore.removeEntities(MongoUserSessionEntity.class, query, true, invocationContext); query = new QueryBuilder() .and("realmId").is(realm.getId()) .and("lastSessionRefresh").lessThan(currentTime - realm.getSsoSessionIdleTimeout()) .get(); - mongoStore.removeEntities(MongoUserSessionEntity.class, query, invocationContext); + mongoStore.removeEntities(MongoUserSessionEntity.class, query, true, invocationContext); query = new QueryBuilder() .and("sessionId").is(null) .and("realmId").is(realm.getId()) .and("timestamp").lessThan(currentTime - RealmInfoUtil.getDettachedClientSessionLifespan(realm)) .get(); - mongoStore.removeEntities(MongoClientSessionEntity.class, query, invocationContext); + mongoStore.removeEntities(MongoClientSessionEntity.class, query, false, invocationContext); } @Override @@ -291,7 +287,7 @@ public class MongoUserSessionProvider implements UserSessionProvider { .or(new BasicDBObject("username", user.getUsername()), new BasicDBObject("username", user.getEmail())) .and("realmId").is(realm.getId()) .get(); - mongoStore.removeEntities(MongoUsernameLoginFailureEntity.class, query, invocationContext); + mongoStore.removeEntities(MongoUsernameLoginFailureEntity.class, query, true, invocationContext); } @Override diff --git a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/entities/MongoUserSessionEntity.java b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/entities/MongoUserSessionEntity.java index 5b7e8d3bea..0c637db12f 100755 --- a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/entities/MongoUserSessionEntity.java +++ b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/entities/MongoUserSessionEntity.java @@ -121,7 +121,7 @@ public class MongoUserSessionEntity extends AbstractIdentifiableEntity implement DBObject query = new QueryBuilder() .and("sessionId").is(getId()) .get(); - context.getMongoStore().removeEntities(MongoClientSessionEntity.class, query, context); + context.getMongoStore().removeEntities(MongoClientSessionEntity.class, query, true, context); } public Map getNotes() { diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java index 4814f306fd..7647b36345 100755 --- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java +++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java @@ -14,7 +14,7 @@ import org.keycloak.jose.jws.JWSBuilder; import org.keycloak.login.LoginFormsProvider; import org.keycloak.models.ClientModel; import org.keycloak.models.ClientSessionModel; -import org.keycloak.models.GrantedConsentModel; +import org.keycloak.models.UserConsentModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.RealmModel; @@ -420,14 +420,14 @@ public class AuthenticationManager { if (client.isConsentRequired()) { accessCode.setAction(ClientSessionModel.Action.OAUTH_GRANT); - GrantedConsentModel grantedConsent = user.getGrantedConsentByClient(client.getId()); + UserConsentModel grantedConsent = user.getConsentByClient(client.getId()); List realmRoles = new LinkedList(); MultivaluedMap resourceRoles = new MultivaluedMapImpl(); for (RoleModel r : accessCode.getRequestedRoles()) { // Consent already granted by user - if (grantedConsent != null && grantedConsent.getGrantedRoles().contains(r.getId())) { + if (grantedConsent != null && grantedConsent.isRoleGranted(r)) { continue; } @@ -439,10 +439,10 @@ public class AuthenticationManager { } List protocolMappers = new LinkedList(); - for (ProtocolMapperModel model : client.getProtocolMappers()) { - if (model.isConsentRequired() && model.getProtocol().equals(clientSession.getAuthMethod()) && model.getConsentText() != null) { - if (grantedConsent == null || !grantedConsent.getGrantedProtocolMappers().contains(model.getId())) { - protocolMappers.add(model); + for (ProtocolMapperModel protocolMapper : client.getProtocolMappers()) { + if (protocolMapper.isConsentRequired() && protocolMapper.getProtocol().equals(clientSession.getAuthMethod()) && protocolMapper.getConsentText() != null) { + if (grantedConsent == null || !grantedConsent.isProtocolMapperGranted(protocolMapper)) { + protocolMappers.add(protocolMapper); } } } diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java index af1c872531..3a775e1361 100755 --- a/services/src/main/java/org/keycloak/services/resources/AccountService.java +++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java @@ -76,7 +76,6 @@ import javax.ws.rs.core.Variant; import java.lang.reflect.Method; import java.net.URI; -import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -512,7 +511,7 @@ public class AccountService { // Revoke grant in UserModel UserModel user = auth.getUser(); - user.revokeGrantedConsentForClient(client.getId()); + user.revokeConsentForClient(client.getId()); // Logout clientSessions for this user and client List userSessions = session.sessions().getUserSessions(realm, user); diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java index a0eb4dce5c..d4aa8d77d3 100755 --- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java +++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java @@ -34,7 +34,7 @@ import org.keycloak.jose.jws.JWSBuilder; import org.keycloak.login.LoginFormsProvider; import org.keycloak.models.ClientModel; import org.keycloak.models.ClientSessionModel; -import org.keycloak.models.GrantedConsentModel; +import org.keycloak.models.UserConsentModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ModelException; import org.keycloak.models.ProtocolMapperModel; @@ -609,9 +609,10 @@ public class LoginActionsService { return protocol.consentDenied(clientSession); } - GrantedConsentModel grantedConsent = user.getGrantedConsentByClient(client.getId()); + UserConsentModel grantedConsent = user.getConsentByClient(client.getId()); if (grantedConsent == null) { - grantedConsent = user.addGrantedConsent(new GrantedConsentModel(client.getId())); + grantedConsent = new UserConsentModel(realm, client.getId()); + user.addConsent(grantedConsent); } for (String roleId : clientSession.getRoles()) { grantedConsent.addGrantedRole(roleId); @@ -622,7 +623,7 @@ public class LoginActionsService { grantedConsent.addGrantedProtocolMapper(protocolMapper.getId()); } } - user.updateGrantedConsent(grantedConsent); + user.updateConsent(grantedConsent); event.success(); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java index 0e48914430..f7f48b3a74 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java @@ -258,6 +258,7 @@ public class AdapterTest extends AbstractModelTest { commit(); realmModel = model.getRealm("JUGGLER"); app = realmModel.getClientByClientId("test-app"); + user = realmManager.getSession().users().getUserByUsername("bburke", realmModel); Assert.assertTrue(realmModel.removeRoleById(realmRole.getId())); Assert.assertFalse(realmModel.removeRoleById(realmRole.getId())); @@ -266,6 +267,9 @@ public class AdapterTest extends AbstractModelTest { Assert.assertTrue(realmModel.removeRoleById(appRole.getId())); Assert.assertFalse(realmModel.removeRoleById(appRole.getId())); assertNull(app.getRole(appRole.getName())); + + user = realmManager.getSession().users().getUserByUsername("bburke", realmModel); + } @Test diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ClientModelTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ClientModelTest.java index f0fc63b14c..e56d462506 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ClientModelTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ClientModelTest.java @@ -9,6 +9,7 @@ import org.keycloak.models.RoleModel; import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.ProtocolMapperRepresentation; import org.keycloak.services.managers.ClientManager; import java.util.Iterator; @@ -63,6 +64,9 @@ public class ClientModelTest extends AbstractModelTest { public void json() { ClientRepresentation representation = ModelToRepresentation.toRepresentation(client); representation.setId(null); + for (ProtocolMapperRepresentation protocolMapper : representation.getProtocolMappers()) { + protocolMapper.setId(null); + } RealmModel realm = realmManager.createRealm("copy"); ClientModel copy = RepresentationToModel.createClient(session, realm, representation, true); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java index 54f4cbeedd..5bdc191813 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java @@ -14,6 +14,7 @@ import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.RealmModel; import org.keycloak.models.RequiredCredentialModel; import org.keycloak.models.RoleModel; +import org.keycloak.models.UserConsentModel; import org.keycloak.models.UserFederationProvider; import org.keycloak.models.UserFederationProviderFactory; import org.keycloak.models.UserFederationProviderModel; @@ -238,6 +239,23 @@ public class ImportTest extends AbstractModelTest { String includeInIdToken = gssCredentialMapper.getConfig().get(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN); Assert.assertTrue(includeInAccessToken.equalsIgnoreCase("true")); Assert.assertTrue(includeInIdToken == null || Boolean.parseBoolean(includeInIdToken) == false); + + // Test user consents + admin = session.users().getUserByUsername("admin", realm); + Assert.assertEquals(2, admin.getConsents().size()); + + UserConsentModel appAdminConsent = admin.getConsentByClient(application.getId()); + Assert.assertEquals(2, appAdminConsent.getGrantedRoles().size()); + Assert.assertTrue(appAdminConsent.getGrantedProtocolMappers() == null || appAdminConsent.getGrantedProtocolMappers().isEmpty()); + Assert.assertTrue(appAdminConsent.isRoleGranted(realm.getRole("admin"))); + Assert.assertTrue(appAdminConsent.isRoleGranted(application.getRole("app-admin"))); + + UserConsentModel otherAppAdminConsent = admin.getConsentByClient(otherApp.getId()); + Assert.assertEquals(1, otherAppAdminConsent.getGrantedRoles().size()); + Assert.assertEquals(1, otherAppAdminConsent.getGrantedProtocolMappers().size()); + Assert.assertTrue(otherAppAdminConsent.isRoleGranted(realm.getRole("admin"))); + Assert.assertFalse(otherAppAdminConsent.isRoleGranted(application.getRole("app-admin"))); + Assert.assertTrue(otherAppAdminConsent.isProtocolMapperGranted(gssCredentialMapper)); } @Test diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/GrantedConsentModelTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserConsentModelTest.java similarity index 70% rename from testsuite/integration/src/test/java/org/keycloak/testsuite/model/GrantedConsentModelTest.java rename to testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserConsentModelTest.java index 5c555b7690..97cb24b408 100644 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/GrantedConsentModelTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserConsentModelTest.java @@ -1,13 +1,12 @@ package org.keycloak.testsuite.model; import java.util.List; -import java.util.Map; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.keycloak.models.ClientModel; -import org.keycloak.models.GrantedConsentModel; +import org.keycloak.models.UserConsentModel; import org.keycloak.models.ModelException; import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.RealmModel; @@ -20,7 +19,7 @@ import org.keycloak.protocol.oidc.mappers.UserPropertyMapper; /** * @author Marek Posolda */ -public class GrantedConsentModelTest extends AbstractModelTest { +public class UserConsentModelTest extends AbstractModelTest { @Before public void setupEnv() { @@ -47,29 +46,29 @@ public class GrantedConsentModelTest extends AbstractModelTest { UserModel john = session.users().addUser(realm, "john"); UserModel mary = session.users().addUser(realm, "mary"); - GrantedConsentModel johnFooGrant = new GrantedConsentModel(fooClient.getId()); + UserConsentModel johnFooGrant = new UserConsentModel(realm, fooClient.getId()); johnFooGrant.addGrantedRole(realmRole.getId()); johnFooGrant.addGrantedRole(barClientRole.getId()); johnFooGrant.addGrantedProtocolMapper(fooMapper.getId()); - john.addGrantedConsent(johnFooGrant); + john.addConsent(johnFooGrant); - GrantedConsentModel johnBarGrant = new GrantedConsentModel(barClient.getId()); + UserConsentModel johnBarGrant = new UserConsentModel(realm, barClient.getId()); johnBarGrant.addGrantedProtocolMapper(barMapper.getId()); johnBarGrant.addGrantedRole(realmRole.getId()); // Update should fail as grant doesn't yet exists try { - john.updateGrantedConsent(johnBarGrant); + john.updateConsent(johnBarGrant); Assert.fail("Not expected to end here"); } catch (ModelException expected) { } - john.addGrantedConsent(johnBarGrant); + john.addConsent(johnBarGrant); - GrantedConsentModel maryFooGrant = new GrantedConsentModel(fooClient.getId()); + UserConsentModel maryFooGrant = new UserConsentModel(realm, fooClient.getId()); maryFooGrant.addGrantedRole(realmRole.getId()); maryFooGrant.addGrantedProtocolMapper(fooMapper.getId()); - mary.addGrantedConsent(maryFooGrant); + mary.addConsent(maryFooGrant); commit(); } @@ -77,52 +76,50 @@ public class GrantedConsentModelTest extends AbstractModelTest { @Test public void basicConsentTest() { RealmModel realm = realmManager.getRealm("original"); - Map clients = realm.getClientNameMap(); - ClientModel fooClient = clients.get("foo-client"); - ClientModel barClient = clients.get("bar-client"); + ClientModel fooClient = realm.getClientByClientId("foo-client"); + ClientModel barClient = realm.getClientByClientId("bar-client"); UserModel john = session.users().getUserByUsername("john", realm); UserModel mary = session.users().getUserByUsername("mary", realm); - GrantedConsentModel johnFooConsent = john.getGrantedConsentByClient(fooClient.getId()); + UserConsentModel johnFooConsent = john.getConsentByClient(fooClient.getId()); Assert.assertEquals(johnFooConsent.getGrantedRoles().size(), 2); Assert.assertEquals(johnFooConsent.getGrantedProtocolMappers().size(), 1); Assert.assertTrue(isRoleGranted(realm, "realm-role", johnFooConsent)); Assert.assertTrue(isRoleGranted(barClient, "bar-client-role", johnFooConsent)); Assert.assertTrue(isMapperGranted(fooClient, "foo", johnFooConsent)); - GrantedConsentModel johnBarConsent = john.getGrantedConsentByClient(barClient.getId()); + UserConsentModel johnBarConsent = john.getConsentByClient(barClient.getId()); Assert.assertEquals(johnBarConsent.getGrantedRoles().size(), 1); Assert.assertEquals(johnBarConsent.getGrantedProtocolMappers().size(), 1); Assert.assertTrue(isRoleGranted(realm, "realm-role", johnBarConsent)); Assert.assertTrue(isMapperGranted(barClient, "bar", johnBarConsent)); - GrantedConsentModel maryConsent = mary.getGrantedConsentByClient(fooClient.getId()); + UserConsentModel maryConsent = mary.getConsentByClient(fooClient.getId()); Assert.assertEquals(maryConsent.getGrantedRoles().size(), 1); Assert.assertEquals(maryConsent.getGrantedProtocolMappers().size(), 1); Assert.assertTrue(isRoleGranted(realm, "realm-role", maryConsent)); Assert.assertFalse(isRoleGranted(barClient, "bar-client-role", maryConsent)); Assert.assertTrue(isMapperGranted(fooClient, "foo", maryConsent)); - Assert.assertNull(mary.getGrantedConsentByClient(barClient.getId())); + Assert.assertNull(mary.getConsentByClient(barClient.getId())); } @Test public void getAllConsentTest() { RealmModel realm = realmManager.getRealm("original"); - Map clients = realm.getClientNameMap(); - ClientModel fooClient = clients.get("foo-client"); + ClientModel fooClient = realm.getClientByClientId("foo-client"); UserModel john = session.users().getUserByUsername("john", realm); UserModel mary = session.users().getUserByUsername("mary", realm); - List johnConsents = john.getGrantedConsents(); + List johnConsents = john.getConsents(); Assert.assertEquals(2, johnConsents.size()); - List maryConsents = mary.getGrantedConsents(); + List maryConsents = mary.getConsents(); Assert.assertEquals(1, maryConsents.size()); - GrantedConsentModel maryConsent = maryConsents.get(0); - Assert.assertEquals(maryConsent.getClientId(), fooClient.getId()); + UserConsentModel maryConsent = maryConsents.get(0); + Assert.assertEquals(maryConsent.getClient().getId(), fooClient.getId()); Assert.assertEquals(maryConsent.getGrantedRoles().size(), 1); Assert.assertEquals(maryConsent.getGrantedProtocolMappers().size(), 1); Assert.assertTrue(isRoleGranted(realm, "realm-role", maryConsent)); @@ -132,30 +129,30 @@ public class GrantedConsentModelTest extends AbstractModelTest { @Test public void updateWithRoleRemovalTest() { RealmModel realm = realmManager.getRealm("original"); - ClientModel fooClient = realm.getClientNameMap().get("foo-client"); + ClientModel fooClient = realm.getClientByClientId("foo-client"); UserModel john = session.users().getUserByUsername("john", realm); - GrantedConsentModel johnConsent = john.getGrantedConsentByClient(fooClient.getId()); + UserConsentModel johnConsent = john.getConsentByClient(fooClient.getId()); // Remove foo protocol mapper from johnConsent ProtocolMapperModel protMapperModel = fooClient.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, "foo"); - johnConsent.getGrantedProtocolMappers().remove(protMapperModel.getId()); + johnConsent.getGrantedProtocolMappers().remove(protMapperModel); // Remove realm-role and add new-realm-role to johnConsent RoleModel realmRole = realm.getRole("realm-role"); - johnConsent.getGrantedRoles().remove(realmRole.getId()); + johnConsent.getGrantedRoles().remove(realmRole); RoleModel newRealmRole = realm.addRole("new-realm-role"); johnConsent.addGrantedRole(newRealmRole.getId()); - john.updateGrantedConsent(johnConsent); + john.updateConsent(johnConsent); commit(); realm = realmManager.getRealm("original"); - fooClient = realm.getClientNameMap().get("foo-client"); + fooClient = realm.getClientByClientId("foo-client"); john = session.users().getUserByUsername("john", realm); - johnConsent = john.getGrantedConsentByClient(fooClient.getId()); + johnConsent = john.getConsentByClient(fooClient.getId()); Assert.assertEquals(johnConsent.getGrantedRoles().size(), 2); Assert.assertEquals(johnConsent.getGrantedProtocolMappers().size(), 0); @@ -167,16 +164,16 @@ public class GrantedConsentModelTest extends AbstractModelTest { @Test public void revokeTest() { RealmModel realm = realmManager.getRealm("original"); - ClientModel fooClient = realm.getClientNameMap().get("foo-client"); + ClientModel fooClient = realm.getClientByClientId("foo-client"); UserModel john = session.users().getUserByUsername("john", realm); - john.revokeGrantedConsentForClient(fooClient.getId()); + john.revokeConsentForClient(fooClient.getId()); commit(); realm = realmManager.getRealm("original"); john = session.users().getUserByUsername("john", realm); - Assert.assertNull(john.getGrantedConsentByClient(fooClient.getId())); + Assert.assertNull(john.getConsentByClient(fooClient.getId())); } @Test @@ -190,77 +187,72 @@ public class GrantedConsentModelTest extends AbstractModelTest { @Test public void deleteProtocolMapperTest() { RealmModel realm = realmManager.getRealm("original"); - ClientModel fooClient = realm.getClientNameMap().get("foo-client"); + ClientModel fooClient = realm.getClientByClientId("foo-client"); ProtocolMapperModel fooMapper = fooClient.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, "foo"); - String fooMapperId = fooMapper.getId(); fooClient.removeProtocolMapper(fooMapper); commit(); realm = realmManager.getRealm("original"); - fooClient = realm.getClientNameMap().get("foo-client"); + fooClient = realm.getClientByClientId("foo-client"); UserModel john = session.users().getUserByUsername("john", realm); - GrantedConsentModel johnConsent = john.getGrantedConsentByClient(fooClient.getId()); + UserConsentModel johnConsent = john.getConsentByClient(fooClient.getId()); Assert.assertEquals(johnConsent.getGrantedRoles().size(), 2); Assert.assertEquals(johnConsent.getGrantedProtocolMappers().size(), 0); - Assert.assertFalse(johnConsent.isProtocolMapperGranted(fooMapperId)); + Assert.assertFalse(johnConsent.isProtocolMapperGranted(fooMapper)); } @Test public void deleteRoleTest() { RealmModel realm = realmManager.getRealm("original"); RoleModel realmRole = realm.getRole("realm-role"); - String realmRoleId = realmRole.getId(); realm.removeRole(realmRole); commit(); realm = realmManager.getRealm("original"); - Map clients = realm.getClientNameMap(); - ClientModel fooClient = clients.get("foo-client"); - ClientModel barClient = clients.get("bar-client"); + ClientModel fooClient = realm.getClientByClientId("foo-client"); + ClientModel barClient = realm.getClientByClientId("bar-client"); UserModel john = session.users().getUserByUsername("john", realm); - GrantedConsentModel johnConsent = john.getGrantedConsentByClient(fooClient.getId()); + UserConsentModel johnConsent = john.getConsentByClient(fooClient.getId()); Assert.assertEquals(johnConsent.getGrantedRoles().size(), 1); Assert.assertEquals(johnConsent.getGrantedProtocolMappers().size(), 1); - Assert.assertFalse(johnConsent.isRoleGranted(realmRoleId)); + Assert.assertFalse(johnConsent.isRoleGranted(realmRole)); Assert.assertTrue(isRoleGranted(barClient, "bar-client-role", johnConsent)); } @Test public void deleteClientTest() { RealmModel realm = realmManager.getRealm("original"); - Map clients = realm.getClientNameMap(); - ClientModel barClient = clients.get("bar-client"); + ClientModel barClient = realm.getClientByClientId("bar-client"); realm.removeClient(barClient.getId()); commit(); realm = realmManager.getRealm("original"); - clients = realm.getClientNameMap(); - ClientModel fooClient = clients.get("foo-client"); - Assert.assertNull(clients.get("bar-client")); + ClientModel fooClient = realm.getClientByClientId("foo-client"); + Assert.assertNull(realm.getClientByClientId("bar-client")); UserModel john = session.users().getUserByUsername("john", realm); - GrantedConsentModel johnFooConsent = john.getGrantedConsentByClient(fooClient.getId()); + UserConsentModel johnFooConsent = john.getConsentByClient(fooClient.getId()); Assert.assertEquals(johnFooConsent.getGrantedRoles().size(), 1); Assert.assertEquals(johnFooConsent.getGrantedProtocolMappers().size(), 1); Assert.assertTrue(isRoleGranted(realm, "realm-role", johnFooConsent)); Assert.assertTrue(isMapperGranted(fooClient, "foo", johnFooConsent)); - Assert.assertNull(john.getGrantedConsentByClient(barClient.getId())); + Assert.assertNull(john.getConsentByClient(barClient.getId())); } - private boolean isRoleGranted(RoleContainerModel roleContainer, String roleName, GrantedConsentModel consentModel) { + private boolean isRoleGranted(RoleContainerModel roleContainer, String roleName, UserConsentModel consentModel) { RoleModel role = roleContainer.getRole(roleName); - return consentModel.isRoleGranted(role.getId()); + return consentModel.isRoleGranted(role); } - private boolean isMapperGranted(ClientModel client, String protocolMapperName, GrantedConsentModel consentModel) { + private boolean isMapperGranted(ClientModel client, String protocolMapperName, UserConsentModel consentModel) { ProtocolMapperModel protocolMapper = client.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, protocolMapperName); - return consentModel.isProtocolMapperGranted(protocolMapper.getId()); + return consentModel.isProtocolMapperGranted(protocolMapper); } } diff --git a/testsuite/integration/src/test/resources/model/testrealm.json b/testsuite/integration/src/test/resources/model/testrealm.json index 4044399254..1c0b7e64a2 100755 --- a/testsuite/integration/src/test/resources/model/testrealm.json +++ b/testsuite/integration/src/test/resources/model/testrealm.json @@ -74,6 +74,15 @@ "applicationRoles": { "Application": [ "app-admin" ], "OtherApp": [ "otherapp-admin" ] + }, + "clientConsents": { + "Application": { + "grantedRoles": [ "456", "789" ] + }, + "OtherApp": { + "grantedProtocolMappers": [ "123" ], + "grantedRoles": [ "456" ] + } } }, { @@ -113,6 +122,7 @@ "enabled": true, "protocolMappers" : [ { + "id": "123", "name" : "gss delegation credential", "protocol" : "openid-connect", "protocolMapper" : "oidc-usersessionmodel-note-mapper", @@ -138,12 +148,14 @@ "roles" : { "realm" : [ { + "id": "456", "name": "admin" } ], "application" : { "Application" : [ { + "id": "789", "name": "app-admin" }, {