diff --git a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/ClientSessionAdapter.java b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/ClientSessionAdapter.java index 4e0f53ca08..92773f856d 100755 --- a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/ClientSessionAdapter.java +++ b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/ClientSessionAdapter.java @@ -17,20 +17,19 @@ import java.util.Set; /** * @author Stian Thorgersen */ -public class ClientSessionAdapter implements ClientSessionModel { +public class ClientSessionAdapter extends AbstractMongoAdapter implements ClientSessionModel { private KeycloakSession session; private MongoUserSessionProvider provider; private RealmModel realm; private MongoClientSessionEntity entity; - private MongoStoreInvocationContext invContext; public ClientSessionAdapter(KeycloakSession session, MongoUserSessionProvider provider, RealmModel realm, MongoClientSessionEntity entity, MongoStoreInvocationContext invContext) { + super(invContext); this.session = session; this.provider = provider; this.realm = realm; this.entity = entity; - this.invContext = invContext; } @Override @@ -58,13 +57,15 @@ public class ClientSessionAdapter implements ClientSessionModel { public void setUserSession(UserSessionModel userSession) { MongoUserSessionEntity userSessionEntity = provider.getUserSessionEntity(realm, userSession.getId()); entity.setSessionId(userSessionEntity.getId()); - provider.getMongoStore().pushItemToList(userSessionEntity, "clientSessions", entity.getId(), true, invContext); + updateMongoEntity(); + + provider.getMongoStore().pushItemToList(userSessionEntity, "clientSessions", entity.getId(), true, invocationContext); } @Override public void setRedirectUri(String uri) { entity.setRedirectUri(uri); - + updateMongoEntity(); } @Override @@ -72,6 +73,7 @@ public class ClientSessionAdapter implements ClientSessionModel { List list = new LinkedList(); list.addAll(roles); entity.setRoles(list); + updateMongoEntity(); } @Override @@ -87,6 +89,7 @@ public class ClientSessionAdapter implements ClientSessionModel { @Override public void setTimestamp(int timestamp) { entity.setTimestamp(timestamp); + updateMongoEntity(); } @Override @@ -97,6 +100,7 @@ public class ClientSessionAdapter implements ClientSessionModel { @Override public void setAction(Action action) { entity.setAction(action); + updateMongoEntity(); } @Override @@ -112,13 +116,13 @@ public class ClientSessionAdapter implements ClientSessionModel { @Override public void setNote(String name, String value) { entity.getNotes().put(name, value); - + updateMongoEntity(); } @Override public void removeNote(String name) { entity.getNotes().remove(name); - + updateMongoEntity(); } @Override @@ -129,5 +133,11 @@ public class ClientSessionAdapter implements ClientSessionModel { @Override public void setAuthMethod(String method) { entity.setAuthMethod(method); + updateMongoEntity(); + } + + @Override + protected MongoClientSessionEntity getMongoEntity() { + return entity; } } 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 9c5f6fd3bd..c032e1b17b 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 @@ -19,6 +19,7 @@ import org.keycloak.models.sessions.mongo.entities.MongoUsernameLoginFailureEnti import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.util.Time; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; @@ -49,6 +50,9 @@ public class MongoUserSessionProvider implements UserSessionProvider { entity.setTimestamp(Time.currentTime()); entity.setClientId(client.getId()); entity.setRealmId(realm.getId()); + + mongoStore.insertEntity(entity, invocationContext); + return new ClientSessionAdapter(session, this, realm, entity, invocationContext); } @@ -125,24 +129,22 @@ public class MongoUserSessionProvider implements UserSessionProvider { public List getUserSessions(RealmModel realm, ClientModel client, int firstResult, int maxResults) { DBObject query = new QueryBuilder() - .and("clientSessions.clientId").is(client.getId()) + .and("clientId").is(client.getId()) .get(); - DBObject sort = new BasicDBObject("started", 1).append("id", 1); + DBObject sort = new BasicDBObject("timestamp", 1).append("id", 1); - List sessions = mongoStore.loadEntities(MongoUserSessionEntity.class, query, sort, firstResult, maxResults, invocationContext); + List clientSessions = mongoStore.loadEntities(MongoClientSessionEntity.class, query, sort, firstResult, maxResults, invocationContext); List result = new LinkedList(); - for (MongoUserSessionEntity session : sessions) { - result.add(new UserSessionAdapter(this.session, this, session, realm, invocationContext)); + for (MongoClientSessionEntity clientSession : clientSessions) { + MongoUserSessionEntity userSession = mongoStore.loadEntity(MongoUserSessionEntity.class, clientSession.getSessionId(), invocationContext); + result.add(new UserSessionAdapter(session, this, userSession, realm, invocationContext)); } return result; } @Override public int getActiveUserSessions(RealmModel realm, ClientModel client) { - DBObject query = new QueryBuilder() - .and("clientSessions.clientId").is(client.getId()) - .get(); - return mongoStore.countEntities(MongoUserSessionEntity.class, query, invocationContext); + return getUserSessions(realm, client).size(); } @Override @@ -232,7 +234,14 @@ public class MongoUserSessionProvider implements UserSessionProvider { DBObject query = new QueryBuilder() .and("clientId").is(client.getId()) .get(); - mongoStore.removeEntities(MongoUserSessionEntity.class, query, invocationContext); + DBObject sort = new BasicDBObject("timestamp", 1).append("id", 1); + + List clientSessions = mongoStore.loadEntities(MongoClientSessionEntity.class, query, sort, -1, -1, invocationContext); + for (MongoClientSessionEntity clientSession : clientSessions) { + MongoUserSessionEntity userSession = mongoStore.loadEntity(MongoUserSessionEntity.class, clientSession.getSessionId(), invocationContext); + getMongoStore().pullItemFromList(userSession, "clientSessions", clientSession.getId(), invocationContext); + mongoStore.removeEntity(clientSession, invocationContext); + } } @Override diff --git a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/UserSessionAdapter.java b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/UserSessionAdapter.java index 70ba85a095..0f30e5aa72 100755 --- a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/UserSessionAdapter.java +++ b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/UserSessionAdapter.java @@ -1,13 +1,11 @@ package org.keycloak.models.sessions.mongo; -import org.jboss.logging.Logger; import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext; import org.keycloak.models.ClientSessionModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; -import org.keycloak.models.sessions.mongo.entities.MongoClientSessionEntity; import org.keycloak.models.sessions.mongo.entities.MongoUserSessionEntity; import java.util.LinkedList; diff --git a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/entities/MongoClientSessionEntity.java b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/entities/MongoClientSessionEntity.java index 6dd1c0913c..91ec6820cb 100755 --- a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/entities/MongoClientSessionEntity.java +++ b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/entities/MongoClientSessionEntity.java @@ -1,5 +1,6 @@ package org.keycloak.models.sessions.mongo.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.ClientSessionModel; @@ -12,6 +13,7 @@ import java.util.Map; /** * @author Stian Thorgersen */ +@MongoCollection(collectionName = "clientSessions") public class MongoClientSessionEntity extends AbstractIdentifiableEntity implements MongoIdentifiableEntity { private String id; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java index 79bc1427d7..7900cc7db9 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java @@ -56,6 +56,7 @@ import javax.ws.rs.core.UriInfo; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -189,10 +190,10 @@ public class UsersResource { user.setAttribute(attr.getKey(), attr.getValue()); } - for (String key : user.getAttributes().keySet()) { - if (!rep.getAttributes().containsKey(key)) { - user.removeAttribute(key); - } + Set attrToRemove = new HashSet(user.getAttributes().keySet()); + attrToRemove.removeAll(rep.getAttributes().keySet()); + for (String attr : attrToRemove) { + user.removeAttribute(attr); } } } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java index 33d02c9ad8..353c70905e 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java @@ -406,6 +406,7 @@ public class OAuthClient { private String refreshToken; private String error; + private String errorDescription; public AccessTokenResponse(HttpResponse response) throws Exception { statusCode = response.getStatusLine().getStatusCode(); @@ -426,6 +427,7 @@ public class OAuthClient { } } else { error = responseJson.getString(OAuth2Constants.ERROR); + errorDescription = responseJson.has(OAuth2Constants.ERROR_DESCRIPTION) ? responseJson.getString(OAuth2Constants.ERROR_DESCRIPTION) : null; } } @@ -437,6 +439,10 @@ public class OAuthClient { return error; } + public String getErrorDescription() { + return errorDescription; + } + public int getExpiresIn() { return expiresIn; } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java index db16965193..2d9983e1f8 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java @@ -66,7 +66,7 @@ public class UserSessionProviderTest { @Test public void testUpdateSession() { UserSessionModel[] sessions = createSessions(); - sessions[0].setLastSessionRefresh(1000); + session.sessions().getUserSession(realm, sessions[0].getId()).setLastSessionRefresh(1000); resetSession(); @@ -137,6 +137,8 @@ public class UserSessionProviderTest { List clientSessionsRemoved = new LinkedList(); List clientSessionsKept = new LinkedList(); for (UserSessionModel s : sessions) { + s = session.sessions().getUserSession(realm, s.getId()); + for (ClientSessionModel c : s.getClientSessions()) { if (c.getUserSession().getUser().getUsername().equals("user1")) { clientSessionsRemoved.add(c.getId()); @@ -349,8 +351,11 @@ public class UserSessionProviderTest { resetSession(); + failure1 = session.sessions().getUserLoginFailure(realm, "user1"); failure1.clearFailures(); + resetSession(); + failure1 = session.sessions().getUserLoginFailure(realm, "user1"); assertEquals(0, failure1.getNumFailures()); }