KEYCLOAK-904 Offline tokens - model changes, admin console, export/import, docs
This commit is contained in:
parent
018866aa81
commit
6fbb8ccf71
42 changed files with 1369 additions and 575 deletions
|
@ -0,0 +1,35 @@
|
||||||
|
package org.keycloak.representations.idm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
*/
|
||||||
|
public class OfflineClientSessionRepresentation {
|
||||||
|
|
||||||
|
private String clientSessionId;
|
||||||
|
private String client; // clientId (not DB ID)
|
||||||
|
private String data;
|
||||||
|
|
||||||
|
public String getClientSessionId() {
|
||||||
|
return clientSessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClientSessionId(String clientSessionId) {
|
||||||
|
this.clientSessionId = clientSessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClient() {
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClient(String client) {
|
||||||
|
this.client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(String data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package org.keycloak.representations.idm;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
*/
|
||||||
|
public class OfflineUserSessionRepresentation {
|
||||||
|
|
||||||
|
private String userSessionId;
|
||||||
|
private String data;
|
||||||
|
private List<OfflineClientSessionRepresentation> offlineClientSessions;
|
||||||
|
|
||||||
|
public String getUserSessionId() {
|
||||||
|
return userSessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserSessionId(String userSessionId) {
|
||||||
|
this.userSessionId = userSessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(String data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<OfflineClientSessionRepresentation> getOfflineClientSessions() {
|
||||||
|
return offlineClientSessions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOfflineClientSessions(List<OfflineClientSessionRepresentation> offlineClientSessions) {
|
||||||
|
this.offlineClientSessions = offlineClientSessions;
|
||||||
|
}
|
||||||
|
}
|
|
@ -36,6 +36,7 @@ public class UserRepresentation {
|
||||||
protected List<String> realmRoles;
|
protected List<String> realmRoles;
|
||||||
protected Map<String, List<String>> clientRoles;
|
protected Map<String, List<String>> clientRoles;
|
||||||
protected List<UserConsentRepresentation> clientConsents;
|
protected List<UserConsentRepresentation> clientConsents;
|
||||||
|
protected List<OfflineUserSessionRepresentation> offlineUserSessions;
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
protected Map<String, List<String>> applicationRoles;
|
protected Map<String, List<String>> applicationRoles;
|
||||||
|
@ -218,4 +219,12 @@ public class UserRepresentation {
|
||||||
public void setServiceAccountClientId(String serviceAccountClientId) {
|
public void setServiceAccountClientId(String serviceAccountClientId) {
|
||||||
this.serviceAccountClientId = serviceAccountClientId;
|
this.serviceAccountClientId = serviceAccountClientId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<OfflineUserSessionRepresentation> getOfflineUserSessions() {
|
||||||
|
return offlineUserSessions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOfflineUserSessions(List<OfflineUserSessionRepresentation> offlineUserSessions) {
|
||||||
|
this.offlineUserSessions = offlineUserSessions;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<chapter id="timeouts">
|
<chapter id="timeouts">
|
||||||
<title>Cookie settings, Session Timeouts, and Token Lifespans</title>
|
<title>Cookie settings, Session Timeouts, and Token Lifespans</title>
|
||||||
<para>
|
<para>
|
||||||
Keycloak has a bunch of fine-grain settings to manage browser cookies, user login sessions, and token lifespans.
|
Keycloak has a bunch of fine-grain settings to manage browser cookies, user login sessions, and token lifespans.
|
||||||
|
@ -52,4 +52,38 @@
|
||||||
back to your application as an authentnicated user. This value is relatively short and is usually measured in minutes.
|
back to your application as an authentnicated user. This value is relatively short and is usually measured in minutes.
|
||||||
</para>
|
</para>
|
||||||
</section>
|
</section>
|
||||||
|
<section id="offline-access">
|
||||||
|
<title>Offline Access</title>
|
||||||
|
<para>
|
||||||
|
The Offline access is the feature described in <ulink url="http://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess">OpenID Connect specification</ulink> .
|
||||||
|
The idea is that during login, your client application will request Offline token instead of classic Refresh token.
|
||||||
|
Then the application can save this offline token in the database and can use it anytime later even if user is logged out.
|
||||||
|
This is useful for example if your application needs to do some "offline" actions on behalf of user even if user is not online. For example
|
||||||
|
periodic backup of some data every night etc.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Your application is responsible for persist the offline token in some storage (usually database) and then use it to
|
||||||
|
manually retrieve new access token from Keycloak server.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
The difference between classic Refresh token and Offline token is, that offline token will never expire and is not subject of <literal>SSO Session Idle timeout</literal> .
|
||||||
|
The offline token is valid even after user logout or server restart. User can revoke the offline tokens in Account management UI. The admin
|
||||||
|
user can revoke offline tokens for individual users in admin console (The <literal>Consent</literal> tab of particular user) and he can
|
||||||
|
see all the offline tokens of all users for particular client application in the settings of the client. Revoking of all offline tokens for particular
|
||||||
|
client is possible by set <literal>notBefore</literal> policy for the client.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
For requesting the offline token, user needs to be in realm role <literal>offline_access</literal> and client needs to have
|
||||||
|
scope for this role. If client has <literal>Full scope allowed</literal>, the scope is granted by default. Also users are automatically
|
||||||
|
members of the role as it's the default role.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
The client can request offline token by adding parameter <literal>scope=offline_access</literal>
|
||||||
|
when sending authorization request to Keycloak. The adapter automatically adds this parameter when you use it to access secured
|
||||||
|
URL of your application (ie. http://localhost:8080/customer-portal/secured?scope=offline_access ).
|
||||||
|
The <link linkend='direct-access-grants'>Direct Access Grant</link> or <link linkend="service-accounts">Service account</link> flows also support
|
||||||
|
offline tokens if you include <literal>scope=offline_access</literal> in the body of the authentication request. For more details,
|
||||||
|
see the <literal>offline-access-app</literal> example from Keycloak demo.
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
</chapter>
|
</chapter>
|
|
@ -1,5 +1,8 @@
|
||||||
package org.keycloak.exportimport.util;
|
package org.keycloak.exportimport.util;
|
||||||
|
|
||||||
|
import org.keycloak.models.OfflineClientSessionModel;
|
||||||
|
import org.keycloak.models.OfflineUserSessionModel;
|
||||||
|
import org.keycloak.representations.idm.OfflineUserSessionRepresentation;
|
||||||
import org.keycloak.util.Base64;
|
import org.keycloak.util.Base64;
|
||||||
import org.codehaus.jackson.JsonEncoding;
|
import org.codehaus.jackson.JsonEncoding;
|
||||||
import org.codehaus.jackson.JsonFactory;
|
import org.codehaus.jackson.JsonFactory;
|
||||||
|
@ -295,6 +298,28 @@ public class ExportUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Offline sessions
|
||||||
|
List<OfflineUserSessionRepresentation> offlineSessionReps = new LinkedList<>();
|
||||||
|
Collection<OfflineUserSessionModel> offlineSessions = session.users().getOfflineUserSessions(realm, user);
|
||||||
|
Collection<OfflineClientSessionModel> offlineClientSessions = session.users().getOfflineClientSessions(realm, user);
|
||||||
|
|
||||||
|
Map<String, List<OfflineClientSessionModel>> processed = new HashMap<>();
|
||||||
|
for (OfflineClientSessionModel clsm : offlineClientSessions) {
|
||||||
|
String userSessionId = clsm.getUserSessionId();
|
||||||
|
List<OfflineClientSessionModel> current = processed.get(userSessionId);
|
||||||
|
if (current == null) {
|
||||||
|
current = new LinkedList<>();
|
||||||
|
processed.put(userSessionId, current);
|
||||||
|
}
|
||||||
|
current.add(clsm);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (OfflineUserSessionModel userSession : offlineSessions) {
|
||||||
|
OfflineUserSessionRepresentation sessionRep = ModelToRepresentation.toRepresentation(realm, userSession, processed.get(userSession.getUserSessionId()));
|
||||||
|
offlineSessionReps.add(sessionRep);
|
||||||
|
}
|
||||||
|
userRep.setOfflineUserSessions(offlineSessionReps);
|
||||||
|
|
||||||
return userRep;
|
return userRep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -202,7 +202,7 @@ public class FreeMarkerAccountProvider implements AccountProvider {
|
||||||
attributes.put("sessions", new SessionsBean(realm, sessions));
|
attributes.put("sessions", new SessionsBean(realm, sessions));
|
||||||
break;
|
break;
|
||||||
case APPLICATIONS:
|
case APPLICATIONS:
|
||||||
attributes.put("applications", new ApplicationsBean(realm, user));
|
attributes.put("applications", new ApplicationsBean(session, realm, user));
|
||||||
attributes.put("advancedMsg", new AdvancedMessageFormatterMethod(locale, messagesBundle));
|
attributes.put("advancedMsg", new AdvancedMessageFormatterMethod(locale, messagesBundle));
|
||||||
break;
|
break;
|
||||||
case PASSWORD:
|
case PASSWORD:
|
||||||
|
|
|
@ -6,6 +6,7 @@ import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.UserConsentModel;
|
import org.keycloak.models.UserConsentModel;
|
||||||
import org.keycloak.models.ProtocolMapperModel;
|
import org.keycloak.models.ProtocolMapperModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
@ -22,9 +23,9 @@ public class ApplicationsBean {
|
||||||
|
|
||||||
private List<ApplicationEntry> applications = new LinkedList<ApplicationEntry>();
|
private List<ApplicationEntry> applications = new LinkedList<ApplicationEntry>();
|
||||||
|
|
||||||
public ApplicationsBean(RealmModel realm, UserModel user) {
|
public ApplicationsBean(KeycloakSession session, RealmModel realm, UserModel user) {
|
||||||
|
|
||||||
Set<ClientModel> offlineClients = OfflineTokenUtils.findClientsWithOfflineToken(realm, user);
|
Set<ClientModel> offlineClients = OfflineTokenUtils.findClientsWithOfflineToken(session, realm, user);
|
||||||
|
|
||||||
List<ClientModel> realmClients = realm.getClients();
|
List<ClientModel> realmClients = realm.getClients();
|
||||||
for (ClientModel client : realmClients) {
|
for (ClientModel client : realmClients) {
|
||||||
|
|
|
@ -637,6 +637,21 @@ module.config([ '$routeProvider', function($routeProvider) {
|
||||||
},
|
},
|
||||||
controller : 'ClientSessionsCtrl'
|
controller : 'ClientSessionsCtrl'
|
||||||
})
|
})
|
||||||
|
.when('/realms/:realm/clients/:client/offline-access', {
|
||||||
|
templateUrl : resourceUrl + '/partials/client-offline-sessions.html',
|
||||||
|
resolve : {
|
||||||
|
realm : function(RealmLoader) {
|
||||||
|
return RealmLoader();
|
||||||
|
},
|
||||||
|
client : function(ClientLoader) {
|
||||||
|
return ClientLoader();
|
||||||
|
},
|
||||||
|
offlineSessionCount : function(ClientOfflineSessionCountLoader) {
|
||||||
|
return ClientOfflineSessionCountLoader();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
controller : 'ClientOfflineSessionsCtrl'
|
||||||
|
})
|
||||||
.when('/realms/:realm/clients/:client/credentials', {
|
.when('/realms/:realm/clients/:client/credentials', {
|
||||||
templateUrl : resourceUrl + '/partials/client-credentials.html',
|
templateUrl : resourceUrl + '/partials/client-credentials.html',
|
||||||
resolve : {
|
resolve : {
|
||||||
|
|
|
@ -507,6 +507,54 @@ module.controller('ClientSessionsCtrl', function($scope, realm, sessionCount, cl
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.controller('ClientOfflineSessionsCtrl', function($scope, realm, offlineSessionCount, client,
|
||||||
|
ClientOfflineSessions) {
|
||||||
|
$scope.realm = realm;
|
||||||
|
$scope.count = offlineSessionCount.count;
|
||||||
|
$scope.sessions = [];
|
||||||
|
$scope.client = client;
|
||||||
|
|
||||||
|
$scope.page = 0;
|
||||||
|
|
||||||
|
$scope.query = {
|
||||||
|
realm : realm.realm,
|
||||||
|
client: $scope.client.id,
|
||||||
|
max : 5,
|
||||||
|
first : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.firstPage = function() {
|
||||||
|
$scope.query.first = 0;
|
||||||
|
if ($scope.query.first < 0) {
|
||||||
|
$scope.query.first = 0;
|
||||||
|
}
|
||||||
|
$scope.loadUsers();
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.previousPage = function() {
|
||||||
|
$scope.query.first -= parseInt($scope.query.max);
|
||||||
|
if ($scope.query.first < 0) {
|
||||||
|
$scope.query.first = 0;
|
||||||
|
}
|
||||||
|
$scope.loadUsers();
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.nextPage = function() {
|
||||||
|
$scope.query.first += parseInt($scope.query.max);
|
||||||
|
$scope.loadUsers();
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.toDate = function(val) {
|
||||||
|
return new Date(val);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.loadUsers = function() {
|
||||||
|
ClientOfflineSessions.query($scope.query, function(updated) {
|
||||||
|
$scope.sessions = updated;
|
||||||
|
})
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
module.controller('ClientRoleDetailCtrl', function($scope, realm, client, role, roles, clients,
|
module.controller('ClientRoleDetailCtrl', function($scope, realm, client, role, roles, clients,
|
||||||
Role, ClientRole, RoleById, RoleRealmComposites, RoleClientComposites,
|
Role, ClientRole, RoleById, RoleRealmComposites, RoleClientComposites,
|
||||||
$http, $location, Dialog, Notifications) {
|
$http, $location, Dialog, Notifications) {
|
||||||
|
|
|
@ -208,9 +208,9 @@ module.controller('UserConsentsCtrl', function($scope, realm, user, userConsents
|
||||||
UserConsents.query({realm: realm.realm, user: user.id}, function(updated) {
|
UserConsents.query({realm: realm.realm, user: user.id}, function(updated) {
|
||||||
$scope.userConsents = updated;
|
$scope.userConsents = updated;
|
||||||
})
|
})
|
||||||
Notifications.success('Consent revoked successfully');
|
Notifications.success('Grant revoked successfully');
|
||||||
}, function() {
|
}, function() {
|
||||||
Notifications.error("Consent couldn't be revoked");
|
Notifications.error("Grant couldn't be revoked");
|
||||||
});
|
});
|
||||||
console.log("Revoke consent " + clientId);
|
console.log("Revoke consent " + clientId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -246,6 +246,15 @@ module.factory('ClientSessionCountLoader', function(Loader, ClientSessionCount,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.factory('ClientOfflineSessionCountLoader', function(Loader, ClientOfflineSessionCount, $route, $q) {
|
||||||
|
return Loader.get(ClientOfflineSessionCount, function() {
|
||||||
|
return {
|
||||||
|
realm : $route.current.params.realm,
|
||||||
|
client : $route.current.params.client
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
module.factory('ClientClaimsLoader', function(Loader, ClientClaims, $route, $q) {
|
module.factory('ClientClaimsLoader', function(Loader, ClientClaims, $route, $q) {
|
||||||
return Loader.get(ClientClaims, function() {
|
return Loader.get(ClientClaims, function() {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -841,6 +841,20 @@ module.factory('ClientUserSessions', function($resource) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.factory('ClientOfflineSessionCount', function($resource) {
|
||||||
|
return $resource(authUrl + '/admin/realms/:realm/clients/:client/offline-session-count', {
|
||||||
|
realm : '@realm',
|
||||||
|
client : "@client"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.factory('ClientOfflineSessions', function($resource) {
|
||||||
|
return $resource(authUrl + '/admin/realms/:realm/clients/:client/offline-sessions', {
|
||||||
|
realm : '@realm',
|
||||||
|
client : "@client"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
module.factory('ClientLogoutAll', function($resource) {
|
module.factory('ClientLogoutAll', function($resource) {
|
||||||
return $resource(authUrl + '/admin/realms/:realm/clients/:client/logout-all', {
|
return $resource(authUrl + '/admin/realms/:realm/clients/:client/logout-all', {
|
||||||
realm : '@realm',
|
realm : '@realm',
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
|
||||||
|
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li><a href="#/realms/{{realm.realm}}/clients">Clients</a></li>
|
||||||
|
<li>{{client.clientId}}</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<kc-tabs-client></kc-tabs-client>
|
||||||
|
|
||||||
|
<form class="form-horizontal" name="sessionStats">
|
||||||
|
<fieldset class="border-top">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-2 control-label" for="activeSessions">Offline Tokens</label>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input class="form-control" type="text" id="activeSessions" name="activeSessions" data-ng-model="count" ng-disabled="true">
|
||||||
|
</div>
|
||||||
|
<kc-tooltip>Total number of active offline tokens for this client.</kc-tooltip>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
<table class="table table-striped table-bordered" data-ng-show="count > 0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="kc-table-actions" colspan="3">
|
||||||
|
<div class="pull-right">
|
||||||
|
<a class="btn btn-default" ng-click="loadUsers()" tooltip-placement="left" tooltip-trigger="mouseover mouseout" tooltip="Warning, this is a potentially expensive operation depending on number of offline tokens.">Show Offline Tokens</a>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
<tr data-ng-show="sessions">
|
||||||
|
<th>User</th>
|
||||||
|
<th>From IP</th>
|
||||||
|
<th>Token Issued</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tfoot data-ng-show="sessions && (sessions.length >= 5 || query.first != 0)">
|
||||||
|
<tr>
|
||||||
|
<td colspan="7">
|
||||||
|
<div class="table-nav">
|
||||||
|
<button data-ng-click="firstPage()" class="first" ng-disabled="query.first == 0">First page</button>
|
||||||
|
<button data-ng-click="previousPage()" class="prev" ng-disabled="query.first == 0">Previous page</button>
|
||||||
|
<button data-ng-click="nextPage()" class="next" ng-disabled="sessions.length < query.max">Next page</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
<tbody>
|
||||||
|
<tr data-ng-repeat="session in sessions">
|
||||||
|
<td><a href="#/realms/{{realm.realm}}/users/{{session.userId}}">{{session.username}}</a></td>
|
||||||
|
<td>{{session.ipAddress}}</td>
|
||||||
|
<td>{{session.start | date:'medium'}}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<kc-menu></kc-menu>
|
|
@ -12,6 +12,7 @@
|
||||||
<th>Client</th>
|
<th>Client</th>
|
||||||
<th>Granted Roles</th>
|
<th>Granted Roles</th>
|
||||||
<th>Granted Protocol Mappers</th>
|
<th>Granted Protocol Mappers</th>
|
||||||
|
<th>Additional Grants</th>
|
||||||
<th>Action</th>
|
<th>Action</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -35,6 +36,11 @@
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
<span data-ng-repeat="additionalGrant in consent.additionalGrants">
|
||||||
|
<span ng-if="!$first">, </span>{{additionalGrant}}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
<td class="kc-action-cell">
|
<td class="kc-action-cell">
|
||||||
<button class="btn btn-default btn-block btn-sm" ng-click="revokeConsent(consent.clientId)">
|
<button class="btn btn-default btn-block btn-sm" ng-click="revokeConsent(consent.clientId)">
|
||||||
<i class="pficon pficon-delete"></i> Revoke
|
<i class="pficon pficon-delete"></i> Revoke
|
||||||
|
|
|
@ -26,6 +26,13 @@
|
||||||
<kc-tooltip>View active sessions for this client. Allows you to see which users are active and when they logged in.</kc-tooltip>
|
<kc-tooltip>View active sessions for this client. Allows you to see which users are active and when they logged in.</kc-tooltip>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li ng-class="{active: path[4] == 'offline-access'}" data-ng-show="!client.bearerOnly">
|
||||||
|
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/offline-access">Offline Access</a>
|
||||||
|
<kc-tooltip>View offline sessions for this client. Allows you to see which users retrieve offline token and when they retrieve it.
|
||||||
|
To revoke all tokens for the client, go to Revocation tab and set new not before value.
|
||||||
|
</kc-tooltip>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li ng-class="{active: path[4] == 'clustering'}" data-ng-show="!client.publicClient"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/clustering">Clustering</a></li>
|
<li ng-class="{active: path[4] == 'clustering'}" data-ng-show="!client.publicClient"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/clustering">Clustering</a></li>
|
||||||
|
|
||||||
<li ng-class="{active: path[4] == 'installation'}" data-ng-show="client.protocol != 'saml'">
|
<li ng-class="{active: path[4] == 'installation'}" data-ng-show="client.protocol != 'saml'">
|
||||||
|
|
|
@ -3,8 +3,10 @@ package org.keycloak.migration.migrators;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.keycloak.migration.ModelVersion;
|
import org.keycloak.migration.ModelVersion;
|
||||||
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -17,6 +19,16 @@ public class MigrateTo1_6_0 {
|
||||||
public void migrate(KeycloakSession session) {
|
public void migrate(KeycloakSession session) {
|
||||||
List<RealmModel> realms = session.realms().getRealms();
|
List<RealmModel> realms = session.realms().getRealms();
|
||||||
for (RealmModel realm : realms) {
|
for (RealmModel realm : realms) {
|
||||||
|
|
||||||
|
for (RoleModel realmRole : realm.getRoles()) {
|
||||||
|
realmRole.setScopeParamRequired(false);
|
||||||
|
}
|
||||||
|
for (ClientModel client : realm.getClients()) {
|
||||||
|
for (RoleModel clientRole : client.getRoles()) {
|
||||||
|
clientRole.setScopeParamRequired(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
KeycloakModelUtils.setupOfflineTokens(realm);
|
KeycloakModelUtils.setupOfflineTokens(realm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ public class OfflineClientSessionModel {
|
||||||
private String clientSessionId;
|
private String clientSessionId;
|
||||||
private String userSessionId;
|
private String userSessionId;
|
||||||
private String clientId;
|
private String clientId;
|
||||||
|
private String userId;
|
||||||
private String data;
|
private String data;
|
||||||
|
|
||||||
public String getClientSessionId() {
|
public String getClientSessionId() {
|
||||||
|
@ -34,6 +35,14 @@ public class OfflineClientSessionModel {
|
||||||
this.clientId = clientId;
|
this.clientId = clientId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getUserId() {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserId(String userId) {
|
||||||
|
this.userId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
public String getData() {
|
public String getData() {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import org.jboss.logging.Logger;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -474,6 +475,70 @@ public class UserFederationManager implements UserProvider {
|
||||||
return (result != null) ? result : CredentialValidationOutput.failed();
|
return (result != null) ? result : CredentialValidationOutput.failed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addOfflineUserSession(RealmModel realm, UserModel user, OfflineUserSessionModel offlineUserSession) {
|
||||||
|
validateUser(realm, user);
|
||||||
|
if (user == null) throw new IllegalStateException("Federated user no longer valid");
|
||||||
|
session.userStorage().addOfflineUserSession(realm, user, offlineUserSession);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OfflineUserSessionModel getOfflineUserSession(RealmModel realm, UserModel user, String userSessionId) {
|
||||||
|
validateUser(realm, user);
|
||||||
|
if (user == null) throw new IllegalStateException("Federated user no longer valid");
|
||||||
|
return session.userStorage().getOfflineUserSession(realm, user, userSessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<OfflineUserSessionModel> getOfflineUserSessions(RealmModel realm, UserModel user) {
|
||||||
|
validateUser(realm, user);
|
||||||
|
if (user == null) throw new IllegalStateException("Federated user no longer valid");
|
||||||
|
return session.userStorage().getOfflineUserSessions(realm, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeOfflineUserSession(RealmModel realm, UserModel user, String userSessionId) {
|
||||||
|
validateUser(realm, user);
|
||||||
|
if (user == null) throw new IllegalStateException("Federated user no longer valid");
|
||||||
|
return session.userStorage().removeOfflineUserSession(realm, user, userSessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addOfflineClientSession(RealmModel realm, OfflineClientSessionModel offlineClientSession) {
|
||||||
|
session.userStorage().addOfflineClientSession(realm, offlineClientSession);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OfflineClientSessionModel getOfflineClientSession(RealmModel realm, UserModel user, String clientSessionId) {
|
||||||
|
validateUser(realm, user);
|
||||||
|
if (user == null) throw new IllegalStateException("Federated user no longer valid");
|
||||||
|
return session.userStorage().getOfflineClientSession(realm, user, clientSessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<OfflineClientSessionModel> getOfflineClientSessions(RealmModel realm, UserModel user) {
|
||||||
|
validateUser(realm, user);
|
||||||
|
if (user == null) throw new IllegalStateException("Federated user no longer valid");
|
||||||
|
return session.userStorage().getOfflineClientSessions(realm, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeOfflineClientSession(RealmModel realm, UserModel user, String clientSessionId) {
|
||||||
|
validateUser(realm, user);
|
||||||
|
if (user == null) throw new IllegalStateException("Federated user no longer valid");
|
||||||
|
return session.userStorage().removeOfflineClientSession(realm, user, clientSessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOfflineClientSessionsCount(RealmModel realm, ClientModel client) {
|
||||||
|
return session.userStorage().getOfflineClientSessionsCount(realm, client);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<OfflineClientSessionModel> getOfflineClientSessions(RealmModel realm, ClientModel client, int firstResult, int maxResults) {
|
||||||
|
return session.userStorage().getOfflineClientSessions(realm, client, firstResult, maxResults);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,15 +114,6 @@ public interface UserModel {
|
||||||
void updateConsent(UserConsentModel consent);
|
void updateConsent(UserConsentModel consent);
|
||||||
boolean revokeConsentForClient(String clientInternalId);
|
boolean revokeConsentForClient(String clientInternalId);
|
||||||
|
|
||||||
void addOfflineUserSession(OfflineUserSessionModel offlineUserSession);
|
|
||||||
OfflineUserSessionModel getOfflineUserSession(String userSessionId);
|
|
||||||
Collection<OfflineUserSessionModel> getOfflineUserSessions();
|
|
||||||
boolean removeOfflineUserSession(String userSessionId);
|
|
||||||
void addOfflineClientSession(OfflineClientSessionModel offlineClientSession);
|
|
||||||
OfflineClientSessionModel getOfflineClientSession(String clientSessionId);
|
|
||||||
Collection<OfflineClientSessionModel> getOfflineClientSessions();
|
|
||||||
boolean removeOfflineClientSession(String clientSessionId);
|
|
||||||
|
|
||||||
public static enum RequiredAction {
|
public static enum RequiredAction {
|
||||||
VERIFY_EMAIL, UPDATE_PROFILE, CONFIGURE_TOTP, UPDATE_PASSWORD
|
VERIFY_EMAIL, UPDATE_PROFILE, CONFIGURE_TOTP, UPDATE_PASSWORD
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.keycloak.models;
|
||||||
|
|
||||||
import org.keycloak.provider.Provider;
|
import org.keycloak.provider.Provider;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -55,5 +56,17 @@ public interface UserProvider extends Provider {
|
||||||
boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input);
|
boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input);
|
||||||
CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel... input);
|
CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel... input);
|
||||||
|
|
||||||
|
void addOfflineUserSession(RealmModel realm, UserModel user, OfflineUserSessionModel offlineUserSession);
|
||||||
|
OfflineUserSessionModel getOfflineUserSession(RealmModel realm, UserModel user, String userSessionId);
|
||||||
|
Collection<OfflineUserSessionModel> getOfflineUserSessions(RealmModel realm, UserModel user);
|
||||||
|
boolean removeOfflineUserSession(RealmModel realm, UserModel user, String userSessionId);
|
||||||
|
void addOfflineClientSession(RealmModel realm, OfflineClientSessionModel offlineClientSession);
|
||||||
|
OfflineClientSessionModel getOfflineClientSession(RealmModel realm, UserModel user, String clientSessionId);
|
||||||
|
Collection<OfflineClientSessionModel> getOfflineClientSessions(RealmModel realm, UserModel user);
|
||||||
|
boolean removeOfflineClientSession(RealmModel realm, UserModel user, String clientSessionId);
|
||||||
|
|
||||||
|
int getOfflineClientSessionsCount(RealmModel realm, ClientModel client);
|
||||||
|
Collection<OfflineClientSessionModel> getOfflineClientSessions(RealmModel realm, ClientModel client, int first, int max);
|
||||||
|
|
||||||
void close();
|
void close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@ import org.keycloak.models.IdentityProviderMapperModel;
|
||||||
import org.keycloak.models.IdentityProviderModel;
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
import org.keycloak.models.ModelException;
|
import org.keycloak.models.ModelException;
|
||||||
import org.keycloak.models.OTPPolicy;
|
import org.keycloak.models.OTPPolicy;
|
||||||
|
import org.keycloak.models.OfflineClientSessionModel;
|
||||||
|
import org.keycloak.models.OfflineUserSessionModel;
|
||||||
import org.keycloak.models.ProtocolMapperModel;
|
import org.keycloak.models.ProtocolMapperModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RequiredActionProviderModel;
|
import org.keycloak.models.RequiredActionProviderModel;
|
||||||
|
@ -30,6 +32,8 @@ import org.keycloak.representations.idm.CredentialRepresentation;
|
||||||
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
|
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
|
||||||
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
||||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||||
|
import org.keycloak.representations.idm.OfflineClientSessionRepresentation;
|
||||||
|
import org.keycloak.representations.idm.OfflineUserSessionRepresentation;
|
||||||
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
|
import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
|
@ -43,6 +47,7 @@ import org.keycloak.representations.idm.UserSessionRepresentation;
|
||||||
import org.keycloak.util.Time;
|
import org.keycloak.util.Time;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
@ -506,7 +511,31 @@ public class ModelToRepresentation {
|
||||||
return rep;
|
return rep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static OfflineUserSessionRepresentation toRepresentation(RealmModel realm, OfflineUserSessionModel model, Collection<OfflineClientSessionModel> clientSessions) {
|
||||||
|
OfflineUserSessionRepresentation rep = new OfflineUserSessionRepresentation();
|
||||||
|
rep.setData(model.getData());
|
||||||
|
rep.setUserSessionId(model.getUserSessionId());
|
||||||
|
|
||||||
|
List<OfflineClientSessionRepresentation> clientSessionReps = new LinkedList<>();
|
||||||
|
for (OfflineClientSessionModel clsm : clientSessions) {
|
||||||
|
OfflineClientSessionRepresentation clrep = toRepresentation(realm, clsm);
|
||||||
|
clientSessionReps.add(clrep);
|
||||||
|
}
|
||||||
|
rep.setOfflineClientSessions(clientSessionReps);
|
||||||
|
return rep;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OfflineClientSessionRepresentation toRepresentation(RealmModel realm, OfflineClientSessionModel model) {
|
||||||
|
OfflineClientSessionRepresentation rep = new OfflineClientSessionRepresentation();
|
||||||
|
|
||||||
|
String clientInternalId = model.getClientId();
|
||||||
|
ClientModel client = realm.getClientById(clientInternalId);
|
||||||
|
rep.setClient(client.getClientId());
|
||||||
|
|
||||||
|
rep.setClientSessionId(model.getClientSessionId());
|
||||||
|
rep.setData(model.getData());
|
||||||
|
return rep;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
package org.keycloak.models.utils;
|
package org.keycloak.models.utils;
|
||||||
|
|
||||||
|
import org.keycloak.models.OfflineClientSessionModel;
|
||||||
|
import org.keycloak.models.OfflineUserSessionModel;
|
||||||
|
import org.keycloak.representations.idm.OfflineClientSessionRepresentation;
|
||||||
|
import org.keycloak.representations.idm.OfflineUserSessionRepresentation;
|
||||||
import org.keycloak.util.Base64;
|
import org.keycloak.util.Base64;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.enums.SslRequired;
|
import org.keycloak.enums.SslRequired;
|
||||||
|
@ -981,6 +985,11 @@ public class RepresentationToModel {
|
||||||
user.addConsent(consentModel);
|
user.addConsent(consentModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (userRep.getOfflineUserSessions() != null) {
|
||||||
|
for (OfflineUserSessionRepresentation sessionRep : userRep.getOfflineUserSessions()) {
|
||||||
|
importOfflineSession(session, newRealm, user, sessionRep);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (userRep.getServiceAccountClientId() != null) {
|
if (userRep.getServiceAccountClientId() != null) {
|
||||||
String clientId = userRep.getServiceAccountClientId();
|
String clientId = userRep.getServiceAccountClientId();
|
||||||
ClientModel client = clientMap.get(clientId);
|
ClientModel client = clientMap.get(clientId);
|
||||||
|
@ -1151,6 +1160,29 @@ public class RepresentationToModel {
|
||||||
return consentModel;
|
return consentModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void importOfflineSession(KeycloakSession session, RealmModel newRealm, UserModel user, OfflineUserSessionRepresentation sessionRep) {
|
||||||
|
OfflineUserSessionModel model = new OfflineUserSessionModel();
|
||||||
|
model.setUserSessionId(sessionRep.getUserSessionId());
|
||||||
|
model.setData(sessionRep.getData());
|
||||||
|
session.users().addOfflineUserSession(newRealm, user, model);
|
||||||
|
|
||||||
|
for (OfflineClientSessionRepresentation csRep : sessionRep.getOfflineClientSessions()) {
|
||||||
|
OfflineClientSessionModel csModel = new OfflineClientSessionModel();
|
||||||
|
String clientId = csRep.getClient();
|
||||||
|
ClientModel client = newRealm.getClientByClientId(clientId);
|
||||||
|
if (client == null) {
|
||||||
|
throw new RuntimeException("Unable to find client " + clientId + " referenced from offlineClientSession of user " + user.getUsername());
|
||||||
|
}
|
||||||
|
csModel.setClientId(client.getId());
|
||||||
|
csModel.setUserId(user.getId());
|
||||||
|
csModel.setClientSessionId(csRep.getClientSessionId());
|
||||||
|
csModel.setUserSessionId(sessionRep.getUserSessionId());
|
||||||
|
csModel.setData(csRep.getData());
|
||||||
|
|
||||||
|
session.users().addOfflineClientSession(newRealm, csModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static AuthenticationFlowModel toModel(AuthenticationFlowRepresentation rep) {
|
public static AuthenticationFlowModel toModel(AuthenticationFlowRepresentation rep) {
|
||||||
AuthenticationFlowModel model = new AuthenticationFlowModel();
|
AuthenticationFlowModel model = new AuthenticationFlowModel();
|
||||||
model.setBuiltIn(rep.isBuiltIn());
|
model.setBuiltIn(rep.isBuiltIn());
|
||||||
|
|
|
@ -258,44 +258,4 @@ public class UserModelDelegate implements UserModel {
|
||||||
public void setCreatedTimestamp(Long timestamp){
|
public void setCreatedTimestamp(Long timestamp){
|
||||||
delegate.setCreatedTimestamp(timestamp);
|
delegate.setCreatedTimestamp(timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addOfflineUserSession(OfflineUserSessionModel userSession) {
|
|
||||||
delegate.addOfflineUserSession(userSession);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public OfflineUserSessionModel getOfflineUserSession(String userSessionId) {
|
|
||||||
return delegate.getOfflineUserSession(userSessionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<OfflineUserSessionModel> getOfflineUserSessions() {
|
|
||||||
return delegate.getOfflineUserSessions();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean removeOfflineUserSession(String userSessionId) {
|
|
||||||
return delegate.removeOfflineUserSession(userSessionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addOfflineClientSession(OfflineClientSessionModel clientSession) {
|
|
||||||
delegate.addOfflineClientSession(clientSession);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public OfflineClientSessionModel getOfflineClientSession(String clientSessionId) {
|
|
||||||
return delegate.getOfflineClientSession(clientSessionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<OfflineClientSessionModel> getOfflineClientSessions() {
|
|
||||||
return delegate.getOfflineClientSessions();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean removeOfflineClientSession(String clientSessionId) {
|
|
||||||
return delegate.removeOfflineClientSession(clientSessionId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,9 @@ import org.keycloak.models.CredentialValidationOutput;
|
||||||
import org.keycloak.models.FederatedIdentityModel;
|
import org.keycloak.models.FederatedIdentityModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.ModelDuplicateException;
|
import org.keycloak.models.ModelDuplicateException;
|
||||||
|
import org.keycloak.models.ModelException;
|
||||||
|
import org.keycloak.models.OfflineClientSessionModel;
|
||||||
|
import org.keycloak.models.OfflineUserSessionModel;
|
||||||
import org.keycloak.models.ProtocolMapperModel;
|
import org.keycloak.models.ProtocolMapperModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RequiredActionProviderModel;
|
import org.keycloak.models.RequiredActionProviderModel;
|
||||||
|
@ -32,6 +35,8 @@ import org.keycloak.models.UserFederationProviderModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserProvider;
|
import org.keycloak.models.UserProvider;
|
||||||
import org.keycloak.models.entities.FederatedIdentityEntity;
|
import org.keycloak.models.entities.FederatedIdentityEntity;
|
||||||
|
import org.keycloak.models.entities.OfflineClientSessionEntity;
|
||||||
|
import org.keycloak.models.entities.OfflineUserSessionEntity;
|
||||||
import org.keycloak.models.entities.UserEntity;
|
import org.keycloak.models.entities.UserEntity;
|
||||||
import org.keycloak.models.file.adapter.UserAdapter;
|
import org.keycloak.models.file.adapter.UserAdapter;
|
||||||
import org.keycloak.models.utils.CredentialValidation;
|
import org.keycloak.models.utils.CredentialValidation;
|
||||||
|
@ -41,6 +46,7 @@ import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -489,4 +495,187 @@ public class FileUserProvider implements UserProvider {
|
||||||
return null; // not supported yet
|
return null; // not supported yet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addOfflineUserSession(RealmModel realm, UserModel userModel, OfflineUserSessionModel userSession) {
|
||||||
|
userModel = getUserById(userModel.getId(), realm);
|
||||||
|
UserEntity userEntity = ((UserAdapter) userModel).getUserEntity();
|
||||||
|
|
||||||
|
if (userEntity.getOfflineUserSessions() == null) {
|
||||||
|
userEntity.setOfflineUserSessions(new ArrayList<OfflineUserSessionEntity>());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getUserSessionEntityById(userEntity, userSession.getUserSessionId()) != null) {
|
||||||
|
throw new ModelDuplicateException("User session already exists with id " + userSession.getUserSessionId() + " for user " + userEntity.getUsername());
|
||||||
|
}
|
||||||
|
|
||||||
|
OfflineUserSessionEntity entity = new OfflineUserSessionEntity();
|
||||||
|
entity.setUserSessionId(userSession.getUserSessionId());
|
||||||
|
entity.setData(userSession.getData());
|
||||||
|
entity.setOfflineClientSessions(new ArrayList<OfflineClientSessionEntity>());
|
||||||
|
userEntity.getOfflineUserSessions().add(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OfflineUserSessionModel getOfflineUserSession(RealmModel realm, UserModel userModel, String userSessionId) {
|
||||||
|
userModel = getUserById(userModel.getId(), realm);
|
||||||
|
UserEntity userEntity = ((UserAdapter) userModel).getUserEntity();
|
||||||
|
|
||||||
|
OfflineUserSessionEntity entity = getUserSessionEntityById(userEntity, userSessionId);
|
||||||
|
return entity==null ? null : toModel(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<OfflineUserSessionModel> getOfflineUserSessions(RealmModel realm, UserModel userModel) {
|
||||||
|
userModel = getUserById(userModel.getId(), realm);
|
||||||
|
UserEntity user = ((UserAdapter) userModel).getUserEntity();
|
||||||
|
|
||||||
|
if (user.getOfflineUserSessions()==null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
} else {
|
||||||
|
List<OfflineUserSessionModel> result = new ArrayList<>();
|
||||||
|
for (OfflineUserSessionEntity entity : user.getOfflineUserSessions()) {
|
||||||
|
result.add(toModel(entity));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private OfflineUserSessionModel toModel(OfflineUserSessionEntity entity) {
|
||||||
|
OfflineUserSessionModel model = new OfflineUserSessionModel();
|
||||||
|
model.setUserSessionId(entity.getUserSessionId());
|
||||||
|
model.setData(entity.getData());
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeOfflineUserSession(RealmModel realm, UserModel userModel, String userSessionId) {
|
||||||
|
userModel = getUserById(userModel.getId(), realm);
|
||||||
|
UserEntity user = ((UserAdapter) userModel).getUserEntity();
|
||||||
|
|
||||||
|
OfflineUserSessionEntity entity = getUserSessionEntityById(user, userSessionId);
|
||||||
|
if (entity != null) {
|
||||||
|
user.getOfflineUserSessions().remove(entity);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private OfflineUserSessionEntity getUserSessionEntityById(UserEntity user, String userSessionId) {
|
||||||
|
if (user.getOfflineUserSessions() != null) {
|
||||||
|
for (OfflineUserSessionEntity entity : user.getOfflineUserSessions()) {
|
||||||
|
if (entity.getUserSessionId().equals(userSessionId)) {
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addOfflineClientSession(RealmModel realm, OfflineClientSessionModel clientSession) {
|
||||||
|
UserModel userModel = getUserById(clientSession.getUserId(), realm);
|
||||||
|
UserEntity user = ((UserAdapter) userModel).getUserEntity();
|
||||||
|
|
||||||
|
OfflineUserSessionEntity userSessionEntity = getUserSessionEntityById(user, clientSession.getUserSessionId());
|
||||||
|
if (userSessionEntity == null) {
|
||||||
|
throw new ModelException("OfflineUserSession with ID " + clientSession.getUserSessionId() + " doesn't exist for user " + user.getUsername());
|
||||||
|
}
|
||||||
|
|
||||||
|
OfflineClientSessionEntity clEntity = new OfflineClientSessionEntity();
|
||||||
|
clEntity.setClientSessionId(clientSession.getClientSessionId());
|
||||||
|
clEntity.setClientId(clientSession.getClientId());
|
||||||
|
clEntity.setData(clientSession.getData());
|
||||||
|
|
||||||
|
userSessionEntity.getOfflineClientSessions().add(clEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OfflineClientSessionModel getOfflineClientSession(RealmModel realm, UserModel userModel, String clientSessionId) {
|
||||||
|
userModel = getUserById(userModel.getId(), realm);
|
||||||
|
UserEntity user = ((UserAdapter) userModel).getUserEntity();
|
||||||
|
|
||||||
|
if (user.getOfflineUserSessions() != null) {
|
||||||
|
for (OfflineUserSessionEntity userSession : user.getOfflineUserSessions()) {
|
||||||
|
for (OfflineClientSessionEntity clSession : userSession.getOfflineClientSessions()) {
|
||||||
|
if (clSession.getClientSessionId().equals(clientSessionId)) {
|
||||||
|
return toModel(clSession, userSession.getUserSessionId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private OfflineClientSessionModel toModel(OfflineClientSessionEntity cls, String userSessionId) {
|
||||||
|
OfflineClientSessionModel model = new OfflineClientSessionModel();
|
||||||
|
model.setClientSessionId(cls.getClientSessionId());
|
||||||
|
model.setClientId(cls.getClientId());
|
||||||
|
model.setData(cls.getData());
|
||||||
|
model.setUserSessionId(userSessionId);
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<OfflineClientSessionModel> getOfflineClientSessions(RealmModel realm, UserModel userModel) {
|
||||||
|
userModel = getUserById(userModel.getId(), realm);
|
||||||
|
UserEntity user = ((UserAdapter) userModel).getUserEntity();
|
||||||
|
|
||||||
|
List<OfflineClientSessionModel> result = new ArrayList<>();
|
||||||
|
|
||||||
|
if (user.getOfflineUserSessions() != null) {
|
||||||
|
for (OfflineUserSessionEntity userSession : user.getOfflineUserSessions()) {
|
||||||
|
for (OfflineClientSessionEntity clSession : userSession.getOfflineClientSessions()) {
|
||||||
|
result.add(toModel(clSession, userSession.getUserSessionId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeOfflineClientSession(RealmModel realm, UserModel userModel, String clientSessionId) {
|
||||||
|
userModel = getUserById(userModel.getId(), realm);
|
||||||
|
UserEntity user = ((UserAdapter) userModel).getUserEntity();
|
||||||
|
|
||||||
|
if (user.getOfflineUserSessions() != null) {
|
||||||
|
for (OfflineUserSessionEntity userSession : user.getOfflineUserSessions()) {
|
||||||
|
for (OfflineClientSessionEntity clSession : userSession.getOfflineClientSessions()) {
|
||||||
|
if (clSession.getClientSessionId().equals(clientSessionId)) {
|
||||||
|
userSession.getOfflineClientSessions().remove(clSession);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOfflineClientSessionsCount(RealmModel realm, ClientModel client) {
|
||||||
|
return getOfflineClientSessions(realm, client, -1, -1).size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<OfflineClientSessionModel> getOfflineClientSessions(RealmModel realm, ClientModel client, int firstResult, int maxResults) {
|
||||||
|
List<OfflineClientSessionModel> result = new LinkedList<>();
|
||||||
|
|
||||||
|
List<UserModel> users = new ArrayList<>(inMemoryModel.getUsers(realm.getId()));
|
||||||
|
users = sortedSubList(users, firstResult, maxResults);
|
||||||
|
|
||||||
|
for (UserModel userModel : users) {
|
||||||
|
UserEntity user = ((UserAdapter) userModel).getUserEntity();
|
||||||
|
for (OfflineUserSessionEntity userSession : user.getOfflineUserSessions()) {
|
||||||
|
for (OfflineClientSessionEntity clSession : userSession.getOfflineClientSessions()) {
|
||||||
|
if (clSession.getClientId().equals(client.getId())) {
|
||||||
|
result.add(toModel(clSession, userSession.getUserSessionId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -574,141 +574,6 @@ public class UserAdapter implements UserModel, Comparable {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addOfflineUserSession(OfflineUserSessionModel userSession) {
|
|
||||||
if (user.getOfflineUserSessions() == null) {
|
|
||||||
user.setOfflineUserSessions(new ArrayList<OfflineUserSessionEntity>());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getUserSessionEntityById(userSession.getUserSessionId()) != null) {
|
|
||||||
throw new ModelDuplicateException("User session already exists with id " + userSession.getUserSessionId() + " for user " + user.getUsername());
|
|
||||||
}
|
|
||||||
|
|
||||||
OfflineUserSessionEntity entity = new OfflineUserSessionEntity();
|
|
||||||
entity.setUserSessionId(userSession.getUserSessionId());
|
|
||||||
entity.setData(userSession.getData());
|
|
||||||
entity.setOfflineClientSessions(new ArrayList<OfflineClientSessionEntity>());
|
|
||||||
user.getOfflineUserSessions().add(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public OfflineUserSessionModel getOfflineUserSession(String userSessionId) {
|
|
||||||
OfflineUserSessionEntity entity = getUserSessionEntityById(userSessionId);
|
|
||||||
return entity==null ? null : toModel(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<OfflineUserSessionModel> getOfflineUserSessions() {
|
|
||||||
if (user.getOfflineUserSessions()==null) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
} else {
|
|
||||||
List<OfflineUserSessionModel> result = new ArrayList<>();
|
|
||||||
for (OfflineUserSessionEntity entity : user.getOfflineUserSessions()) {
|
|
||||||
result.add(toModel(entity));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private OfflineUserSessionModel toModel(OfflineUserSessionEntity entity) {
|
|
||||||
OfflineUserSessionModel model = new OfflineUserSessionModel();
|
|
||||||
model.setUserSessionId(entity.getUserSessionId());
|
|
||||||
model.setData(entity.getData());
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean removeOfflineUserSession(String userSessionId) {
|
|
||||||
OfflineUserSessionEntity entity = getUserSessionEntityById(userSessionId);
|
|
||||||
if (entity != null) {
|
|
||||||
user.getOfflineUserSessions().remove(entity);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private OfflineUserSessionEntity getUserSessionEntityById(String userSessionId) {
|
|
||||||
if (user.getOfflineUserSessions() != null) {
|
|
||||||
for (OfflineUserSessionEntity entity : user.getOfflineUserSessions()) {
|
|
||||||
if (entity.getUserSessionId().equals(userSessionId)) {
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addOfflineClientSession(OfflineClientSessionModel clientSession) {
|
|
||||||
OfflineUserSessionEntity userSessionEntity = getUserSessionEntityById(clientSession.getUserSessionId());
|
|
||||||
if (userSessionEntity == null) {
|
|
||||||
throw new ModelException("OfflineUserSession with ID " + clientSession.getUserSessionId() + " doesn't exist for user " + user.getUsername());
|
|
||||||
}
|
|
||||||
|
|
||||||
OfflineClientSessionEntity clEntity = new OfflineClientSessionEntity();
|
|
||||||
clEntity.setClientSessionId(clientSession.getClientSessionId());
|
|
||||||
clEntity.setClientId(clientSession.getClientId());
|
|
||||||
clEntity.setData(clientSession.getData());
|
|
||||||
|
|
||||||
userSessionEntity.getOfflineClientSessions().add(clEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public OfflineClientSessionModel getOfflineClientSession(String clientSessionId) {
|
|
||||||
if (user.getOfflineUserSessions() != null) {
|
|
||||||
for (OfflineUserSessionEntity userSession : user.getOfflineUserSessions()) {
|
|
||||||
for (OfflineClientSessionEntity clSession : userSession.getOfflineClientSessions()) {
|
|
||||||
if (clSession.getClientSessionId().equals(clientSessionId)) {
|
|
||||||
return toModel(clSession, userSession.getUserSessionId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private OfflineClientSessionModel toModel(OfflineClientSessionEntity cls, String userSessionId) {
|
|
||||||
OfflineClientSessionModel model = new OfflineClientSessionModel();
|
|
||||||
model.setClientSessionId(cls.getClientSessionId());
|
|
||||||
model.setClientId(cls.getClientId());
|
|
||||||
model.setData(cls.getData());
|
|
||||||
model.setUserSessionId(userSessionId);
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<OfflineClientSessionModel> getOfflineClientSessions() {
|
|
||||||
List<OfflineClientSessionModel> result = new ArrayList<>();
|
|
||||||
|
|
||||||
if (user.getOfflineUserSessions() != null) {
|
|
||||||
for (OfflineUserSessionEntity userSession : user.getOfflineUserSessions()) {
|
|
||||||
for (OfflineClientSessionEntity clSession : userSession.getOfflineClientSessions()) {
|
|
||||||
result.add(toModel(clSession, userSession.getUserSessionId()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean removeOfflineClientSession(String clientSessionId) {
|
|
||||||
if (user.getOfflineUserSessions() != null) {
|
|
||||||
for (OfflineUserSessionEntity userSession : user.getOfflineUserSessions()) {
|
|
||||||
for (OfflineClientSessionEntity clSession : userSession.getOfflineClientSessions()) {
|
|
||||||
if (clSession.getClientSessionId().equals(clientSessionId)) {
|
|
||||||
userSession.getOfflineClientSessions().remove(clSession);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
|
|
|
@ -104,13 +104,14 @@ public class DefaultCacheUserProvider implements CacheUserProvider {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isRegisteredForInvalidation(RealmModel realm, String userId) {
|
||||||
|
return realmInvalidations.contains(realm.getId()) || userInvalidations.containsKey(userId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserModel getUserById(String id, RealmModel realm) {
|
public UserModel getUserById(String id, RealmModel realm) {
|
||||||
if (!cache.isEnabled()) return getDelegate().getUserById(id, realm);
|
if (!cache.isEnabled()) return getDelegate().getUserById(id, realm);
|
||||||
if (realmInvalidations.contains(realm.getId())) {
|
if (isRegisteredForInvalidation(realm, id)) {
|
||||||
return getDelegate().getUserById(id, realm);
|
|
||||||
}
|
|
||||||
if (userInvalidations.containsKey(id)) {
|
|
||||||
return getDelegate().getUserById(id, realm);
|
return getDelegate().getUserById(id, realm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,7 +121,7 @@ public class DefaultCacheUserProvider implements CacheUserProvider {
|
||||||
if (model == null) return null;
|
if (model == null) return null;
|
||||||
if (managedUsers.containsKey(id)) return managedUsers.get(id);
|
if (managedUsers.containsKey(id)) return managedUsers.get(id);
|
||||||
if (userInvalidations.containsKey(id)) return model;
|
if (userInvalidations.containsKey(id)) return model;
|
||||||
cached = new CachedUser(realm, model);
|
cached = new CachedUser(this, realm, model);
|
||||||
cache.addCachedUser(realm.getId(), cached);
|
cache.addCachedUser(realm.getId(), cached);
|
||||||
} else if (managedUsers.containsKey(id)) {
|
} else if (managedUsers.containsKey(id)) {
|
||||||
return managedUsers.get(id);
|
return managedUsers.get(id);
|
||||||
|
@ -145,7 +146,7 @@ public class DefaultCacheUserProvider implements CacheUserProvider {
|
||||||
if (model == null) return null;
|
if (model == null) return null;
|
||||||
if (managedUsers.containsKey(model.getId())) return managedUsers.get(model.getId());
|
if (managedUsers.containsKey(model.getId())) return managedUsers.get(model.getId());
|
||||||
if (userInvalidations.containsKey(model.getId())) return model;
|
if (userInvalidations.containsKey(model.getId())) return model;
|
||||||
cached = new CachedUser(realm, model);
|
cached = new CachedUser(this, realm, model);
|
||||||
cache.addCachedUser(realm.getId(), cached);
|
cache.addCachedUser(realm.getId(), cached);
|
||||||
} else if (userInvalidations.containsKey(cached.getId())) {
|
} else if (userInvalidations.containsKey(cached.getId())) {
|
||||||
return getDelegate().getUserById(cached.getId(), realm);
|
return getDelegate().getUserById(cached.getId(), realm);
|
||||||
|
@ -172,7 +173,7 @@ public class DefaultCacheUserProvider implements CacheUserProvider {
|
||||||
UserModel model = getDelegate().getUserByEmail(email, realm);
|
UserModel model = getDelegate().getUserByEmail(email, realm);
|
||||||
if (model == null) return null;
|
if (model == null) return null;
|
||||||
if (userInvalidations.containsKey(model.getId())) return model;
|
if (userInvalidations.containsKey(model.getId())) return model;
|
||||||
cached = new CachedUser(realm, model);
|
cached = new CachedUser(this, realm, model);
|
||||||
cache.addCachedUser(realm.getId(), cached);
|
cache.addCachedUser(realm.getId(), cached);
|
||||||
} else if (userInvalidations.containsKey(cached.getId())) {
|
} else if (userInvalidations.containsKey(cached.getId())) {
|
||||||
return getDelegate().getUserByEmail(email, realm);
|
return getDelegate().getUserByEmail(email, realm);
|
||||||
|
@ -327,4 +328,94 @@ public class DefaultCacheUserProvider implements CacheUserProvider {
|
||||||
public void preRemove(ClientModel client, ProtocolMapperModel protocolMapper) {
|
public void preRemove(ClientModel client, ProtocolMapperModel protocolMapper) {
|
||||||
getDelegate().preRemove(client, protocolMapper);
|
getDelegate().preRemove(client, protocolMapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addOfflineUserSession(RealmModel realm, UserModel user, OfflineUserSessionModel offlineUserSession) {
|
||||||
|
registerUserInvalidation(realm, user.getId());
|
||||||
|
getDelegate().addOfflineUserSession(realm, user, offlineUserSession);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OfflineUserSessionModel getOfflineUserSession(RealmModel realm, UserModel user, String userSessionId) {
|
||||||
|
if (isRegisteredForInvalidation(realm, user.getId())) {
|
||||||
|
return getDelegate().getOfflineUserSession(realm, user, userSessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
CachedUser cachedUser = cache.getCachedUser(realm.getId(), user.getId());
|
||||||
|
if (cachedUser == null) {
|
||||||
|
return getDelegate().getOfflineUserSession(realm, user, userSessionId);
|
||||||
|
} else {
|
||||||
|
return cachedUser.getOfflineUserSessions().get(userSessionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<OfflineUserSessionModel> getOfflineUserSessions(RealmModel realm, UserModel user) {
|
||||||
|
if (isRegisteredForInvalidation(realm, user.getId())) {
|
||||||
|
return getDelegate().getOfflineUserSessions(realm, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
CachedUser cachedUser = cache.getCachedUser(realm.getId(), user.getId());
|
||||||
|
if (cachedUser == null) {
|
||||||
|
return getDelegate().getOfflineUserSessions(realm, user);
|
||||||
|
} else {
|
||||||
|
return cachedUser.getOfflineUserSessions().values();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeOfflineUserSession(RealmModel realm, UserModel user, String userSessionId) {
|
||||||
|
registerUserInvalidation(realm, user.getId());
|
||||||
|
return getDelegate().removeOfflineUserSession(realm, user, userSessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addOfflineClientSession(RealmModel realm, OfflineClientSessionModel offlineClientSession) {
|
||||||
|
registerUserInvalidation(realm, offlineClientSession.getUserId());
|
||||||
|
getDelegate().addOfflineClientSession(realm, offlineClientSession);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OfflineClientSessionModel getOfflineClientSession(RealmModel realm, UserModel user, String clientSessionId) {
|
||||||
|
if (isRegisteredForInvalidation(realm, user.getId())) {
|
||||||
|
return getDelegate().getOfflineClientSession(realm, user, clientSessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
CachedUser cachedUser = cache.getCachedUser(realm.getId(), user.getId());
|
||||||
|
if (cachedUser == null) {
|
||||||
|
return getDelegate().getOfflineClientSession(realm, user, clientSessionId);
|
||||||
|
} else {
|
||||||
|
return cachedUser.getOfflineClientSessions().get(clientSessionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<OfflineClientSessionModel> getOfflineClientSessions(RealmModel realm, UserModel user) {
|
||||||
|
if (isRegisteredForInvalidation(realm, user.getId())) {
|
||||||
|
return getDelegate().getOfflineClientSessions(realm, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
CachedUser cachedUser = cache.getCachedUser(realm.getId(), user.getId());
|
||||||
|
if (cachedUser == null) {
|
||||||
|
return getDelegate().getOfflineClientSessions(realm, user);
|
||||||
|
} else {
|
||||||
|
return cachedUser.getOfflineClientSessions().values();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeOfflineClientSession(RealmModel realm, UserModel user, String clientSessionId) {
|
||||||
|
registerUserInvalidation(realm, user.getId());
|
||||||
|
return getDelegate().removeOfflineClientSession(realm, user, clientSessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOfflineClientSessionsCount(RealmModel realm, ClientModel client) {
|
||||||
|
return getDelegate().getOfflineClientSessionsCount(realm, client);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<OfflineClientSessionModel> getOfflineClientSessions(RealmModel realm, ClientModel client, int firstResult, int maxResults) {
|
||||||
|
return getDelegate().getOfflineClientSessions(realm, client, firstResult, maxResults);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -348,52 +348,4 @@ public class UserAdapter implements UserModel {
|
||||||
getDelegateForUpdate();
|
getDelegateForUpdate();
|
||||||
return updated.revokeConsentForClient(clientId);
|
return updated.revokeConsentForClient(clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addOfflineUserSession(OfflineUserSessionModel userSession) {
|
|
||||||
getDelegateForUpdate();
|
|
||||||
updated.addOfflineUserSession(userSession);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public OfflineUserSessionModel getOfflineUserSession(String userSessionId) {
|
|
||||||
if (updated != null) return updated.getOfflineUserSession(userSessionId);
|
|
||||||
return cached.getOfflineUserSessions().get(userSessionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<OfflineUserSessionModel> getOfflineUserSessions() {
|
|
||||||
if (updated != null) return updated.getOfflineUserSessions();
|
|
||||||
return cached.getOfflineUserSessions().values();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean removeOfflineUserSession(String userSessionId) {
|
|
||||||
getDelegateForUpdate();
|
|
||||||
return updated.removeOfflineUserSession(userSessionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addOfflineClientSession(OfflineClientSessionModel clientSession) {
|
|
||||||
getDelegateForUpdate();
|
|
||||||
updated.addOfflineClientSession(clientSession);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public OfflineClientSessionModel getOfflineClientSession(String clientSessionId) {
|
|
||||||
if (updated != null) return updated.getOfflineClientSession(clientSessionId);
|
|
||||||
return cached.getOfflineClientSessions().get(clientSessionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<OfflineClientSessionModel> getOfflineClientSessions() {
|
|
||||||
if (updated != null) return updated.getOfflineClientSessions();
|
|
||||||
return cached.getOfflineClientSessions().values();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean removeOfflineClientSession(String clientSessionId) {
|
|
||||||
getDelegateForUpdate();
|
|
||||||
return updated.removeOfflineClientSession(clientSessionId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserCredentialValueModel;
|
import org.keycloak.models.UserCredentialValueModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.cache.CacheUserProvider;
|
||||||
import org.keycloak.util.MultivaluedHashMap;
|
import org.keycloak.util.MultivaluedHashMap;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
@ -40,7 +41,7 @@ public class CachedUser implements Serializable {
|
||||||
private Map<String, OfflineUserSessionModel> offlineUserSessions = new HashMap<>();
|
private Map<String, OfflineUserSessionModel> offlineUserSessions = new HashMap<>();
|
||||||
private Map<String, OfflineClientSessionModel> offlineClientSessions = new HashMap<>();
|
private Map<String, OfflineClientSessionModel> offlineClientSessions = new HashMap<>();
|
||||||
|
|
||||||
public CachedUser(RealmModel realm, UserModel user) {
|
public CachedUser(CacheUserProvider cacheUserProvider, RealmModel realm, UserModel user) {
|
||||||
this.id = user.getId();
|
this.id = user.getId();
|
||||||
this.realm = realm.getId();
|
this.realm = realm.getId();
|
||||||
this.username = user.getUsername();
|
this.username = user.getUsername();
|
||||||
|
@ -59,10 +60,10 @@ public class CachedUser implements Serializable {
|
||||||
for (RoleModel role : user.getRoleMappings()) {
|
for (RoleModel role : user.getRoleMappings()) {
|
||||||
roleMappings.add(role.getId());
|
roleMappings.add(role.getId());
|
||||||
}
|
}
|
||||||
for (OfflineUserSessionModel offlineSession : user.getOfflineUserSessions()) {
|
for (OfflineUserSessionModel offlineSession : cacheUserProvider.getDelegate().getOfflineUserSessions(realm, user)) {
|
||||||
offlineUserSessions.put(offlineSession.getUserSessionId(), offlineSession);
|
offlineUserSessions.put(offlineSession.getUserSessionId(), offlineSession);
|
||||||
}
|
}
|
||||||
for (OfflineClientSessionModel offlineSession : user.getOfflineClientSessions()) {
|
for (OfflineClientSessionModel offlineSession : cacheUserProvider.getDelegate().getOfflineClientSessions(realm, user)) {
|
||||||
offlineClientSessions.put(offlineSession.getClientSessionId(), offlineSession);
|
offlineClientSessions.put(offlineSession.getClientSessionId(), offlineSession);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.CredentialValidationOutput;
|
import org.keycloak.models.CredentialValidationOutput;
|
||||||
import org.keycloak.models.FederatedIdentityModel;
|
import org.keycloak.models.FederatedIdentityModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.OfflineClientSessionModel;
|
||||||
|
import org.keycloak.models.OfflineUserSessionModel;
|
||||||
import org.keycloak.models.ProtocolMapperModel;
|
import org.keycloak.models.ProtocolMapperModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RequiredActionProviderModel;
|
import org.keycloak.models.RequiredActionProviderModel;
|
||||||
|
@ -13,6 +15,8 @@ import org.keycloak.models.UserFederationProviderModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserProvider;
|
import org.keycloak.models.UserProvider;
|
||||||
import org.keycloak.models.jpa.entities.FederatedIdentityEntity;
|
import org.keycloak.models.jpa.entities.FederatedIdentityEntity;
|
||||||
|
import org.keycloak.models.jpa.entities.OfflineClientSessionEntity;
|
||||||
|
import org.keycloak.models.jpa.entities.OfflineUserSessionEntity;
|
||||||
import org.keycloak.models.jpa.entities.UserAttributeEntity;
|
import org.keycloak.models.jpa.entities.UserAttributeEntity;
|
||||||
import org.keycloak.models.jpa.entities.UserEntity;
|
import org.keycloak.models.jpa.entities.UserEntity;
|
||||||
import org.keycloak.models.utils.CredentialValidation;
|
import org.keycloak.models.utils.CredentialValidation;
|
||||||
|
@ -22,8 +26,10 @@ import javax.persistence.EntityManager;
|
||||||
import javax.persistence.Query;
|
import javax.persistence.Query;
|
||||||
import javax.persistence.TypedQuery;
|
import javax.persistence.TypedQuery;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -473,4 +479,167 @@ public class JpaUserProvider implements UserProvider {
|
||||||
// Not supported yet
|
// Not supported yet
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addOfflineUserSession(RealmModel realm, UserModel user, OfflineUserSessionModel offlineUserSession) {
|
||||||
|
UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
|
||||||
|
|
||||||
|
OfflineUserSessionEntity entity = new OfflineUserSessionEntity();
|
||||||
|
entity.setUser(userEntity);
|
||||||
|
entity.setUserSessionId(offlineUserSession.getUserSessionId());
|
||||||
|
entity.setData(offlineUserSession.getData());
|
||||||
|
em.persist(entity);
|
||||||
|
userEntity.getOfflineUserSessions().add(entity);
|
||||||
|
em.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OfflineUserSessionModel getOfflineUserSession(RealmModel realm, UserModel user, String userSessionId) {
|
||||||
|
UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
|
||||||
|
|
||||||
|
for (OfflineUserSessionEntity entity : userEntity.getOfflineUserSessions()) {
|
||||||
|
if (entity.getUserSessionId().equals(userSessionId)) {
|
||||||
|
return toModel(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private OfflineUserSessionModel toModel(OfflineUserSessionEntity entity) {
|
||||||
|
OfflineUserSessionModel model = new OfflineUserSessionModel();
|
||||||
|
model.setUserSessionId(entity.getUserSessionId());
|
||||||
|
model.setData(entity.getData());
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<OfflineUserSessionModel> getOfflineUserSessions(RealmModel realm, UserModel user) {
|
||||||
|
UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
|
||||||
|
|
||||||
|
List<OfflineUserSessionModel> result = new LinkedList<>();
|
||||||
|
for (OfflineUserSessionEntity entity : userEntity.getOfflineUserSessions()) {
|
||||||
|
result.add(toModel(entity));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeOfflineUserSession(RealmModel realm, UserModel user, String userSessionId) {
|
||||||
|
UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
|
||||||
|
|
||||||
|
OfflineUserSessionEntity found = null;
|
||||||
|
for (OfflineUserSessionEntity session : userEntity.getOfflineUserSessions()) {
|
||||||
|
if (session.getUserSessionId().equals(userSessionId)) {
|
||||||
|
found = session;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found == null) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
userEntity.getOfflineUserSessions().remove(found);
|
||||||
|
em.remove(found);
|
||||||
|
em.flush();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addOfflineClientSession(RealmModel realm, OfflineClientSessionModel offlineClientSession) {
|
||||||
|
UserEntity userEntity = em.getReference(UserEntity.class, offlineClientSession.getUserId());
|
||||||
|
|
||||||
|
OfflineClientSessionEntity entity = new OfflineClientSessionEntity();
|
||||||
|
entity.setUser(userEntity);
|
||||||
|
entity.setClientSessionId(offlineClientSession.getClientSessionId());
|
||||||
|
entity.setUserSessionId(offlineClientSession.getUserSessionId());
|
||||||
|
entity.setClientId(offlineClientSession.getClientId());
|
||||||
|
entity.setData(offlineClientSession.getData());
|
||||||
|
em.persist(entity);
|
||||||
|
userEntity.getOfflineClientSessions().add(entity);
|
||||||
|
em.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OfflineClientSessionModel getOfflineClientSession(RealmModel realm, UserModel user, String clientSessionId) {
|
||||||
|
UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
|
||||||
|
|
||||||
|
for (OfflineClientSessionEntity entity : userEntity.getOfflineClientSessions()) {
|
||||||
|
if (entity.getClientSessionId().equals(clientSessionId)) {
|
||||||
|
return toModel(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private OfflineClientSessionModel toModel(OfflineClientSessionEntity entity) {
|
||||||
|
OfflineClientSessionModel model = new OfflineClientSessionModel();
|
||||||
|
model.setClientSessionId(entity.getClientSessionId());
|
||||||
|
model.setClientId(entity.getClientId());
|
||||||
|
model.setUserId(entity.getUser().getId());
|
||||||
|
model.setUserSessionId(entity.getUserSessionId());
|
||||||
|
model.setData(entity.getData());
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<OfflineClientSessionModel> getOfflineClientSessions(RealmModel realm, UserModel user) {
|
||||||
|
UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
|
||||||
|
|
||||||
|
List<OfflineClientSessionModel> result = new LinkedList<>();
|
||||||
|
for (OfflineClientSessionEntity entity : userEntity.getOfflineClientSessions()) {
|
||||||
|
result.add(toModel(entity));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeOfflineClientSession(RealmModel realm, UserModel user, String clientSessionId) {
|
||||||
|
UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
|
||||||
|
|
||||||
|
OfflineClientSessionEntity found = null;
|
||||||
|
for (OfflineClientSessionEntity session : userEntity.getOfflineClientSessions()) {
|
||||||
|
if (session.getClientSessionId().equals(clientSessionId)) {
|
||||||
|
found = session;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found == null) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
userEntity.getOfflineClientSessions().remove(found);
|
||||||
|
em.remove(found);
|
||||||
|
em.flush();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOfflineClientSessionsCount(RealmModel realm, ClientModel client) {
|
||||||
|
Query query = em.createNamedQuery("findOfflineClientSessionsCountByClient");
|
||||||
|
query.setParameter("clientId", client.getId());
|
||||||
|
Number n = (Number) query.getSingleResult();
|
||||||
|
return n.intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<OfflineClientSessionModel> getOfflineClientSessions(RealmModel realm, ClientModel client, int firstResult, int maxResults) {
|
||||||
|
TypedQuery<OfflineClientSessionEntity> query = em.createNamedQuery("findOfflineClientSessionsByClient", OfflineClientSessionEntity.class);
|
||||||
|
query.setParameter("clientId", client.getId());
|
||||||
|
|
||||||
|
if (firstResult != -1) {
|
||||||
|
query.setFirstResult(firstResult);
|
||||||
|
}
|
||||||
|
if (maxResults != -1) {
|
||||||
|
query.setMaxResults(maxResults);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<OfflineClientSessionEntity> results = query.getResultList();
|
||||||
|
Set<OfflineClientSessionModel> set = new HashSet<>();
|
||||||
|
for (OfflineClientSessionEntity entity : results) {
|
||||||
|
set.add(toModel(entity));
|
||||||
|
}
|
||||||
|
return set;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -755,124 +755,6 @@ public class UserAdapter implements UserModel {
|
||||||
em.flush();
|
em.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addOfflineUserSession(OfflineUserSessionModel offlineSession) {
|
|
||||||
OfflineUserSessionEntity entity = new OfflineUserSessionEntity();
|
|
||||||
entity.setUser(user);
|
|
||||||
entity.setUserSessionId(offlineSession.getUserSessionId());
|
|
||||||
entity.setData(offlineSession.getData());
|
|
||||||
em.persist(entity);
|
|
||||||
user.getOfflineUserSessions().add(entity);
|
|
||||||
em.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public OfflineUserSessionModel getOfflineUserSession(String userSessionId) {
|
|
||||||
for (OfflineUserSessionEntity entity : user.getOfflineUserSessions()) {
|
|
||||||
if (entity.getUserSessionId().equals(userSessionId)) {
|
|
||||||
return toModel(entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private OfflineUserSessionModel toModel(OfflineUserSessionEntity entity) {
|
|
||||||
OfflineUserSessionModel model = new OfflineUserSessionModel();
|
|
||||||
model.setUserSessionId(entity.getUserSessionId());
|
|
||||||
model.setData(entity.getData());
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<OfflineUserSessionModel> getOfflineUserSessions() {
|
|
||||||
List<OfflineUserSessionModel> result = new LinkedList<>();
|
|
||||||
for (OfflineUserSessionEntity entity : user.getOfflineUserSessions()) {
|
|
||||||
result.add(toModel(entity));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean removeOfflineUserSession(String userSessionId) {
|
|
||||||
OfflineUserSessionEntity found = null;
|
|
||||||
for (OfflineUserSessionEntity session : user.getOfflineUserSessions()) {
|
|
||||||
if (session.getUserSessionId().equals(userSessionId)) {
|
|
||||||
found = session;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (found == null) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
user.getOfflineUserSessions().remove(found);
|
|
||||||
em.remove(found);
|
|
||||||
em.flush();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addOfflineClientSession(OfflineClientSessionModel clientSession) {
|
|
||||||
OfflineClientSessionEntity entity = new OfflineClientSessionEntity();
|
|
||||||
entity.setUser(user);
|
|
||||||
entity.setClientSessionId(clientSession.getClientSessionId());
|
|
||||||
entity.setUserSessionId(clientSession.getUserSessionId());
|
|
||||||
entity.setClientId(clientSession.getClientId());
|
|
||||||
entity.setData(clientSession.getData());
|
|
||||||
em.persist(entity);
|
|
||||||
user.getOfflineClientSessions().add(entity);
|
|
||||||
em.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public OfflineClientSessionModel getOfflineClientSession(String clientSessionId) {
|
|
||||||
for (OfflineClientSessionEntity entity : user.getOfflineClientSessions()) {
|
|
||||||
if (entity.getClientSessionId().equals(clientSessionId)) {
|
|
||||||
return toModel(entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private OfflineClientSessionModel toModel(OfflineClientSessionEntity entity) {
|
|
||||||
OfflineClientSessionModel model = new OfflineClientSessionModel();
|
|
||||||
model.setClientSessionId(entity.getClientSessionId());
|
|
||||||
model.setClientId(entity.getClientId());
|
|
||||||
model.setUserSessionId(entity.getUserSessionId());
|
|
||||||
model.setData(entity.getData());
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<OfflineClientSessionModel> getOfflineClientSessions() {
|
|
||||||
List<OfflineClientSessionModel> result = new LinkedList<>();
|
|
||||||
for (OfflineClientSessionEntity entity : user.getOfflineClientSessions()) {
|
|
||||||
result.add(toModel(entity));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean removeOfflineClientSession(String clientSessionId) {
|
|
||||||
OfflineClientSessionEntity found = null;
|
|
||||||
for (OfflineClientSessionEntity session : user.getOfflineClientSessions()) {
|
|
||||||
if (session.getClientSessionId().equals(clientSessionId)) {
|
|
||||||
found = session;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (found == null) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
user.getOfflineClientSessions().remove(found);
|
|
||||||
em.remove(found);
|
|
||||||
em.flush();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
|
|
|
@ -16,7 +16,9 @@ import javax.persistence.Table;
|
||||||
@NamedQueries({
|
@NamedQueries({
|
||||||
@NamedQuery(name="deleteOfflineClientSessionsByRealm", query="delete from OfflineClientSessionEntity sess where sess.user IN (select u from UserEntity u where u.realmId=:realmId)"),
|
@NamedQuery(name="deleteOfflineClientSessionsByRealm", query="delete from OfflineClientSessionEntity sess where sess.user IN (select u from UserEntity u where u.realmId=:realmId)"),
|
||||||
@NamedQuery(name="deleteOfflineClientSessionsByRealmAndLink", query="delete from OfflineClientSessionEntity sess where sess.user IN (select u from UserEntity u where u.realmId=:realmId and u.federationLink=:link)"),
|
@NamedQuery(name="deleteOfflineClientSessionsByRealmAndLink", query="delete from OfflineClientSessionEntity sess where sess.user IN (select u from UserEntity u where u.realmId=:realmId and u.federationLink=:link)"),
|
||||||
@NamedQuery(name="deleteOfflineClientSessionsByClient", query="delete from OfflineClientSessionEntity sess where sess.clientId=:clientId")
|
@NamedQuery(name="deleteOfflineClientSessionsByClient", query="delete from OfflineClientSessionEntity sess where sess.clientId=:clientId"),
|
||||||
|
@NamedQuery(name="findOfflineClientSessionsCountByClient", query="select count(sess) from OfflineClientSessionEntity sess where sess.clientId=:clientId"),
|
||||||
|
@NamedQuery(name="findOfflineClientSessionsByClient", query="select sess from OfflineClientSessionEntity sess where sess.clientId=:clientId order by sess.user.username")
|
||||||
})
|
})
|
||||||
@Table(name="OFFLINE_CLIENT_SESSION")
|
@Table(name="OFFLINE_CLIENT_SESSION")
|
||||||
@Entity
|
@Entity
|
||||||
|
|
|
@ -10,6 +10,10 @@ import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.CredentialValidationOutput;
|
import org.keycloak.models.CredentialValidationOutput;
|
||||||
import org.keycloak.models.FederatedIdentityModel;
|
import org.keycloak.models.FederatedIdentityModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.ModelDuplicateException;
|
||||||
|
import org.keycloak.models.ModelException;
|
||||||
|
import org.keycloak.models.OfflineClientSessionModel;
|
||||||
|
import org.keycloak.models.OfflineUserSessionModel;
|
||||||
import org.keycloak.models.ProtocolMapperModel;
|
import org.keycloak.models.ProtocolMapperModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RequiredActionProviderModel;
|
import org.keycloak.models.RequiredActionProviderModel;
|
||||||
|
@ -26,8 +30,10 @@ import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity;
|
||||||
import org.keycloak.models.utils.CredentialValidation;
|
import org.keycloak.models.utils.CredentialValidation;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -51,7 +57,7 @@ public class MongoUserProvider implements UserProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserModel getUserById(String id, RealmModel realm) {
|
public UserAdapter getUserById(String id, RealmModel realm) {
|
||||||
MongoUserEntity user = getMongoStore().loadEntity(MongoUserEntity.class, id, invocationContext);
|
MongoUserEntity user = getMongoStore().loadEntity(MongoUserEntity.class, id, invocationContext);
|
||||||
|
|
||||||
// Check that it's user from this realm
|
// Check that it's user from this realm
|
||||||
|
@ -244,8 +250,8 @@ public class MongoUserProvider implements UserProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<FederatedIdentityModel> getFederatedIdentities(UserModel userModel, RealmModel realm) {
|
public Set<FederatedIdentityModel> getFederatedIdentities(UserModel userModel, RealmModel realm) {
|
||||||
UserModel user = getUserById(userModel.getId(), realm);
|
UserAdapter user = getUserById(userModel.getId(), realm);
|
||||||
MongoUserEntity userEntity = ((UserAdapter) user).getUser();
|
MongoUserEntity userEntity = user.getUser();
|
||||||
List<FederatedIdentityEntity> linkEntities = userEntity.getFederatedIdentities();
|
List<FederatedIdentityEntity> linkEntities = userEntity.getFederatedIdentities();
|
||||||
|
|
||||||
if (linkEntities == null) {
|
if (linkEntities == null) {
|
||||||
|
@ -263,8 +269,8 @@ public class MongoUserProvider implements UserProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FederatedIdentityModel getFederatedIdentity(UserModel user, String socialProvider, RealmModel realm) {
|
public FederatedIdentityModel getFederatedIdentity(UserModel user, String socialProvider, RealmModel realm) {
|
||||||
user = getUserById(user.getId(), realm);
|
UserAdapter mongoUser = getUserById(user.getId(), realm);
|
||||||
MongoUserEntity userEntity = ((UserAdapter) user).getUser();
|
MongoUserEntity userEntity = mongoUser.getUser();
|
||||||
FederatedIdentityEntity federatedIdentityEntity = findFederatedIdentityLink(userEntity, socialProvider);
|
FederatedIdentityEntity federatedIdentityEntity = findFederatedIdentityLink(userEntity, socialProvider);
|
||||||
|
|
||||||
return federatedIdentityEntity != null ? new FederatedIdentityModel(federatedIdentityEntity.getIdentityProvider(), federatedIdentityEntity.getUserId(),
|
return federatedIdentityEntity != null ? new FederatedIdentityModel(federatedIdentityEntity.getIdentityProvider(), federatedIdentityEntity.getUserId(),
|
||||||
|
@ -320,8 +326,8 @@ public class MongoUserProvider implements UserProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addFederatedIdentity(RealmModel realm, UserModel user, FederatedIdentityModel identity) {
|
public void addFederatedIdentity(RealmModel realm, UserModel user, FederatedIdentityModel identity) {
|
||||||
user = getUserById(user.getId(), realm);
|
UserAdapter mongoUser = getUserById(user.getId(), realm);
|
||||||
MongoUserEntity userEntity = ((UserAdapter) user).getUser();
|
MongoUserEntity userEntity = mongoUser.getUser();
|
||||||
FederatedIdentityEntity federatedIdentityEntity = new FederatedIdentityEntity();
|
FederatedIdentityEntity federatedIdentityEntity = new FederatedIdentityEntity();
|
||||||
federatedIdentityEntity.setIdentityProvider(identity.getIdentityProvider());
|
federatedIdentityEntity.setIdentityProvider(identity.getIdentityProvider());
|
||||||
federatedIdentityEntity.setUserId(identity.getUserId());
|
federatedIdentityEntity.setUserId(identity.getUserId());
|
||||||
|
@ -333,8 +339,8 @@ public class MongoUserProvider implements UserProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateFederatedIdentity(RealmModel realm, UserModel federatedUser, FederatedIdentityModel federatedIdentityModel) {
|
public void updateFederatedIdentity(RealmModel realm, UserModel federatedUser, FederatedIdentityModel federatedIdentityModel) {
|
||||||
federatedUser = getUserById(federatedUser.getId(), realm);
|
UserAdapter mongoUser = getUserById(federatedUser.getId(), realm);
|
||||||
MongoUserEntity userEntity = ((UserAdapter) federatedUser).getUser();
|
MongoUserEntity userEntity = mongoUser.getUser();
|
||||||
FederatedIdentityEntity federatedIdentityEntity = findFederatedIdentityLink(userEntity, federatedIdentityModel.getIdentityProvider());
|
FederatedIdentityEntity federatedIdentityEntity = findFederatedIdentityLink(userEntity, federatedIdentityModel.getIdentityProvider());
|
||||||
|
|
||||||
federatedIdentityEntity.setToken(federatedIdentityModel.getToken());
|
federatedIdentityEntity.setToken(federatedIdentityModel.getToken());
|
||||||
|
@ -342,8 +348,8 @@ public class MongoUserProvider implements UserProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean removeFederatedIdentity(RealmModel realm, UserModel userModel, String socialProvider) {
|
public boolean removeFederatedIdentity(RealmModel realm, UserModel userModel, String socialProvider) {
|
||||||
UserModel user = getUserById(userModel.getId(), realm);
|
UserAdapter user = getUserById(userModel.getId(), realm);
|
||||||
MongoUserEntity userEntity = ((UserAdapter) user).getUser();
|
MongoUserEntity userEntity = user.getUser();
|
||||||
FederatedIdentityEntity federatedIdentityEntity = findFederatedIdentityLink(userEntity, socialProvider);
|
FederatedIdentityEntity federatedIdentityEntity = findFederatedIdentityLink(userEntity, socialProvider);
|
||||||
if (federatedIdentityEntity == null) {
|
if (federatedIdentityEntity == null) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -476,4 +482,205 @@ public class MongoUserProvider implements UserProvider {
|
||||||
// Not supported yet
|
// Not supported yet
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addOfflineUserSession(RealmModel realm, UserModel userModel, OfflineUserSessionModel userSession) {
|
||||||
|
MongoUserEntity user = getUserById(userModel.getId(), realm).getUser();
|
||||||
|
|
||||||
|
if (user.getOfflineUserSessions() == null) {
|
||||||
|
user.setOfflineUserSessions(new ArrayList<OfflineUserSessionEntity>());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getUserSessionEntityById(user, userSession.getUserSessionId()) != null) {
|
||||||
|
throw new ModelDuplicateException("User session already exists with id " + userSession.getUserSessionId() + " for user " + user.getUsername());
|
||||||
|
}
|
||||||
|
|
||||||
|
OfflineUserSessionEntity entity = new OfflineUserSessionEntity();
|
||||||
|
entity.setUserSessionId(userSession.getUserSessionId());
|
||||||
|
entity.setData(userSession.getData());
|
||||||
|
entity.setOfflineClientSessions(new ArrayList<OfflineClientSessionEntity>());
|
||||||
|
user.getOfflineUserSessions().add(entity);
|
||||||
|
|
||||||
|
getMongoStore().updateEntity(user, invocationContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OfflineUserSessionModel toModel(OfflineUserSessionEntity entity) {
|
||||||
|
OfflineUserSessionModel model = new OfflineUserSessionModel();
|
||||||
|
model.setUserSessionId(entity.getUserSessionId());
|
||||||
|
model.setData(entity.getData());
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
private OfflineUserSessionEntity getUserSessionEntityById(MongoUserEntity user, String userSessionId) {
|
||||||
|
if (user.getOfflineUserSessions() != null) {
|
||||||
|
for (OfflineUserSessionEntity entity : user.getOfflineUserSessions()) {
|
||||||
|
if (entity.getUserSessionId().equals(userSessionId)) {
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OfflineUserSessionModel getOfflineUserSession(RealmModel realm, UserModel userModel, String userSessionId) {
|
||||||
|
MongoUserEntity user = getUserById(userModel.getId(), realm).getUser();
|
||||||
|
|
||||||
|
OfflineUserSessionEntity entity = getUserSessionEntityById(user, userSessionId);
|
||||||
|
return entity==null ? null : toModel(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<OfflineUserSessionModel> getOfflineUserSessions(RealmModel realm, UserModel userModel) {
|
||||||
|
MongoUserEntity user = getUserById(userModel.getId(), realm).getUser();
|
||||||
|
|
||||||
|
if (user.getOfflineUserSessions()==null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
} else {
|
||||||
|
List<OfflineUserSessionModel> result = new ArrayList<>();
|
||||||
|
for (OfflineUserSessionEntity entity : user.getOfflineUserSessions()) {
|
||||||
|
result.add(toModel(entity));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeOfflineUserSession(RealmModel realm, UserModel userModel, String userSessionId) {
|
||||||
|
MongoUserEntity user = getUserById(userModel.getId(), realm).getUser();
|
||||||
|
|
||||||
|
OfflineUserSessionEntity entity = getUserSessionEntityById(user, userSessionId);
|
||||||
|
if (entity != null) {
|
||||||
|
user.getOfflineUserSessions().remove(entity);
|
||||||
|
getMongoStore().updateEntity(user, invocationContext);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addOfflineClientSession(RealmModel realm, OfflineClientSessionModel clientSession) {
|
||||||
|
MongoUserEntity user = getUserById(clientSession.getUserId(), realm).getUser();
|
||||||
|
|
||||||
|
OfflineUserSessionEntity userSessionEntity = getUserSessionEntityById(user, clientSession.getUserSessionId());
|
||||||
|
if (userSessionEntity == null) {
|
||||||
|
throw new ModelException("OfflineUserSession with ID " + clientSession.getUserSessionId() + " doesn't exist for user " + user.getUsername());
|
||||||
|
}
|
||||||
|
|
||||||
|
OfflineClientSessionEntity clEntity = new OfflineClientSessionEntity();
|
||||||
|
clEntity.setClientSessionId(clientSession.getClientSessionId());
|
||||||
|
clEntity.setClientId(clientSession.getClientId());
|
||||||
|
clEntity.setData(clientSession.getData());
|
||||||
|
|
||||||
|
userSessionEntity.getOfflineClientSessions().add(clEntity);
|
||||||
|
getMongoStore().updateEntity(user, invocationContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OfflineClientSessionModel getOfflineClientSession(RealmModel realm, UserModel userModel, String clientSessionId) {
|
||||||
|
MongoUserEntity user = getUserById(userModel.getId(), realm).getUser();
|
||||||
|
|
||||||
|
if (user.getOfflineUserSessions() != null) {
|
||||||
|
for (OfflineUserSessionEntity userSession : user.getOfflineUserSessions()) {
|
||||||
|
for (OfflineClientSessionEntity clSession : userSession.getOfflineClientSessions()) {
|
||||||
|
if (clSession.getClientSessionId().equals(clientSessionId)) {
|
||||||
|
return toModel(clSession, user.getId(), userSession.getUserSessionId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private OfflineClientSessionModel toModel(OfflineClientSessionEntity cls, String userId, String userSessionId) {
|
||||||
|
OfflineClientSessionModel model = new OfflineClientSessionModel();
|
||||||
|
model.setClientSessionId(cls.getClientSessionId());
|
||||||
|
model.setClientId(cls.getClientId());
|
||||||
|
model.setUserId(userId);
|
||||||
|
model.setData(cls.getData());
|
||||||
|
model.setUserSessionId(userSessionId);
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<OfflineClientSessionModel> getOfflineClientSessions(RealmModel realm, UserModel userModel) {
|
||||||
|
MongoUserEntity user = getUserById(userModel.getId(), realm).getUser();
|
||||||
|
|
||||||
|
List<OfflineClientSessionModel> result = new ArrayList<>();
|
||||||
|
|
||||||
|
if (user.getOfflineUserSessions() != null) {
|
||||||
|
for (OfflineUserSessionEntity userSession : user.getOfflineUserSessions()) {
|
||||||
|
for (OfflineClientSessionEntity clSession : userSession.getOfflineClientSessions()) {
|
||||||
|
result.add(toModel(clSession, user.getId(), userSession.getUserSessionId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeOfflineClientSession(RealmModel realm, UserModel userModel, String clientSessionId) {
|
||||||
|
MongoUserEntity user = getUserById(userModel.getId(), realm).getUser();
|
||||||
|
boolean updated = false;
|
||||||
|
|
||||||
|
if (user.getOfflineUserSessions() != null) {
|
||||||
|
for (OfflineUserSessionEntity userSession : user.getOfflineUserSessions()) {
|
||||||
|
for (OfflineClientSessionEntity clSession : userSession.getOfflineClientSessions()) {
|
||||||
|
if (clSession.getClientSessionId().equals(clientSessionId)) {
|
||||||
|
userSession.getOfflineClientSessions().remove(clSession);
|
||||||
|
updated = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updated && userSession.getOfflineClientSessions().isEmpty()) {
|
||||||
|
user.getOfflineUserSessions().remove(userSession);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updated) {
|
||||||
|
getMongoStore().updateEntity(user, invocationContext);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOfflineClientSessionsCount(RealmModel realm, ClientModel client) {
|
||||||
|
DBObject query = new QueryBuilder()
|
||||||
|
.and("realmId").is(realm.getId())
|
||||||
|
.and("offlineUserSessions.offlineClientSessions.clientId").is(client.getId())
|
||||||
|
.get();
|
||||||
|
return getMongoStore().countEntities(MongoUserEntity.class, query, invocationContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<OfflineClientSessionModel> getOfflineClientSessions(RealmModel realm, ClientModel client, int firstResult, int maxResults) {
|
||||||
|
DBObject query = new QueryBuilder()
|
||||||
|
.and("realmId").is(realm.getId())
|
||||||
|
.and("offlineUserSessions.offlineClientSessions.clientId").is(client.getId())
|
||||||
|
.get();
|
||||||
|
DBObject sort = new BasicDBObject("username", 1);
|
||||||
|
List<MongoUserEntity> users = getMongoStore().loadEntities(MongoUserEntity.class, query, sort, firstResult, maxResults, invocationContext);
|
||||||
|
|
||||||
|
List<OfflineClientSessionModel> result = new LinkedList<>();
|
||||||
|
for (MongoUserEntity user : users) {
|
||||||
|
for (OfflineUserSessionEntity userSession : user.getOfflineUserSessions()) {
|
||||||
|
for (OfflineClientSessionEntity clSession : userSession.getOfflineClientSessions()) {
|
||||||
|
if (clSession.getClientId().equals(client.getId())) {
|
||||||
|
OfflineClientSessionModel model = toModel(clSession, user.getId(), userSession.getUserSessionId());
|
||||||
|
result.add(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -632,145 +632,6 @@ public class UserAdapter extends AbstractMongoAdapter<MongoUserEntity> implement
|
||||||
return getMongoStore().removeEntity(entity, invocationContext);
|
return getMongoStore().removeEntity(entity, invocationContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addOfflineUserSession(OfflineUserSessionModel userSession) {
|
|
||||||
if (user.getOfflineUserSessions() == null) {
|
|
||||||
user.setOfflineUserSessions(new ArrayList<OfflineUserSessionEntity>());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getUserSessionEntityById(userSession.getUserSessionId()) != null) {
|
|
||||||
throw new ModelDuplicateException("User session already exists with id " + userSession.getUserSessionId() + " for user " + getMongoEntity().getUsername());
|
|
||||||
}
|
|
||||||
|
|
||||||
OfflineUserSessionEntity entity = new OfflineUserSessionEntity();
|
|
||||||
entity.setUserSessionId(userSession.getUserSessionId());
|
|
||||||
entity.setData(userSession.getData());
|
|
||||||
entity.setOfflineClientSessions(new ArrayList<OfflineClientSessionEntity>());
|
|
||||||
user.getOfflineUserSessions().add(entity);
|
|
||||||
updateUser();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public OfflineUserSessionModel getOfflineUserSession(String userSessionId) {
|
|
||||||
OfflineUserSessionEntity entity = getUserSessionEntityById(userSessionId);
|
|
||||||
return entity==null ? null : toModel(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<OfflineUserSessionModel> getOfflineUserSessions() {
|
|
||||||
if (user.getOfflineUserSessions()==null) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
} else {
|
|
||||||
List<OfflineUserSessionModel> result = new ArrayList<>();
|
|
||||||
for (OfflineUserSessionEntity entity : user.getOfflineUserSessions()) {
|
|
||||||
result.add(toModel(entity));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private OfflineUserSessionModel toModel(OfflineUserSessionEntity entity) {
|
|
||||||
OfflineUserSessionModel model = new OfflineUserSessionModel();
|
|
||||||
model.setUserSessionId(entity.getUserSessionId());
|
|
||||||
model.setData(entity.getData());
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean removeOfflineUserSession(String userSessionId) {
|
|
||||||
OfflineUserSessionEntity entity = getUserSessionEntityById(userSessionId);
|
|
||||||
if (entity != null) {
|
|
||||||
user.getOfflineUserSessions().remove(entity);
|
|
||||||
updateUser();
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private OfflineUserSessionEntity getUserSessionEntityById(String userSessionId) {
|
|
||||||
if (user.getOfflineUserSessions() != null) {
|
|
||||||
for (OfflineUserSessionEntity entity : user.getOfflineUserSessions()) {
|
|
||||||
if (entity.getUserSessionId().equals(userSessionId)) {
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addOfflineClientSession(OfflineClientSessionModel clientSession) {
|
|
||||||
OfflineUserSessionEntity userSessionEntity = getUserSessionEntityById(clientSession.getUserSessionId());
|
|
||||||
if (userSessionEntity == null) {
|
|
||||||
throw new ModelException("OfflineUserSession with ID " + clientSession.getUserSessionId() + " doesn't exist for user " + getMongoEntity().getUsername());
|
|
||||||
}
|
|
||||||
|
|
||||||
OfflineClientSessionEntity clEntity = new OfflineClientSessionEntity();
|
|
||||||
clEntity.setClientSessionId(clientSession.getClientSessionId());
|
|
||||||
clEntity.setClientId(clientSession.getClientId());
|
|
||||||
clEntity.setData(clientSession.getData());
|
|
||||||
|
|
||||||
userSessionEntity.getOfflineClientSessions().add(clEntity);
|
|
||||||
updateUser();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public OfflineClientSessionModel getOfflineClientSession(String clientSessionId) {
|
|
||||||
if (user.getOfflineUserSessions() != null) {
|
|
||||||
for (OfflineUserSessionEntity userSession : user.getOfflineUserSessions()) {
|
|
||||||
for (OfflineClientSessionEntity clSession : userSession.getOfflineClientSessions()) {
|
|
||||||
if (clSession.getClientSessionId().equals(clientSessionId)) {
|
|
||||||
return toModel(clSession, userSession.getUserSessionId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private OfflineClientSessionModel toModel(OfflineClientSessionEntity cls, String userSessionId) {
|
|
||||||
OfflineClientSessionModel model = new OfflineClientSessionModel();
|
|
||||||
model.setClientSessionId(cls.getClientSessionId());
|
|
||||||
model.setClientId(cls.getClientId());
|
|
||||||
model.setData(cls.getData());
|
|
||||||
model.setUserSessionId(userSessionId);
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<OfflineClientSessionModel> getOfflineClientSessions() {
|
|
||||||
List<OfflineClientSessionModel> result = new ArrayList<>();
|
|
||||||
|
|
||||||
if (user.getOfflineUserSessions() != null) {
|
|
||||||
for (OfflineUserSessionEntity userSession : user.getOfflineUserSessions()) {
|
|
||||||
for (OfflineClientSessionEntity clSession : userSession.getOfflineClientSessions()) {
|
|
||||||
result.add(toModel(clSession, userSession.getUserSessionId()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean removeOfflineClientSession(String clientSessionId) {
|
|
||||||
if (user.getOfflineUserSessions() != null) {
|
|
||||||
for (OfflineUserSessionEntity userSession : user.getOfflineUserSessions()) {
|
|
||||||
for (OfflineClientSessionEntity clSession : userSession.getOfflineClientSessions()) {
|
|
||||||
if (clSession.getClientSessionId().equals(clientSessionId)) {
|
|
||||||
userSession.getOfflineClientSessions().remove(clSession);
|
|
||||||
updateUser();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
|
|
|
@ -98,7 +98,7 @@ public class TokenManager {
|
||||||
ClientSessionModel clientSession = null;
|
ClientSessionModel clientSession = null;
|
||||||
if (RefreshTokenUtil.TOKEN_TYPE_OFFLINE.equals(oldToken.getType())) {
|
if (RefreshTokenUtil.TOKEN_TYPE_OFFLINE.equals(oldToken.getType())) {
|
||||||
|
|
||||||
clientSession = OfflineTokenUtils.findOfflineClientSession(realm, user, oldToken.getClientSession(), oldToken.getSessionState());
|
clientSession = OfflineTokenUtils.findOfflineClientSession(session, realm, user, oldToken.getClientSession(), oldToken.getSessionState());
|
||||||
if (clientSession != null) {
|
if (clientSession != null) {
|
||||||
userSession = clientSession.getUserSession();
|
userSession = clientSession.getUserSession();
|
||||||
}
|
}
|
||||||
|
@ -496,7 +496,7 @@ public class TokenManager {
|
||||||
|
|
||||||
refreshToken = new RefreshToken(accessToken);
|
refreshToken = new RefreshToken(accessToken);
|
||||||
refreshToken.type(RefreshTokenUtil.TOKEN_TYPE_OFFLINE);
|
refreshToken.type(RefreshTokenUtil.TOKEN_TYPE_OFFLINE);
|
||||||
OfflineTokenUtils.persistOfflineSession(clientSession, userSession);
|
OfflineTokenUtils.persistOfflineSession(session, realm, clientSession, userSession);
|
||||||
} else {
|
} else {
|
||||||
refreshToken = new RefreshToken(accessToken);
|
refreshToken = new RefreshToken(accessToken);
|
||||||
refreshToken.expiration(Time.currentTime() + realm.getSsoSessionIdleTimeout());
|
refreshToken.expiration(Time.currentTime() + realm.getSsoSessionIdleTimeout());
|
||||||
|
|
|
@ -75,7 +75,7 @@ public class OfflineClientSessionAdapter implements ClientSessionModel {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getRedirectUri() {
|
public String getRedirectUri() {
|
||||||
return data.getRedirectUri();
|
return getData().getRedirectUri();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -85,7 +85,7 @@ public class OfflineClientSessionAdapter implements ClientSessionModel {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getTimestamp() {
|
public int getTimestamp() {
|
||||||
return 0;
|
return getData().getTimestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -238,6 +238,9 @@ public class OfflineClientSessionAdapter implements ClientSessionModel {
|
||||||
@JsonProperty("authenticatorStatus")
|
@JsonProperty("authenticatorStatus")
|
||||||
private Map<String, ClientSessionModel.ExecutionStatus> authenticatorStatus = new HashMap<>();
|
private Map<String, ClientSessionModel.ExecutionStatus> authenticatorStatus = new HashMap<>();
|
||||||
|
|
||||||
|
@JsonProperty("timestamp")
|
||||||
|
private int timestamp;
|
||||||
|
|
||||||
public String getAuthMethod() {
|
public String getAuthMethod() {
|
||||||
return authMethod;
|
return authMethod;
|
||||||
}
|
}
|
||||||
|
@ -285,5 +288,13 @@ public class OfflineClientSessionAdapter implements ClientSessionModel {
|
||||||
public void setAuthenticatorStatus(Map<String, ClientSessionModel.ExecutionStatus> authenticatorStatus) {
|
public void setAuthenticatorStatus(Map<String, ClientSessionModel.ExecutionStatus> authenticatorStatus) {
|
||||||
this.authenticatorStatus = authenticatorStatus;
|
this.authenticatorStatus = authenticatorStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getTimestamp() {
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimestamp(int timestamp) {
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import org.jboss.logging.Logger;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.ClientSessionModel;
|
import org.keycloak.models.ClientSessionModel;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.ModelException;
|
import org.keycloak.models.ModelException;
|
||||||
import org.keycloak.models.OfflineClientSessionModel;
|
import org.keycloak.models.OfflineClientSessionModel;
|
||||||
import org.keycloak.models.OfflineUserSessionModel;
|
import org.keycloak.models.OfflineUserSessionModel;
|
||||||
|
@ -17,9 +18,9 @@ import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserSessionModel;
|
import org.keycloak.models.UserSessionModel;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
import org.keycloak.util.Time;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Change to utils?
|
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
*/
|
*/
|
||||||
|
@ -27,12 +28,12 @@ public class OfflineTokenUtils {
|
||||||
|
|
||||||
protected static Logger logger = Logger.getLogger(OfflineTokenUtils.class);
|
protected static Logger logger = Logger.getLogger(OfflineTokenUtils.class);
|
||||||
|
|
||||||
public static void persistOfflineSession(ClientSessionModel clientSession, UserSessionModel userSession) {
|
public static void persistOfflineSession(KeycloakSession kcSession, RealmModel realm, ClientSessionModel clientSession, UserSessionModel userSession) {
|
||||||
UserModel user = userSession.getUser();
|
UserModel user = userSession.getUser();
|
||||||
ClientModel client = clientSession.getClient();
|
ClientModel client = clientSession.getClient();
|
||||||
|
|
||||||
// First verify if we already have offlineToken for this user+client . If yes, then invalidate it (This is to avoid leaks)
|
// First verify if we already have offlineToken for this user+client . If yes, then invalidate it (This is to avoid leaks)
|
||||||
Collection<OfflineClientSessionModel> clientSessions = user.getOfflineClientSessions();
|
Collection<OfflineClientSessionModel> clientSessions = kcSession.users().getOfflineClientSessions(realm, user);
|
||||||
for (OfflineClientSessionModel existing : clientSessions) {
|
for (OfflineClientSessionModel existing : clientSessions) {
|
||||||
if (existing.getClientId().equals(client.getId())) {
|
if (existing.getClientId().equals(client.getId())) {
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
|
@ -40,28 +41,28 @@ public class OfflineTokenUtils {
|
||||||
user.getUsername(), client.getClientId(), existing.getClientSessionId());
|
user.getUsername(), client.getClientId(), existing.getClientSessionId());
|
||||||
}
|
}
|
||||||
|
|
||||||
user.removeOfflineClientSession(existing.getClientSessionId());
|
kcSession.users().removeOfflineClientSession(realm, user, existing.getClientSessionId());
|
||||||
|
|
||||||
// Check if userSession is ours. If not, then check if it has other clientSessions and remove it otherwise
|
// Check if userSession is ours. If not, then check if it has other clientSessions and remove it otherwise
|
||||||
if (!existing.getUserSessionId().equals(userSession.getId())) {
|
if (!existing.getUserSessionId().equals(userSession.getId())) {
|
||||||
checkUserSessionHasClientSessions(user, existing.getUserSessionId());
|
checkUserSessionHasClientSessions(kcSession, realm, user, existing.getUserSessionId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify if we already have UserSession with this ID. If yes, don't create another one
|
// Verify if we already have UserSession with this ID. If yes, don't create another one
|
||||||
OfflineUserSessionModel userSessionRep = user.getOfflineUserSession(userSession.getId());
|
OfflineUserSessionModel userSessionRep = kcSession.users().getOfflineUserSession(realm, user, userSession.getId());
|
||||||
if (userSessionRep == null) {
|
if (userSessionRep == null) {
|
||||||
createOfflineUserSession(user, userSession);
|
createOfflineUserSession(kcSession, realm, user, userSession);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create clientRep and save to DB.
|
// Create clientRep and save to DB.
|
||||||
createOfflineClientSession(user, clientSession, userSession);
|
createOfflineClientSession(kcSession, realm, user, clientSession, userSession);
|
||||||
}
|
}
|
||||||
|
|
||||||
// userSessionId is provided from offline token. It's used just to verify if it match the ID from clientSession representation
|
// userSessionId is provided from offline token. It's used just to verify if it match the ID from clientSession representation
|
||||||
public static ClientSessionModel findOfflineClientSession(RealmModel realm, UserModel user, String clientSessionId, String userSessionId) {
|
public static ClientSessionModel findOfflineClientSession(KeycloakSession kcSession, RealmModel realm, UserModel user, String clientSessionId, String userSessionId) {
|
||||||
OfflineClientSessionModel clientSession = user.getOfflineClientSession(clientSessionId);
|
OfflineClientSessionModel clientSession = kcSession.users().getOfflineClientSession(realm, user, clientSessionId);
|
||||||
if (clientSession == null) {
|
if (clientSession == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -71,7 +72,7 @@ public class OfflineTokenUtils {
|
||||||
" Wanted user session: " + userSessionId);
|
" Wanted user session: " + userSessionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
OfflineUserSessionModel userSession = user.getOfflineUserSession(userSessionId);
|
OfflineUserSessionModel userSession = kcSession.users().getOfflineUserSession(realm, user, userSessionId);
|
||||||
if (userSession == null) {
|
if (userSession == null) {
|
||||||
throw new ModelException("Found clientSession " + clientSessionId + " but not userSession " + userSessionId);
|
throw new ModelException("Found clientSession " + clientSessionId + " but not userSession " + userSessionId);
|
||||||
}
|
}
|
||||||
|
@ -79,13 +80,11 @@ public class OfflineTokenUtils {
|
||||||
OfflineUserSessionAdapter userSessionAdapter = new OfflineUserSessionAdapter(userSession, user);
|
OfflineUserSessionAdapter userSessionAdapter = new OfflineUserSessionAdapter(userSession, user);
|
||||||
|
|
||||||
ClientModel client = realm.getClientById(clientSession.getClientId());
|
ClientModel client = realm.getClientById(clientSession.getClientId());
|
||||||
OfflineClientSessionAdapter clientSessionAdapter = new OfflineClientSessionAdapter(clientSession, realm, client, userSessionAdapter);
|
return new OfflineClientSessionAdapter(clientSession, realm, client, userSessionAdapter);
|
||||||
|
|
||||||
return clientSessionAdapter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Set<ClientModel> findClientsWithOfflineToken(RealmModel realm, UserModel user) {
|
public static Set<ClientModel> findClientsWithOfflineToken(KeycloakSession kcSession, RealmModel realm, UserModel user) {
|
||||||
Collection<OfflineClientSessionModel> clientSessions = user.getOfflineClientSessions();
|
Collection<OfflineClientSessionModel> clientSessions = kcSession.users().getOfflineClientSessions(realm, user);
|
||||||
Set<ClientModel> clients = new HashSet<>();
|
Set<ClientModel> clients = new HashSet<>();
|
||||||
for (OfflineClientSessionModel clientSession : clientSessions) {
|
for (OfflineClientSessionModel clientSession : clientSessions) {
|
||||||
ClientModel client = realm.getClientById(clientSession.getClientId());
|
ClientModel client = realm.getClientById(clientSession.getClientId());
|
||||||
|
@ -94,8 +93,8 @@ public class OfflineTokenUtils {
|
||||||
return clients;
|
return clients;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean revokeOfflineToken(UserModel user, ClientModel client) {
|
public static boolean revokeOfflineToken(KeycloakSession kcSession, RealmModel realm, UserModel user, ClientModel client) {
|
||||||
Collection<OfflineClientSessionModel> clientSessions = user.getOfflineClientSessions();
|
Collection<OfflineClientSessionModel> clientSessions = kcSession.users().getOfflineClientSessions(realm, user);
|
||||||
boolean anyRemoved = false;
|
boolean anyRemoved = false;
|
||||||
for (OfflineClientSessionModel clientSession : clientSessions) {
|
for (OfflineClientSessionModel clientSession : clientSessions) {
|
||||||
if (clientSession.getClientId().equals(client.getId())) {
|
if (clientSession.getClientId().equals(client.getId())) {
|
||||||
|
@ -104,8 +103,8 @@ public class OfflineTokenUtils {
|
||||||
user.getUsername(), client.getClientId(), clientSession.getClientSessionId());
|
user.getUsername(), client.getClientId(), clientSession.getClientSessionId());
|
||||||
}
|
}
|
||||||
|
|
||||||
user.removeOfflineClientSession(clientSession.getClientSessionId());
|
kcSession.users().removeOfflineClientSession(realm, user, clientSession.getClientSessionId());
|
||||||
checkUserSessionHasClientSessions(user, clientSession.getUserSessionId());
|
checkUserSessionHasClientSessions(kcSession, realm, user, clientSession.getUserSessionId());
|
||||||
anyRemoved = true;
|
anyRemoved = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,7 +122,7 @@ public class OfflineTokenUtils {
|
||||||
return clientSession.getRoles().contains(offlineAccessRole.getId());
|
return clientSession.getRoles().contains(offlineAccessRole.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void createOfflineUserSession(UserModel user, UserSessionModel userSession) {
|
private static void createOfflineUserSession(KeycloakSession kcSession, RealmModel realm, UserModel user, UserSessionModel userSession) {
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.tracef("Creating new offline user session. UserSessionID: '%s' , Username: '%s'", userSession.getId(), user.getUsername());
|
logger.tracef("Creating new offline user session. UserSessionID: '%s' , Username: '%s'", userSession.getId(), user.getUsername());
|
||||||
}
|
}
|
||||||
|
@ -141,13 +140,13 @@ public class OfflineTokenUtils {
|
||||||
OfflineUserSessionModel sessionModel = new OfflineUserSessionModel();
|
OfflineUserSessionModel sessionModel = new OfflineUserSessionModel();
|
||||||
sessionModel.setUserSessionId(userSession.getId());
|
sessionModel.setUserSessionId(userSession.getId());
|
||||||
sessionModel.setData(stringRep);
|
sessionModel.setData(stringRep);
|
||||||
user.addOfflineUserSession(sessionModel);
|
kcSession.users().addOfflineUserSession(realm, user, sessionModel);
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
throw new ModelException(ioe);
|
throw new ModelException(ioe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void createOfflineClientSession(UserModel user, ClientSessionModel clientSession, UserSessionModel userSession) {
|
private static void createOfflineClientSession(KeycloakSession kcSession, RealmModel realm, UserModel user, ClientSessionModel clientSession, UserSessionModel userSession) {
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.tracef("Creating new offline token client session. ClientSessionId: '%s', UserSessionID: '%s' , Username: '%s', Client: '%s'" ,
|
logger.tracef("Creating new offline token client session. ClientSessionId: '%s', UserSessionID: '%s' , Username: '%s', Client: '%s'" ,
|
||||||
clientSession.getId(), userSession.getId(), user.getUsername(), clientSession.getClient().getClientId());
|
clientSession.getId(), userSession.getId(), user.getUsername(), clientSession.getClient().getClientId());
|
||||||
|
@ -159,23 +158,25 @@ public class OfflineTokenUtils {
|
||||||
rep.setRoles(clientSession.getRoles());
|
rep.setRoles(clientSession.getRoles());
|
||||||
rep.setNotes(clientSession.getNotes());
|
rep.setNotes(clientSession.getNotes());
|
||||||
rep.setAuthenticatorStatus(clientSession.getExecutionStatus());
|
rep.setAuthenticatorStatus(clientSession.getExecutionStatus());
|
||||||
|
rep.setTimestamp(Time.currentTime());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String stringRep = JsonSerialization.writeValueAsString(rep);
|
String stringRep = JsonSerialization.writeValueAsString(rep);
|
||||||
OfflineClientSessionModel clsModel = new OfflineClientSessionModel();
|
OfflineClientSessionModel clsModel = new OfflineClientSessionModel();
|
||||||
clsModel.setClientSessionId(clientSession.getId());
|
clsModel.setClientSessionId(clientSession.getId());
|
||||||
clsModel.setClientId(clientSession.getClient().getId());
|
clsModel.setClientId(clientSession.getClient().getId());
|
||||||
|
clsModel.setUserId(user.getId());
|
||||||
clsModel.setUserSessionId(userSession.getId());
|
clsModel.setUserSessionId(userSession.getId());
|
||||||
clsModel.setData(stringRep);
|
clsModel.setData(stringRep);
|
||||||
user.addOfflineClientSession(clsModel);
|
kcSession.users().addOfflineClientSession(realm, clsModel);
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
throw new ModelException(ioe);
|
throw new ModelException(ioe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if userSession has any offline clientSessions attached to it. Remove userSession if not
|
// Check if userSession has any offline clientSessions attached to it. Remove userSession if not
|
||||||
private static void checkUserSessionHasClientSessions(UserModel user, String userSessionId) {
|
private static void checkUserSessionHasClientSessions(KeycloakSession kcSession, RealmModel realm, UserModel user, String userSessionId) {
|
||||||
Collection<OfflineClientSessionModel> clientSessions = user.getOfflineClientSessions();
|
Collection<OfflineClientSessionModel> clientSessions = kcSession.users().getOfflineClientSessions(realm, user);
|
||||||
|
|
||||||
for (OfflineClientSessionModel clientSession : clientSessions) {
|
for (OfflineClientSessionModel clientSession : clientSessions) {
|
||||||
if (clientSession.getUserSessionId().equals(userSessionId)) {
|
if (clientSession.getUserSessionId().equals(userSessionId)) {
|
||||||
|
@ -186,6 +187,6 @@ public class OfflineTokenUtils {
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.tracef("Removing offline userSession for user %s as it doesn't have any client sessions attached. UserSessionID: %s", user.getUsername(), userSessionId);
|
logger.tracef("Removing offline userSession for user %s as it doesn't have any client sessions attached. UserSessionID: %s", user.getUsername(), userSessionId);
|
||||||
}
|
}
|
||||||
user.removeOfflineUserSession(userSessionId);
|
kcSession.users().removeOfflineUserSession(realm, user, userSessionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -486,7 +486,7 @@ public class AccountService extends AbstractSecuredLocalService {
|
||||||
// Revoke grant in UserModel
|
// Revoke grant in UserModel
|
||||||
UserModel user = auth.getUser();
|
UserModel user = auth.getUser();
|
||||||
user.revokeConsentForClient(client.getId());
|
user.revokeConsentForClient(client.getId());
|
||||||
OfflineTokenUtils.revokeOfflineToken(user, client);
|
OfflineTokenUtils.revokeOfflineToken(session, realm, user, client);
|
||||||
|
|
||||||
// Logout clientSessions for this user and client
|
// Logout clientSessions for this user and client
|
||||||
AuthenticationManager.backchannelUserFromClient(session, realm, user, client, uriInfo, headers);
|
AuthenticationManager.backchannelUserFromClient(session, realm, user, client, uriInfo, headers);
|
||||||
|
|
|
@ -7,8 +7,11 @@ import org.jboss.resteasy.spi.NotFoundException;
|
||||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||||
import org.keycloak.events.admin.OperationType;
|
import org.keycloak.events.admin.OperationType;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
|
import org.keycloak.models.ClientSessionModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.ModelDuplicateException;
|
import org.keycloak.models.ModelDuplicateException;
|
||||||
|
import org.keycloak.models.OfflineClientSessionModel;
|
||||||
|
import org.keycloak.models.OfflineUserSessionModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserCredentialModel;
|
import org.keycloak.models.UserCredentialModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
@ -24,6 +27,8 @@ import org.keycloak.representations.idm.UserSessionRepresentation;
|
||||||
import org.keycloak.services.managers.ClientManager;
|
import org.keycloak.services.managers.ClientManager;
|
||||||
import org.keycloak.services.managers.RealmManager;
|
import org.keycloak.services.managers.RealmManager;
|
||||||
import org.keycloak.services.managers.ResourceAdminManager;
|
import org.keycloak.services.managers.ResourceAdminManager;
|
||||||
|
import org.keycloak.services.offline.OfflineClientSessionAdapter;
|
||||||
|
import org.keycloak.services.offline.OfflineUserSessionAdapter;
|
||||||
import org.keycloak.services.resources.KeycloakApplication;
|
import org.keycloak.services.resources.KeycloakApplication;
|
||||||
import org.keycloak.services.ErrorResponse;
|
import org.keycloak.services.ErrorResponse;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
@ -390,6 +395,65 @@ public class ClientResource {
|
||||||
return sessions;
|
return sessions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get application offline session count
|
||||||
|
*
|
||||||
|
* Returns a number of offline user sessions associated with this client
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* "count": number
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Path("offline-session-count")
|
||||||
|
@GET
|
||||||
|
@NoCache
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Map<String, Integer> getOfflineSessionCount() {
|
||||||
|
auth.requireView();
|
||||||
|
Map<String, Integer> map = new HashMap<String, Integer>();
|
||||||
|
map.put("count", session.users().getOfflineClientSessionsCount(client.getRealm(), client));
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get offline sessions for client
|
||||||
|
*
|
||||||
|
* Returns a list of offline user sessions associated with this client
|
||||||
|
*
|
||||||
|
* @param firstResult Paging offset
|
||||||
|
* @param maxResults Paging size
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Path("offline-sessions")
|
||||||
|
@GET
|
||||||
|
@NoCache
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public List<UserSessionRepresentation> getOfflineUserSessions(@QueryParam("first") Integer firstResult, @QueryParam("max") Integer maxResults) {
|
||||||
|
auth.requireView();
|
||||||
|
firstResult = firstResult != null ? firstResult : -1;
|
||||||
|
maxResults = maxResults != null ? maxResults : -1;
|
||||||
|
List<UserSessionRepresentation> sessions = new ArrayList<UserSessionRepresentation>();
|
||||||
|
for (OfflineClientSessionModel offlineClientSession : session.users().getOfflineClientSessions(client.getRealm(), client, firstResult, maxResults)) {
|
||||||
|
UserModel user = session.users().getUserById(offlineClientSession.getUserId(), client.getRealm());
|
||||||
|
OfflineUserSessionModel offlineUserSession = session.users().getOfflineUserSession(client.getRealm(), user, offlineClientSession.getUserSessionId());
|
||||||
|
OfflineUserSessionAdapter sessionAdapter = new OfflineUserSessionAdapter(offlineUserSession, user);
|
||||||
|
OfflineClientSessionAdapter clientSessionAdapter = new OfflineClientSessionAdapter(offlineClientSession, client.getRealm(), client, sessionAdapter);
|
||||||
|
|
||||||
|
UserSessionRepresentation rep = new UserSessionRepresentation();
|
||||||
|
rep.setId(sessionAdapter.getId());
|
||||||
|
rep.setStart(Time.toMillis(clientSessionAdapter.getTimestamp()));
|
||||||
|
rep.setUsername(user.getUsername());
|
||||||
|
rep.setUserId(user.getId());
|
||||||
|
rep.setIpAddress(sessionAdapter.getIpAddress());
|
||||||
|
|
||||||
|
sessions.add(rep);
|
||||||
|
}
|
||||||
|
return sessions;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logout all sessions
|
* Logout all sessions
|
||||||
*
|
*
|
||||||
|
|
|
@ -66,15 +66,18 @@ import javax.ws.rs.WebApplicationException;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import org.keycloak.models.UsernameLoginFailureModel;
|
import org.keycloak.models.UsernameLoginFailureModel;
|
||||||
import org.keycloak.services.managers.BruteForceProtector;
|
import org.keycloak.services.managers.BruteForceProtector;
|
||||||
|
import org.keycloak.services.offline.OfflineTokenUtils;
|
||||||
import org.keycloak.services.resources.AccountService;
|
import org.keycloak.services.resources.AccountService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -439,25 +442,44 @@ public class UsersResource {
|
||||||
@GET
|
@GET
|
||||||
@NoCache
|
@NoCache
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
public List<UserConsentRepresentation> getConsents(final @PathParam("id") String id) {
|
public List<Map<String, Object>> getConsents(final @PathParam("id") String id) {
|
||||||
auth.requireView();
|
auth.requireView();
|
||||||
UserModel user = session.users().getUserById(id, realm);
|
UserModel user = session.users().getUserById(id, realm);
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new NotFoundException("User not found");
|
throw new NotFoundException("User not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
List<UserConsentModel> consents = user.getConsents();
|
List<Map<String, Object>> result = new LinkedList<>();
|
||||||
List<UserConsentRepresentation> result = new ArrayList<UserConsentRepresentation>();
|
|
||||||
|
|
||||||
for (UserConsentModel consent : consents) {
|
Set<ClientModel> offlineClients = OfflineTokenUtils.findClientsWithOfflineToken(session, realm, user);
|
||||||
UserConsentRepresentation rep = ModelToRepresentation.toRepresentation(consent);
|
|
||||||
result.add(rep);
|
for (ClientModel client : realm.getClients()) {
|
||||||
|
UserConsentModel consent = user.getConsentByClient(client.getId());
|
||||||
|
boolean hasOfflineToken = offlineClients.contains(client);
|
||||||
|
|
||||||
|
if (consent == null && !hasOfflineToken) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
UserConsentRepresentation rep = (consent == null) ? null : ModelToRepresentation.toRepresentation(consent);
|
||||||
|
|
||||||
|
Map<String, Object> currentRep = new HashMap<>();
|
||||||
|
currentRep.put("clientId", client.getClientId());
|
||||||
|
currentRep.put("grantedProtocolMappers", (rep==null ? Collections.emptyMap() : rep.getGrantedProtocolMappers()));
|
||||||
|
currentRep.put("grantedRealmRoles", (rep==null ? Collections.emptyList() : rep.getGrantedRealmRoles()));
|
||||||
|
currentRep.put("grantedClientRoles", (rep==null ? Collections.emptyMap() : rep.getGrantedClientRoles()));
|
||||||
|
|
||||||
|
List<String> additionalGrants = hasOfflineToken ? Arrays.asList("Offline Token") : Collections.<String>emptyList();
|
||||||
|
currentRep.put("additionalGrants", additionalGrants);
|
||||||
|
|
||||||
|
result.add(currentRep);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Revoke consent for particular client from user
|
* Revoke consent and offline tokens for particular client from user
|
||||||
*
|
*
|
||||||
* @param id User id
|
* @param id User id
|
||||||
* @param clientId Client id
|
* @param clientId Client id
|
||||||
|
@ -473,12 +495,16 @@ public class UsersResource {
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientModel client = realm.getClientByClientId(clientId);
|
ClientModel client = realm.getClientByClientId(clientId);
|
||||||
boolean revoked = user.revokeConsentForClient(client.getId());
|
boolean revokedConsent = user.revokeConsentForClient(client.getId());
|
||||||
if (revoked) {
|
boolean revokedOfflineToken = OfflineTokenUtils.revokeOfflineToken(session, realm, user, client);
|
||||||
|
|
||||||
|
if (revokedConsent) {
|
||||||
// Logout clientSessions for this user and client
|
// Logout clientSessions for this user and client
|
||||||
AuthenticationManager.backchannelUserFromClient(session, realm, user, client, uriInfo, headers);
|
AuthenticationManager.backchannelUserFromClient(session, realm, user, client, uriInfo, headers);
|
||||||
} else {
|
}
|
||||||
throw new NotFoundException("Consent not found");
|
|
||||||
|
if (!revokedConsent && !revokedOfflineToken) {
|
||||||
|
throw new NotFoundException("Consent nor offline token not found");
|
||||||
}
|
}
|
||||||
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success();
|
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success();
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@ import org.keycloak.models.FederatedIdentityModel;
|
||||||
import org.keycloak.models.IdentityProviderModel;
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.LDAPConstants;
|
import org.keycloak.models.LDAPConstants;
|
||||||
|
import org.keycloak.models.OfflineClientSessionModel;
|
||||||
|
import org.keycloak.models.OfflineUserSessionModel;
|
||||||
import org.keycloak.models.ProtocolMapperModel;
|
import org.keycloak.models.ProtocolMapperModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RequiredCredentialModel;
|
import org.keycloak.models.RequiredCredentialModel;
|
||||||
|
@ -32,6 +34,7 @@ import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.services.managers.RealmManager;
|
import org.keycloak.services.managers.RealmManager;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -327,6 +330,21 @@ public class ImportTest extends AbstractModelTest {
|
||||||
Assert.assertFalse(otherAppAdminConsent.isRoleGranted(application.getRole("app-admin")));
|
Assert.assertFalse(otherAppAdminConsent.isRoleGranted(application.getRole("app-admin")));
|
||||||
Assert.assertTrue(otherAppAdminConsent.isProtocolMapperGranted(gssCredentialMapper));
|
Assert.assertTrue(otherAppAdminConsent.isProtocolMapperGranted(gssCredentialMapper));
|
||||||
|
|
||||||
|
// Test offline sessions
|
||||||
|
Collection<OfflineUserSessionModel> offlineUserSessions = session.users().getOfflineUserSessions(realm, admin);
|
||||||
|
Collection<OfflineClientSessionModel> offlineClientSessions = session.users().getOfflineClientSessions(realm, admin);
|
||||||
|
Assert.assertEquals(offlineUserSessions.size(), 1);
|
||||||
|
Assert.assertEquals(offlineClientSessions.size(), 1);
|
||||||
|
OfflineUserSessionModel offlineSession = offlineUserSessions.iterator().next();
|
||||||
|
OfflineClientSessionModel offlineClSession = offlineClientSessions.iterator().next();
|
||||||
|
Assert.assertEquals(offlineSession.getData(), "something1");
|
||||||
|
Assert.assertEquals(offlineSession.getUserSessionId(), "123");
|
||||||
|
Assert.assertEquals(offlineClSession.getClientId(), otherApp.getId());
|
||||||
|
Assert.assertEquals(offlineClSession.getUserSessionId(), "123");
|
||||||
|
Assert.assertEquals(offlineClSession.getUserId(), admin.getId());
|
||||||
|
Assert.assertEquals(offlineClSession.getData(), "something2");
|
||||||
|
|
||||||
|
|
||||||
// Test service accounts
|
// Test service accounts
|
||||||
Assert.assertFalse(application.isServiceAccountsEnabled());
|
Assert.assertFalse(application.isServiceAccountsEnabled());
|
||||||
Assert.assertTrue(otherApp.isServiceAccountsEnabled());
|
Assert.assertTrue(otherApp.isServiceAccountsEnabled());
|
||||||
|
|
|
@ -15,6 +15,7 @@ import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -292,12 +293,35 @@ public class UserModelTest extends AbstractModelTest {
|
||||||
ClientModel barClient = realm.addClient("bar");
|
ClientModel barClient = realm.addClient("bar");
|
||||||
|
|
||||||
UserModel user1 = session.users().addUser(realm, "user1");
|
UserModel user1 = session.users().addUser(realm, "user1");
|
||||||
addOfflineUserSession(user1, "123", "something1");
|
UserModel user2 = session.users().addUser(realm, "user2");
|
||||||
addOfflineClientSession(user1, "456", "123", fooClient.getId(), "something2");
|
|
||||||
addOfflineClientSession(user1, "789", "123", barClient.getId(), "something3");
|
addOfflineUserSession(realm, user1, "123", "something1");
|
||||||
|
addOfflineClientSession(realm, user1, "456", "123", fooClient.getId(), "something2");
|
||||||
|
addOfflineClientSession(realm, user1, "789", "123", barClient.getId(), "something3");
|
||||||
|
|
||||||
|
addOfflineUserSession(realm, user2, "2123", "something4");
|
||||||
|
addOfflineClientSession(realm, user2, "2456", "2123", fooClient.getId(), "something5");
|
||||||
|
|
||||||
commit();
|
commit();
|
||||||
|
|
||||||
|
// Searching by clients
|
||||||
|
Assert.assertEquals(2, session.users().getOfflineClientSessionsCount(realm, fooClient));
|
||||||
|
Assert.assertEquals(1, session.users().getOfflineClientSessionsCount(realm, barClient));
|
||||||
|
|
||||||
|
Collection<OfflineClientSessionModel> clientSessions = session.users().getOfflineClientSessions(realm, fooClient, 0, 10);
|
||||||
|
Assert.assertEquals(2, clientSessions.size());
|
||||||
|
clientSessions = session.users().getOfflineClientSessions(realm, fooClient, 0, 1);
|
||||||
|
OfflineClientSessionModel cls = clientSessions.iterator().next();
|
||||||
|
assertSessionEquals(cls, "456", "123", fooClient.getId(), user1.getId(), "something2");
|
||||||
|
clientSessions = session.users().getOfflineClientSessions(realm, fooClient, 1, 1);
|
||||||
|
cls = clientSessions.iterator().next();
|
||||||
|
assertSessionEquals(cls, "2456", "2123", fooClient.getId(), user2.getId(), "something5");
|
||||||
|
|
||||||
|
clientSessions = session.users().getOfflineClientSessions(realm, barClient, 0, 10);
|
||||||
|
Assert.assertEquals(1, clientSessions.size());
|
||||||
|
cls = clientSessions.iterator().next();
|
||||||
|
assertSessionEquals(cls, "789", "123", barClient.getId(), user1.getId(), "something3");
|
||||||
|
|
||||||
realm = realmManager.getRealmByName("original");
|
realm = realmManager.getRealmByName("original");
|
||||||
realm.removeClient(barClient.getId());
|
realm.removeClient(barClient.getId());
|
||||||
|
|
||||||
|
@ -305,9 +329,9 @@ public class UserModelTest extends AbstractModelTest {
|
||||||
|
|
||||||
realm = realmManager.getRealmByName("original");
|
realm = realmManager.getRealmByName("original");
|
||||||
user1 = session.users().getUserByUsername("user1", realm);
|
user1 = session.users().getUserByUsername("user1", realm);
|
||||||
Assert.assertEquals("something1", user1.getOfflineUserSession("123").getData());
|
Assert.assertEquals("something1", session.users().getOfflineUserSession(realm, user1, "123").getData());
|
||||||
Assert.assertEquals("something2", user1.getOfflineClientSession("456").getData());
|
Assert.assertEquals("something2", session.users().getOfflineClientSession(realm, user1, "456").getData());
|
||||||
Assert.assertNull(user1.getOfflineClientSession("789"));
|
Assert.assertNull(session.users().getOfflineClientSession(realm, user1, "789"));
|
||||||
|
|
||||||
realm.removeClient(fooClient.getId());
|
realm.removeClient(fooClient.getId());
|
||||||
|
|
||||||
|
@ -315,27 +339,28 @@ public class UserModelTest extends AbstractModelTest {
|
||||||
|
|
||||||
realm = realmManager.getRealmByName("original");
|
realm = realmManager.getRealmByName("original");
|
||||||
user1 = session.users().getUserByUsername("user1", realm);
|
user1 = session.users().getUserByUsername("user1", realm);
|
||||||
Assert.assertNull(user1.getOfflineClientSession("456"));
|
Assert.assertNull(session.users().getOfflineClientSession(realm, user1, "456"));
|
||||||
Assert.assertNull(user1.getOfflineClientSession("789"));
|
Assert.assertNull(session.users().getOfflineClientSession(realm, user1, "789"));
|
||||||
Assert.assertNull(user1.getOfflineUserSession("123"));
|
Assert.assertNull(session.users().getOfflineUserSession(realm, user1, "123"));
|
||||||
Assert.assertEquals(0, user1.getOfflineUserSessions().size());
|
Assert.assertEquals(0, session.users().getOfflineUserSessions(realm, user1).size());
|
||||||
Assert.assertEquals(0, user1.getOfflineClientSessions().size());
|
Assert.assertEquals(0, session.users().getOfflineClientSessions(realm, user1).size());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addOfflineUserSession(UserModel user, String userSessionId, String data) {
|
private void addOfflineUserSession(RealmModel realm, UserModel user, String userSessionId, String data) {
|
||||||
OfflineUserSessionModel model = new OfflineUserSessionModel();
|
OfflineUserSessionModel model = new OfflineUserSessionModel();
|
||||||
model.setUserSessionId(userSessionId);
|
model.setUserSessionId(userSessionId);
|
||||||
model.setData(data);
|
model.setData(data);
|
||||||
user.addOfflineUserSession(model);
|
session.users().addOfflineUserSession(realm, user, model);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addOfflineClientSession(UserModel user, String clientSessionId, String userSessionId, String clientId, String data) {
|
private void addOfflineClientSession(RealmModel realm, UserModel user, String clientSessionId, String userSessionId, String clientId, String data) {
|
||||||
OfflineClientSessionModel model = new OfflineClientSessionModel();
|
OfflineClientSessionModel model = new OfflineClientSessionModel();
|
||||||
model.setClientSessionId(clientSessionId);
|
model.setClientSessionId(clientSessionId);
|
||||||
model.setUserSessionId(userSessionId);
|
model.setUserSessionId(userSessionId);
|
||||||
|
model.setUserId(user.getId());
|
||||||
model.setClientId(clientId);
|
model.setClientId(clientId);
|
||||||
model.setData(data);
|
model.setData(data);
|
||||||
user.addOfflineClientSession(model);
|
session.users().addOfflineClientSession(realm, model);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void assertEquals(UserModel expected, UserModel actual) {
|
public static void assertEquals(UserModel expected, UserModel actual) {
|
||||||
|
@ -352,5 +377,14 @@ public class UserModelTest extends AbstractModelTest {
|
||||||
Assert.assertArrayEquals(expectedRequiredActions, actualRequiredActions);
|
Assert.assertArrayEquals(expectedRequiredActions, actualRequiredActions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void assertSessionEquals(OfflineClientSessionModel cls, String expectedClientSessionId, String expectedUserSessionId,
|
||||||
|
String expectedClientId, String expectedUserId, String expectedData) {
|
||||||
|
Assert.assertEquals(cls.getData(), expectedData);
|
||||||
|
Assert.assertEquals(cls.getClientSessionId(), expectedClientSessionId);
|
||||||
|
Assert.assertEquals(cls.getUserSessionId(), expectedUserSessionId);
|
||||||
|
Assert.assertEquals(cls.getUserId(), expectedUserId);
|
||||||
|
Assert.assertEquals(cls.getClientId(), expectedClientId);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -119,6 +119,19 @@
|
||||||
"openid-connect": [ "gss delegation credential" ]
|
"openid-connect": [ "gss delegation credential" ]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"offlineUserSessions": [
|
||||||
|
{
|
||||||
|
"userSessionId": "123",
|
||||||
|
"data": "something1",
|
||||||
|
"offlineClientSessions": [
|
||||||
|
{
|
||||||
|
"clientSessionId": "456",
|
||||||
|
"client": "OtherApp",
|
||||||
|
"data": "something2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue