Skip to content

Commit

Permalink
KEYCLOAK-1961 revokeRefreshToken support for offline tokens and other…
Browse files Browse the repository at this point in the history
… fixes
  • Loading branch information
mposolda committed Oct 15, 2015
1 parent b4520ba commit 6743579
Show file tree
Hide file tree
Showing 30 changed files with 451 additions and 167 deletions.
Expand Up @@ -4,6 +4,9 @@

<addColumn tableName="REALM">
<column name="OFFLINE_SESSION_IDLE_TIMEOUT" type="INT"/>
<column name="REVOKE_REFRESH_TOKEN" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
</addColumn>

<addColumn tableName="KEYCLOAK_ROLE">
Expand Down Expand Up @@ -47,16 +50,11 @@
<column name="OFFLINE" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="TIMESTAMP" type="INT"/>
<column name="DATA" type="CLOB"/>
</createTable>

<addPrimaryKey columnNames="USER_SESSION_ID, OFFLINE" constraintName="CONSTRAINT_OFFLINE_US_SES_PK" tableName="OFFLINE_USER_SESSION"/>
<addPrimaryKey columnNames="CLIENT_SESSION_ID, OFFLINE" constraintName="CONSTRAINT_OFFLINE_CL_SES_PK" tableName="OFFLINE_CLIENT_SESSION"/>

