KEYCLOAK-759 Added adapter clustering to admin console
This commit is contained in:
parent
9565a785b2
commit
01c705b4d3
18 changed files with 576 additions and 83 deletions
|
@ -23,6 +23,11 @@ public class BasicDBObjectToMapMapper implements Mapper<BasicDBObject, Map> {
|
|||
String key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
|
||||
// Workaround as manually inserted numbers into mongo may be treated as "Double"
|
||||
if (value instanceof Double && context.getGenericTypes().get(1) == Integer.class) {
|
||||
value = ((Double)value).intValue();
|
||||
}
|
||||
|
||||
if (key.contains(MapMapper.DOT_PLACEHOLDER)) {
|
||||
key = key.replaceAll(MapMapper.DOT_PLACEHOLDER, ".");
|
||||
}
|
||||
|
|
|
@ -10,8 +10,7 @@ public interface AdapterConstants {
|
|||
public static final String K_LOGOUT = "k_logout";
|
||||
public static final String K_VERSION = "k_version";
|
||||
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_TEST_AVAILABLE = "k_test_available";
|
||||
public static final String K_QUERY_BEARER_TOKEN = "k_query_bearer_token";
|
||||
|
||||
// This param name is defined again in Keycloak Subsystem class
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
package org.keycloak.representations.adapters.action;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Result of the "global" request (like push notBefore or logoutAll), which is send to all cluster nodes
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class GlobalRequestResult {
|
||||
|
||||
private List<String> successRequests;
|
||||
private List<String> failedRequests;
|
||||
|
||||
public void addSuccessRequest(String reqUri) {
|
||||
if (successRequests == null) {
|
||||
successRequests = new ArrayList<String>();
|
||||
}
|
||||
successRequests.add(reqUri);
|
||||
}
|
||||
|
||||
public void addFailedRequest(String reqUri) {
|
||||
if (failedRequests == null) {
|
||||
failedRequests = new ArrayList<String>();
|
||||
}
|
||||
failedRequests.add(reqUri);
|
||||
}
|
||||
|
||||
public void addAllSuccessRequests(List<String> reqUris) {
|
||||
if (successRequests == null) {
|
||||
successRequests = new ArrayList<String>();
|
||||
}
|
||||
successRequests.addAll(reqUris);
|
||||
}
|
||||
|
||||
public void addAllFailedRequests(List<String> reqUris) {
|
||||
if (failedRequests == null) {
|
||||
failedRequests = new ArrayList<String>();
|
||||
}
|
||||
failedRequests.addAll(reqUris);
|
||||
}
|
||||
|
||||
public void addAll(GlobalRequestResult merged) {
|
||||
if (merged.getSuccessRequests() != null && merged.getSuccessRequests().size() > 0) {
|
||||
addAllSuccessRequests(merged.getSuccessRequests());
|
||||
}
|
||||
if (merged.getFailedRequests() != null && merged.getFailedRequests().size() > 0) {
|
||||
addAllFailedRequests(merged.getFailedRequests());
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getSuccessRequests() {
|
||||
return successRequests;
|
||||
}
|
||||
|
||||
public List<String> getFailedRequests() {
|
||||
return failedRequests;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package org.keycloak.representations.adapters.action;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class TestAvailabilityAction extends AdminAction {
|
||||
|
||||
public static final String TEST_AVAILABILITY = "TEST_AVAILABILITY";
|
||||
|
||||
public TestAvailabilityAction() {
|
||||
}
|
||||
|
||||
public TestAvailabilityAction(String id, int expiration, String resource) {
|
||||
super(id, expiration, resource, TEST_AVAILABILITY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate() {
|
||||
return TEST_AVAILABILITY.equals(action);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
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;
|
||||
}
|
||||
}
|
|
@ -141,6 +141,14 @@
|
|||
"/product-portal/*"
|
||||
],
|
||||
"secret": "password"
|
||||
},
|
||||
{
|
||||
"name": "database-service",
|
||||
"enabled": true,
|
||||
"adminUrl": "/database",
|
||||
"baseUrl": "/database",
|
||||
"bearerOnly": true,
|
||||
"secret": "password"
|
||||
}
|
||||
],
|
||||
"oauthClients": [
|
||||
|
|
|
@ -440,6 +440,42 @@ module.config([ '$routeProvider', function($routeProvider) {
|
|||
},
|
||||
controller : 'ApplicationCredentialsCtrl'
|
||||
})
|
||||
.when('/realms/:realm/applications/:application/clustering', {
|
||||
templateUrl : 'partials/application-clustering.html',
|
||||
resolve : {
|
||||
realm : function(RealmLoader) {
|
||||
return RealmLoader();
|
||||
},
|
||||
application : function(ApplicationLoader) {
|
||||
return ApplicationLoader();
|
||||
}
|
||||
},
|
||||
controller : 'ApplicationClusteringCtrl'
|
||||
})
|
||||
.when('/register-node/realms/:realm/applications/:application/clustering', {
|
||||
templateUrl : 'partials/application-clustering-node.html',
|
||||
resolve : {
|
||||
realm : function(RealmLoader) {
|
||||
return RealmLoader();
|
||||
},
|
||||
application : function(ApplicationLoader) {
|
||||
return ApplicationLoader();
|
||||
}
|
||||
},
|
||||
controller : 'ApplicationClusteringNodeCtrl'
|
||||
})
|
||||
.when('/realms/:realm/applications/:application/clustering/:node', {
|
||||
templateUrl : 'partials/application-clustering-node.html',
|
||||
resolve : {
|
||||
realm : function(RealmLoader) {
|
||||
return RealmLoader();
|
||||
},
|
||||
application : function(ApplicationLoader) {
|
||||
return ApplicationLoader();
|
||||
}
|
||||
},
|
||||
controller : 'ApplicationClusteringNodeCtrl'
|
||||
})
|
||||
.when('/realms/:realm/applications/:application/certificate', {
|
||||
templateUrl : 'partials/application-keys.html',
|
||||
resolve : {
|
||||
|
|
|
@ -592,15 +592,134 @@ module.controller('ApplicationRevocationCtrl', function($scope, realm, applicati
|
|||
$scope.setNotBeforeNow = function() {
|
||||
$scope.application.notBefore = new Date().getTime()/1000;
|
||||
Application.update({ realm : realm.realm, application: $scope.application.id}, $scope.application, function () {
|
||||
Notifications.success('Not Before cleared for application.');
|
||||
Notifications.success('Not Before set for application.');
|
||||
refresh();
|
||||
});
|
||||
}
|
||||
$scope.pushRevocation = function() {
|
||||
ApplicationPushRevocation.save({realm : realm.realm, application: $scope.application.id}, function () {
|
||||
Notifications.success('Push sent for application.');
|
||||
ApplicationPushRevocation.save({realm : realm.realm, application: $scope.application.id}, function (globalReqResult) {
|
||||
var successCount = globalReqResult.successRequests ? globalReqResult.successRequests.length : 0;
|
||||
var failedCount = globalReqResult.failedRequests ? globalReqResult.failedRequests.length : 0;
|
||||
|
||||
if (successCount==0 && failedCount==0) {
|
||||
Notifications.warn('No push sent. No admin URI configured or no registered cluster nodes available');
|
||||
} else if (failedCount > 0) {
|
||||
var msgStart = successCount>0 ? 'Successfully push notBefore to: ' + globalReqResult.successRequests + ' . ' : '';
|
||||
Notifications.error(msgStart + 'Failed to push notBefore to: ' + globalReqResult.failedRequests + '. Verify availability of failed hosts and try again');
|
||||
} else {
|
||||
Notifications.success('Successfully push notBefore to: ' + globalReqResult.successRequests);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
module.controller('ApplicationClusteringCtrl', function($scope, application, Application, ApplicationTestNodesAvailable, realm, $location, $route, Notifications, TimeUnit) {
|
||||
$scope.application = application;
|
||||
$scope.realm = realm;
|
||||
|
||||
var oldCopy = angular.copy($scope.application);
|
||||
$scope.changed = false;
|
||||
|
||||
$scope.$watch('application', function() {
|
||||
if (!angular.equals($scope.application, oldCopy)) {
|
||||
$scope.changed = true;
|
||||
}
|
||||
}, true);
|
||||
|
||||
$scope.application.nodeReRegistrationTimeoutUnit = TimeUnit.autoUnit(application.nodeReRegistrationTimeout);
|
||||
$scope.application.nodeReRegistrationTimeout = TimeUnit.toUnit(application.nodeReRegistrationTimeout, $scope.application.nodeReRegistrationTimeoutUnit);
|
||||
$scope.$watch('application.nodeReRegistrationTimeoutUnit', function(to, from) {
|
||||
$scope.application.nodeReRegistrationTimeout = TimeUnit.convert($scope.application.nodeReRegistrationTimeout, from, to);
|
||||
});
|
||||
|
||||
$scope.save = function() {
|
||||
var appCopy = angular.copy($scope.application);
|
||||
delete appCopy['nodeReRegistrationTimeoutUnit'];
|
||||
appCopy.nodeReRegistrationTimeout = TimeUnit.toSeconds($scope.application.nodeReRegistrationTimeout, $scope.application.nodeReRegistrationTimeoutUnit)
|
||||
Application.update({ realm : realm.realm, application : application.id }, appCopy, function () {
|
||||
$route.reload();
|
||||
Notifications.success('Your changes have been saved to the application.');
|
||||
});
|
||||
};
|
||||
|
||||
$scope.reset = function() {
|
||||
$route.reload();
|
||||
};
|
||||
|
||||
$scope.testNodesAvailable = function() {
|
||||
console.log('testNodesAvailable');
|
||||
ApplicationTestNodesAvailable.get({ realm : realm.realm, application : application.id }, function(globalReqResult) {
|
||||
$route.reload();
|
||||
|
||||
var successCount = globalReqResult.successRequests ? globalReqResult.successRequests.length : 0;
|
||||
var failedCount = globalReqResult.failedRequests ? globalReqResult.failedRequests.length : 0;
|
||||
|
||||
if (successCount==0 && failedCount==0) {
|
||||
Notifications.warn('No requests sent. No admin URI configured or no registered cluster nodes available');
|
||||
} else if (failedCount > 0) {
|
||||
var msgStart = successCount>0 ? 'Successfully verify availability for ' + globalReqResult.successRequests + ' . ' : '';
|
||||
Notifications.error(msgStart + 'Failed to verify availability for: ' + globalReqResult.failedRequests + '. Fix or unregister failed cluster nodes and try again');
|
||||
} else {
|
||||
Notifications.success('Successfully sent requests to: ' + globalReqResult.successRequests);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (application.registeredNodes) {
|
||||
var nodeRegistrations = [];
|
||||
for (node in application.registeredNodes) {
|
||||
reg = {
|
||||
host: node,
|
||||
lastRegistration: new Date(application.registeredNodes[node] * 1000)
|
||||
}
|
||||
nodeRegistrations.push(reg);
|
||||
}
|
||||
|
||||
$scope.nodeRegistrations = nodeRegistrations;
|
||||
};
|
||||
});
|
||||
|
||||
module.controller('ApplicationClusteringNodeCtrl', function($scope, application, Application, ApplicationClusterNode, realm, $location, $routeParams, Notifications) {
|
||||
$scope.application = application;
|
||||
$scope.realm = realm;
|
||||
$scope.create = !$routeParams.node;
|
||||
|
||||
$scope.save = function() {
|
||||
console.log('registerNode: ' + $scope.node.host);
|
||||
ApplicationClusterNode.save({ realm : realm.realm, application : application.id , node: $scope.node.host }, function() {
|
||||
Notifications.success('Node ' + $scope.node.host + ' registered successfully.');
|
||||
$location.url('/realms/' + realm.realm + '/applications/' + application.id + '/clustering');
|
||||
});
|
||||
}
|
||||
|
||||
$scope.unregisterNode = function() {
|
||||
console.log('unregisterNode: ' + $scope.node.host);
|
||||
ApplicationClusterNode.remove({ realm : realm.realm, application : application.id , node: $scope.node.host }, function() {
|
||||
Notifications.success('Node ' + $scope.node.host + ' unregistered successfully.');
|
||||
$location.url('/realms/' + realm.realm + '/applications/' + application.id + '/clustering');
|
||||
});
|
||||
}
|
||||
|
||||
if ($scope.create) {
|
||||
$scope.node = {}
|
||||
$scope.registered = false;
|
||||
} else {
|
||||
var lastRegTime = application.registeredNodes[$routeParams.node];
|
||||
|
||||
if (lastRegTime) {
|
||||
$scope.registered = true;
|
||||
$scope.node = {
|
||||
host: $routeParams.node,
|
||||
lastRegistration: new Date(lastRegTime * 1000)
|
||||
}
|
||||
|
||||
} else {
|
||||
$scope.registered = false;
|
||||
$scope.node = {
|
||||
host: $routeParams.node
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -766,10 +766,18 @@ module.controller('RealmSessionStatsCtrl', function($scope, realm, stats, RealmA
|
|||
console.log(stats);
|
||||
|
||||
$scope.logoutAll = function() {
|
||||
RealmLogoutAll.save({realm : realm.realm}, function () {
|
||||
Notifications.success('Logged out all users');
|
||||
RealmApplicationSessionStats.get({realm: realm.realm}, function(updated) {
|
||||
Notifications.success('Logged out all users');
|
||||
RealmLogoutAll.save({realm : realm.realm}, function (globalReqResult) {
|
||||
var successCount = globalReqResult.successRequests ? globalReqResult.successRequests.length : 0;
|
||||
var failedCount = globalReqResult.failedRequests ? globalReqResult.failedRequests.length : 0;
|
||||
|
||||
if (failedCount > 0) {
|
||||
var msgStart = successCount>0 ? 'Successfully logout all users under: ' + globalReqResult.successRequests + ' . ' : '';
|
||||
Notifications.error(msgStart + 'Failed to logout users under: ' + globalReqResult.failedRequests + '. Verify availability of failed hosts and try again');
|
||||
} else {
|
||||
Notifications.success('Successfully logout all users from the realm');
|
||||
}
|
||||
|
||||
RealmApplicationSessionStats.query({realm: realm.realm}, function(updated) {
|
||||
$scope.stats = updated;
|
||||
})
|
||||
});
|
||||
|
@ -809,13 +817,23 @@ module.controller('RealmRevocationCtrl', function($scope, Realm, RealmPushRevoca
|
|||
}
|
||||
$scope.setNotBeforeNow = function() {
|
||||
Realm.update({ realm: realm.realm, notBefore : new Date().getTime()/1000}, function () {
|
||||
Notifications.success('Not Before cleared for realm.');
|
||||
Notifications.success('Not Before set for realm.');
|
||||
reset();
|
||||
});
|
||||
}
|
||||
$scope.pushRevocation = function() {
|
||||
RealmPushRevocation.save({ realm: realm.realm}, function () {
|
||||
Notifications.success('Push sent for realm.');
|
||||
RealmPushRevocation.save({ realm: realm.realm}, function (globalReqResult) {
|
||||
var successCount = globalReqResult.successRequests ? globalReqResult.successRequests.length : 0;
|
||||
var failedCount = globalReqResult.failedRequests ? globalReqResult.failedRequests.length : 0;
|
||||
|
||||
if (successCount==0 && failedCount==0) {
|
||||
Notifications.warn('No push sent. No admin URI configured or no registered cluster nodes available');
|
||||
} else if (failedCount > 0) {
|
||||
var msgStart = successCount>0 ? 'Successfully push notBefore to: ' + globalReqResult.successRequests + ' . ' : '';
|
||||
Notifications.error(msgStart + 'Failed to push notBefore to: ' + globalReqResult.failedRequests + '. Verify availability of failed hosts and try again');
|
||||
} else {
|
||||
Notifications.success('Successfully push notBefore to all configured applications');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -693,6 +693,20 @@ module.factory('ApplicationPushRevocation', function($resource) {
|
|||
});
|
||||
});
|
||||
|
||||
module.factory('ApplicationClusterNode', function($resource) {
|
||||
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/nodes/:node', {
|
||||
realm : '@realm',
|
||||
application : "@application"
|
||||
});
|
||||
});
|
||||
|
||||
module.factory('ApplicationTestNodesAvailable', function($resource) {
|
||||
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/test-nodes-available', {
|
||||
realm : '@realm',
|
||||
application : "@application"
|
||||
});
|
||||
});
|
||||
|
||||
module.factory('ApplicationCertificate', function($resource) {
|
||||
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/certificates', {
|
||||
realm : '@realm',
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<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">
|
||||
<kc-navigation-application></kc-navigation-application>
|
||||
<div id="content">
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="#/realms/{{realm.realm}}/applications">Applications</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/applications/{{application.id}}">{{application.name}}</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/clustering">Clustering</a></li>
|
||||
<li class="active">{{node.host}}</li>
|
||||
</ol>
|
||||
<h2 data-ng-show="create || registered"><span>{{application.name}} Clustering</span></h2>
|
||||
<h2 data-ng-hide="create || registered">Cluster node on host <span>{{node.host}}</span> not registered!</h2>
|
||||
<form class="form-horizontal" name="clusteringForm" novalidate kc-read-only="!access.manageApplications" data-ng-show="create || registered">
|
||||
<fieldset >
|
||||
<legend><span class="text">Configuration of cluster node</span></legend>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="host">Host</label>
|
||||
<div class="col-sm-6">
|
||||
<input ng-disabled="!create" class="form-control" type="text" id="host" name="host" data-ng-model="node.host">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="lastRegistration">Last Registration</label>
|
||||
<div class="col-sm-6">
|
||||
<input ng-disabled="true" class="form-control" type="text" id="lastRegistration" name="lastRegistration" data-ng-model="node.lastRegistration">
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div class="pull-right form-actions" data-ng-show="access.manageRealm">
|
||||
<button data-kc-save data-ng-show="create">Register node</button>
|
||||
<button data-kc-delete data-ng-hide="create" data-ng-click="unregisterNode()">Unregister node</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,75 @@
|
|||
<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">
|
||||
<kc-navigation-application></kc-navigation-application>
|
||||
<div id="content">
|
||||
<ol class="breadcrumb" data-ng-hide="create">
|
||||
<li><a href="#/realms/{{realm.realm}}/applications">Applications</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/applications/{{application.id}}">{{application.name}}</a></li>
|
||||
<li class="active">Clustering</li>
|
||||
</ol>
|
||||
<h2 data-ng-hide="create"><span>{{application.name}}</span> Clustering</h2>
|
||||
<form class="form-horizontal" name="clusteringForm" novalidate kc-read-only="!access.manageApplications">
|
||||
<legend><span class="text">Basic configuration</span></legend>
|
||||
<fieldset >
|
||||
<div class="form-group clearfix">
|
||||
<label class="col-sm-2 control-label" for="nodeReRegistrationTimeout">Node Re-registration Timeout</label>
|
||||
<div class="col-sm-5">
|
||||
<div class="row">
|
||||
<div class="col-sm-4">
|
||||
<input class="form-control" type="number" required
|
||||
max="31536000" data-ng-model="application.nodeReRegistrationTimeout"
|
||||
id="nodeReRegistrationTimeout" name="nodeReRegistrationTimeout"/>
|
||||
</div>
|
||||
<div class="col-sm-4 select-kc">
|
||||
<select name="nodeReRegistrationTimeoutUnit" data-ng-model="application.nodeReRegistrationTimeoutUnit" >
|
||||
<option data-ng-selected="!application.nodeReRegistrationTimeoutUnit">Seconds</option>
|
||||
<option>Minutes</option>
|
||||
<option>Hours</option>
|
||||
<option>Days</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span tooltip-placement="right" tooltip="Interval to specify max time for registered application cluster nodes to re-register. If cluster node won't send re-registration request to Keycloak within this time, it will be unregistered from Keycloak" class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
<div class="pull-right form-actions" data-ng-show="access.manageRealm">
|
||||
<button data-kc-reset data-ng-show="changed">Clear changes</button>
|
||||
<button data-kc-save data-ng-show="changed">Save</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend><span class="text">Registered cluster nodes</span></legend>
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="kc-table-actions" colspan="3" data-ng-show="access.manageApplications">
|
||||
<div class="pull-right">
|
||||
<a class="btn btn-primary" tooltip="Manually register cluster node. This is usually not needed as cluster node should be registered automatically by adapter"
|
||||
tooltip-placement="bottom" href="#/register-node/realms/{{realm.realm}}/applications/{{application.id}}/clustering">Register node manually</a>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
<tr data-ng-hide="!nodeRegistrations || nodeRegistrations.length == 0">
|
||||
<th>Node host</th>
|
||||
<th>Last registration</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="node in nodeRegistrations">
|
||||
<td><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/clustering/{{node.host}}">{{node.host}}</a></td>
|
||||
<td>{{node.lastRegistration}}</td>
|
||||
</tr>
|
||||
<tr data-ng-show="!nodeRegistrations || nodeRegistrations.length == 0">
|
||||
<td>No registered cluster nodes available</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="pull-right form-actions" data-ng-show="access.manageRealm && nodeRegistrations && nodeRegistrations.length > 0">
|
||||
<a class="btn btn-primary" data-ng-click="testNodesAvailable()">Test cluster availability</a>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
|
@ -1,11 +1,12 @@
|
|||
<ul class="nav nav-tabs nav-tabs-pf" data-ng-show="!create">
|
||||
<li ng-class="{active: !path[4]}"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}">Settings</a></li>
|
||||
<li ng-class="{active: path[4] == 'credentials'}" data-ng-show="!application.bearerOnly && !application.publicClient && application.protocol != 'saml'"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/credentials">Credentials</a></li>
|
||||
<li ng-class="{active: path[4] == 'credentials'}" data-ng-show="!application.publicClient && application.protocol != 'saml'"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/credentials">Credentials</a></li>
|
||||
<li ng-class="{active: path[4] == 'certificate'}" data-ng-show="application.protocol == 'saml' && application.attributes['samlClientSignature'] == 'true'"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/certificate">Application Keys</a></li>
|
||||
<li ng-class="{active: path[4] == 'roles'}"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/roles">Roles</a></li>
|
||||
<li ng-class="{active: path[4] == 'claims'}" data-ng-show="!application.bearerOnly"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/claims">Claims</a></li>
|
||||
<li ng-class="{active: path[4] == 'scope-mappings'}" data-ng-show="!application.bearerOnly"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/scope-mappings">Scope</a></li>
|
||||
<li ng-class="{active: path[4] == 'revocation'}"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/revocation">Revocation</a></li>
|
||||
<li ng-class="{active: path[4] == 'sessions'}" data-ng-show="!application.bearerOnly"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/sessions">Sessions</a></li>
|
||||
<li ng-class="{active: path[4] == 'clustering'}" data-ng-show="!application.publicClient"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/clustering">Clustering</a></li>
|
||||
<li ng-class="{active: path[4] == 'installation'}" data-ng-show="application.protocol != 'saml'"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/installation">Installation</a></li>
|
||||
</ul>
|
|
@ -7,12 +7,10 @@ import org.keycloak.jose.jws.crypto.RSAProvider;
|
|||
import org.keycloak.representations.adapters.action.AdminAction;
|
||||
import org.keycloak.representations.adapters.action.LogoutAction;
|
||||
import org.keycloak.representations.adapters.action.PushNotBeforeAction;
|
||||
import org.keycloak.representations.adapters.action.TestAvailabilityAction;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
import org.keycloak.util.StreamUtil;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
|
@ -59,6 +57,9 @@ public class PreAuthActionsHandler {
|
|||
} else if (requestUri.endsWith(AdapterConstants.K_VERSION)) {
|
||||
handleVersion();
|
||||
return true;
|
||||
} else if (requestUri.endsWith(AdapterConstants.K_TEST_AVAILABLE)) {
|
||||
handleTestAvailable();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -144,6 +145,22 @@ public class PreAuthActionsHandler {
|
|||
}
|
||||
}
|
||||
|
||||
protected void handleTestAvailable() {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("K_TEST_AVAILABLE sent");
|
||||
}
|
||||
try {
|
||||
JWSInput token = verifyAdminRequest();
|
||||
if (token == null) {
|
||||
return;
|
||||
}
|
||||
TestAvailabilityAction action = JsonSerialization.readValue(token.getContent(), TestAvailabilityAction.class);
|
||||
validateAction(action);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected JWSInput verifyAdminRequest() throws Exception {
|
||||
if (!facade.getRequest().isSecure() && deployment.getSslRequired().isRequired(facade.getRequest().getRemoteAddr())) {
|
||||
log.warn("SSL is required for adapter admin action");
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package org.keycloak.admin.client.resource;
|
||||
|
||||
import org.keycloak.representations.adapters.action.UserStats;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.representations.idm.SocialLinkRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
|
@ -17,7 +16,6 @@ import javax.ws.rs.Produces;
|
|||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author rodrigo.sasaki@icarros.com.br
|
||||
|
@ -51,10 +49,6 @@ public interface UserResource {
|
|||
@Path("reset-password-email")
|
||||
public void resetPasswordEmail();
|
||||
|
||||
@GET
|
||||
@Path("session-stats")
|
||||
public Map<String, UserStats> getUserStats();
|
||||
|
||||
@GET
|
||||
@Path("sessions")
|
||||
public List<UserSessionRepresentation> getUserSessions();
|
||||
|
|
|
@ -15,9 +15,10 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.protocol.oidc.TokenManager;
|
||||
import org.keycloak.representations.adapters.action.GlobalRequestResult;
|
||||
import org.keycloak.representations.adapters.action.LogoutAction;
|
||||
import org.keycloak.representations.adapters.action.PushNotBeforeAction;
|
||||
import org.keycloak.representations.adapters.action.UserStats;
|
||||
import org.keycloak.representations.adapters.action.TestAvailabilityAction;
|
||||
import org.keycloak.services.util.HttpClientBuilder;
|
||||
import org.keycloak.services.util.ResolveRelative;
|
||||
import org.keycloak.util.KeycloakUriBuilder;
|
||||
|
@ -64,6 +65,8 @@ public class ResourceAdminManager {
|
|||
return StringPropertyReplacer.replaceProperties(absoluteURI);
|
||||
}
|
||||
|
||||
// For non-cluster setup, return just single configured managementUrls
|
||||
// For cluster setup, return the management Urls corresponding to all registered cluster nodes
|
||||
private List<String> getAllManagementUrls(URI requestUri, ApplicationModel application) {
|
||||
String baseMgmtUrl = getManagementUrl(requestUri, application);
|
||||
if (baseMgmtUrl == null) {
|
||||
|
@ -211,49 +214,55 @@ public class ResourceAdminManager {
|
|||
|
||||
// Methods for logout all
|
||||
|
||||
public void logoutAll(URI requestUri, RealmModel realm) {
|
||||
public GlobalRequestResult logoutAll(URI requestUri, RealmModel realm) {
|
||||
ApacheHttpClient4Executor executor = createExecutor();
|
||||
|
||||
try {
|
||||
realm.setNotBefore(Time.currentTime());
|
||||
List<ApplicationModel> resources = realm.getApplications();
|
||||
logger.debugv("logging out {0} resources ", resources.size());
|
||||
|
||||
GlobalRequestResult finalResult = new GlobalRequestResult();
|
||||
for (ApplicationModel resource : resources) {
|
||||
logoutApplication(requestUri, realm, resource, executor, realm.getNotBefore());
|
||||
GlobalRequestResult currentResult = logoutApplication(requestUri, realm, resource, executor, realm.getNotBefore());
|
||||
finalResult.addAll(currentResult);
|
||||
}
|
||||
return finalResult;
|
||||
} finally {
|
||||
executor.getHttpClient().getConnectionManager().shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public void logoutApplication(URI requestUri, RealmModel realm, ApplicationModel resource) {
|
||||
public GlobalRequestResult logoutApplication(URI requestUri, RealmModel realm, ApplicationModel resource) {
|
||||
ApacheHttpClient4Executor executor = createExecutor();
|
||||
try {
|
||||
resource.setNotBefore(Time.currentTime());
|
||||
logoutApplication(requestUri, realm, resource, executor, resource.getNotBefore());
|
||||
return logoutApplication(requestUri, realm, resource, executor, resource.getNotBefore());
|
||||
} finally {
|
||||
executor.getHttpClient().getConnectionManager().shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected boolean logoutApplication(URI requestUri, RealmModel realm, ApplicationModel resource, ApacheHttpClient4Executor executor, int notBefore) {
|
||||
protected GlobalRequestResult logoutApplication(URI requestUri, RealmModel realm, ApplicationModel resource, ApacheHttpClient4Executor executor, int notBefore) {
|
||||
List<String> mgmtUrls = getAllManagementUrls(requestUri, resource);
|
||||
if (mgmtUrls.isEmpty()) {
|
||||
logger.debug("No management URL or no registered cluster nodes for the application " + resource.getName());
|
||||
return false;
|
||||
return new GlobalRequestResult();
|
||||
}
|
||||
|
||||
logger.info("Send logoutApplication for URLs: " + mgmtUrls);
|
||||
|
||||
// Propagate this to all hosts
|
||||
boolean anyFailed = false;
|
||||
GlobalRequestResult result = new GlobalRequestResult();
|
||||
for (String mgmtUrl : mgmtUrls) {
|
||||
if (!sendLogoutRequest(realm, resource, null, executor, notBefore, mgmtUrl)) {
|
||||
anyFailed = true;
|
||||
if (sendLogoutRequest(realm, resource, null, executor, notBefore, mgmtUrl)) {
|
||||
result.addSuccessRequest(mgmtUrl);
|
||||
} else {
|
||||
result.addFailedRequest(mgmtUrl);
|
||||
}
|
||||
}
|
||||
return !anyFailed;
|
||||
return result;
|
||||
}
|
||||
|
||||
protected boolean sendLogoutRequest(RealmModel realm, ApplicationModel resource, List<String> adapterSessionIds, ApacheHttpClient4Executor client, int notBefore, String managementUrl) {
|
||||
|
@ -263,60 +272,65 @@ public class ResourceAdminManager {
|
|||
ClientRequest request = client.createRequest(UriBuilder.fromUri(managementUrl).path(AdapterConstants.K_LOGOUT).build().toString());
|
||||
ClientResponse response;
|
||||
try {
|
||||
response = request.body(MediaType.TEXT_PLAIN_TYPE, token).post(UserStats.class);
|
||||
response = request.body(MediaType.TEXT_PLAIN_TYPE, token).post();
|
||||
} catch (Exception e) {
|
||||
logger.warn("Logout for application '" + resource.getName() + "' failed", e);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
boolean success = response.getStatus() == 204;
|
||||
logger.debug("logout success.");
|
||||
boolean success = response.getStatus() == 204 || response.getStatus() == 200;
|
||||
logger.debugf("logout success for %s: %s", managementUrl, success);
|
||||
return success;
|
||||
} finally {
|
||||
response.releaseConnection();
|
||||
}
|
||||
}
|
||||
|
||||
public void pushRealmRevocationPolicy(URI requestUri, RealmModel realm) {
|
||||
public GlobalRequestResult pushRealmRevocationPolicy(URI requestUri, RealmModel realm) {
|
||||
ApacheHttpClient4Executor executor = createExecutor();
|
||||
|
||||
try {
|
||||
GlobalRequestResult finalResult = new GlobalRequestResult();
|
||||
for (ApplicationModel application : realm.getApplications()) {
|
||||
pushRevocationPolicy(requestUri, realm, application, realm.getNotBefore(), executor);
|
||||
GlobalRequestResult currentResult = pushRevocationPolicy(requestUri, realm, application, realm.getNotBefore(), executor);
|
||||
finalResult.addAll(currentResult);
|
||||
}
|
||||
return finalResult;
|
||||
} finally {
|
||||
executor.getHttpClient().getConnectionManager().shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public void pushApplicationRevocationPolicy(URI requestUri, RealmModel realm, ApplicationModel application) {
|
||||
public GlobalRequestResult pushApplicationRevocationPolicy(URI requestUri, RealmModel realm, ApplicationModel application) {
|
||||
ApacheHttpClient4Executor executor = createExecutor();
|
||||
|
||||
try {
|
||||
pushRevocationPolicy(requestUri, realm, application, application.getNotBefore(), executor);
|
||||
return pushRevocationPolicy(requestUri, realm, application, application.getNotBefore(), executor);
|
||||
} finally {
|
||||
executor.getHttpClient().getConnectionManager().shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected boolean pushRevocationPolicy(URI requestUri, RealmModel realm, ApplicationModel resource, int notBefore, ApacheHttpClient4Executor executor) {
|
||||
protected GlobalRequestResult pushRevocationPolicy(URI requestUri, RealmModel realm, ApplicationModel resource, int notBefore, ApacheHttpClient4Executor executor) {
|
||||
List<String> mgmtUrls = getAllManagementUrls(requestUri, resource);
|
||||
if (mgmtUrls.isEmpty()) {
|
||||
logger.debug("No management URL or no registered cluster nodes for the application " + resource.getName());
|
||||
return false;
|
||||
logger.debugf("No management URL or no registered cluster nodes for the application %s", resource.getName());
|
||||
return new GlobalRequestResult();
|
||||
}
|
||||
|
||||
logger.info("Sending push revocation to URLS: " + mgmtUrls);
|
||||
|
||||
// Propagate this to all hosts
|
||||
boolean anyFailed= false;
|
||||
GlobalRequestResult result = new GlobalRequestResult();
|
||||
for (String mgmtUrl : mgmtUrls) {
|
||||
if (!sendPushRevocationPolicyRequest(realm, resource, notBefore, executor, mgmtUrl)) {
|
||||
anyFailed = true;
|
||||
if (sendPushRevocationPolicyRequest(realm, resource, notBefore, executor, mgmtUrl)) {
|
||||
result.addSuccessRequest(mgmtUrl);
|
||||
} else {
|
||||
result.addFailedRequest(mgmtUrl);
|
||||
}
|
||||
}
|
||||
return !anyFailed;
|
||||
return result;
|
||||
}
|
||||
|
||||
protected boolean sendPushRevocationPolicyRequest(RealmModel realm, ApplicationModel resource, int notBefore, ApacheHttpClient4Executor client, String managementUrl) {
|
||||
|
@ -332,11 +346,60 @@ public class ResourceAdminManager {
|
|||
return false;
|
||||
}
|
||||
try {
|
||||
boolean success = response.getStatus() == 204;
|
||||
logger.debug("pushRevocation success.");
|
||||
boolean success = response.getStatus() == 204 || response.getStatus() == 200;
|
||||
logger.debugf("pushRevocation success for %s: %s", managementUrl, success);
|
||||
return success;
|
||||
} finally {
|
||||
response.releaseConnection();
|
||||
}
|
||||
}
|
||||
|
||||
public GlobalRequestResult testNodesAvailability(URI requestUri, RealmModel realm, ApplicationModel application) {
|
||||
List<String> mgmtUrls = getAllManagementUrls(requestUri, application);
|
||||
if (mgmtUrls.isEmpty()) {
|
||||
logger.debug("No management URL or no registered cluster nodes for the application " + application.getName());
|
||||
return new GlobalRequestResult();
|
||||
}
|
||||
|
||||
ApacheHttpClient4Executor executor = createExecutor();
|
||||
|
||||
try {
|
||||
logger.info("Sending test nodes availability: " + mgmtUrls);
|
||||
|
||||
// Propagate this to all hosts
|
||||
GlobalRequestResult result = new GlobalRequestResult();
|
||||
for (String mgmtUrl : mgmtUrls) {
|
||||
if (sendTestNodeAvailabilityRequest(realm, application, executor, mgmtUrl)) {
|
||||
result.addSuccessRequest(mgmtUrl);
|
||||
} else {
|
||||
result.addFailedRequest(mgmtUrl);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} finally {
|
||||
executor.getHttpClient().getConnectionManager().shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean sendTestNodeAvailabilityRequest(RealmModel realm, ApplicationModel application, ApacheHttpClient4Executor client, String managementUrl) {
|
||||
TestAvailabilityAction adminAction = new TestAvailabilityAction(TokenIdGenerator.generateId(), Time.currentTime() + 30, application.getName());
|
||||
String token = new TokenManager().encodeToken(realm, adminAction);
|
||||
logger.infov("testNodes availability resource: {0} url: {1}", application.getName(), managementUrl);
|
||||
ClientRequest request = client.createRequest(UriBuilder.fromUri(managementUrl).path(AdapterConstants.K_TEST_AVAILABLE).build().toString());
|
||||
ClientResponse response;
|
||||
try {
|
||||
response = request.body(MediaType.TEXT_PLAIN_TYPE, token).post();
|
||||
} catch (Exception e) {
|
||||
logger.warn("Availability test failed for uri '" + managementUrl + "'", e);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
boolean success = response.getStatus() == 204 || response.getStatus() == 200;
|
||||
logger.debugf("testAvailability success for %s: %s", managementUrl, success);
|
||||
return success;
|
||||
} finally {
|
||||
response.releaseConnection();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.keycloak.services.resources.admin;
|
|||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.jboss.resteasy.spi.BadRequestException;
|
||||
import org.jboss.resteasy.spi.NotFoundException;
|
||||
import org.keycloak.models.ApplicationModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
@ -13,6 +14,7 @@ import org.keycloak.models.UserSessionModel;
|
|||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.models.utils.ModelToRepresentation;
|
||||
import org.keycloak.models.utils.RepresentationToModel;
|
||||
import org.keycloak.representations.adapters.action.GlobalRequestResult;
|
||||
import org.keycloak.representations.idm.ApplicationRepresentation;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.representations.idm.UserSessionRepresentation;
|
||||
|
@ -22,6 +24,7 @@ import org.keycloak.services.managers.ResourceAdminManager;
|
|||
import org.keycloak.services.resources.KeycloakApplication;
|
||||
import org.keycloak.services.resources.flows.Flows;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
import org.keycloak.util.Time;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
|
@ -281,9 +284,9 @@ public class ApplicationResource {
|
|||
*/
|
||||
@Path("push-revocation")
|
||||
@POST
|
||||
public void pushRevocation() {
|
||||
public GlobalRequestResult pushRevocation() {
|
||||
auth.requireManage();
|
||||
new ResourceAdminManager().pushApplicationRevocationPolicy(uriInfo.getRequestUri(), realm, application);
|
||||
return new ResourceAdminManager().pushApplicationRevocationPolicy(uriInfo.getRequestUri(), realm, application);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -333,9 +336,9 @@ public class ApplicationResource {
|
|||
*/
|
||||
@Path("logout-all")
|
||||
@POST
|
||||
public void logoutAll() {
|
||||
public GlobalRequestResult logoutAll() {
|
||||
auth.requireManage();
|
||||
new ResourceAdminManager().logoutApplication(uriInfo.getRequestUri(), realm, application);
|
||||
return new ResourceAdminManager().logoutApplication(uriInfo.getRequestUri(), realm, application);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -354,10 +357,58 @@ public class ApplicationResource {
|
|||
new ResourceAdminManager().logoutUserFromApplication(uriInfo.getRequestUri(), realm, application, user, session);
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually register cluster node to this application - usually it's not needed to call this directly as adapter should handle
|
||||
* by sending registration request to Keycloak
|
||||
*
|
||||
* @param formParams
|
||||
*/
|
||||
@Path("nodes")
|
||||
@POST
|
||||
@Consumes("application/json")
|
||||
public void registerNode(Map<String, String> formParams) {
|
||||
auth.requireManage();
|
||||
String node = formParams.get("node");
|
||||
if (node == null) {
|
||||
throw new BadRequestException("Node not found in params");
|
||||
}
|
||||
logger.info("Register node: " + node);
|
||||
application.registerNode(node, Time.currentTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister cluster node from this application
|
||||
*
|
||||
* @param node
|
||||
*/
|
||||
@Path("nodes/{node}")
|
||||
@DELETE
|
||||
@NoCache
|
||||
public void unregisterNode(final @PathParam("node") String node) {
|
||||
auth.requireManage();
|
||||
logger.info("Unregister node: " + node);
|
||||
|
||||
Integer time = application.getRegisteredNodes().get(node);
|
||||
if (time == null) {
|
||||
throw new NotFoundException("Application does not have a node " + node);
|
||||
}
|
||||
|
||||
application.unregisterNode(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if registered cluster nodes are available by sending 'ping' request to all of them
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Path("test-nodes-available")
|
||||
@GET
|
||||
@NoCache
|
||||
public GlobalRequestResult testNodesAvailable() {
|
||||
auth.requireManage();
|
||||
logger.info("Test availability of cluster nodes");
|
||||
|
||||
return new ResourceAdminManager().testNodesAvailability(uriInfo.getRequestUri(), realm, application);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import org.keycloak.models.cache.CacheUserProvider;
|
|||
import org.keycloak.models.utils.ModelToRepresentation;
|
||||
import org.keycloak.models.utils.RepresentationToModel;
|
||||
import org.keycloak.protocol.oidc.TokenManager;
|
||||
import org.keycloak.representations.adapters.action.GlobalRequestResult;
|
||||
import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.services.managers.LDAPConnectionTestManager;
|
||||
|
@ -255,9 +256,9 @@ public class RealmAdminResource {
|
|||
*/
|
||||
@Path("push-revocation")
|
||||
@POST
|
||||
public void pushRevocation() {
|
||||
public GlobalRequestResult pushRevocation() {
|
||||
auth.requireManage();
|
||||
new ResourceAdminManager().pushRealmRevocationPolicy(uriInfo.getRequestUri(), realm);
|
||||
return new ResourceAdminManager().pushRealmRevocationPolicy(uriInfo.getRequestUri(), realm);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -267,10 +268,10 @@ public class RealmAdminResource {
|
|||
*/
|
||||
@Path("logout-all")
|
||||
@POST
|
||||
public void logoutAll() {
|
||||
public GlobalRequestResult logoutAll() {
|
||||
auth.requireManage();
|
||||
session.sessions().removeUserSessions(realm);
|
||||
new ResourceAdminManager().logoutAll(uriInfo.getRequestUri(), realm);
|
||||
return new ResourceAdminManager().logoutAll(uriInfo.getRequestUri(), realm);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue