keycloak-uncached

session mgmt

3/5/2014 12:25:33 AM

Changes

Details

diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js
index bfb76f7..bce292e 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js
@@ -176,6 +176,21 @@ module.config([ '$routeProvider', function($routeProvider) {
             },
             controller : 'UserRoleMappingCtrl'
         })
+        .when('/realms/:realm/users/:user/sessions', {
+            templateUrl : 'partials/user-sessions.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                user : function(UserLoader) {
+                    return UserLoader();
+                },
+                stats : function(UserSessionStatsLoader) {
+                    return UserSessionStatsLoader();
+                }
+            },
+            controller : 'UserSessionsCtrl'
+        })
         .when('/realms/:realm/users', {
             templateUrl : 'partials/user-list.html',
             resolve : {
@@ -292,6 +307,21 @@ module.config([ '$routeProvider', function($routeProvider) {
             },
             controller : 'ApplicationClaimsCtrl'
         })
+        .when('/realms/:realm/applications/:application/sessions', {
+            templateUrl : 'partials/application-sessions.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                application : function(ApplicationLoader) {
+                    return ApplicationLoader();
+                },
+                stats : function(ApplicationSessionStatsLoader) {
+                    return ApplicationSessionStatsLoader();
+                }
+            },
+            controller : 'ApplicationSessionsCtrl'
+        })
         .when('/realms/:realm/applications/:application/credentials', {
             templateUrl : 'partials/application-credentials.html',
             resolve : {
@@ -540,6 +570,18 @@ module.config([ '$routeProvider', function($routeProvider) {
             },
             controller : 'RealmRevocationCtrl'
         })
+        .when('/realms/:realm/sessions/realm', {
+            templateUrl : 'partials/session-realm.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                stats : function(RealmSessionStatsLoader) {
+                    return RealmSessionStatsLoader();
+                }
+            },
+            controller : 'RealmSessionStatsCtrl'
+        })
 
         .otherwise({
             templateUrl : 'partials/notfound.html'
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/applications.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/applications.js
index 27607e9..d7416a0 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/applications.js
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/applications.js
@@ -43,6 +43,45 @@ module.controller('ApplicationCredentialsCtrl', function($scope, $location, real
     });
 });
 
+module.controller('ApplicationSessionsCtrl', function($scope, realm, stats, application,
+                                                      ApplicationLogoutUser,
+                                                      ApplicationLogoutAll,
+                                                      ApplicationSessionStats,
+                                                      ApplicationSessionStatsWithUsers,
+                                                    $location, Dialog, Notifications) {
+    $scope.realm = realm;
+    $scope.stats = stats;
+    $scope.users = {};
+    $scope.application = application;
+
+    $scope.toDate = function(val) {
+        return new Date(val);
+    };
+
+    $scope.loadUsers = function() {
+        ApplicationSessionStatsWithUsers.get({ realm : realm.realm, application: $scope.application.name }, function(updated) {
+            $scope.stats = updated;
+            $scope.users = updated.users;
+        })
+    };
+
+    $scope.logoutAll = function() {
+        ApplicationLogoutAll.save({realm : realm.realm, application: $scope.application.name}, function () {
+            Notifications.success('Logged out all users');
+            $scope.loadUsers();
+        });
+    };
+
+    $scope.logoutUser = function(user) {
+        console.log('Trying to logout user: ' + user);
+        ApplicationLogoutUser.save({realm : realm.realm, application: $scope.application.name, user: user}, function () {
+            Notifications.success('Logged out user' + user);
+            $scope.loadUsers();
+        });
+    };
+
+});
+
 module.controller('ApplicationClaimsCtrl', function($scope, realm, application, claims,
                                                         ApplicationClaims,
                                                         $http, $location, Dialog, Notifications) {
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js
index cb0fa24..d2e4e68 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js
@@ -690,6 +690,26 @@ module.controller('RealmKeysDetailCtrl', function($scope, Realm, realm, $http, $
     };
 });
 
+module.controller('RealmSessionStatsCtrl', function($scope, realm, stats, RealmSessionStats, RealmLogoutAll, Notifications) {
+    $scope.realm = realm;
+    $scope.stats = stats;
+
+    console.log(stats);
+
+    $scope.logoutAll = function() {
+        RealmLogoutAll.save({realm : realm.realm}, function () {
+            Notifications.success('Logged out all users');
+            RealmSessionStats.get({realm: realm.realm}, function(updated) {
+                Notifications.success('Logged out all users');
+                $scope.stats = updated;
+            })
+        });
+    };
+
+
+});
+
+
 module.controller('RealmRevocationCtrl', function($scope, Realm, RealmPushRevocation, realm, $http, $location, Dialog, Notifications) {
     $scope.realm = angular.copy(realm);
 
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/users.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/users.js
index b0048a9..178e26b 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/users.js
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/users.js
@@ -118,6 +118,35 @@ module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, ro
 
 });
 
+module.controller('UserSessionsCtrl', function($scope, realm, user, stats, UserLogout, ApplicationLogoutUser, UserSessionStats, Notifications) {
+    $scope.realm = realm;
+    $scope.user = user;
+    $scope.stats = stats;
+
+    $scope.logoutAll = function() {
+        UserLogout.save({realm : realm.realm, user: user.username}, function () {
+            Notifications.success('Logged out user in all applications');
+            UserSessionStats.get({realm: realm.realm, user: user.username}, function(updated) {
+                $scope.stats = updated;
+            })
+        });
+    };
+
+    $scope.logoutApplication = function(app) {
+        console.log('log user out of app: ' + app);
+        ApplicationLogoutUser.save({realm : realm.realm, application: app, user: user.username}, function () {
+            Notifications.success('Logged out user from application');
+            UserSessionStats.get({realm: realm.realm, user: user.username}, function(updated) {
+                $scope.stats = updated;
+            })
+        });
+    };
+
+
+
+});
+
+
 module.controller('UserListCtrl', function($scope, realm, User) {
     $scope.realm = realm;
     $scope.searchQuery = function() {
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/loaders.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/loaders.js
index d5b3917..84f8dbc 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/js/loaders.js
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/loaders.js
@@ -48,20 +48,37 @@ module.factory('RealmLoader', function(Loader, Realm, $route, $q) {
 });
 
 module.factory('UserListLoader', function(Loader, User, $route, $q) {
-	return Loader.query(User, function() {
-		return {
-			realm : $route.current.params.realm
-		}
-	});
+    return Loader.query(User, function() {
+        return {
+            realm : $route.current.params.realm
+        }
+    });
+});
+
+module.factory('RealmSessionStatsLoader', function(Loader, RealmSessionStats, $route, $q) {
+    return Loader.get(RealmSessionStats, function() {
+        return {
+            realm : $route.current.params.realm
+        }
+    });
 });
 
 module.factory('UserLoader', function(Loader, User, $route, $q) {
-	return Loader.get(User, function() {
-		return {
-			realm : $route.current.params.realm,
-			userId : $route.current.params.user
-		}
-	});
+    return Loader.get(User, function() {
+        return {
+            realm : $route.current.params.realm,
+            userId : $route.current.params.user
+        }
+    });
+});
+
+module.factory('UserSessionStatsLoader', function(Loader, UserSessionStats, $route, $q) {
+    return Loader.get(UserSessionStats, function() {
+        return {
+            realm : $route.current.params.realm,
+            user : $route.current.params.user
+        }
+    });
 });
 
 module.factory('RoleLoader', function(Loader, Role, $route, $q) {
@@ -91,6 +108,15 @@ module.factory('ApplicationRoleLoader', function(Loader, ApplicationRole, $route
     });
 });
 
+module.factory('ApplicationSessionStatsLoader', function(Loader, ApplicationSessionStats, $route, $q) {
+    return Loader.get(ApplicationSessionStats, function() {
+        return {
+            realm : $route.current.params.realm,
+            application : $route.current.params.application
+        }
+    });
+});
+
 module.factory('ApplicationClaimsLoader', function(Loader, ApplicationClaims, $route, $q) {
     return Loader.get(ApplicationClaims, function() {
         return {
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/services.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/services.js
index 1397eb1..cb31b5e 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/js/services.js
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/services.js
@@ -158,9 +158,15 @@ module.factory('User', function($resource) {
 });
 
 module.factory('UserSessionStats', function($resource) {
-    return $resource('/auth/rest/admin/realms/:realm/users/:userId/session-stats', {
+    return $resource('/auth/rest/admin/realms/:realm/users/:user/session-stats', {
         realm : '@realm',
-        userId : '@userId'
+        user : '@user'
+    });
+});
+module.factory('UserLogout', function($resource) {
+    return $resource('/auth/rest/admin/realms/:realm/users/:user/logout', {
+        realm : '@realm',
+        user : '@user'
     });
 });
 
@@ -489,6 +495,32 @@ module.factory('ApplicationSessionStats', function($resource) {
     });
 });
 
+module.factory('ApplicationSessionStatsWithUsers', function($resource) {
+    return $resource('/auth/rest/admin/realms/:realm/applications/:application/session-stats?users=true', {
+        realm : '@realm',
+        application : "@application"
+    });
+});
+
+module.factory('ApplicationLogoutAll', function($resource) {
+    return $resource('/auth/rest/admin/realms/:realm/applications/:application/logout-all', {
+        realm : '@realm',
+        application : "@application"
+    });
+});
+module.factory('ApplicationLogoutUser', function($resource) {
+    return $resource('/auth/rest/admin/realms/:realm/applications/:application/logout-user/:user', {
+        realm : '@realm',
+        application : "@application",
+        user : "@user"
+    });
+});
+module.factory('RealmLogoutAll', function($resource) {
+    return $resource('/auth/rest/admin/realms/:realm/logout-all', {
+        realm : '@realm'
+    });
+});
+
 module.factory('ApplicationPushRevocation', function($resource) {
     return $resource('/auth/rest/admin/realms/:realm/applications/:application/push-revocation', {
         realm : '@realm',
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-claims.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-claims.html
index 2b2e34b..f33e8cd 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-claims.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-claims.html
@@ -8,6 +8,7 @@
         <li class="active"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/claims">Claims</a></li>
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/scope-mappings">Scope</a></li>
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/revocation">Revocation</a></li>
+        <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
     </ul>
     <div id="content">
         <ol class="breadcrumb" data-ng-hide="create">
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-credentials.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-credentials.html
index e95bcc1..b3b9839 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-credentials.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-credentials.html
@@ -8,6 +8,7 @@
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/claims">Claims</a></li>
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/scope-mappings">Scope</a></li>
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/revocation">Revocation</a></li>
+        <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
     </ul>
     <div id="content">
         <ol class="breadcrumb" data-ng-hide="create">
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-detail.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-detail.html
index d8b1dc4..dc834c3 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-detail.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-detail.html
@@ -8,6 +8,7 @@
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/claims">Claims</a></li>
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/scope-mappings">Scope</a></li>
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/revocation">Revocation</a></li>
+        <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
     </ul>
     <div id="content">
         <ol class="breadcrumb" data-ng-show="create">
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-installation.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-installation.html
index 2caaa81..9c527d7 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-installation.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-installation.html
@@ -9,6 +9,7 @@
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/claims">Claims</a></li>
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/scope-mappings">Scope</a></li>
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/revocation">Revocation</a></li>
+        <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
     </ul>
 
     <div class="top-nav" data-ng-show="create">
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-revocation.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-revocation.html
index 8295782..6b7e951 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-revocation.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-revocation.html
@@ -8,6 +8,7 @@
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/claims">Claims</a></li>
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/scope-mappings">Scope</a></li>
         <li class="active"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/revocation">Revocation</a></li>
+        <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
     </ul>
     <div id="content">
         <ol class="breadcrumb">
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-role-detail.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-role-detail.html
index a7bae45..8e3d603 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-role-detail.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-role-detail.html
@@ -8,6 +8,7 @@
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/claims">Claims</a></li>
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/scope-mappings">Scope</a></li>
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/revocation">Revocation</a></li>
+        <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
     </ul>
 
     <div id="content">
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-role-list.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-role-list.html
index 49329fe..77b4065 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-role-list.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-role-list.html
@@ -9,6 +9,7 @@
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/claims">Claims</a></li>
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/scope-mappings">Scope</a></li>
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/revocation">Revocation</a></li>
+        <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
     </ul>
 
     <div id="content">
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-scope-mappings.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-scope-mappings.html
index 5be878c..ac6dd6f 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-scope-mappings.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-scope-mappings.html
@@ -9,6 +9,7 @@
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/claims">Claims</a></li>
         <li class="active"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/scope-mappings">Scope</a></li>
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/revocation">Revocation</a></li>
+        <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
     </ul>
 
     <div id="content">
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-sessions.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-sessions.html
new file mode 100755
index 0000000..129647c
--- /dev/null
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-sessions.html
@@ -0,0 +1,60 @@
+<div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="'partials/realm-menu.html'"></div>
+<div id="content-area" class="col-md-9" role="main">
+    <ul class="nav nav-tabs nav-tabs-pf">
+        <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}">Settings</a></li>
+        <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/credentials">Credentials</a></li>
+        <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/installation">Installation</a></li>
+        <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/roles">Roles</a></li>
+        <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/claims">Claims</a></li>
+        <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/scope-mappings">Scope</a></li>
+        <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/revocation">Revocation</a></li>
+        <li class="active"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
+    </ul>
+    <div id="content">
+        <ol class="breadcrumb">
+            <li><a href="#/realms/{{realm.realm}}">{{application.name}}</a></li>
+            <li class="active">Application Sessions</li>
+        </ol>
+        <h2><span>{{application.name}}</span> Sessions</h2>
+        <form class="form-horizontal" name="sessionStats">
+            <fieldset class="border-top">
+                <div class="form-group">
+                    <label class="col-sm-2 control-label" for="activeSessions">Active Sessions</label>
+                    <div class="col-sm-4">
+                        <input class="form-control" type="text" id="activeSessions" name="activeSessions" data-ng-model="stats.activeSessions" ng-disabled="true">
+                    </div>
+                </div>
+                <div class="form-group">
+                    <label class="col-sm-2 control-label" for="activeUsers">Active Users</label>
+                    <div class="col-sm-4">
+                        <input class="form-control" type="text" id="activeUsers" name="activeUsers" data-ng-model="stats.activeUsers" ng-disabled="true">
+                    </div>
+                </div>
+            </fieldset>
+        </form>
+        <table class="table" data-ng-show="stats.activeSessions > 0">
+            <thead>
+            <tr>
+                <th class="kc-table-actions" colspan="3">
+                    <div class="pull-right">
+                        <a class="btn btn-primary" ng-click="logoutAll()">Logout All</a>
+                        <a class="btn btn-primary" ng-click="loadUsers()">Show Users</a>
+                    </div>
+                </th>
+            </tr>
+            <tr>
+                <th>User</th>
+                <th>Login Time</th>
+                <th></th>
+            </tr>
+            </thead>
+            <tbody>
+            <tr data-ng-repeat="(user, data) in users">
+                <td><a href="#/realms/{{realm.realm}}/users/{{user}}">{{user}}</a></td>
+                <td>{{data.whenLoggedIn | date:'medium'}}</td>
+                <td><a ng-click="logoutUser(user)">logout</a> </td>
+            </tr>
+            </tbody>
+        </table>
+    </div>
+</div>
\ No newline at end of file
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/role-mappings.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/role-mappings.html
index c290d7a..63e34da 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/role-mappings.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/role-mappings.html
@@ -4,6 +4,7 @@
         <li><a href="#/realms/{{realm.realm}}/users/{{user.username}}">Attributes</a></li>
         <li data-ng-show="access.manageUsers"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/user-credentials">Credentials</a></li>
         <li class="active"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/role-mappings">Role Mappings</a></li>
+        <li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/sessions">Sessions</a></li>
     </ul>
     <div id="content">
         <ol class="breadcrumb">
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/session-realm.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/session-realm.html
new file mode 100755
index 0000000..27a4e4e
--- /dev/null
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/session-realm.html
@@ -0,0 +1,37 @@
+<div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="'partials/realm-menu.html'"></div>
+<div id="content-area" class="col-md-9" role="main">
+    <ul class="nav nav-tabs nav-tabs-pf">
+        <li><a href="#/realms/{{realm.realm}}/sessions/revocation">Revocation</a></li>
+        <li class="active"><a href="#/realms/{{realm.realm}}/sessions/realm">Realm Sessions</a></li>
+    </ul>
+    <div id="content">
+        <ol class="breadcrumb">
+            <li><a href="#/realms/{{realm.realm}}">{{realm.realm}}</a></li>
+            <li class="active">Realm Sessions</li>
+        </ol>
+        <h2><span>{{realm.realm}}</span> Sessions</h2>
+        <table class="table">
+            <thead>
+            <tr>
+                <th class="kc-table-actions" colspan="3">
+                    <div class="pull-right">
+                        <a class="btn btn-primary" ng-click="logoutAll()">Logout All</a>
+                    </div>
+                </th>
+            </tr>
+            <tr>
+                <th>Application</th>
+                <th>Active Sessions</th>
+                <th>Active Users</th>
+            </tr>
+            </thead>
+            <tbody>
+            <tr data-ng-repeat="(application, data) in stats">
+                <td><a href="#/realms/{{realm.realm}}/applications/{{application}}/sessions">{{application}}</a></td>
+                <td>{{data.activeSessions}}</td>
+                <td>{{data.activeUsers}}</td>
+            </tr>
+            </tbody>
+        </table>
+    </div>
+</div>
\ No newline at end of file
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/session-revocation.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/session-revocation.html
index e8cf513..3ca24d0 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/session-revocation.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/session-revocation.html
@@ -2,6 +2,7 @@
 <div id="content-area" class="col-md-9" role="main">
     <ul class="nav nav-tabs nav-tabs-pf"  data-ng-show="!create">
         <li class="active"><a href="#/realms/{{realm.realm}}/sessions/revocation">Revocation</a></li>
+        <li><a href="#/realms/{{realm.realm}}/sessions/realm">Realm Sessions</a></li>
     </ul>
     <div id="content">
         <ol class="breadcrumb">
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/user-credentials.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/user-credentials.html
index 6d0fd70..38ca069 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/user-credentials.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/user-credentials.html
@@ -5,6 +5,7 @@
         <li><a href="#/realms/{{realm.realm}}/users/{{user.username}}">Attributes</a></li>
         <li class="active"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/user-credentials">Credentials</a></li>
         <li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/role-mappings">Role Mappings</a></li>
+        <li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/sessions">Sessions</a></li>
     </ul>
 
     <div id="content">
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/user-detail.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/user-detail.html
index 4ce093a..0894d18 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/user-detail.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/user-detail.html
@@ -5,6 +5,7 @@
         <li class="active"><a href="#/realms/{{realm.realm}}/users/{{user.username}}">Attributes</a></li>
         <li data-ng-show="access.manageUsers"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/user-credentials">Credentials</a></li>
         <li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/role-mappings">Role Mappings</a></li>
+        <li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/sessions">Sessions</a></li>
     </ul>
 
     <div id="content">
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/user-sessions.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/user-sessions.html
new file mode 100755
index 0000000..0011ad3
--- /dev/null
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/user-sessions.html
@@ -0,0 +1,41 @@
+<div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="'partials/realm-menu.html'"></div>
+<div id="content-area" class="col-md-9" role="main">
+    <ul class="nav nav-tabs nav-tabs-pf">
+        <li><a href="#/realms/{{realm.realm}}/users/{{user.username}}">Attributes</a></li>
+        <li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/user-credentials">Credentials</a></li>
+        <li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/role-mappings">Role Mappings</a></li>
+        <li class="active"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/sessions">Sessions</a></li>
+    </ul>
+    <div id="content">
+        <ol class="breadcrumb">
+            <li><a href="#/realms/{{realm.realm}}">{{realm.realm}}</a></li>
+            <li><a href="#/realms/{{realm.realm}}/users">Users</a></li>
+            <li><a href="#/realms/{{realm.realm}}/users/{{user.username}}">{{user.username}}</a></li>
+            <li class="active">User Sessions</li>
+        </ol>
+        <h2><span>{{user.username}}</span> Sessions</h2>
+        <table class="table">
+            <thead>
+            <tr>
+                <th class="kc-table-actions" colspan="3">
+                    <div class="pull-right">
+                        <a class="btn btn-primary" ng-click="logoutAll()">Logout</a>
+                    </div>
+                </th>
+            </tr>
+            <tr>
+                <th>Application</th>
+                <th>Login Time</th>
+                <th></th>
+            </tr>
+            </thead>
+            <tbody>
+            <tr data-ng-repeat="(application, data) in stats">
+                <td><a href="#/realms/{{realm.realm}}/applications/{{application}}/sessions">{{application}}</a></td>
+                <td>{{data.whenLoggedIn | date:'medium'}}</td>
+                <td><a ng-click="logoutApplication(application)">logout</a> </td>
+            </tr>
+            </tbody>
+        </table>
+    </div>
+</div>
\ No newline at end of file
diff --git a/core/src/main/java/org/keycloak/representations/adapters/action/AdminAction.java b/core/src/main/java/org/keycloak/representations/adapters/action/AdminAction.java
index 4340114..7fcc3a0 100755
--- a/core/src/main/java/org/keycloak/representations/adapters/action/AdminAction.java
+++ b/core/src/main/java/org/keycloak/representations/adapters/action/AdminAction.java
@@ -59,5 +59,13 @@ public abstract class AdminAction {
         this.resource = resource;
     }
 
+    public String getAction() {
+        return action;
+    }
+
+    public void setAction(String action) {
+        this.action = action;
+    }
+
     public abstract boolean validate();
 }
diff --git a/core/src/main/java/org/keycloak/representations/adapters/action/SessionStats.java b/core/src/main/java/org/keycloak/representations/adapters/action/SessionStats.java
index b272f72..84d08c1 100755
--- a/core/src/main/java/org/keycloak/representations/adapters/action/SessionStats.java
+++ b/core/src/main/java/org/keycloak/representations/adapters/action/SessionStats.java
@@ -1,6 +1,7 @@
 package org.keycloak.representations.adapters.action;
 
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -10,7 +11,7 @@ import java.util.Set;
 public class SessionStats {
     protected int activeSessions;
     protected int activeUsers;
-    protected Set<String> users;
+    protected Map<String, UserStats> users;
 
     public int getActiveSessions() {
         return activeSessions;
@@ -28,11 +29,11 @@ public class SessionStats {
         this.activeUsers = activeUsers;
     }
 
-    public Set<String> getUsers() {
+    public Map<String, UserStats> getUsers() {
         return users;
     }
 
-    public void setUsers(Set<String> users) {
+    public void setUsers(Map<String, UserStats> users) {
         this.users = users;
     }
 }
diff --git a/core-jaxrs/src/main/java/org/keycloak/SkeletonKeyContextResolver.java b/core-jaxrs/src/main/java/org/keycloak/SkeletonKeyContextResolver.java
index 805a843..01aafd6 100755
--- a/core-jaxrs/src/main/java/org/keycloak/SkeletonKeyContextResolver.java
+++ b/core-jaxrs/src/main/java/org/keycloak/SkeletonKeyContextResolver.java
@@ -18,11 +18,11 @@ public class SkeletonKeyContextResolver implements ContextResolver<ObjectMapper>
     protected ObjectMapper mapper = new ObjectMapper();
 
     public SkeletonKeyContextResolver() {
-        mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT);
+        mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
     }
 
     public SkeletonKeyContextResolver(boolean indent) {
-        mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT);
+        mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
         if (indent) {
             mapper.enable(SerializationConfig.Feature.INDENT_OUTPUT);
         }
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletAdminActionsHandler.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletAdminActionsHandler.java
index 64ea788..092b99b 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletAdminActionsHandler.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletAdminActionsHandler.java
@@ -24,6 +24,10 @@ import org.keycloak.util.StreamUtil;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -133,7 +137,7 @@ public class ServletAdminActionsHandler implements HttpHandler {
 
     protected boolean validateAction(HttpServletResponse response, AdminAction action) throws IOException {
         if (!action.validate()) {
-            log.warn("admin request failed, not validated");
+            log.warn("admin request failed, not validated" + action.getAction());
             response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Not validated");
             return false;
         }
@@ -160,8 +164,15 @@ public class ServletAdminActionsHandler implements HttpHandler {
         SessionStats stats = new SessionStats();
         stats.setActiveSessions(userSessionManagement.getActiveSessions());
         stats.setActiveUsers(userSessionManagement.getActiveUsers().size());
-        if (action.isListUsers()) stats.setUsers(userSessionManagement.getActiveUsers());
+        if (action.isListUsers() && userSessionManagement.getActiveSessions() > 0) {
+            Map<String, UserStats> list = new HashMap<String, UserStats>();
+            for (String user : userSessionManagement.getActiveUsers()) {
+                list.put(user, getUserStats(user));
+            }
+            stats.setUsers(list);
+        }
         response.setStatus(200);
+        response.setContentType("application/json");
         JsonSerialization.writeValueToStream(response.getOutputStream(), stats);
         return;
     }
@@ -171,16 +182,23 @@ public class ServletAdminActionsHandler implements HttpHandler {
         if (token == null) return;
         UserStatsAction action = JsonSerialization.readValue(token.getContent(), UserStatsAction.class);
         if (!validateAction(response, action)) return;
+        String user = action.getUser();
+        UserStats stats = getUserStats(user);
+        response.setStatus(200);
+        response.setContentType("application/json");
+        JsonSerialization.writeValueToStream(response.getOutputStream(), stats);
+        return;
+    }
+
+    protected UserStats getUserStats(String user) {
         UserStats stats = new UserStats();
-        Long loginTime = userSessionManagement.getUserLoginTime(action.getUser());
+        Long loginTime = userSessionManagement.getUserLoginTime(user);
         if (loginTime != null) {
             stats.setLoggedIn(true);
             stats.setWhenLoggedIn(loginTime);
         } else {
             stats.setLoggedIn(false);
         }
-        response.setStatus(200);
-        JsonSerialization.writeValueToStream(response.getOutputStream(), stats);
-        return;
+        return stats;
     }
 }
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UserSessionManagement.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UserSessionManagement.java
index 1ed9133..fcf502b 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UserSessionManagement.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UserSessionManagement.java
@@ -124,8 +124,8 @@ public class UserSessionManagement implements SessionListener {
         synchronized (userSessionMap) {
             UserSessions sessions = userSessionMap.get(username);
             if (sessions == null) {
-                UserSessions session = new UserSessions();
-                userSessionMap.put(username, session);
+                sessions = new UserSessions();
+                userSessionMap.put(username, sessions);
             }
             sessions.getSessionIds().add(sessionId);
         }
diff --git a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java
index f0dafea..96a49f0 100755
--- a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java
@@ -17,7 +17,9 @@ import org.keycloak.representations.adapters.action.UserStatsAction;
 
 import javax.ws.rs.client.Entity;
 import javax.ws.rs.core.Response;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -52,6 +54,18 @@ public class ResourceAdminManager {
                 return null;
             }
             SessionStats stats = response.readEntity(SessionStats.class);
+
+            // replace with username
+            if (users && stats.getUsers() != null) {
+                Map<String, UserStats> newUsers = new HashMap<String, UserStats>();
+                for (Map.Entry<String, UserStats> entry : stats.getUsers().entrySet()) {
+                    UserModel user = realm.getUserById(entry.getKey());
+                    if (user == null) continue;
+                    newUsers.put(user.getLoginName(), entry.getValue());
+
+                }
+                stats.setUsers(newUsers);
+            }
             return stats;
         } else {
             logger.info("no management url.");
@@ -94,7 +108,22 @@ public class ResourceAdminManager {
 
     }
 
-    public void singleLogOut(RealmModel realm, String user) {
+    public void logoutUser(RealmModel realm, String user) {
+        ResteasyClient client = new ResteasyClientBuilder()
+                .disableTrustManager() // todo fix this, should have a trust manager or a good default
+                .build();
+
+        try {
+            List<ApplicationModel> resources = realm.getApplications();
+            logger.debug("logging out {0} resources ", resources.size());
+            for (ApplicationModel resource : resources) {
+                logoutApplication(realm, resource, user, client);
+            }
+        } finally {
+            client.close();
+        }
+    }
+    public void logoutAll(RealmModel realm) {
         ResteasyClient client = new ResteasyClientBuilder()
                 .disableTrustManager() // todo fix this, should have a trust manager or a good default
                 .build();
@@ -103,14 +132,28 @@ public class ResourceAdminManager {
             List<ApplicationModel> resources = realm.getApplications();
             logger.debug("logging out {0} resources ", resources.size());
             for (ApplicationModel resource : resources) {
-                logoutResource(realm, resource, user, client);
+                logoutApplication(realm, resource, null, client);
             }
         } finally {
             client.close();
         }
     }
 
-    protected boolean logoutResource(RealmModel realm, ApplicationModel resource, String user, ResteasyClient client) {
+    public void logoutApplication(RealmModel realm, ApplicationModel resource, String user) {
+        ResteasyClient client = new ResteasyClientBuilder()
+                .disableTrustManager() // todo fix this, should have a trust manager or a good default
+                .build();
+
+        try {
+            logoutApplication(realm, resource, user, client);
+        } finally {
+            client.close();
+        }
+
+    }
+
+
+    protected boolean logoutApplication(RealmModel realm, ApplicationModel resource, String user, ResteasyClient client) {
         String managementUrl = resource.getManagementUrl();
         if (managementUrl != null) {
             LogoutAction adminAction = new LogoutAction(TokenIdGenerator.generateId(), (int)(System.currentTimeMillis() / 1000) + 30, resource.getName(), user);
@@ -122,7 +165,7 @@ public class ResourceAdminManager {
             logger.info("logout success.");
             return success;
         } else {
-            logger.info("logout failure.");
+            logger.info("Can't logout" + resource.getName() + " no mgmt url.");
             return false;
         }
     }
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java
index d9c8122..085bbec 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java
@@ -6,7 +6,9 @@ import org.keycloak.models.ApplicationModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
 import org.keycloak.representations.adapters.action.SessionStats;
+import org.keycloak.representations.adapters.action.UserStats;
 import org.keycloak.representations.idm.ApplicationRepresentation;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.services.managers.ApplicationManager;
@@ -18,12 +20,15 @@ import org.keycloak.util.JsonSerialization;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
 import javax.ws.rs.GET;
 import javax.ws.rs.NotFoundException;
 import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Application;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
@@ -38,7 +43,7 @@ import java.util.Set;
  * @version $Revision: 1 $
  */
 public class ApplicationResource {
-    protected static final Logger logger = Logger.getLogger(RealmAdminResource.class);
+    protected static final Logger logger = Logger.getLogger(ApplicationResource.class);
     protected RealmModel realm;
     private RealmAuth auth;
     protected ApplicationModel application;
@@ -200,10 +205,45 @@ public class ApplicationResource {
     @GET
     @NoCache
     @Produces(MediaType.APPLICATION_JSON)
-    public SessionStats getSessionStats() {
-        return new ResourceAdminManager().getSessionStats(realm, application, true);
+    public SessionStats getSessionStats(@QueryParam("users") @DefaultValue("false") boolean users) {
+        logger.info("session-stats");
+        auth.requireView();
+        if (application.getManagementUrl() == null || application.getManagementUrl().trim().equals("")) {
+            logger.info("sending empty stats");
+            SessionStats stats = new SessionStats();
+            if (users) stats.setUsers(new HashMap<String, UserStats>());
+            return stats;
+        }
+        SessionStats stats = new ResourceAdminManager().getSessionStats(realm, application, users);
+        if (stats == null) {
+            logger.info("app returned null stats");
+        } else {
+            logger.info("activeUsers: " + stats.getActiveUsers());
+            logger.info("activeSessions: " + stats.getActiveSessions());
+        }
+        return stats;
+     }
+
+    @Path("logout-all")
+    @POST
+    public void logoutAll() {
+        auth.requireManage();
+        new ResourceAdminManager().logoutApplication(realm, application, null);
     }
 
+    @Path("logout-user/{username}")
+    @POST
+    public void logout(final @PathParam("username") String username) {
+        auth.requireManage();
+        UserModel user = realm.getUser(username);
+        if (user == null) {
+            throw new NotFoundException();
+        }
+        new ResourceAdminManager().logoutApplication(realm, application, user.getId());
+    }
+
+
+
 
 
 
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index 8c56749..c9bf4f4 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -118,12 +118,20 @@ public class RealmAdminResource {
         new ResourceAdminManager().pushRealmRevocationPolicy(realm);
     }
 
+    @Path("logout-all")
+    @POST
+    public void logoutAll() {
+        auth.requireManage();
+        new ResourceAdminManager().logoutAll(realm);
+    }
+
     @Path("session-stats")
     @GET
     @NoCache
     @Produces(MediaType.APPLICATION_JSON)
     public Map<String,SessionStats> getSessionStats() {
-
+        logger.info("session-stats");
+        auth.requireView();
         Map<String, SessionStats> stats = new HashMap<String, SessionStats>();
         for (ApplicationModel applicationModel : realm.getApplications()) {
             if (applicationModel.getManagementUrl() == null) continue;
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
index 605f739..e3f9bbe 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
@@ -153,6 +153,7 @@ public class UsersResource {
     @NoCache
     @Produces(MediaType.APPLICATION_JSON)
     public Map<String,UserStats> getSessionStats(final @PathParam("username") String username) {
+        logger.info("session-stats");
         auth.requireView();
         UserModel user = realm.getUser(username);
         if (user == null) {
@@ -162,11 +163,23 @@ public class UsersResource {
         for (ApplicationModel applicationModel : realm.getApplications()) {
             if (applicationModel.getManagementUrl() == null) continue;
             UserStats appStats = new ResourceAdminManager().getUserStats(realm, applicationModel, user);
-            stats.put(applicationModel.getName(), appStats);
+            if (appStats.isLoggedIn()) stats.put(applicationModel.getName(), appStats);
         }
         return stats;
     }
 
+    @Path("{username}/logout")
+    @POST
+    public void logout(final @PathParam("username") String username) {
+        auth.requireManage();
+        UserModel user = realm.getUser(username);
+        if (user == null) {
+            throw new NotFoundException();
+        }
+        new ResourceAdminManager().logoutUser(realm, user.getId());
+    }
+
+
 
     @Path("{username}")
     @DELETE
diff --git a/services/src/main/java/org/keycloak/services/resources/TokenService.java b/services/src/main/java/org/keycloak/services/resources/TokenService.java
index bfc1f20..0cae7c2 100755
--- a/services/src/main/java/org/keycloak/services/resources/TokenService.java
+++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java
@@ -5,17 +5,13 @@ import org.jboss.resteasy.logging.Logger;
 import org.jboss.resteasy.spi.HttpRequest;
 import org.jboss.resteasy.spi.HttpResponse;
 import org.keycloak.OAuthErrorException;
-import org.keycloak.jose.jws.JWSBuilder;
 import org.keycloak.jose.jws.JWSInput;
 import org.keycloak.jose.jws.crypto.RSAProvider;
-import org.keycloak.models.ApplicationModel;
 import org.keycloak.models.ClientModel;
-import org.keycloak.models.Constants;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakTransaction;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RequiredCredentialModel;
-import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.representations.AccessToken;
@@ -54,7 +50,6 @@ import javax.ws.rs.core.SecurityContext;
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
 import javax.ws.rs.ext.Providers;
-import java.security.PrivateKey;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -563,7 +558,7 @@ public class TokenService {
             logger.info("Logging out: {0}", user.getLoginName());
             authManager.expireIdentityCookie(realm, uriInfo);
             authManager.expireRememberMeCookie(realm, uriInfo);
-            resourceAdminManager.singleLogOut(realm, user.getId());
+            resourceAdminManager.logoutUser(realm, user.getId());
         } else {
             logger.info("No user logged in for logout");
         }