<addColumn tableName="REALM">
<column name="REVOKE_REFRESH_TOKEN" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
</addColumn>
</changeSet>
</databaseChangeLog>
Expand Up @@ -339,6 +339,7 @@ show-offline-tokens=Show Offline Tokens
show-offline-tokens.tooltip=Warning, this is a potentially expensive operation depending on number of offline tokens.
token-issued=Token Issued
last-access=Last Access
last-refresh=Last Refresh
key-export=Key Export
key-import=Key Import
export-saml-key=Export SAML Key
Expand Down
Expand Up @@ -31,7 +31,7 @@
<th>{{:: 'user' | translate}}</th>
<th>{{:: 'from-ip' | translate}}</th>
<th>{{:: 'token-issued' | translate}}</th>
<th>{{:: 'last-access' | translate}}</th>
<th>{{:: 'last-refresh' | translate}}</th>
</tr>
</thead>
<tfoot data-ng-show="sessions && (sessions.length >= 5 || query.first != 0)">
Expand Down
Expand Up @@ -13,7 +13,7 @@
<tr>
<th>IP Address</th>
<th>Started</th>
<th>Last Access</th>
<th>Last Refresh</th>
</tr>
</thead>
<tbody>
Expand Down
Expand Up @@ -59,6 +59,10 @@ public interface UserSessionProvider extends Provider {
int getOfflineSessionsCount(RealmModel realm, ClientModel client);
List<UserSessionModel> getOfflineUserSessions(RealmModel realm, ClientModel client, int first, int max);

// Triggered by persister during pre-load
UserSessionModel importUserSession(UserSessionModel persistentUserSession, boolean offline);
ClientSessionModel importClientSession(ClientSessionModel persistentClientSession, boolean offline);

void close();

}
Expand Up @@ -7,6 +7,7 @@ public class PersistentClientSessionEntity {

private String clientSessionId;
private String clientId;
private int timestamp;
private String data;

public String getClientSessionId() {
Expand All @@ -25,6 +26,14 @@ public void setClientId(String clientId) {
this.clientId = clientId;
}

public int getTimestamp() {
return timestamp;
}

public void setTimestamp(int timestamp) {
this.timestamp = timestamp;
}

public String getData() {
return data;
}
Expand Down
Expand Up @@ -92,6 +92,11 @@ public void clearDetachedUserSessions() {

}

@Override
public void updateAllTimestamps(int time) {

}

@Override
public List<UserSessionModel> loadUserSessions(int firstResult, int maxResults, boolean offline) {
return Collections.emptyList();
Expand Down
Expand Up @@ -37,7 +37,6 @@ public PersistentClientSessionAdapter(ClientSessionModel clientSession) {
data.setProtocolMappers(clientSession.getProtocolMappers());
data.setRedirectUri(clientSession.getRedirectUri());
data.setRoles(clientSession.getRoles());
data.setTimestamp(clientSession.getTimestamp());
data.setUserSessionNotes(clientSession.getUserSessionNotes());

model = new PersistentClientSessionModel();
Expand All @@ -47,6 +46,7 @@ public PersistentClientSessionAdapter(ClientSessionModel clientSession) {
model.setUserId(clientSession.getAuthenticatedUser().getId());
}
model.setUserSessionId(clientSession.getUserSession().getId());
model.setTimestamp(clientSession.getTimestamp());

realm = clientSession.getRealm();
client = clientSession.getClient();
Expand Down Expand Up @@ -122,12 +122,12 @@ public void setRedirectUri(String uri) {

@Override
public int getTimestamp() {
return getData().getTimestamp();
return model.getTimestamp();
}

@Override
public void setTimestamp(int timestamp) {
getData().setTimestamp(timestamp);
model.setTimestamp(timestamp);
}

@Override
Expand Down Expand Up @@ -309,9 +309,6 @@ protected static class PersistentClientSessionData {
@JsonProperty("executionStatus")
private Map<String, ClientSessionModel.ExecutionStatus> executionStatus = new HashMap<>();

@JsonProperty("timestamp")
private int timestamp;

@JsonProperty("action")
private String action;

Expand Down Expand Up @@ -374,14 +371,6 @@ public void setExecutionStatus(Map<String, ClientSessionModel.ExecutionStatus> e
this.executionStatus = executionStatus;
}

public int getTimestamp() {
return timestamp;
}

public void setTimestamp(int timestamp) {
this.timestamp = timestamp;
}

public String getAction() {
return action;
}
Expand Down
Expand Up @@ -9,6 +9,7 @@ public class PersistentClientSessionModel {
private String userSessionId;
private String clientId;
private String userId;
private int timestamp;
private String data;

public String getClientSessionId() {
Expand Down Expand Up @@ -43,6 +44,14 @@ public void setUserId(String userId) {
this.userId = userId;
}

public int getTimestamp() {
return timestamp;
}

public void setTimestamp(int timestamp) {
this.timestamp = timestamp;
}

public String getData() {
return data;
}
Expand Down
Expand Up @@ -35,6 +35,9 @@ public interface UserSessionPersisterProvider extends Provider {
// Called at startup to remove userSessions without any clientSession
void clearDetachedUserSessions();

// Update "lastSessionRefresh" of all userSessions and "timestamp" of all clientSessions to specified time
void updateAllTimestamps(int time);

// Called during startup. For each userSession, it loads also clientSessions
List<UserSessionModel> loadUserSessions(int firstResult, int maxResults, boolean offline);

Expand Down
Expand Up @@ -58,6 +58,7 @@ public void createClientSession(ClientSessionModel clientSession, boolean offlin
PersistentClientSessionEntity entity = new PersistentClientSessionEntity();
entity.setClientSessionId(clientSession.getId());
entity.setClientId(clientSession.getClient().getId());
entity.setTimestamp(clientSession.getTimestamp());
entity.setOffline(offline);
entity.setUserSessionId(clientSession.getUserSession().getId());
entity.setData(model.getData());
Expand Down Expand Up @@ -128,26 +129,32 @@ private List<PersistentClientSessionEntity> getClientSessionsByUserSession(Strin

@Override
public void onRealmRemoved(RealmModel realm) {
em.createNamedQuery("deleteClientSessionsByRealm").setParameter("realmId", realm.getId()).executeUpdate();
em.createNamedQuery("deleteUserSessionsByRealm").setParameter("realmId", realm.getId()).executeUpdate();
int num = em.createNamedQuery("deleteClientSessionsByRealm").setParameter("realmId", realm.getId()).executeUpdate();
num = em.createNamedQuery("deleteUserSessionsByRealm").setParameter("realmId", realm.getId()).executeUpdate();
}

@Override
public void onClientRemoved(RealmModel realm, ClientModel client) {
em.createNamedQuery("deleteClientSessionsByClient").setParameter("clientId", client.getId()).executeUpdate();
em.createNamedQuery("deleteDetachedUserSessions").executeUpdate();
int num = em.createNamedQuery("deleteClientSessionsByClient").setParameter("clientId", client.getId()).executeUpdate();
num = em.createNamedQuery("deleteDetachedUserSessions").executeUpdate();
}

@Override
public void onUserRemoved(RealmModel realm, UserModel user) {
em.createNamedQuery("deleteClientSessionsByUser").setParameter("userId", user.getId()).executeUpdate();
em.createNamedQuery("deleteUserSessionsByUser").setParameter("userId", user.getId()).executeUpdate();
int num = em.createNamedQuery("deleteClientSessionsByUser").setParameter("userId", user.getId()).executeUpdate();
num = em.createNamedQuery("deleteUserSessionsByUser").setParameter("userId", user.getId()).executeUpdate();
}

@Override
public void clearDetachedUserSessions() {
em.createNamedQuery("deleteDetachedClientSessions").executeUpdate();
em.createNamedQuery("deleteDetachedUserSessions").executeUpdate();
int num = em.createNamedQuery("deleteDetachedClientSessions").executeUpdate();
num = em.createNamedQuery("deleteDetachedUserSessions").executeUpdate();
}

@Override
public void updateAllTimestamps(int time) {
int num = em.createNamedQuery("updateClientSessionsTimestamps").setParameter("timestamp", time).executeUpdate();
num = em.createNamedQuery("updateUserSessionsTimestamps").setParameter("lastSessionRefresh", time).executeUpdate();
}

@Override
Expand Down Expand Up @@ -220,6 +227,7 @@ private PersistentClientSessionAdapter toAdapter(RealmModel realm, PersistentUse
model.setClientId(entity.getClientId());
model.setUserSessionId(userSession.getId());
model.setUserId(userSession.getUser().getId());
model.setTimestamp(entity.getTimestamp());
model.setData(entity.getData());
return new PersistentClientSessionAdapter(model, realm, client, userSession);
}
Expand Down
Expand Up @@ -17,13 +17,14 @@
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@NamedQueries({
@NamedQuery(name="deleteClientSessionsByRealm", query="delete from PersistentClientSessionEntity sess where sess.userSessionId in (select u from PersistentUserSessionEntity u where u.realmId=:realmId)"),
@NamedQuery(name="deleteClientSessionsByRealm", query="delete from PersistentClientSessionEntity sess where sess.userSessionId IN (select u.userSessionId from PersistentUserSessionEntity u where u.realmId=:realmId)"),
@NamedQuery(name="deleteClientSessionsByClient", query="delete from PersistentClientSessionEntity sess where sess.clientId=:clientId"),
@NamedQuery(name="deleteClientSessionsByUser", query="delete from PersistentClientSessionEntity sess where sess.userSessionId in (select u from PersistentUserSessionEntity u where u.userId=:userId)"),
@NamedQuery(name="deleteClientSessionsByUser", query="delete from PersistentClientSessionEntity sess where sess.userSessionId IN (select u.userSessionId from PersistentUserSessionEntity u where u.userId=:userId)"),
@NamedQuery(name="deleteClientSessionsByUserSession", query="delete from PersistentClientSessionEntity sess where sess.userSessionId=:userSessionId and offline=:offline"),
@NamedQuery(name="deleteDetachedClientSessions", query="delete from PersistentClientSessionEntity sess where sess.userSessionId NOT IN (select u.userSessionId from PersistentUserSessionEntity u)"),
@NamedQuery(name="findClientSessionsByUserSession", query="select sess from PersistentClientSessionEntity sess where sess.userSessionId=:userSessionId and offline=:offline"),
@NamedQuery(name="findClientSessionsByUserSessions", query="select sess from PersistentClientSessionEntity sess where offline=:offline and sess.userSessionId IN (:userSessionIds) order by sess.userSessionId"),
@NamedQuery(name="updateClientSessionsTimestamps", query="update PersistentClientSessionEntity c set timestamp=:timestamp"),
})
@Table(name="OFFLINE_CLIENT_SESSION")
@Entity
Expand All @@ -40,6 +41,9 @@ public class PersistentClientSessionEntity {
@Column(name="CLIENT_ID", length = 36)
protected String clientId;

@Column(name="TIMESTAMP")
protected int timestamp;

@Id
@Column(name = "OFFLINE")
protected boolean offline;
Expand Down Expand Up @@ -71,6 +75,14 @@ public void setClientId(String clientId) {
this.clientId = clientId;
}

public int getTimestamp() {
return timestamp;
}

public void setTimestamp(int timestamp) {
this.timestamp = timestamp;
}

public boolean isOffline() {
return offline;
}
Expand Down
Expand Up @@ -26,7 +26,8 @@
@NamedQuery(name="deleteUserSessionsByUser", query="delete from PersistentUserSessionEntity sess where sess.userId=:userId"),
@NamedQuery(name="deleteDetachedUserSessions", query="delete from PersistentUserSessionEntity sess where sess.userSessionId NOT IN (select c.userSessionId from PersistentClientSessionEntity c)"),
@NamedQuery(name="findUserSessionsCount", query="select count(sess) from PersistentUserSessionEntity sess where offline=:offline"),
@NamedQuery(name="findUserSessions", query="select sess from PersistentUserSessionEntity sess where offline=:offline order by sess.userSessionId")
@NamedQuery(name="findUserSessions", query="select sess from PersistentUserSessionEntity sess where offline=:offline order by sess.userSessionId"),
@NamedQuery(name="updateUserSessionsTimestamps", query="update PersistentUserSessionEntity c set lastSessionRefresh=:lastSessionRefresh"),

})
@Table(name="OFFLINE_USER_SESSION")
Expand Down
Expand Up @@ -45,10 +45,6 @@ protected MongoStore getMongoStore() {
return invocationContext.getMongoStore();
}

private Class<? extends PersistentUserSessionEntity> getClazz(boolean offline) {
return offline ? MongoOfflineUserSessionEntity.class : MongoOnlineUserSessionEntity.class;
}

private MongoUserSessionEntity loadUserSession(String userSessionId, boolean offline) {
Class<? extends MongoUserSessionEntity> clazz = offline ? MongoOfflineUserSessionEntity.class : MongoOnlineUserSessionEntity.class;
return getMongoStore().loadEntity(clazz, userSessionId, invocationContext);
Expand Down Expand Up @@ -220,6 +216,41 @@ public int getUserSessionsCount(boolean offline) {
return getMongoStore().countEntities(clazz, query, invocationContext);
}

@Override
public void updateAllTimestamps(int time) {
// 1) Update timestamp of clientSessions

DBObject timestampSubquery = new QueryBuilder()
.and("timestamp").notEquals(time).get();

DBObject query = new QueryBuilder()
.and("clientSessions").elemMatch(timestampSubquery).get();


DBObject update = new QueryBuilder()
.and("$set").is(new BasicDBObject("clientSessions.$.timestamp", time)).get();

// Not sure how to do in single query :/
int countModified = 1;
while (countModified > 0) {
countModified = getMongoStore().updateEntities(MongoOfflineUserSessionEntity.class, query, update, invocationContext);
}

countModified = 1;
while (countModified > 0) {
countModified = getMongoStore().updateEntities(MongoOnlineUserSessionEntity.class, query, update, invocationContext);
}

// 2) update lastSessionRefresh of userSessions
query = new QueryBuilder().get();

update = new QueryBuilder()
.and("$set").is(new BasicDBObject("lastSessionRefresh", time)).get();

getMongoStore().updateEntities(MongoOfflineUserSessionEntity.class, query, update, invocationContext);
getMongoStore().updateEntities(MongoOnlineUserSessionEntity.class, query, update, invocationContext);
}

@Override
public List<UserSessionModel> loadUserSessions(int firstResult, int maxResults, boolean offline) {
DBObject query = new QueryBuilder()
Expand All @@ -232,13 +263,13 @@ public List<UserSessionModel> loadUserSessions(int firstResult, int maxResults,

List<UserSessionModel> results = new LinkedList<>();
for (MongoUserSessionEntity entity : entities) {
PersistentUserSessionAdapter userSession = toAdapter(entity, offline);
PersistentUserSessionAdapter userSession = toAdapter(entity);
results.add(userSession);
}
return results;
}

private PersistentUserSessionAdapter toAdapter(PersistentUserSessionEntity entity, boolean offline) {
private PersistentUserSessionAdapter toAdapter(PersistentUserSessionEntity entity) {
RealmModel realm = session.realms().getRealm(entity.getRealmId());
UserModel user = session.users().getUserById(entity.getUserId(), realm);

Expand All @@ -250,21 +281,22 @@ private PersistentUserSessionAdapter toAdapter(PersistentUserSessionEntity entit
List<ClientSessionModel> clientSessions = new LinkedList<>();
PersistentUserSessionAdapter userSessionAdapter = new PersistentUserSessionAdapter(model, realm, user, clientSessions);
for (PersistentClientSessionEntity clientSessEntity : entity.getClientSessions()) {
PersistentClientSessionAdapter clientSessAdapter = toAdapter(realm, userSessionAdapter, offline, clientSessEntity);
PersistentClientSessionAdapter clientSessAdapter = toAdapter(realm, userSessionAdapter, clientSessEntity);
clientSessions.add(clientSessAdapter);
}

return userSessionAdapter;
}

private PersistentClientSessionAdapter toAdapter(RealmModel realm, PersistentUserSessionAdapter userSession, boolean offline, PersistentClientSessionEntity entity) {
private PersistentClientSessionAdapter toAdapter(RealmModel realm, PersistentUserSessionAdapter userSession, PersistentClientSessionEntity entity) {
ClientModel client = realm.getClientById(entity.getClientId());

PersistentClientSessionModel model = new PersistentClientSessionModel();
model.setClientSessionId(entity.getClientSessionId());
model.setClientId(entity.getClientId());
model.setUserSessionId(userSession.getId());
model.setUserId(userSession.getUser().getId());
model.setTimestamp(entity.getTimestamp());
model.setData(entity.getData());
return new PersistentClientSessionAdapter(model, realm, client, userSession);
}
Expand Down

0 comments on commit 6743579

Please sign in to comment.