commit
34260b7428
41 changed files with 952 additions and 82 deletions
|
@ -176,6 +176,21 @@ module.config([ '$routeProvider', function($routeProvider) {
|
||||||
},
|
},
|
||||||
controller : 'UserRoleMappingCtrl'
|
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', {
|
.when('/realms/:realm/users', {
|
||||||
templateUrl : 'partials/user-list.html',
|
templateUrl : 'partials/user-list.html',
|
||||||
resolve : {
|
resolve : {
|
||||||
|
@ -292,6 +307,21 @@ module.config([ '$routeProvider', function($routeProvider) {
|
||||||
},
|
},
|
||||||
controller : 'ApplicationClaimsCtrl'
|
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', {
|
.when('/realms/:realm/applications/:application/credentials', {
|
||||||
templateUrl : 'partials/application-credentials.html',
|
templateUrl : 'partials/application-credentials.html',
|
||||||
resolve : {
|
resolve : {
|
||||||
|
@ -540,6 +570,18 @@ module.config([ '$routeProvider', function($routeProvider) {
|
||||||
},
|
},
|
||||||
controller : 'RealmRevocationCtrl'
|
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({
|
.otherwise({
|
||||||
templateUrl : 'partials/notfound.html'
|
templateUrl : 'partials/notfound.html'
|
||||||
|
|
|
@ -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,
|
module.controller('ApplicationClaimsCtrl', function($scope, realm, application, claims,
|
||||||
ApplicationClaims,
|
ApplicationClaims,
|
||||||
$http, $location, Dialog, Notifications) {
|
$http, $location, Dialog, Notifications) {
|
||||||
|
|
|
@ -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) {
|
module.controller('RealmRevocationCtrl', function($scope, Realm, RealmPushRevocation, realm, $http, $location, Dialog, Notifications) {
|
||||||
$scope.realm = angular.copy(realm);
|
$scope.realm = angular.copy(realm);
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
module.controller('UserListCtrl', function($scope, realm, User) {
|
||||||
$scope.realm = realm;
|
$scope.realm = realm;
|
||||||
$scope.searchQuery = function() {
|
$scope.searchQuery = function() {
|
||||||
|
|
|
@ -48,20 +48,37 @@ module.factory('RealmLoader', function(Loader, Realm, $route, $q) {
|
||||||
});
|
});
|
||||||
|
|
||||||
module.factory('UserListLoader', function(Loader, User, $route, $q) {
|
module.factory('UserListLoader', function(Loader, User, $route, $q) {
|
||||||
return Loader.query(User, function() {
|
return Loader.query(User, function() {
|
||||||
return {
|
return {
|
||||||
realm : $route.current.params.realm
|
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) {
|
module.factory('UserLoader', function(Loader, User, $route, $q) {
|
||||||
return Loader.get(User, function() {
|
return Loader.get(User, function() {
|
||||||
return {
|
return {
|
||||||
realm : $route.current.params.realm,
|
realm : $route.current.params.realm,
|
||||||
userId : $route.current.params.user
|
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) {
|
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) {
|
module.factory('ApplicationClaimsLoader', function(Loader, ApplicationClaims, $route, $q) {
|
||||||
return Loader.get(ApplicationClaims, function() {
|
return Loader.get(ApplicationClaims, function() {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -147,14 +147,27 @@ module.factory('ServerInfo', function($resource) {
|
||||||
});
|
});
|
||||||
|
|
||||||
module.factory('User', function($resource) {
|
module.factory('User', function($resource) {
|
||||||
return $resource('/auth/rest/admin/realms/:realm/users/:userId', {
|
return $resource('/auth/rest/admin/realms/:realm/users/:userId', {
|
||||||
realm : '@realm',
|
realm : '@realm',
|
||||||
userId : '@userId'
|
userId : '@userId'
|
||||||
}, {
|
}, {
|
||||||
update : {
|
update : {
|
||||||
method : 'PUT'
|
method : 'PUT'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.factory('UserSessionStats', function($resource) {
|
||||||
|
return $resource('/auth/rest/admin/realms/:realm/users/:user/session-stats', {
|
||||||
|
realm : '@realm',
|
||||||
|
user : '@user'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
module.factory('UserLogout', function($resource) {
|
||||||
|
return $resource('/auth/rest/admin/realms/:realm/users/:user/logout', {
|
||||||
|
realm : '@realm',
|
||||||
|
user : '@user'
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
module.factory('UserCredentials', function($resource) {
|
module.factory('UserCredentials', function($resource) {
|
||||||
|
@ -241,6 +254,13 @@ module.factory('RealmPushRevocation', function($resource) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.factory('RealmSessionStats', function($resource) {
|
||||||
|
return $resource('/auth/rest/admin/realms/:realm/session-stats', {
|
||||||
|
realm : '@realm'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
module.factory('RoleApplicationComposites', function($resource) {
|
module.factory('RoleApplicationComposites', function($resource) {
|
||||||
return $resource('/auth/rest/admin/realms/:realm/roles-by-id/:role/composites/applications/:application', {
|
return $resource('/auth/rest/admin/realms/:realm/roles-by-id/:role/composites/applications/:application', {
|
||||||
realm : '@realm',
|
realm : '@realm',
|
||||||
|
@ -456,6 +476,7 @@ module.factory('ApplicationRole', function($resource) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
module.factory('ApplicationClaims', function($resource) {
|
module.factory('ApplicationClaims', function($resource) {
|
||||||
return $resource('/auth/rest/admin/realms/:realm/applications/:application/claims', {
|
return $resource('/auth/rest/admin/realms/:realm/applications/:application/claims', {
|
||||||
realm : '@realm',
|
realm : '@realm',
|
||||||
|
@ -467,6 +488,39 @@ module.factory('ApplicationClaims', function($resource) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.factory('ApplicationSessionStats', function($resource) {
|
||||||
|
return $resource('/auth/rest/admin/realms/:realm/applications/:application/session-stats', {
|
||||||
|
realm : '@realm',
|
||||||
|
application : "@application"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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) {
|
module.factory('ApplicationPushRevocation', function($resource) {
|
||||||
return $resource('/auth/rest/admin/realms/:realm/applications/:application/push-revocation', {
|
return $resource('/auth/rest/admin/realms/:realm/applications/:application/push-revocation', {
|
||||||
realm : '@realm',
|
realm : '@realm',
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
<li class="active"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/claims">Claims</a></li>
|
<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}}/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}}/revocation">Revocation</a></li>
|
||||||
|
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<ol class="breadcrumb" data-ng-hide="create">
|
<ol class="breadcrumb" data-ng-hide="create">
|
||||||
|
|
|
@ -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}}/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}}/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}}/revocation">Revocation</a></li>
|
||||||
|
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<ol class="breadcrumb" data-ng-hide="create">
|
<ol class="breadcrumb" data-ng-hide="create">
|
||||||
|
|
|
@ -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}}/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}}/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}}/revocation">Revocation</a></li>
|
||||||
|
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<ol class="breadcrumb" data-ng-show="create">
|
<ol class="breadcrumb" data-ng-show="create">
|
||||||
|
|
|
@ -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}}/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}}/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}}/revocation">Revocation</a></li>
|
||||||
|
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="top-nav" data-ng-show="create">
|
<div class="top-nav" data-ng-show="create">
|
||||||
|
|
|
@ -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}}/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}}/scope-mappings">Scope</a></li>
|
||||||
<li class="active"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/revocation">Revocation</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>
|
</ul>
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
|
|
|
@ -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}}/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}}/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}}/revocation">Revocation</a></li>
|
||||||
|
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div id="content">
|
<div id="content">
|
||||||
|
|
|
@ -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}}/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}}/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}}/revocation">Revocation</a></li>
|
||||||
|
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div id="content">
|
<div id="content">
|
||||||
|
|
|
@ -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}}/claims">Claims</a></li>
|
||||||
<li class="active"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/scope-mappings">Scope</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}}/revocation">Revocation</a></li>
|
||||||
|
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div id="content">
|
<div id="content">
|
||||||
|
|
|
@ -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>
|
|
@ -4,6 +4,7 @@
|
||||||
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}">Attributes</a></li>
|
<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 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 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>
|
</ul>
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
|
|
|
@ -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>
|
|
@ -2,6 +2,7 @@
|
||||||
<div id="content-area" class="col-md-9" role="main">
|
<div id="content-area" class="col-md-9" role="main">
|
||||||
<ul class="nav nav-tabs nav-tabs-pf" data-ng-show="!create">
|
<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 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>
|
</ul>
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}">Attributes</a></li>
|
<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 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}}/role-mappings">Role Mappings</a></li>
|
||||||
|
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/sessions">Sessions</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div id="content">
|
<div id="content">
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
<li class="active"><a href="#/realms/{{realm.realm}}/users/{{user.username}}">Attributes</a></li>
|
<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 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}}/role-mappings">Role Mappings</a></li>
|
||||||
|
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/sessions">Sessions</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div id="content">
|
<div id="content">
|
||||||
|
|
|
@ -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>
|
|
@ -18,11 +18,11 @@ public class SkeletonKeyContextResolver implements ContextResolver<ObjectMapper>
|
||||||
protected ObjectMapper mapper = new ObjectMapper();
|
protected ObjectMapper mapper = new ObjectMapper();
|
||||||
|
|
||||||
public SkeletonKeyContextResolver() {
|
public SkeletonKeyContextResolver() {
|
||||||
mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT);
|
mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SkeletonKeyContextResolver(boolean indent) {
|
public SkeletonKeyContextResolver(boolean indent) {
|
||||||
mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT);
|
mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
|
||||||
if (indent) {
|
if (indent) {
|
||||||
mapper.enable(SerializationConfig.Feature.INDENT_OUTPUT);
|
mapper.enable(SerializationConfig.Feature.INDENT_OUTPUT);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ public interface AdapterConstants {
|
||||||
// URL endpoints
|
// URL endpoints
|
||||||
public static final String K_LOGOUT = "k_logout";
|
public static final String K_LOGOUT = "k_logout";
|
||||||
public static final String K_PUSH_NOT_BEFORE = "k_push_not_before";
|
public static final String K_PUSH_NOT_BEFORE = "k_push_not_before";
|
||||||
|
public static final String K_GET_USER_STATS = "k_get_user_stats";
|
||||||
|
public static final String K_GET_SESSION_STATS = "k_get_session_stats";
|
||||||
public static final String K_QUERY_BEARER_TOKEN = "k_query_bearer_token";
|
public static final String K_QUERY_BEARER_TOKEN = "k_query_bearer_token";
|
||||||
|
|
||||||
// This param name is defined again in Keycloak Subsystem class
|
// This param name is defined again in Keycloak Subsystem class
|
||||||
|
|
|
@ -8,18 +8,20 @@ import org.codehaus.jackson.annotate.JsonIgnore;
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class AdminAction {
|
public abstract class AdminAction {
|
||||||
protected String id;
|
protected String id;
|
||||||
protected int expiration;
|
protected int expiration;
|
||||||
protected String resource;
|
protected String resource;
|
||||||
|
protected String action;
|
||||||
|
|
||||||
public AdminAction() {
|
public AdminAction() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public AdminAction(String id, int expiration, String resource) {
|
public AdminAction(String id, int expiration, String resource, String action) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.expiration = expiration;
|
this.expiration = expiration;
|
||||||
this.resource = resource;
|
this.resource = resource;
|
||||||
|
this.action = action;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
|
@ -56,4 +58,14 @@ public class AdminAction {
|
||||||
public void setResource(String resource) {
|
public void setResource(String resource) {
|
||||||
this.resource = resource;
|
this.resource = resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getAction() {
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAction(String action) {
|
||||||
|
this.action = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract boolean validate();
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,13 +5,14 @@ package org.keycloak.representations.adapters.action;
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class LogoutAction extends AdminAction {
|
public class LogoutAction extends AdminAction {
|
||||||
|
public static final String LOGOUT = "LOGOUT";
|
||||||
protected String user;
|
protected String user;
|
||||||
|
|
||||||
public LogoutAction() {
|
public LogoutAction() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public LogoutAction(String id, int expiration, String resource, String user) {
|
public LogoutAction(String id, int expiration, String resource, String user) {
|
||||||
super(id, expiration, resource);
|
super(id, expiration, resource, LOGOUT);
|
||||||
this.user = user;
|
this.user = user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,4 +23,9 @@ public class LogoutAction extends AdminAction {
|
||||||
public void setUser(String user) {
|
public void setUser(String user) {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean validate() {
|
||||||
|
return LOGOUT.equals(action);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,13 +6,14 @@ package org.keycloak.representations.adapters.action;
|
||||||
*/
|
*/
|
||||||
public class PushNotBeforeAction extends AdminAction {
|
public class PushNotBeforeAction extends AdminAction {
|
||||||
|
|
||||||
|
public static final String PUSH_NOT_BEFORE = "PUSH_NOT_BEFORE";
|
||||||
protected int notBefore;
|
protected int notBefore;
|
||||||
|
|
||||||
public PushNotBeforeAction() {
|
public PushNotBeforeAction() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public PushNotBeforeAction(String id, int expiration, String resource, int notBefore) {
|
public PushNotBeforeAction(String id, int expiration, String resource, int notBefore) {
|
||||||
super(id, expiration, resource);
|
super(id, expiration, resource, PUSH_NOT_BEFORE);
|
||||||
this.notBefore = notBefore;
|
this.notBefore = notBefore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,4 +24,10 @@ public class PushNotBeforeAction extends AdminAction {
|
||||||
public void setNotBefore(int notBefore) {
|
public void setNotBefore(int notBefore) {
|
||||||
this.notBefore = notBefore;
|
this.notBefore = notBefore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean validate() {
|
||||||
|
return PUSH_NOT_BEFORE.equals(action);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
package org.keycloak.representations.adapters.action;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class SessionStats {
|
||||||
|
protected int activeSessions;
|
||||||
|
protected int activeUsers;
|
||||||
|
protected Map<String, UserStats> users;
|
||||||
|
|
||||||
|
public int getActiveSessions() {
|
||||||
|
return activeSessions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActiveSessions(int activeSessions) {
|
||||||
|
this.activeSessions = activeSessions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getActiveUsers() {
|
||||||
|
return activeUsers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActiveUsers(int activeUsers) {
|
||||||
|
this.activeUsers = activeUsers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, UserStats> getUsers() {
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsers(Map<String, UserStats> users) {
|
||||||
|
this.users = users;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package org.keycloak.representations.adapters.action;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query session stats.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class SessionStatsAction extends AdminAction {
|
||||||
|
|
||||||
|
public static final String SESSION_STATS = "SESSION_STATS";
|
||||||
|
|
||||||
|
protected boolean listUsers;
|
||||||
|
|
||||||
|
public SessionStatsAction() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionStatsAction(String id, int expiration, String resource) {
|
||||||
|
super(id, expiration, resource, SESSION_STATS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isListUsers() {
|
||||||
|
return listUsers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setListUsers(boolean listUsers) {
|
||||||
|
this.listUsers = listUsers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean validate() {
|
||||||
|
return SESSION_STATS.equals(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package org.keycloak.representations.adapters.action;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class UserStats {
|
||||||
|
protected boolean loggedIn;
|
||||||
|
protected long whenLoggedIn;
|
||||||
|
|
||||||
|
public boolean isLoggedIn() {
|
||||||
|
return loggedIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLoggedIn(boolean loggedIn) {
|
||||||
|
this.loggedIn = loggedIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getWhenLoggedIn() {
|
||||||
|
return whenLoggedIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWhenLoggedIn(long whenLoggedIn) {
|
||||||
|
this.whenLoggedIn = whenLoggedIn;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package org.keycloak.representations.adapters.action;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query session stats.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class UserStatsAction extends AdminAction {
|
||||||
|
|
||||||
|
public static final String USER_STATS = "USER_STATS";
|
||||||
|
protected String user;
|
||||||
|
|
||||||
|
public UserStatsAction() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserStatsAction(String id, int expiration, String resource, String user) {
|
||||||
|
super(id, expiration, resource, USER_STATS);
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean validate() {
|
||||||
|
return USER_STATS.equals(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import org.codehaus.jackson.map.annotate.JsonSerialize;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility class to handle simple JSON serializable for Keycloak.
|
* Utility class to handle simple JSON serializable for Keycloak.
|
||||||
|
@ -25,6 +26,11 @@ public class JsonSerialization {
|
||||||
prettyMapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
|
prettyMapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void writeValueToStream(OutputStream os, Object obj) throws IOException {
|
||||||
|
mapper.writeValue(os, obj);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public static String writeValueAsString(Object obj) throws IOException {
|
public static String writeValueAsString(Object obj) throws IOException {
|
||||||
return mapper.writeValueAsString(obj);
|
return mapper.writeValueAsString(obj);
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,7 @@ public class RefreshableKeycloakSession extends KeycloakSecurityContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refreshExpiredToken() {
|
public void refreshExpiredToken() {
|
||||||
|
log.info("checking whether to refresh.");
|
||||||
if (isActive()) return;
|
if (isActive()) return;
|
||||||
if (this.realmConfiguration == null || refreshToken == null) return; // Might be serialized in HttpSession?
|
if (this.realmConfiguration == null || refreshToken == null) return; // Might be serialized in HttpSession?
|
||||||
|
|
||||||
|
@ -75,6 +76,10 @@ public class RefreshableKeycloakSession extends KeycloakSecurityContext {
|
||||||
} catch (VerificationException e) {
|
} catch (VerificationException e) {
|
||||||
log.error("failed verification of token");
|
log.error("failed verification of token");
|
||||||
}
|
}
|
||||||
|
if (response.getNotBeforePolicy() > realmConfiguration.getNotBefore()) {
|
||||||
|
realmConfiguration.setNotBefore(response.getNotBeforePolicy());
|
||||||
|
}
|
||||||
|
|
||||||
this.token = token;
|
this.token = token;
|
||||||
this.refreshToken = response.getRefreshToken();
|
this.refreshToken = response.getRefreshToken();
|
||||||
this.tokenString = tokenString;
|
this.tokenString = tokenString;
|
||||||
|
|
|
@ -156,7 +156,7 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
|
||||||
|
|
||||||
protected void pushNotBefore(JWSInput token, HttpServletResponse response) throws IOException {
|
protected void pushNotBefore(JWSInput token, HttpServletResponse response) throws IOException {
|
||||||
try {
|
try {
|
||||||
log.debug("->> pushNotBefore: ");
|
log.info("->> pushNotBefore: ");
|
||||||
PushNotBeforeAction action = JsonSerialization.readValue(token.getContent(), PushNotBeforeAction.class);
|
PushNotBeforeAction action = JsonSerialization.readValue(token.getContent(), PushNotBeforeAction.class);
|
||||||
if (action.isExpired()) {
|
if (action.isExpired()) {
|
||||||
log.warn("admin request failed, expired token");
|
log.warn("admin request failed, expired token");
|
||||||
|
|
|
@ -12,12 +12,22 @@ import org.keycloak.adapters.ResourceMetadata;
|
||||||
import org.keycloak.adapters.config.RealmConfiguration;
|
import org.keycloak.adapters.config.RealmConfiguration;
|
||||||
import org.keycloak.jose.jws.JWSInput;
|
import org.keycloak.jose.jws.JWSInput;
|
||||||
import org.keycloak.jose.jws.crypto.RSAProvider;
|
import org.keycloak.jose.jws.crypto.RSAProvider;
|
||||||
|
import org.keycloak.representations.adapters.action.AdminAction;
|
||||||
import org.keycloak.representations.adapters.action.PushNotBeforeAction;
|
import org.keycloak.representations.adapters.action.PushNotBeforeAction;
|
||||||
|
import org.keycloak.representations.adapters.action.SessionStats;
|
||||||
|
import org.keycloak.representations.adapters.action.SessionStatsAction;
|
||||||
|
import org.keycloak.representations.adapters.action.UserStats;
|
||||||
|
import org.keycloak.representations.adapters.action.UserStatsAction;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
import org.keycloak.util.StreamUtil;
|
import org.keycloak.util.StreamUtil;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
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>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
@ -101,6 +111,12 @@ public class ServletAdminActionsHandler implements HttpHandler {
|
||||||
} else if (requestUri.endsWith(AdapterConstants.K_PUSH_NOT_BEFORE)) {
|
} else if (requestUri.endsWith(AdapterConstants.K_PUSH_NOT_BEFORE)) {
|
||||||
handlePushNotBefore(request, response);
|
handlePushNotBefore(request, response);
|
||||||
return;
|
return;
|
||||||
|
} else if (requestUri.endsWith(AdapterConstants.K_GET_SESSION_STATS)) {
|
||||||
|
handleGetSessionStats(request, response);
|
||||||
|
return;
|
||||||
|
}else if (requestUri.endsWith(AdapterConstants.K_GET_USER_STATS)) {
|
||||||
|
handleGetUserStats(request, response);
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
next.handleRequest(exchange);
|
next.handleRequest(exchange);
|
||||||
return;
|
return;
|
||||||
|
@ -110,20 +126,79 @@ public class ServletAdminActionsHandler implements HttpHandler {
|
||||||
protected void handlePushNotBefore(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
protected void handlePushNotBefore(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||||
log.info("K_PUSH_NOT_BEFORE sent");
|
log.info("K_PUSH_NOT_BEFORE sent");
|
||||||
JWSInput token = verifyAdminRequest(request, response);
|
JWSInput token = verifyAdminRequest(request, response);
|
||||||
if (token == null) return;
|
if (token == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
PushNotBeforeAction action = JsonSerialization.readValue(token.getContent(), PushNotBeforeAction.class);
|
PushNotBeforeAction action = JsonSerialization.readValue(token.getContent(), PushNotBeforeAction.class);
|
||||||
|
if (!validateAction(response, action)) return;
|
||||||
|
realmConfig.setNotBefore(action.getNotBefore());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean validateAction(HttpServletResponse response, AdminAction action) throws IOException {
|
||||||
|
if (!action.validate()) {
|
||||||
|
log.warn("admin request failed, not validated" + action.getAction());
|
||||||
|
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Not validated");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (action.isExpired()) {
|
if (action.isExpired()) {
|
||||||
log.warn("admin request failed, expired token");
|
log.warn("admin request failed, expired token");
|
||||||
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Expired token");
|
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Expired token");
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
if (!resourceMetadata.getResourceName().equals(action.getResource())) {
|
if (!resourceMetadata.getResourceName().equals(action.getResource())) {
|
||||||
log.warn("Resource name does not match");
|
log.warn("Resource name does not match");
|
||||||
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Resource name does not match");
|
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Resource name does not match");
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
realmConfig.setNotBefore(action.getNotBefore());
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void handleGetSessionStats(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||||
|
log.info("K_GET_SESSION_STATS sent");
|
||||||
|
JWSInput token = verifyAdminRequest(request, response);
|
||||||
|
if (token == null) return;
|
||||||
|
SessionStatsAction action = JsonSerialization.readValue(token.getContent(), SessionStatsAction.class);
|
||||||
|
if (!validateAction(response, action)) return;
|
||||||
|
SessionStats stats = new SessionStats();
|
||||||
|
stats.setActiveSessions(userSessionManagement.getActiveSessions());
|
||||||
|
stats.setActiveUsers(userSessionManagement.getActiveUsers().size());
|
||||||
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
protected void handleGetUserStats(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||||
|
log.info("K_GET_USER_STATS sent");
|
||||||
|
JWSInput token = verifyAdminRequest(request, response);
|
||||||
|
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(user);
|
||||||
|
if (loginTime != null) {
|
||||||
|
stats.setLoggedIn(true);
|
||||||
|
stats.setWhenLoggedIn(loginTime);
|
||||||
|
} else {
|
||||||
|
stats.setLoggedIn(false);
|
||||||
|
}
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||||
public class UserSessionManagement implements SessionListener {
|
public class UserSessionManagement implements SessionListener {
|
||||||
private static final Logger log = Logger.getLogger(UserSessionManagement.class);
|
private static final Logger log = Logger.getLogger(UserSessionManagement.class);
|
||||||
private static final String AUTH_SESSION_NAME = CachedAuthenticatedSessionHandler.class.getName() + ".AuthenticatedSession";
|
private static final String AUTH_SESSION_NAME = CachedAuthenticatedSessionHandler.class.getName() + ".AuthenticatedSession";
|
||||||
protected ConcurrentHashMap<String, Set<String>> userSessionMap = new ConcurrentHashMap<String, Set<String>>();
|
protected ConcurrentHashMap<String, UserSessions> userSessionMap = new ConcurrentHashMap<String, UserSessions>();
|
||||||
|
|
||||||
protected RealmConfiguration realmInfo;
|
protected RealmConfiguration realmInfo;
|
||||||
|
|
||||||
|
@ -39,6 +39,51 @@ public class UserSessionManagement implements SessionListener {
|
||||||
this.realmInfo = realmInfo;
|
this.realmInfo = realmInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class UserSessions {
|
||||||
|
protected Set<String> sessionIds = new HashSet<String>();
|
||||||
|
protected long loggedIn = System.currentTimeMillis();
|
||||||
|
|
||||||
|
public Set<String> getSessionIds() {
|
||||||
|
return sessionIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLoggedIn() {
|
||||||
|
return loggedIn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNumUserLogins() {
|
||||||
|
return userSessionMap.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getActiveSessions() {
|
||||||
|
int active = 0;
|
||||||
|
synchronized (userSessionMap) {
|
||||||
|
for (UserSessions sessions : userSessionMap.values()) {
|
||||||
|
active += sessions.getSessionIds().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return active;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param username
|
||||||
|
* @return null if user not logged in
|
||||||
|
*/
|
||||||
|
public Long getUserLoginTime(String username) {
|
||||||
|
UserSessions sessions = userSessionMap.get(username);
|
||||||
|
if (sessions == null) return null;
|
||||||
|
return sessions.getLoggedIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getActiveUsers() {
|
||||||
|
HashSet<String> set = new HashSet<String>();
|
||||||
|
set.addAll(userSessionMap.keySet());
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
public void remoteLogout(JWSInput token, SessionManager manager, HttpServletResponse response) throws IOException {
|
public void remoteLogout(JWSInput token, SessionManager manager, HttpServletResponse response) throws IOException {
|
||||||
try {
|
try {
|
||||||
log.info("->> remoteLogout: ");
|
log.info("->> remoteLogout: ");
|
||||||
|
@ -77,28 +122,22 @@ public class UserSessionManagement implements SessionListener {
|
||||||
|
|
||||||
protected void addAuthenticatedSession(String username, String sessionId) {
|
protected void addAuthenticatedSession(String username, String sessionId) {
|
||||||
synchronized (userSessionMap) {
|
synchronized (userSessionMap) {
|
||||||
Set<String> map = userSessionMap.get(username);
|
UserSessions sessions = userSessionMap.get(username);
|
||||||
if (map == null) {
|
if (sessions == null) {
|
||||||
final Set<String> value = new HashSet<String>();
|
sessions = new UserSessions();
|
||||||
map = userSessionMap.putIfAbsent(username, value);
|
userSessionMap.put(username, sessions);
|
||||||
if (map == null) {
|
|
||||||
map = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
synchronized (map) {
|
sessions.getSessionIds().add(sessionId);
|
||||||
map.add(sessionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void removeAuthenticatedSession(String sessionId, String username) {
|
protected void removeAuthenticatedSession(String sessionId, String username) {
|
||||||
synchronized (userSessionMap) {
|
synchronized (userSessionMap) {
|
||||||
Set<String> map = userSessionMap.get(username);
|
UserSessions sessions = userSessionMap.get(username);
|
||||||
if (map == null) return;
|
if (sessions == null) return;
|
||||||
synchronized (map) {
|
sessions.getSessionIds().remove(sessionId);
|
||||||
map.remove(sessionId);
|
if (sessions.getSessionIds().isEmpty()) {
|
||||||
if (map.isEmpty()) userSessionMap.remove(username);
|
userSessionMap.remove(username);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,24 +158,24 @@ public class UserSessionManagement implements SessionListener {
|
||||||
|
|
||||||
public void logout(SessionManager manager, String user) {
|
public void logout(SessionManager manager, String user) {
|
||||||
log.info("logoutUser: " + user);
|
log.info("logoutUser: " + user);
|
||||||
Set<String> map = userSessionMap.remove(user);
|
UserSessions sessions = null;
|
||||||
if (map == null) {
|
synchronized (userSessionMap) {
|
||||||
|
sessions = userSessionMap.remove(user);
|
||||||
|
}
|
||||||
|
if (sessions == null) {
|
||||||
log.info("no session for user: " + user);
|
log.info("no session for user: " + user);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
log.info("found session for user");
|
log.info("found session for user");
|
||||||
synchronized (map) {
|
for (String id : sessions.getSessionIds()) {
|
||||||
for (String id : map) {
|
log.debug("invalidating session for user: " + user);
|
||||||
log.debug("invalidating session for user: " + user);
|
Session session = manager.getSession(id);
|
||||||
Session session = manager.getSession(id);
|
try {
|
||||||
try {
|
session.invalidate(null);
|
||||||
session.invalidate(null);
|
} catch (Exception e) {
|
||||||
} catch (Exception e) {
|
log.warn("Session already invalidated.");
|
||||||
log.warn("Session already invalidated.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -7,12 +7,19 @@ import org.keycloak.TokenIdGenerator;
|
||||||
import org.keycloak.adapters.AdapterConstants;
|
import org.keycloak.adapters.AdapterConstants;
|
||||||
import org.keycloak.models.ApplicationModel;
|
import org.keycloak.models.ApplicationModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.representations.adapters.action.LogoutAction;
|
import org.keycloak.representations.adapters.action.LogoutAction;
|
||||||
import org.keycloak.representations.adapters.action.PushNotBeforeAction;
|
import org.keycloak.representations.adapters.action.PushNotBeforeAction;
|
||||||
|
import org.keycloak.representations.adapters.action.SessionStats;
|
||||||
|
import org.keycloak.representations.adapters.action.SessionStatsAction;
|
||||||
|
import org.keycloak.representations.adapters.action.UserStats;
|
||||||
|
import org.keycloak.representations.adapters.action.UserStatsAction;
|
||||||
|
|
||||||
import javax.ws.rs.client.Entity;
|
import javax.ws.rs.client.Entity;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
@ -21,7 +28,87 @@ import java.util.List;
|
||||||
public class ResourceAdminManager {
|
public class ResourceAdminManager {
|
||||||
protected static Logger logger = Logger.getLogger(ResourceAdminManager.class);
|
protected static Logger logger = Logger.getLogger(ResourceAdminManager.class);
|
||||||
|
|
||||||
public void singleLogOut(RealmModel realm, String user) {
|
public SessionStats getSessionStats(RealmModel realm, ApplicationModel application, boolean users) {
|
||||||
|
ResteasyClient client = new ResteasyClientBuilder()
|
||||||
|
.disableTrustManager() // todo fix this, should have a trust manager or a good default
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try {
|
||||||
|
return getSessionStats(realm, application, users, client);
|
||||||
|
} finally {
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionStats getSessionStats(RealmModel realm, ApplicationModel application, boolean users, ResteasyClient client) {
|
||||||
|
String managementUrl = application.getManagementUrl();
|
||||||
|
if (managementUrl != null) {
|
||||||
|
SessionStatsAction adminAction = new SessionStatsAction(TokenIdGenerator.generateId(), (int)(System.currentTimeMillis() / 1000) + 30, application.getName());
|
||||||
|
adminAction.setListUsers(users);
|
||||||
|
String token = new TokenManager().encodeToken(realm, adminAction);
|
||||||
|
logger.info("session stats for application: {0} url: {1}", application.getName(), managementUrl);
|
||||||
|
Response response = client.target(managementUrl).path(AdapterConstants.K_GET_SESSION_STATS).request().post(Entity.text(token));
|
||||||
|
if (response.getStatus() != 200) {
|
||||||
|
logger.warn("Failed to get stats: " + response.getStatus());
|
||||||
|
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.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserStats getUserStats(RealmModel realm, ApplicationModel application, UserModel user) {
|
||||||
|
ResteasyClient client = new ResteasyClientBuilder()
|
||||||
|
.disableTrustManager() // todo fix this, should have a trust manager or a good default
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try {
|
||||||
|
return getUserStats(realm, application, user, client);
|
||||||
|
} finally {
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public UserStats getUserStats(RealmModel realm, ApplicationModel application, UserModel user, ResteasyClient client) {
|
||||||
|
String managementUrl = application.getManagementUrl();
|
||||||
|
if (managementUrl != null) {
|
||||||
|
UserStatsAction adminAction = new UserStatsAction(TokenIdGenerator.generateId(), (int)(System.currentTimeMillis() / 1000) + 30, application.getName(), user.getId());
|
||||||
|
String token = new TokenManager().encodeToken(realm, adminAction);
|
||||||
|
logger.info("session stats for application: {0} url: {1}", application.getName(), managementUrl);
|
||||||
|
Response response = client.target(managementUrl).path(AdapterConstants.K_GET_USER_STATS).request().post(Entity.text(token));
|
||||||
|
if (response.getStatus() != 200) {
|
||||||
|
logger.warn("Failed to get stats: " + response.getStatus());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
UserStats stats = response.readEntity(UserStats.class);
|
||||||
|
return stats;
|
||||||
|
} else {
|
||||||
|
logger.info("no management url.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void logoutUser(RealmModel realm, String user) {
|
||||||
ResteasyClient client = new ResteasyClientBuilder()
|
ResteasyClient client = new ResteasyClientBuilder()
|
||||||
.disableTrustManager() // todo fix this, should have a trust manager or a good default
|
.disableTrustManager() // todo fix this, should have a trust manager or a good default
|
||||||
.build();
|
.build();
|
||||||
|
@ -30,14 +117,43 @@ public class ResourceAdminManager {
|
||||||
List<ApplicationModel> resources = realm.getApplications();
|
List<ApplicationModel> resources = realm.getApplications();
|
||||||
logger.debug("logging out {0} resources ", resources.size());
|
logger.debug("logging out {0} resources ", resources.size());
|
||||||
for (ApplicationModel resource : resources) {
|
for (ApplicationModel resource : resources) {
|
||||||
logoutResource(realm, resource, user, client);
|
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();
|
||||||
|
|
||||||
|
try {
|
||||||
|
List<ApplicationModel> resources = realm.getApplications();
|
||||||
|
logger.debug("logging out {0} resources ", resources.size());
|
||||||
|
for (ApplicationModel resource : resources) {
|
||||||
|
logoutApplication(realm, resource, null, client);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
client.close();
|
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();
|
String managementUrl = resource.getManagementUrl();
|
||||||
if (managementUrl != null) {
|
if (managementUrl != null) {
|
||||||
LogoutAction adminAction = new LogoutAction(TokenIdGenerator.generateId(), (int)(System.currentTimeMillis() / 1000) + 30, resource.getName(), user);
|
LogoutAction adminAction = new LogoutAction(TokenIdGenerator.generateId(), (int)(System.currentTimeMillis() / 1000) + 30, resource.getName(), user);
|
||||||
|
@ -49,7 +165,7 @@ public class ResourceAdminManager {
|
||||||
logger.info("logout success.");
|
logger.info("logout success.");
|
||||||
return success;
|
return success;
|
||||||
} else {
|
} else {
|
||||||
logger.info("logout failure.");
|
logger.info("Can't logout" + resource.getName() + " no mgmt url.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -366,18 +366,20 @@ public class TokenManager {
|
||||||
return encodedToken;
|
return encodedToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AccessTokenResponseBuilder responseBuilder(RealmModel realm) {
|
public AccessTokenResponseBuilder responseBuilder(RealmModel realm, ClientModel client) {
|
||||||
return new AccessTokenResponseBuilder(realm);
|
return new AccessTokenResponseBuilder(realm, client);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AccessTokenResponseBuilder {
|
public class AccessTokenResponseBuilder {
|
||||||
RealmModel realm;
|
RealmModel realm;
|
||||||
|
ClientModel client;
|
||||||
AccessToken accessToken;
|
AccessToken accessToken;
|
||||||
RefreshToken refreshToken;
|
RefreshToken refreshToken;
|
||||||
IDToken idToken;
|
IDToken idToken;
|
||||||
|
|
||||||
public AccessTokenResponseBuilder(RealmModel realm) {
|
public AccessTokenResponseBuilder(RealmModel realm, ClientModel client) {
|
||||||
this.realm = realm;
|
this.realm = realm;
|
||||||
|
this.client = client;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AccessTokenResponseBuilder accessToken(AccessToken accessToken) {
|
public AccessTokenResponseBuilder accessToken(AccessToken accessToken) {
|
||||||
|
@ -465,7 +467,9 @@ public class TokenManager {
|
||||||
String encodedToken = new JWSBuilder().jsonContent(refreshToken).rsa256(realm.getPrivateKey());
|
String encodedToken = new JWSBuilder().jsonContent(refreshToken).rsa256(realm.getPrivateKey());
|
||||||
res.setRefreshToken(encodedToken);
|
res.setRefreshToken(encodedToken);
|
||||||
}
|
}
|
||||||
res.setNotBeforePolicy(realm.getNotBefore());
|
int notBefore = realm.getNotBefore();
|
||||||
|
if (client.getNotBefore() > notBefore) notBefore = client.getNotBefore();
|
||||||
|
res.setNotBeforePolicy(notBefore);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,17 +5,13 @@ import org.jboss.resteasy.logging.Logger;
|
||||||
import org.jboss.resteasy.spi.HttpRequest;
|
import org.jboss.resteasy.spi.HttpRequest;
|
||||||
import org.jboss.resteasy.spi.HttpResponse;
|
import org.jboss.resteasy.spi.HttpResponse;
|
||||||
import org.keycloak.OAuthErrorException;
|
import org.keycloak.OAuthErrorException;
|
||||||
import org.keycloak.jose.jws.JWSBuilder;
|
|
||||||
import org.keycloak.jose.jws.JWSInput;
|
import org.keycloak.jose.jws.JWSInput;
|
||||||
import org.keycloak.jose.jws.crypto.RSAProvider;
|
import org.keycloak.jose.jws.crypto.RSAProvider;
|
||||||
import org.keycloak.models.ApplicationModel;
|
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.Constants;
|
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakTransaction;
|
import org.keycloak.models.KeycloakTransaction;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RequiredCredentialModel;
|
import org.keycloak.models.RequiredCredentialModel;
|
||||||
import org.keycloak.models.RoleModel;
|
|
||||||
import org.keycloak.models.UserCredentialModel;
|
import org.keycloak.models.UserCredentialModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.representations.AccessToken;
|
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.UriBuilder;
|
||||||
import javax.ws.rs.core.UriInfo;
|
import javax.ws.rs.core.UriInfo;
|
||||||
import javax.ws.rs.ext.Providers;
|
import javax.ws.rs.ext.Providers;
|
||||||
import java.security.PrivateKey;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -161,7 +156,7 @@ public class TokenService {
|
||||||
throw new NotAuthorizedException("Auth failed");
|
throw new NotAuthorizedException("Auth failed");
|
||||||
}
|
}
|
||||||
String scope = form.getFirst("scope");
|
String scope = form.getFirst("scope");
|
||||||
AccessTokenResponse res = tokenManager.responseBuilder(realm)
|
AccessTokenResponse res = tokenManager.responseBuilder(realm, client)
|
||||||
.generateAccessToken(scope, client, user)
|
.generateAccessToken(scope, client, user)
|
||||||
.generateIDToken()
|
.generateIDToken()
|
||||||
.build();
|
.build();
|
||||||
|
@ -191,7 +186,7 @@ public class TokenService {
|
||||||
throw new BadRequestException(Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build(), e);
|
throw new BadRequestException(Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
AccessTokenResponse res = tokenManager.responseBuilder(realm)
|
AccessTokenResponse res = tokenManager.responseBuilder(realm, client)
|
||||||
.accessToken(accessToken)
|
.accessToken(accessToken)
|
||||||
.generateIDToken()
|
.generateIDToken()
|
||||||
.generateRefreshToken().build();
|
.generateRefreshToken().build();
|
||||||
|
@ -421,7 +416,7 @@ public class TokenService {
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
logger.debug("accessRequest SUCCESS");
|
logger.debug("accessRequest SUCCESS");
|
||||||
AccessTokenResponse res = tokenManager.responseBuilder(realm)
|
AccessTokenResponse res = tokenManager.responseBuilder(realm, client)
|
||||||
.accessToken(accessCode.getToken())
|
.accessToken(accessCode.getToken())
|
||||||
.generateIDToken()
|
.generateIDToken()
|
||||||
.generateRefreshToken().build();
|
.generateRefreshToken().build();
|
||||||
|
@ -563,7 +558,7 @@ public class TokenService {
|
||||||
logger.info("Logging out: {0}", user.getLoginName());
|
logger.info("Logging out: {0}", user.getLoginName());
|
||||||
authManager.expireIdentityCookie(realm, uriInfo);
|
authManager.expireIdentityCookie(realm, uriInfo);
|
||||||
authManager.expireRememberMeCookie(realm, uriInfo);
|
authManager.expireRememberMeCookie(realm, uriInfo);
|
||||||
resourceAdminManager.singleLogOut(realm, user.getId());
|
resourceAdminManager.logoutUser(realm, user.getId());
|
||||||
} else {
|
} else {
|
||||||
logger.info("No user logged in for logout");
|
logger.info("No user logged in for logout");
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,9 @@ import org.keycloak.models.ApplicationModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
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.representations.adapters.action.SessionStats;
|
||||||
|
import org.keycloak.representations.adapters.action.UserStats;
|
||||||
import org.keycloak.representations.idm.ApplicationRepresentation;
|
import org.keycloak.representations.idm.ApplicationRepresentation;
|
||||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||||
import org.keycloak.services.managers.ApplicationManager;
|
import org.keycloak.services.managers.ApplicationManager;
|
||||||
|
@ -17,17 +20,22 @@ import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.DELETE;
|
import javax.ws.rs.DELETE;
|
||||||
|
import javax.ws.rs.DefaultValue;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.NotFoundException;
|
import javax.ws.rs.NotFoundException;
|
||||||
import javax.ws.rs.POST;
|
import javax.ws.rs.POST;
|
||||||
import javax.ws.rs.PUT;
|
import javax.ws.rs.PUT;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.QueryParam;
|
||||||
import javax.ws.rs.core.Application;
|
import javax.ws.rs.core.Application;
|
||||||
import javax.ws.rs.core.Context;
|
import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.UriInfo;
|
import javax.ws.rs.core.UriInfo;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,7 +43,7 @@ import java.util.Set;
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class ApplicationResource {
|
public class ApplicationResource {
|
||||||
protected static final Logger logger = Logger.getLogger(RealmAdminResource.class);
|
protected static final Logger logger = Logger.getLogger(ApplicationResource.class);
|
||||||
protected RealmModel realm;
|
protected RealmModel realm;
|
||||||
private RealmAuth auth;
|
private RealmAuth auth;
|
||||||
protected ApplicationModel application;
|
protected ApplicationModel application;
|
||||||
|
@ -193,6 +201,50 @@ public class ApplicationResource {
|
||||||
new ResourceAdminManager().pushApplicationRevocationPolicy(realm, application);
|
new ResourceAdminManager().pushApplicationRevocationPolicy(realm, application);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Path("session-stats")
|
||||||
|
@GET
|
||||||
|
@NoCache
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,10 @@ package org.keycloak.services.resources.admin;
|
||||||
|
|
||||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||||
import org.jboss.resteasy.logging.Logger;
|
import org.jboss.resteasy.logging.Logger;
|
||||||
|
import org.keycloak.models.ApplicationModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.representations.adapters.action.SessionStats;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.services.managers.ModelToRepresentation;
|
import org.keycloak.services.managers.ModelToRepresentation;
|
||||||
import org.keycloak.services.managers.RealmManager;
|
import org.keycloak.services.managers.RealmManager;
|
||||||
|
@ -13,6 +15,10 @@ import org.keycloak.services.managers.TokenManager;
|
||||||
import javax.ws.rs.*;
|
import javax.ws.rs.*;
|
||||||
import javax.ws.rs.container.ResourceContext;
|
import javax.ws.rs.container.ResourceContext;
|
||||||
import javax.ws.rs.core.Context;
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
@ -112,4 +118,27 @@ public class RealmAdminResource {
|
||||||
new ResourceAdminManager().pushRealmRevocationPolicy(realm);
|
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;
|
||||||
|
SessionStats appStats = new ResourceAdminManager().getSessionStats(realm, applicationModel, false);
|
||||||
|
stats.put(applicationModel.getName(), appStats);
|
||||||
|
}
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@ import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserCredentialModel;
|
import org.keycloak.models.UserCredentialModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.representations.adapters.action.SessionStats;
|
||||||
|
import org.keycloak.representations.adapters.action.UserStats;
|
||||||
import org.keycloak.representations.idm.*;
|
import org.keycloak.representations.idm.*;
|
||||||
import org.keycloak.services.email.EmailException;
|
import org.keycloak.services.email.EmailException;
|
||||||
import org.keycloak.services.email.EmailSender;
|
import org.keycloak.services.email.EmailSender;
|
||||||
|
@ -17,6 +19,7 @@ import org.keycloak.services.managers.AccessCodeEntry;
|
||||||
import org.keycloak.services.managers.Auth;
|
import org.keycloak.services.managers.Auth;
|
||||||
import org.keycloak.services.managers.ModelToRepresentation;
|
import org.keycloak.services.managers.ModelToRepresentation;
|
||||||
import org.keycloak.services.managers.RealmManager;
|
import org.keycloak.services.managers.RealmManager;
|
||||||
|
import org.keycloak.services.managers.ResourceAdminManager;
|
||||||
import org.keycloak.services.managers.TokenManager;
|
import org.keycloak.services.managers.TokenManager;
|
||||||
import org.keycloak.services.resources.flows.Flows;
|
import org.keycloak.services.resources.flows.Flows;
|
||||||
import org.keycloak.services.resources.flows.Urls;
|
import org.keycloak.services.resources.flows.Urls;
|
||||||
|
@ -34,6 +37,7 @@ import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.QueryParam;
|
import javax.ws.rs.QueryParam;
|
||||||
import javax.ws.rs.container.ResourceContext;
|
import javax.ws.rs.container.ResourceContext;
|
||||||
import javax.ws.rs.core.Context;
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.UriInfo;
|
import javax.ws.rs.core.UriInfo;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -138,12 +142,45 @@ public class UsersResource {
|
||||||
auth.requireView();
|
auth.requireView();
|
||||||
|
|
||||||
UserModel user = realm.getUser(username);
|
UserModel user = realm.getUser(username);
|
||||||
if (user == null || !isUser(user)) {
|
if (user == null) {
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
return ModelToRepresentation.toRepresentation(user);
|
return ModelToRepresentation.toRepresentation(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Path("{username}/session-stats")
|
||||||
|
@GET
|
||||||
|
@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) {
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
Map<String, UserStats> stats = new HashMap<String, UserStats>();
|
||||||
|
for (ApplicationModel applicationModel : realm.getApplications()) {
|
||||||
|
if (applicationModel.getManagementUrl() == null) continue;
|
||||||
|
UserStats appStats = new ResourceAdminManager().getUserStats(realm, applicationModel, user);
|
||||||
|
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}")
|
@Path("{username}")
|
||||||
@DELETE
|
@DELETE
|
||||||
@NoCache
|
@NoCache
|
||||||
|
@ -191,17 +228,11 @@ public class UsersResource {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (UserModel user : userModels) {
|
for (UserModel user : userModels) {
|
||||||
if (isUser(user)) {
|
results.add(ModelToRepresentation.toRepresentation(user));
|
||||||
results.add(ModelToRepresentation.toRepresentation(user));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isUser(UserModel user) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Path("{username}/role-mappings")
|
@Path("{username}/role-mappings")
|
||||||
@GET
|
@GET
|
||||||
@Produces("application/json")
|
@Produces("application/json")
|
||||||
|
|
Loading…
Reference in a new issue