keycloak-aplcache
Changes
export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportUtils.java 24(+3 -21)
forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js 18(+18 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/role-mappings.html 8(+1 -7)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-consents.html 47(+47 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-credentials.html 9(+1 -8)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-detail.html 9(+1 -8)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-federated-identity.html 8(+1 -7)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-sessions.html 8(+1 -7)
Details
diff --git a/core/src/main/java/org/keycloak/representations/idm/UserConsentRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/UserConsentRepresentation.java
index 113ba84..93dbd73 100644
--- a/core/src/main/java/org/keycloak/representations/idm/UserConsentRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/UserConsentRepresentation.java
@@ -1,28 +1,52 @@
package org.keycloak.representations.idm;
import java.util.List;
+import java.util.Map;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class UserConsentRepresentation {
- protected List<String> grantedRoles; // points to roleIds
- protected List<String> grantedProtocolMappers; // points to protocolMapperIds
+ protected String clientId;
- public List<String> getGrantedRoles() {
- return grantedRoles;
+ // Key is protocol, Value is list of granted consents for this protocol
+ protected Map<String, List<String>> grantedProtocolMappers;
+
+ protected List<String> grantedRealmRoles;
+
+ // Key is clientId, Value is list of granted roles of this client
+ protected Map<String, List<String>> grantedClientRoles;
+
+ public String getClientId() {
+ return clientId;
}
- public void setGrantedRoles(List<String> grantedRoles) {
- this.grantedRoles = grantedRoles;
+ public void setClientId(String clientId) {
+ this.clientId = clientId;
}
- public List<String> getGrantedProtocolMappers() {
+ public Map<String, List<String>> getGrantedProtocolMappers() {
return grantedProtocolMappers;
}
- public void setGrantedProtocolMappers(List<String> grantedProtocolMappers) {
+ public void setGrantedProtocolMappers(Map<String, List<String>> grantedProtocolMappers) {
this.grantedProtocolMappers = grantedProtocolMappers;
}
+
+ public List<String> getGrantedRealmRoles() {
+ return grantedRealmRoles;
+ }
+
+ public void setGrantedRealmRoles(List<String> grantedRealmRoles) {
+ this.grantedRealmRoles = grantedRealmRoles;
+ }
+
+ public Map<String, List<String>> getGrantedClientRoles() {
+ return grantedClientRoles;
+ }
+
+ public void setGrantedClientRoles(Map<String, List<String>> grantedClientRoles) {
+ this.grantedClientRoles = grantedClientRoles;
+ }
}
diff --git a/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
index b9716d3..747b64c 100755
--- a/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
@@ -27,7 +27,7 @@ public class UserRepresentation {
protected List<FederatedIdentityRepresentation> federatedIdentities;
protected List<String> realmRoles;
protected Map<String, List<String>> clientRoles;
- protected Map<String, UserConsentRepresentation> clientConsents;
+ protected List<UserConsentRepresentation> clientConsents;
@Deprecated
protected Map<String, List<String>> applicationRoles;
@@ -177,11 +177,11 @@ public class UserRepresentation {
this.clientRoles = clientRoles;
}
- public Map<String, UserConsentRepresentation> getClientConsents() {
+ public List<UserConsentRepresentation> getClientConsents() {
return clientConsents;
}
- public void setClientConsents(Map<String, UserConsentRepresentation> clientConsents) {
+ public void setClientConsents(List<UserConsentRepresentation> clientConsents) {
this.clientConsents = clientConsents;
}
diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportUtils.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
index bccce2d..8165471 100755
--- a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
+++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
@@ -287,29 +287,11 @@ public class ExportUtils {
// Grants
List<UserConsentModel> consents = user.getConsents();
- Map<String, UserConsentRepresentation> consentReps = new HashMap<String, UserConsentRepresentation>();
+ LinkedList<UserConsentRepresentation> consentReps = new LinkedList<UserConsentRepresentation>();
for (UserConsentModel consent : consents) {
- String clientId = consent.getClient().getClientId();
-
- List<String> grantedProtocolMappers = new LinkedList<String>();
- for (ProtocolMapperModel protocolMapper : consent.getGrantedProtocolMappers()) {
- grantedProtocolMappers.add(protocolMapper.getId());
- }
-
- List<String> grantedRoles = new LinkedList<String>();
- for (RoleModel role : consent.getGrantedRoles()) {
- grantedRoles.add(role.getId());
- }
-
-
- if (grantedRoles.size() > 0 || grantedProtocolMappers.size() > 0) {
- UserConsentRepresentation consentRep = new UserConsentRepresentation();
- if (grantedRoles.size() > 0) consentRep.setGrantedRoles(grantedRoles);
- if (grantedProtocolMappers.size() > 0) consentRep.setGrantedProtocolMappers(grantedProtocolMappers);
- consentReps.put(clientId, consentRep);
- }
+ UserConsentRepresentation consentRep = ModelToRepresentation.toRepresentation(consent);
+ consentReps.add(consentRep);
}
-
if (consentReps.size() > 0) {
userRep.setClientConsents(consentReps);
}
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
index e696714..52de4a0 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
@@ -399,6 +399,21 @@ module.config([ '$routeProvider', function($routeProvider) {
},
controller : 'UserFederatedIdentityCtrl'
})
+ .when('/realms/:realm/users/:user/consents', {
+ templateUrl : resourceUrl + '/partials/user-consents.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ user : function(UserLoader) {
+ return UserLoader();
+ },
+ userConsents : function(UserConsentsLoader) {
+ return UserConsentsLoader();
+ }
+ },
+ controller : 'UserConsentsCtrl'
+ })
.when('/realms/:realm/users', {
templateUrl : resourceUrl + '/partials/user-list.html',
resolve : {
@@ -1418,6 +1433,15 @@ module.directive('kcNavigationClient', function () {
}
});
+module.directive('kcNavigationUser', function () {
+ return {
+ scope: true,
+ restrict: 'E',
+ replace: true,
+ templateUrl: resourceUrl + '/templates/kc-navigation-user.html'
+ }
+});
+
/*
* Used to select the element (invoke $(elem).select()) on specified action list.
* Usages kc-select-action="click mouseover"
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
index 17fbb16..efc33b8 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
@@ -135,6 +135,24 @@ module.controller('UserFederatedIdentityCtrl', function($scope, realm, user, fed
$scope.federatedIdentities = federatedIdentities;
});
+module.controller('UserConsentsCtrl', function($scope, realm, user, userConsents, UserConsents, Notifications) {
+ $scope.realm = realm;
+ $scope.user = user;
+ $scope.userConsents = userConsents;
+
+ $scope.revokeConsent = function(clientId) {
+ UserConsents.delete({realm : realm.realm, user: user.username, client: clientId }, function () {
+ UserConsents.query({realm: realm.realm, user: user.username}, function(updated) {
+ $scope.userConsents = updated;
+ })
+ Notifications.success('Consent revoked successfully');
+ }, function() {
+ Notifications.error("Consent couldn't be revoked");
+ });
+ console.log("Revoke consent " + clientId);
+ }
+});
+
module.controller('UserListCtrl', function($scope, realm, User) {
$scope.realm = realm;
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js
index bf36307..3f72ffe 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js
@@ -144,6 +144,14 @@ module.factory('UserFederatedIdentityLoader', function(Loader, UserFederatedIden
});
});
+module.factory('UserConsentsLoader', function(Loader, UserConsents, $route, $q) {
+ return Loader.query(UserConsents, function() {
+ return {
+ realm : $route.current.params.realm,
+ user : $route.current.params.user
+ }
+ });
+});
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
index ce209a6..7708821 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
@@ -268,6 +268,13 @@ module.factory('UserFederatedIdentity', function($resource) {
user : '@user'
});
});
+module.factory('UserConsents', function($resource) {
+ return $resource(authUrl + '/admin/realms/:realm/users/:user/consents/:client', {
+ realm : '@realm',
+ user : '@user',
+ client: '@client'
+ });
+});
module.factory('UserCredentials', function($resource) {
var credentials = {};
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/role-mappings.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/role-mappings.html
index 314212b..0cb3c9c 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/role-mappings.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/role-mappings.html
@@ -1,12 +1,6 @@
<div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="resourceUrl + '/partials/realm-menu.html'"></div>
<div id="content-area" class="col-md-9" role="main">
- <ul class="nav nav-tabs nav-tabs-pf" data-ng-show="!create">
- <li><a href="#/realms/{{realm.realm}}/users/{{user.username}}">Attributes</a></li>
- <li data-ng-show="access.manageUsers"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/user-credentials">Credentials</a></li>
- <li class="active"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/role-mappings">Role Mappings</a></li>
- <li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/sessions">Sessions</a></li>
- <li data-ng-show="realm.identityFederationEnabled"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/social-links">Federated Identities</a></li>
- </ul>
+ <kc-navigation-user></kc-navigation-user>
<div id="content">
<ol class="breadcrumb">
<li><a href="#/realms/{{realm.realm}}/users">Users</a></li>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-consents.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-consents.html
new file mode 100644
index 0000000..4705e65
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-consents.html
@@ -0,0 +1,47 @@
+<div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="resourceUrl + '/partials/realm-menu.html'"></div>
+<div id="content-area" class="col-md-9" role="main">
+ <kc-navigation-user></kc-navigation-user>
+ <div id="content">
+ <ol class="breadcrumb">
+ <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">Consents</li>
+ </ol>
+ <h2>User <span>{{user.username}}</span> Consents <span tooltip-placement="right" tooltip="This page shows you all the consents, which user granted permissions" class="fa fa-info-circle"></span></h2>
+ <table class="table table-striped table-bordered">
+ <thead>
+ <tr>
+ <th>Client</th>
+ <th>Granted Roles</th>
+ <th>Granted Protocol Mappers</th>
+ <th>Action</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr data-ng-repeat="consent in userConsents">
+ <td>{{consent.clientId}}</td>
+ <td>
+ <span data-ng-repeat="realmRole in consent.grantedRealmRoles">
+ <span ng-if="!$first">, </span>{{realmRole}}
+ </span>
+ <span data-ng-repeat="(clientId, clientRoles) in consent.grantedClientRoles">
+ <span data-ng-repeat="clientRole in clientRoles">
+ <span ng-if="!$first || consent.grantedRealmRoles.length > 0">, </span>{{clientRole}} in {{clientId}}
+ </span>
+ </span>
+ </td>
+ <td>
+ <span data-ng-repeat="protocol in consent.grantedProtocolMappers">
+ <span data-ng-repeat="protocolMapper in protocol">
+ <span ng-if="!$first">, </span>{{protocolMapper}}
+ </span>
+ </span>
+ </td>
+ <td>
+ <button class="btn btn-danger" ng-click="revokeConsent(consent.clientId)">Revoke consent</button>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+</div>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-credentials.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-credentials.html
index 8ca73a6..94b2bca 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-credentials.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-credentials.html
@@ -1,13 +1,6 @@
<div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="resourceUrl + '/partials/realm-menu.html'"></div>
<div id="content-area" class="col-md-9" role="main">
-
- <ul class="nav nav-tabs nav-tabs-pf" data-ng-show="!create">
- <li><a href="#/realms/{{realm.realm}}/users/{{user.username}}">Attributes</a></li>
- <li class="active"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/user-credentials">Credentials</a></li>
- <li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/role-mappings">Role Mappings</a></li>
- <li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/sessions">Sessions</a></li>
- <li data-ng-show="realm.identityFederationEnabled"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/federated-identity">Federated Identities</a></li>
- </ul>
+ <kc-navigation-user></kc-navigation-user>
<div id="content">
<ol class="breadcrumb">
<li><a href="#/realms/{{realm.realm}}/users">Users</a></li>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-detail.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-detail.html
index 5007f12..f6a6f71 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-detail.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-detail.html
@@ -1,13 +1,6 @@
<div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="resourceUrl + '/partials/realm-menu.html'"></div>
<div id="content-area" class="col-md-9" role="main">
-
- <ul class="nav nav-tabs nav-tabs-pf" data-ng-show="!create">
- <li class="active"><a href="#/realms/{{realm.realm}}/users/{{user.username}}">Attributes</a></li>
- <li data-ng-show="access.manageUsers"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/user-credentials">Credentials</a></li>
- <li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/role-mappings">Role Mappings</a></li>
- <li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/sessions">Sessions</a></li>
- <li data-ng-show="realm.identityFederationEnabled"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/federated-identity">Federated Identities</a></li>
- </ul>
+ <kc-navigation-user></kc-navigation-user>
<ul class="nav nav-tabs nav-tabs-pf" data-ng-show="create">
<li class="active"><a href="">User List</a></li>
<li><a href="#/realms/{{realm.realm}}/user-federation">Federation</a></li>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-federated-identity.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-federated-identity.html
index dd23d7d..8d9c328 100644
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-federated-identity.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-federated-identity.html
@@ -1,12 +1,6 @@
<div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="resourceUrl + '/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><a href="#/realms/{{realm.realm}}/users/{{user.username}}/sessions">Sessions</a></li>
- <li class="active"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/federated-identity">Federated Identities</a></li>
- </ul>
+ <kc-navigation-user></kc-navigation-user>
<div id="content">
<ol class="breadcrumb">
<li><a href="#/realms/{{realm.realm}}/users">Users</a></li>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-sessions.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-sessions.html
index 9db3f7d..333bc46 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-sessions.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-sessions.html
@@ -1,12 +1,6 @@
<div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="resourceUrl + '/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>
- <li data-ng-show="realm.identityFederationEnabled"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/federated-identity">Federated Identities</a></li>
- </ul>
+ <kc-navigation-user></kc-navigation-user>
<div id="content">
<ol class="breadcrumb">
<li><a href="#/realms/{{realm.realm}}/users">Users</a></li>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-navigation-user.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-navigation-user.html
new file mode 100644
index 0000000..ae91375
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-navigation-user.html
@@ -0,0 +1,8 @@
+<ul class="nav nav-tabs nav-tabs-pf" data-ng-hide="create">
+ <li ng-class="{active: !path[4]}"><a href="#/realms/{{realm.realm}}/users/{{user.username}}">Attributes</a></li>
+ <li ng-class="{active: path[4] == 'user-credentials'}"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/user-credentials">Credentials</a></li>
+ <li ng-class="{active: path[4] == 'role-mappings'}"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/role-mappings">Role Mappings</a></li>
+ <li ng-class="{active: path[4] == 'sessions'}"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/sessions">Sessions</a></li>
+ <li ng-class="{active: path[4] == 'federated-identity'}" data-ng-show="realm.identityFederationEnabled"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/federated-identity">Federated Identities</a></li>
+ <li ng-class="{active: path[4] == 'consents'}"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/consents">Consents</a></li>
+</ul>
\ No newline at end of file
diff --git a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index a66d327..1da1fcf 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -9,6 +9,7 @@ import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredCredentialModel;
import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserConsentModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
@@ -22,6 +23,7 @@ import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.UserConsentRepresentation;
import org.keycloak.representations.idm.UserFederationProviderRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.representations.idm.UserSessionRepresentation;
@@ -325,4 +327,45 @@ public class ModelToRepresentation {
return rep;
}
+ public static UserConsentRepresentation toRepresentation(UserConsentModel model) {
+ String clientId = model.getClient().getClientId();
+
+ Map<String, List<String>> grantedProtocolMappers = new HashMap<String, List<String>>();
+ for (ProtocolMapperModel protocolMapper : model.getGrantedProtocolMappers()) {
+ String protocol = protocolMapper.getProtocol();
+ List<String> currentProtocolMappers = grantedProtocolMappers.get(protocol);
+ if (currentProtocolMappers == null) {
+ currentProtocolMappers = new LinkedList<String>();
+ grantedProtocolMappers.put(protocol, currentProtocolMappers);
+ }
+ currentProtocolMappers.add(protocolMapper.getName());
+ }
+
+ List<String> grantedRealmRoles = new LinkedList<String>();
+ Map<String, List<String>> grantedClientRoles = new HashMap<String, List<String>>();
+ for (RoleModel role : model.getGrantedRoles()) {
+ if (role.getContainer() instanceof RealmModel) {
+ grantedRealmRoles.add(role.getName());
+ } else {
+ ClientModel client2 = (ClientModel) role.getContainer();
+
+ String clientId2 = client2.getClientId();
+ List<String> currentClientRoles = grantedClientRoles.get(clientId2);
+ if (currentClientRoles == null) {
+ currentClientRoles = new LinkedList<String>();
+ grantedClientRoles.put(clientId2, currentClientRoles);
+ }
+ currentClientRoles.add(role.getName());
+ }
+ }
+
+
+ UserConsentRepresentation consentRep = new UserConsentRepresentation();
+ consentRep.setClientId(clientId);
+ consentRep.setGrantedProtocolMappers(grantedProtocolMappers);
+ consentRep.setGrantedRealmRoles(grantedRealmRoles);
+ consentRep.setGrantedClientRoles(grantedClientRoles);
+ return consentRep;
+ }
+
}
diff --git a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index c53328d..d39356d 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -788,33 +788,8 @@ public class RepresentationToModel {
}
}
if (userRep.getClientConsents() != null) {
- for (Map.Entry<String, UserConsentRepresentation> entry : userRep.getClientConsents().entrySet()) {
- ClientModel client = clientMap.get(entry.getKey());
- if (client == null) {
- throw new RuntimeException("Unable to find client consent mappings for client: " + entry.getKey());
- }
-
- UserConsentModel consentModel = new UserConsentModel(client);
-
- UserConsentRepresentation consentRep = entry.getValue();
- if (consentRep.getGrantedRoles() != null) {
- for (String roleId : consentRep.getGrantedRoles()) {
- RoleModel role = newRealm.getRoleById(roleId);
- if (role == null) {
- throw new RuntimeException("Unable to find realm role referenced in consent mappings of user " + user.getUsername() + ". Role ID: " + roleId);
- }
- consentModel.addGrantedRole(role);
- }
- }
- if (consentRep.getGrantedProtocolMappers() != null) {
- for (String mapperId : consentRep.getGrantedProtocolMappers()) {
- ProtocolMapperModel protocolMapper = client.getProtocolMapperById(mapperId);
- if (protocolMapper == null) {
- throw new RuntimeException("Unable to find protocol mapper referenced in consent mappings of user " + user.getUsername() + ". Protocol mapper ID: " + mapperId);
- }
- consentModel.addGrantedProtocolMapper(protocolMapper);
- }
- }
+ for (UserConsentRepresentation consentRep : userRep.getClientConsents()) {
+ UserConsentModel consentModel = toModel(newRealm, consentRep);
user.addConsent(consentModel);
}
}
@@ -917,4 +892,53 @@ public class RepresentationToModel {
return model;
}
+ public static UserConsentModel toModel(RealmModel newRealm, UserConsentRepresentation consentRep) {
+ ClientModel client = newRealm.getClientByClientId(consentRep.getClientId());
+ if (client == null) {
+ throw new RuntimeException("Unable to find client consent mappings for client: " + consentRep.getClientId());
+ }
+
+ UserConsentModel consentModel = new UserConsentModel(client);
+
+ if (consentRep.getGrantedRealmRoles() != null) {
+ for (String roleName : consentRep.getGrantedRealmRoles()) {
+ RoleModel role = newRealm.getRole(roleName);
+ if (role == null) {
+ throw new RuntimeException("Unable to find realm role referenced in consent mappings of user. Role name: " + roleName);
+ }
+ consentModel.addGrantedRole(role);
+ }
+ }
+ if (consentRep.getGrantedClientRoles() != null) {
+ for (Map.Entry<String, List<String>> entry : consentRep.getGrantedClientRoles().entrySet()) {
+ String clientId2 = entry.getKey();
+ ClientModel client2 = newRealm.getClientByClientId(clientId2);
+ if (client2 == null) {
+ throw new RuntimeException("Unable to find client referenced in consent mappings. Client ID: " + clientId2);
+ }
+ for (String clientRoleName : entry.getValue()) {
+ RoleModel clientRole = client2.getRole(clientRoleName);
+ if (clientRole == null) {
+ throw new RuntimeException("Unable to find client role referenced in consent mappings of user. Role name: " + clientRole + ", Client: " + clientId2);
+ }
+ consentModel.addGrantedRole(clientRole);
+ }
+ }
+ }
+ if (consentRep.getGrantedProtocolMappers() != null) {
+ for (Map.Entry<String, List<String>> protocolEntry : consentRep.getGrantedProtocolMappers().entrySet()) {
+ String protocol = protocolEntry.getKey();
+ for (String protocolMapperName : protocolEntry.getValue()) {
+ ProtocolMapperModel protocolMapper = client.getProtocolMapperByName(protocol, protocolMapperName);
+ if (protocolMapper == null) {
+ throw new RuntimeException("Unable to find protocol mapper for protocol " + protocol + ", mapper name " + protocolMapperName);
+ }
+
+ consentModel.addGrantedProtocolMapper(protocolMapper);
+ }
+ }
+ }
+ return consentModel;
+ }
+
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
index a1dd22d..20aeaa5 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
@@ -17,6 +17,7 @@ import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.ModelReadOnlyException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserConsentModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
@@ -30,6 +31,7 @@ import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
import org.keycloak.representations.idm.MappingsRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.UserConsentRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.representations.idm.UserSessionRepresentation;
import org.keycloak.services.managers.AuthenticationManager;
@@ -311,6 +313,56 @@ public class UsersResource {
}
/**
+ * List set of consents granted by this user.
+ *
+ * @param username
+ * @return
+ */
+ @Path("{username}/consents")
+ @GET
+ @NoCache
+ @Produces(MediaType.APPLICATION_JSON)
+ public List<UserConsentRepresentation> getConsents(final @PathParam("username") String username) {
+ auth.requireView();
+ UserModel user = session.users().getUserByUsername(username, realm);
+ if (user == null) {
+ throw new NotFoundException("User not found");
+ }
+
+ List<UserConsentModel> consents = user.getConsents();
+ List<UserConsentRepresentation> result = new ArrayList<UserConsentRepresentation>();
+
+ for (UserConsentModel consent : consents) {
+ UserConsentRepresentation rep = ModelToRepresentation.toRepresentation(consent);
+ result.add(rep);
+ }
+ return result;
+ }
+
+ /**
+ * Revoke consent for particular client
+ *
+ * @param username
+ * @param clientId
+ */
+ @Path("{username}/consents/{client}")
+ @DELETE
+ @NoCache
+ public void revokeConsent(final @PathParam("username") String username, final @PathParam("client") String clientId) {
+ auth.requireManage();
+ UserModel user = session.users().getUserByUsername(username, realm);
+ if (user == null) {
+ throw new NotFoundException("User not found");
+ }
+
+ ClientModel client = realm.getClientByClientId(clientId);
+ boolean revoked = user.revokeConsentForClient(client.getId());
+ if (!revoked) {
+ throw new NotFoundException("Consent not found for user " + username + " and client " + clientId);
+ }
+ }
+
+ /**
* Remove all user sessions associated with this user. And, for all client that have an admin URL, tell
* them to invalidate the sessions for this particular user.
*
diff --git a/testsuite/integration/src/test/resources/model/testrealm.json b/testsuite/integration/src/test/resources/model/testrealm.json
index e6d4d18..50aee59 100755
--- a/testsuite/integration/src/test/resources/model/testrealm.json
+++ b/testsuite/integration/src/test/resources/model/testrealm.json
@@ -75,15 +75,22 @@
"Application": [ "app-admin" ],
"OtherApp": [ "otherapp-admin" ]
},
- "clientConsents": {
- "Application": {
- "grantedRoles": [ "456", "789" ]
+ "clientConsents": [
+ {
+ "clientId": "Application",
+ "grantedRealmRoles": [ "admin" ],
+ "grantedClientRoles": {
+ "Application": [ "app-admin" ]
+ }
},
- "OtherApp": {
- "grantedProtocolMappers": [ "123" ],
- "grantedRoles": [ "456" ]
+ {
+ "clientId": "OtherApp",
+ "grantedRealmRoles": [ "admin" ],
+ "grantedProtocolMappers": {
+ "openid-connect": [ "gss delegation credential" ]
+ }
}
- }
+ ]
},
{
"username": "mySocialUser",
@@ -124,7 +131,6 @@
"enabled": true,
"protocolMappers" : [
{
- "id": "123",
"name" : "gss delegation credential",
"protocol" : "openid-connect",
"protocolMapper" : "oidc-usersessionmodel-note-mapper",
@@ -150,14 +156,12 @@
"roles" : {
"realm" : [
{
- "id": "456",
"name": "admin"
}
],
"application" : {
"Application" : [
{
- "id": "789",
"name": "app-admin"
},
{