group ui
This commit is contained in:
parent
151c56a304
commit
e25157655b
45 changed files with 1882 additions and 302 deletions
|
@ -12,9 +12,9 @@ import java.util.Map;
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class GroupRepresentation {
|
public class GroupRepresentation {
|
||||||
private String id;
|
protected String id;
|
||||||
private String name;
|
protected String name;
|
||||||
protected Map<String, Object> attributes;
|
protected Map<String, List<String>> attributes;
|
||||||
protected List<String> realmRoles;
|
protected List<String> realmRoles;
|
||||||
protected Map<String, List<String>> clientRoles;
|
protected Map<String, List<String>> clientRoles;
|
||||||
protected List<GroupRepresentation> subGroups;
|
protected List<GroupRepresentation> subGroups;
|
||||||
|
@ -51,17 +51,12 @@ public class GroupRepresentation {
|
||||||
this.clientRoles = clientRoles;
|
this.clientRoles = clientRoles;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Object> getAttributes() {
|
|
||||||
|
public Map<String, List<String>> getAttributes() {
|
||||||
return attributes;
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method can be removed once we can remove backwards compatibility with Keycloak 1.3 (then getAttributes() can be changed to return Map<String, List<String>> )
|
public void setAttributes(Map<String, List<String>> attributes) {
|
||||||
@JsonIgnore
|
|
||||||
public Map<String, List<String>> getAttributesAsListValues() {
|
|
||||||
return (Map) attributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAttributes(Map<String, Object> attributes) {
|
|
||||||
this.attributes = attributes;
|
this.attributes = attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
package org.keycloak.representations.idm;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public class UserRoleMappingRepresentation {
|
|
||||||
protected String self; // link
|
|
||||||
protected String username;
|
|
||||||
protected Set<String> roles;
|
|
||||||
|
|
||||||
public String getSelf() {
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSelf(String self) {
|
|
||||||
this.self = self;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUsername() {
|
|
||||||
return username;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUsername(String username) {
|
|
||||||
this.username = username;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<String> getRoles() {
|
|
||||||
return roles;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRoles(Set<String> roles) {
|
|
||||||
this.roles = roles;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UserRoleMappingRepresentation role(String role) {
|
|
||||||
if (this.roles == null) this.roles = new HashSet<String>();
|
|
||||||
this.roles.add(role);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -25,6 +25,7 @@
|
||||||
<script src="${resourceUrl}/lib/angular/angular-sanitize.js"></script>
|
<script src="${resourceUrl}/lib/angular/angular-sanitize.js"></script>
|
||||||
<script src="${resourceUrl}/lib/angular/angular-translate.js"></script>
|
<script src="${resourceUrl}/lib/angular/angular-translate.js"></script>
|
||||||
<script src="${resourceUrl}/lib/angular/angular-translate-loader-url.js"></script>
|
<script src="${resourceUrl}/lib/angular/angular-translate-loader-url.js"></script>
|
||||||
|
<script src="${resourceUrl}/lib/angular/treeview/angular.treeview.js"></script>
|
||||||
<script src="${resourceUrl}/lib/angular/ui-bootstrap-tpls-0.11.0.js"></script>
|
<script src="${resourceUrl}/lib/angular/ui-bootstrap-tpls-0.11.0.js"></script>
|
||||||
|
|
||||||
<script src="${resourceUrl}/lib/angular/select2.js" type="text/javascript"></script>
|
<script src="${resourceUrl}/lib/angular/select2.js" type="text/javascript"></script>
|
||||||
|
@ -37,6 +38,7 @@
|
||||||
<script src="${resourceUrl}/js/controllers/realm.js" type="text/javascript"></script>
|
<script src="${resourceUrl}/js/controllers/realm.js" type="text/javascript"></script>
|
||||||
<script src="${resourceUrl}/js/controllers/clients.js" type="text/javascript"></script>
|
<script src="${resourceUrl}/js/controllers/clients.js" type="text/javascript"></script>
|
||||||
<script src="${resourceUrl}/js/controllers/users.js" type="text/javascript"></script>
|
<script src="${resourceUrl}/js/controllers/users.js" type="text/javascript"></script>
|
||||||
|
<script src="${resourceUrl}/js/controllers/groups.js" type="text/javascript"></script>
|
||||||
<script src="${resourceUrl}/js/loaders.js" type="text/javascript"></script>
|
<script src="${resourceUrl}/js/loaders.js" type="text/javascript"></script>
|
||||||
<script src="${resourceUrl}/js/services.js" type="text/javascript"></script>
|
<script src="${resourceUrl}/js/services.js" type="text/javascript"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -7,7 +7,7 @@ var configUrl = consoleBaseUrl + "/config";
|
||||||
|
|
||||||
var auth = {};
|
var auth = {};
|
||||||
|
|
||||||
var module = angular.module('keycloak', [ 'keycloak.services', 'keycloak.loaders', 'ui.bootstrap', 'ui.select2', 'angularFileUpload', 'pascalprecht.translate', 'ngCookies', 'ngSanitize']);
|
var module = angular.module('keycloak', [ 'keycloak.services', 'keycloak.loaders', 'ui.bootstrap', 'ui.select2', 'angularFileUpload', 'angularTreeview', 'pascalprecht.translate', 'ngCookies', 'ngSanitize']);
|
||||||
var resourceRequests = 0;
|
var resourceRequests = 0;
|
||||||
var loadingTimer = -1;
|
var loadingTimer = -1;
|
||||||
|
|
||||||
|
@ -574,6 +574,73 @@ module.config([ '$routeProvider', function($routeProvider) {
|
||||||
},
|
},
|
||||||
controller : 'RoleListCtrl'
|
controller : 'RoleListCtrl'
|
||||||
})
|
})
|
||||||
|
.when('/realms/:realm/groups', {
|
||||||
|
templateUrl : resourceUrl + '/partials/group-list.html',
|
||||||
|
resolve : {
|
||||||
|
realm : function(RealmLoader) {
|
||||||
|
return RealmLoader();
|
||||||
|
},
|
||||||
|
groups : function(GroupListLoader) {
|
||||||
|
return GroupListLoader();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
controller : 'GroupListCtrl'
|
||||||
|
})
|
||||||
|
.when('/create/group/:realm/parent/:parentId', {
|
||||||
|
templateUrl : resourceUrl + '/partials/create-group.html',
|
||||||
|
resolve : {
|
||||||
|
realm : function(RealmLoader) {
|
||||||
|
return RealmLoader();
|
||||||
|
},
|
||||||
|
parentId : function($route) {
|
||||||
|
return $route.current.params.parentId;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
controller : 'GroupCreateCtrl'
|
||||||
|
})
|
||||||
|
.when('/realms/:realm/groups/:group', {
|
||||||
|
templateUrl : resourceUrl + '/partials/group-detail.html',
|
||||||
|
resolve : {
|
||||||
|
realm : function(RealmLoader) {
|
||||||
|
return RealmLoader();
|
||||||
|
},
|
||||||
|
group : function(GroupLoader) {
|
||||||
|
return GroupLoader();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
controller : 'GroupDetailCtrl'
|
||||||
|
})
|
||||||
|
.when('/realms/:realm/groups/:group/attributes', {
|
||||||
|
templateUrl : resourceUrl + '/partials/group-attributes.html',
|
||||||
|
resolve : {
|
||||||
|
realm : function(RealmLoader) {
|
||||||
|
return RealmLoader();
|
||||||
|
},
|
||||||
|
group : function(GroupLoader) {
|
||||||
|
return GroupLoader();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
controller : 'GroupDetailCtrl'
|
||||||
|
})
|
||||||
|
.when('/realms/:realm/groups/:group/role-mappings', {
|
||||||
|
templateUrl : resourceUrl + '/partials/group-role-mappings.html',
|
||||||
|
resolve : {
|
||||||
|
realm : function(RealmLoader) {
|
||||||
|
return RealmLoader();
|
||||||
|
},
|
||||||
|
group : function(GroupLoader) {
|
||||||
|
return GroupLoader();
|
||||||
|
},
|
||||||
|
clients : function(ClientListLoader) {
|
||||||
|
return ClientListLoader();
|
||||||
|
},
|
||||||
|
client : function() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
controller : 'GroupRoleMappingCtrl'
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
.when('/create/role/:realm/clients/:client', {
|
.when('/create/role/:realm/clients/:client', {
|
||||||
templateUrl : resourceUrl + '/partials/client-role-detail.html',
|
templateUrl : resourceUrl + '/partials/client-role-detail.html',
|
||||||
|
@ -1856,6 +1923,15 @@ module.directive('kcTabsUser', function () {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.directive('kcTabsGroup', function () {
|
||||||
|
return {
|
||||||
|
scope: true,
|
||||||
|
restrict: 'E',
|
||||||
|
replace: true,
|
||||||
|
templateUrl: resourceUrl + '/templates/kc-tabs-group.html'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
module.directive('kcTabsClient', function () {
|
module.directive('kcTabsClient', function () {
|
||||||
return {
|
return {
|
||||||
scope: true,
|
scope: true,
|
||||||
|
|
|
@ -0,0 +1,321 @@
|
||||||
|
module.controller('GroupListCtrl', function($scope, $route, realm, groups, Groups, Group, GroupChildren, Notifications, $location, Dialog) {
|
||||||
|
$scope.realm = realm;
|
||||||
|
$scope.groupList = [
|
||||||
|
{"id" : "realm", "name": "Groups",
|
||||||
|
"subGroups" : groups}
|
||||||
|
];
|
||||||
|
|
||||||
|
$scope.tree = [];
|
||||||
|
|
||||||
|
$scope.edit = function(selected) {
|
||||||
|
$location.url("/realms/" + realm.realm + "/groups/" + selected.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.cut = function(selected) {
|
||||||
|
$scope.cutNode = selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.isDisabled = function() {
|
||||||
|
if (!$scope.tree.currentNode) return true;
|
||||||
|
return $scope.tree.currentNode.id == 'realm';
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.paste = function(selected) {
|
||||||
|
if (selected == null) return;
|
||||||
|
if ($scope.cutNode == null) return;
|
||||||
|
if (selected.id == 'realm') {
|
||||||
|
Groups.save({realm: realm.realm}, {id:$scope.cutNode.id}, function() {
|
||||||
|
$route.reload();
|
||||||
|
Notifications.success("Group moved.");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
GroupChildren.save({realm: realm.realm, groupId: selected.id}, {id:$scope.cutNode.id}, function() {
|
||||||
|
$route.reload();
|
||||||
|
Notifications.success("Group moved.");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.remove = function(selected) {
|
||||||
|
if (selected == null) return;
|
||||||
|
Dialog.confirmDelete(selected.name, 'group', function() {
|
||||||
|
Group.remove({ realm: realm.realm, groupId : selected.id }, function() {
|
||||||
|
$route.reload();
|
||||||
|
Notifications.success("The group has been deleted.");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.createGroup = function(selected) {
|
||||||
|
var parent = 'realm';
|
||||||
|
if (selected) {
|
||||||
|
parent = selected.id;
|
||||||
|
}
|
||||||
|
$location.url("/create/group/" + realm.realm + '/parent/' + parent);
|
||||||
|
|
||||||
|
}
|
||||||
|
var isLeaf = function(node) {
|
||||||
|
return node.id != "realm" && (!node.subGroups || node.subGroups.length == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.getGroupClass = function(node) {
|
||||||
|
if (node.id == "realm") {
|
||||||
|
return 'pficon pficon-users';
|
||||||
|
}
|
||||||
|
if (isLeaf(node)) {
|
||||||
|
return 'normal';
|
||||||
|
}
|
||||||
|
if (node.subGroups.length && node.collapsed) return 'collapsed';
|
||||||
|
if (node.subGroups.length && !node.collapsed) return 'expanded';
|
||||||
|
return 'collapsed';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.getSelectedClass = function(node) {
|
||||||
|
if (node.selected) {
|
||||||
|
return 'selected';
|
||||||
|
} else if ($scope.cutNode && $scope.cutNode.id == node.id) {
|
||||||
|
return 'cut';
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
module.controller('GroupCreateCtrl', function($scope, $route, realm, parentId, Groups, Group, GroupChildren, Notifications, $location) {
|
||||||
|
$scope.realm = realm;
|
||||||
|
$scope.group = {};
|
||||||
|
$scope.save = function() {
|
||||||
|
console.log('save!!!');
|
||||||
|
if (parentId == 'realm') {
|
||||||
|
console.log('realm')
|
||||||
|
Groups.save({realm: realm.realm, groupId: parentId}, $scope.group, function(data, headers) {
|
||||||
|
var l = headers().location;
|
||||||
|
|
||||||
|
|
||||||
|
var id = l.substring(l.lastIndexOf("/") + 1);
|
||||||
|
|
||||||
|
$location.url("/realms/" + realm.realm + "/groups/" + id);
|
||||||
|
Notifications.success("Group Created.");
|
||||||
|
})
|
||||||
|
|
||||||
|
} else {
|
||||||
|
GroupChildren.save({realm: realm.realm, groupId: parentId}, $scope.group, function(data, headers) {
|
||||||
|
var l = headers().location;
|
||||||
|
|
||||||
|
|
||||||
|
var id = l.substring(l.lastIndexOf("/") + 1);
|
||||||
|
|
||||||
|
$location.url("/realms/" + realm.realm + "/groups/" + id);
|
||||||
|
Notifications.success("Group Created.");
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
$scope.cancel = function() {
|
||||||
|
$location.url("/realms/" + realm.realm + "/groups");
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
module.controller('GroupTabCtrl', function(Dialog, $scope, Current, Group, Notifications, $location) {
|
||||||
|
$scope.removeGroup = function() {
|
||||||
|
Dialog.confirmDelete($scope.group.name, 'group', function() {
|
||||||
|
Group.remove({
|
||||||
|
realm : Current.realm.realm,
|
||||||
|
groupId : $scope.group.id
|
||||||
|
}, function() {
|
||||||
|
$location.url("/realms/" + Current.realm.realm + "/groups");
|
||||||
|
Notifications.success("The group has been deleted.");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
module.controller('GroupDetailCtrl', function(Dialog, $scope, realm, group, Group, Notifications, $location) {
|
||||||
|
$scope.realm = realm;
|
||||||
|
|
||||||
|
if (!group.attributes) {
|
||||||
|
group.attributes = {}
|
||||||
|
}
|
||||||
|
convertAttributeValuesToString(group);
|
||||||
|
|
||||||
|
|
||||||
|
$scope.group = angular.copy(group);
|
||||||
|
|
||||||
|
$scope.changed = false; // $scope.create;
|
||||||
|
$scope.$watch('group', function() {
|
||||||
|
if (!angular.equals($scope.group, group)) {
|
||||||
|
$scope.changed = true;
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
$scope.save = function() {
|
||||||
|
convertAttributeValuesToLists();
|
||||||
|
|
||||||
|
Group.update({
|
||||||
|
realm: realm.realm,
|
||||||
|
groupId: $scope.group.id
|
||||||
|
}, $scope.group, function () {
|
||||||
|
$scope.changed = false;
|
||||||
|
convertAttributeValuesToString($scope.group);
|
||||||
|
group = angular.copy($scope.group);
|
||||||
|
Notifications.success("Your changes have been saved to the group.");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function convertAttributeValuesToLists() {
|
||||||
|
var attrs = $scope.group.attributes;
|
||||||
|
for (var attribute in attrs) {
|
||||||
|
if (typeof attrs[attribute] === "string") {
|
||||||
|
var attrVals = attrs[attribute].split("##");
|
||||||
|
attrs[attribute] = attrVals;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertAttributeValuesToString(group) {
|
||||||
|
var attrs = group.attributes;
|
||||||
|
for (var attribute in attrs) {
|
||||||
|
if (typeof attrs[attribute] === "object") {
|
||||||
|
var attrVals = attrs[attribute].join("##");
|
||||||
|
attrs[attribute] = attrVals;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.reset = function() {
|
||||||
|
$scope.group = angular.copy(group);
|
||||||
|
$scope.changed = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.cancel = function() {
|
||||||
|
$location.url("/realms/" + realm.realm + "/groups");
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.addAttribute = function() {
|
||||||
|
$scope.group.attributes[$scope.newAttribute.key] = $scope.newAttribute.value;
|
||||||
|
delete $scope.newAttribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.removeAttribute = function(key) {
|
||||||
|
delete $scope.group.attributes[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.controller('GroupRoleMappingCtrl', function($scope, $http, realm, group, clients, client, Notifications, GroupRealmRoleMapping,
|
||||||
|
GroupClientRoleMapping, GroupAvailableRealmRoleMapping, GroupAvailableClientRoleMapping,
|
||||||
|
GroupCompositeRealmRoleMapping, GroupCompositeClientRoleMapping) {
|
||||||
|
$scope.realm = realm;
|
||||||
|
$scope.group = group;
|
||||||
|
$scope.selectedRealmRoles = [];
|
||||||
|
$scope.selectedRealmMappings = [];
|
||||||
|
$scope.realmMappings = [];
|
||||||
|
$scope.clients = clients;
|
||||||
|
$scope.client = client;
|
||||||
|
$scope.clientRoles = [];
|
||||||
|
$scope.clientComposite = [];
|
||||||
|
$scope.selectedClientRoles = [];
|
||||||
|
$scope.selectedClientMappings = [];
|
||||||
|
$scope.clientMappings = [];
|
||||||
|
$scope.dummymodel = [];
|
||||||
|
|
||||||
|
$scope.realmMappings = GroupRealmRoleMapping.query({realm : realm.realm, groupId : group.id});
|
||||||
|
$scope.realmRoles = GroupAvailableRealmRoleMapping.query({realm : realm.realm, groupId : group.id});
|
||||||
|
$scope.realmComposite = GroupCompositeRealmRoleMapping.query({realm : realm.realm, groupId : group.id});
|
||||||
|
|
||||||
|
$scope.addRealmRole = function() {
|
||||||
|
var roles = $scope.selectedRealmRoles;
|
||||||
|
$scope.selectedRealmRoles = [];
|
||||||
|
$http.post(authUrl + '/admin/realms/' + realm.realm + '/groups/' + group.id + '/role-mappings/realm',
|
||||||
|
roles).success(function() {
|
||||||
|
$scope.realmMappings = GroupRealmRoleMapping.query({realm : realm.realm, groupId : group.id});
|
||||||
|
$scope.realmRoles = GroupAvailableRealmRoleMapping.query({realm : realm.realm, groupId : group.id});
|
||||||
|
$scope.realmComposite = GroupCompositeRealmRoleMapping.query({realm : realm.realm, groupId : group.id});
|
||||||
|
$scope.selectedRealmMappings = [];
|
||||||
|
$scope.selectRealmRoles = [];
|
||||||
|
if ($scope.targetClient) {
|
||||||
|
console.log('load available');
|
||||||
|
$scope.clientComposite = GroupCompositeClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.targetClient.id});
|
||||||
|
$scope.clientRoles = GroupAvailableClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.targetClient.id});
|
||||||
|
$scope.clientMappings = GroupClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.targetClient.id});
|
||||||
|
$scope.selectedClientRoles = [];
|
||||||
|
$scope.selectedClientMappings = [];
|
||||||
|
}
|
||||||
|
Notifications.success("Role mappings updated.");
|
||||||
|
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.deleteRealmRole = function() {
|
||||||
|
$http.delete(authUrl + '/admin/realms/' + realm.realm + '/groups/' + group.id + '/role-mappings/realm',
|
||||||
|
{data : $scope.selectedRealmMappings, headers : {"content-type" : "application/json"}}).success(function() {
|
||||||
|
$scope.realmMappings = GroupRealmRoleMapping.query({realm : realm.realm, groupId : group.id});
|
||||||
|
$scope.realmRoles = GroupAvailableRealmRoleMapping.query({realm : realm.realm, groupId : group.id});
|
||||||
|
$scope.realmComposite = GroupCompositeRealmRoleMapping.query({realm : realm.realm, groupId : group.id});
|
||||||
|
$scope.selectedRealmMappings = [];
|
||||||
|
$scope.selectRealmRoles = [];
|
||||||
|
if ($scope.targetClient) {
|
||||||
|
console.log('load available');
|
||||||
|
$scope.clientComposite = GroupCompositeClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.targetClient.id});
|
||||||
|
$scope.clientRoles = GroupAvailableClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.targetClient.id});
|
||||||
|
$scope.clientMappings = GroupClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.targetClient.id});
|
||||||
|
$scope.selectedClientRoles = [];
|
||||||
|
$scope.selectedClientMappings = [];
|
||||||
|
}
|
||||||
|
Notifications.success("Role mappings updated.");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.addClientRole = function() {
|
||||||
|
$http.post(authUrl + '/admin/realms/' + realm.realm + '/groups/' + group.id + '/role-mappings/clients/' + $scope.targetClient.id,
|
||||||
|
$scope.selectedClientRoles).success(function() {
|
||||||
|
$scope.clientMappings = GroupClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.targetClient.id});
|
||||||
|
$scope.clientRoles = GroupAvailableClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.targetClient.id});
|
||||||
|
$scope.clientComposite = GroupCompositeClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.targetClient.id});
|
||||||
|
$scope.selectedClientRoles = [];
|
||||||
|
$scope.selectedClientMappings = [];
|
||||||
|
$scope.realmComposite = GroupCompositeRealmRoleMapping.query({realm : realm.realm, groupId : group.id});
|
||||||
|
$scope.realmRoles = GroupAvailableRealmRoleMapping.query({realm : realm.realm, groupId : group.id});
|
||||||
|
Notifications.success("Role mappings updated.");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.deleteClientRole = function() {
|
||||||
|
$http.delete(authUrl + '/admin/realms/' + realm.realm + '/groups/' + group.id + '/role-mappings/clients/' + $scope.targetClient.id,
|
||||||
|
{data : $scope.selectedClientMappings, headers : {"content-type" : "application/json"}}).success(function() {
|
||||||
|
$scope.clientMappings = GroupClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.targetClient.id});
|
||||||
|
$scope.clientRoles = GroupAvailableClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.targetClient.id});
|
||||||
|
$scope.clientComposite = GroupCompositeClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.targetClient.id});
|
||||||
|
$scope.selectedClientRoles = [];
|
||||||
|
$scope.selectedClientMappings = [];
|
||||||
|
$scope.realmComposite = GroupCompositeRealmRoleMapping.query({realm : realm.realm, groupId : group.id});
|
||||||
|
$scope.realmRoles = GroupAvailableRealmRoleMapping.query({realm : realm.realm, groupId : group.id});
|
||||||
|
Notifications.success("Role mappings updated.");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
$scope.changeClient = function() {
|
||||||
|
if ($scope.targetClient) {
|
||||||
|
$scope.clientComposite = GroupCompositeClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.targetClient.id});
|
||||||
|
$scope.clientRoles = GroupAvailableClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.targetClient.id});
|
||||||
|
$scope.clientMappings = GroupClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.targetClient.id});
|
||||||
|
} else {
|
||||||
|
$scope.clientRoles = null;
|
||||||
|
$scope.clientMappings = null;
|
||||||
|
$scope.clientComposite = null;
|
||||||
|
}
|
||||||
|
$scope.selectedClientRoles = [];
|
||||||
|
$scope.selectedClientMappings = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
});
|
|
@ -1994,3 +1994,7 @@ module.controller('AuthenticationConfigCreateCtrl', function($scope, realm, conf
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -458,6 +458,24 @@ module.factory('AuthenticationConfigLoader', function(Loader, AuthenticationConf
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.factory('GroupListLoader', function(Loader, Groups, $route, $q) {
|
||||||
|
return Loader.query(Groups, function() {
|
||||||
|
return {
|
||||||
|
realm : $route.current.params.realm
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.factory('GroupLoader', function(Loader, Group, $route, $q) {
|
||||||
|
return Loader.get(Group, function() {
|
||||||
|
return {
|
||||||
|
realm : $route.current.params.realm,
|
||||||
|
groupId : $route.current.params.group
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1442,6 +1442,78 @@ module.service('SelectRoleDialog', function($modal) {
|
||||||
return dialog
|
return dialog
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.factory('Group', function($resource) {
|
||||||
|
return $resource(authUrl + '/admin/realms/:realm/groups/:groupId', {
|
||||||
|
realm : '@realm',
|
||||||
|
userId : '@groupId'
|
||||||
|
}, {
|
||||||
|
update : {
|
||||||
|
method : 'PUT'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.factory('GroupChildren', function($resource) {
|
||||||
|
return $resource(authUrl + '/admin/realms/:realm/groups/:groupId/children', {
|
||||||
|
realm : '@realm',
|
||||||
|
groupId : '@groupId'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.factory('Groups', function($resource) {
|
||||||
|
return $resource(authUrl + '/admin/realms/:realm/groups', {
|
||||||
|
realm : '@realm'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.factory('GroupRealmRoleMapping', function($resource) {
|
||||||
|
return $resource(authUrl + '/admin/realms/:realm/groups/:groupId/role-mappings/realm', {
|
||||||
|
realm : '@realm',
|
||||||
|
groupId : '@groupId'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.factory('GroupCompositeRealmRoleMapping', function($resource) {
|
||||||
|
return $resource(authUrl + '/admin/realms/:realm/groups/:groupId/role-mappings/realm/composite', {
|
||||||
|
realm : '@realm',
|
||||||
|
groupId : '@groupId'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.factory('GroupAvailableRealmRoleMapping', function($resource) {
|
||||||
|
return $resource(authUrl + '/admin/realms/:realm/groups/:groupId/role-mappings/realm/available', {
|
||||||
|
realm : '@realm',
|
||||||
|
groupId : '@groupId'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
module.factory('GroupClientRoleMapping', function($resource) {
|
||||||
|
return $resource(authUrl + '/admin/realms/:realm/groups/:groupId/role-mappings/clients/:client', {
|
||||||
|
realm : '@realm',
|
||||||
|
groupId : '@groupId',
|
||||||
|
client : "@client"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.factory('GroupAvailableClientRoleMapping', function($resource) {
|
||||||
|
return $resource(authUrl + '/admin/realms/:realm/groups/:groupId/role-mappings/clients/:client/available', {
|
||||||
|
realm : '@realm',
|
||||||
|
groupId : '@groupId',
|
||||||
|
client : "@client"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.factory('GroupCompositeClientRoleMapping', function($resource) {
|
||||||
|
return $resource(authUrl + '/admin/realms/:realm/groups/:groupId/role-mappings/clients/:client/composite', {
|
||||||
|
realm : '@realm',
|
||||||
|
groupId : '@groupId',
|
||||||
|
client : "@client"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
|
||||||
|
<div>
|
||||||
|
<h1>Create Group</h1>
|
||||||
|
</div>
|
||||||
|
<form class="form-horizontal" name="clientForm" novalidate>
|
||||||
|
<fieldset class="border-top">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-2 control-label"for="name">Name <span class="required">*</span></label>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input class="form-control" type="text" id="name" name="name" data-ng-model="group.name" autofocus
|
||||||
|
required >
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-md-10 col-md-offset-2">
|
||||||
|
<button kc-save>Save</button>
|
||||||
|
<button kc-cancel data-ng-click="cancel()">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<kc-menu></kc-menu>
|
|
@ -0,0 +1,45 @@
|
||||||
|
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li><a href="#/realms/{{realm.realm}}/groups">Groups</a></li>
|
||||||
|
<li>{{group.name}}</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<kc-tabs-group></kc-tabs-group>
|
||||||
|
|
||||||
|
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageUsers">
|
||||||
|
<table class="table table-striped table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Key</th>
|
||||||
|
<th>Value</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="(key, value) in group.attributes">
|
||||||
|
<td>{{key}}</td>
|
||||||
|
<td><input ng-model="group.attributes[key]" class="form-control" type="text" name="{{key}}" id="attribute-{{key}}" /></td>
|
||||||
|
<td class="kc-action-cell">
|
||||||
|
<button class="btn btn-default btn-block btn-sm" data-ng-click="removeAttribute(key)">Delete</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><input ng-model="newAttribute.key" class="form-control" type="text" id="newAttributeKey" /></td>
|
||||||
|
<td><input ng-model="newAttribute.value" class="form-control" type="text" id="newAttributeValue" /></td>
|
||||||
|
<td class="kc-action-cell">
|
||||||
|
<button class="btn btn-default btn-block btn-sm" data-ng-click="addAttribute()">Add</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="form-group" data-ng-show="access.manageUsers">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<button kc-save data-ng-disabled="!changed">Save</button>
|
||||||
|
<button kc-reset data-ng-disabled="!changed">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<kc-menu></kc-menu>
|
|
@ -0,0 +1,28 @@
|
||||||
|
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li><a href="#/realms/{{realm.realm}}/groups">Groups</a></li>
|
||||||
|
<li>{{group.name}}</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<kc-tabs-group></kc-tabs-group>
|
||||||
|
<form class="form-horizontal" name="clientForm" novalidate>
|
||||||
|
<fieldset class="border-top">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-2 control-label"for="name">Name <span class="required">*</span></label>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input class="form-control" type="text" id="name" name="name" data-ng-model="group.name" autofocus
|
||||||
|
required >
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<div class="form-group" data-ng-show="access.manageUsers">
|
||||||
|
<div class="col-md-10 col-md-offset-2">
|
||||||
|
<button kc-save data-ng-disabled="!changed">Save</button>
|
||||||
|
<button kc-reset data-ng-disabled="!changed" data-ng-click="cancel()">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<kc-menu></kc-menu>
|
|
@ -0,0 +1,40 @@
|
||||||
|
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
|
||||||
|
<h1>
|
||||||
|
<span>User Groups</span>
|
||||||
|
<kc-tooltip>User groups</kc-tooltip>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<table class="table table-striped table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="kc-table-actions" colspan="5">
|
||||||
|
<div class="form-inline">
|
||||||
|
<div class="pull-right" data-ng-show="access.manageUsers">
|
||||||
|
<button id="createGroup" class="btn btn-default" ng-click="createGroup(tree.currentNode)">New</button>
|
||||||
|
<button id="editGroup" ng-disabled="isDisabled()" class="btn btn-default" ng-click="edit(tree.currentNode)">Edit</button>
|
||||||
|
<button id="cutGroup" ng-disabled="isDisabled()" class="btn btn-default" ng-click="cut(tree.currentNode)">Cut</button>
|
||||||
|
<button id="pasteGroup" ng-disabled="!cutNode" class="btn btn-default" ng-click="paste(tree.currentNode)">Paste</button>
|
||||||
|
<button id="removeGroup" ng-disabled="isDisabled()" class="btn btn-default" ng-click="remove(tree.currentNode)">Delete</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td> <div
|
||||||
|
tree-id="tree"
|
||||||
|
angular-treeview="true"
|
||||||
|
tree-model="groupList"
|
||||||
|
node-id="id"
|
||||||
|
node-label="name"
|
||||||
|
node-children="subGroups" >
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<kc-menu></kc-menu>
|
|
@ -0,0 +1,101 @@
|
||||||
|
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li><a href="#/realms/{{realm.realm}}/groups">Groups</a></li>
|
||||||
|
<li>{{group.name}}</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<kc-tabs-group></kc-tabs-group>
|
||||||
|
|
||||||
|
<form class="form-horizontal" name="realmForm" novalidate>
|
||||||
|
<div class="form-group" kc-read-only="!access.manageUsers">
|
||||||
|
<label class="col-md-2 control-label" class="control-label">Realm Roles</label>
|
||||||
|
|
||||||
|
<div class="col-md-10">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-3">
|
||||||
|
<label class="control-label" for="available">Available Roles</label>
|
||||||
|
<select id="available" class="form-control" multiple size="5"
|
||||||
|
ng-multiple="true"
|
||||||
|
ng-model="selectedRealmRoles"
|
||||||
|
ng-options="r.name for r in realmRoles">
|
||||||
|
</select>
|
||||||
|
<button ng-disabled="selectedRealmRoles.length == 0" ng-disabled="c.length == 0" class="btn btn-default" type="submit" ng-click="addRealmRole()">
|
||||||
|
Add selected <i class="fa fa-angle-right"></i>
|
||||||
|
</button>
|
||||||
|
<kc-tooltip>Realm roles that can be assigned to the group.</kc-tooltip>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<label class="control-label" for="assigned">Assigned Roles</label>
|
||||||
|
<kc-tooltip>Realm roles mapped to the group</kc-tooltip>
|
||||||
|
<select id="assigned" class="form-control" multiple size=5
|
||||||
|
ng-multiple="true"
|
||||||
|
ng-model="selectedRealmMappings"
|
||||||
|
ng-options="r.name for r in realmMappings">
|
||||||
|
</select>
|
||||||
|
<button ng-disabled="selectedRealmMappings.length == 0" class="btn btn-default" type="submit" ng-click="deleteRealmRole()">
|
||||||
|
<i class="fa fa-angle-double-left"></i> Remove selected
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<label class="control-label" for="realm-composite">Effective Roles</label>
|
||||||
|
<kc-tooltip>All realm role mappings. Some roles here might be inherited from a mapped composite role.</kc-tooltip>
|
||||||
|
<select id="realm-composite" class="form-control" multiple size=5
|
||||||
|
disabled="true"
|
||||||
|
ng-model="dummymodel"
|
||||||
|
ng-options="r.name for r in realmComposite">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-2 control-label" class="control-label">
|
||||||
|
<span>Client Roles</span>
|
||||||
|
<select class="form-control" id="clients" name="clients" ng-change="changeClient()" ng-model="targetClient" ng-options="a.clientId for a in clients" ng-disabled="false"></select>
|
||||||
|
</label>
|
||||||
|
<div class="col-md-10" kc-read-only="!access.manageUsers">
|
||||||
|
<div class="row" data-ng-hide="targetClient">
|
||||||
|
<div class="col-md-4"><span class="text-muted">Select client to view roles for client</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="row" data-ng-show="targetClient">
|
||||||
|
<div class="col-md-3">
|
||||||
|
<label class="control-label" for="available-client">Available Roles</label>
|
||||||
|
<kc-tooltip>Assignable roles from this client.</kc-tooltip>
|
||||||
|
<select id="available-client" class="form-control" multiple size="5"
|
||||||
|
ng-multiple="true"
|
||||||
|
ng-model="selectedClientRoles"
|
||||||
|
ng-options="r.name for r in clientRoles">
|
||||||
|
</select>
|
||||||
|
<button ng-disabled="selectedClientRoles.length == 0" class="btn btn-default" type="submit" ng-click="addClientRole()">
|
||||||
|
Add selected <i class="fa fa-angle-double-right"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<label class="control-label" for="assigned-client">Assigned Roles</label>
|
||||||
|
<kc-tooltip>Role mappings for this client.</kc-tooltip>
|
||||||
|
<select id="assigned-client" class="form-control" multiple size=5
|
||||||
|
ng-multiple="true"
|
||||||
|
ng-model="selectedClientMappings"
|
||||||
|
ng-options="r.name for r in clientMappings">
|
||||||
|
</select>
|
||||||
|
<button ng-disabled="selectedClientMappings.length == 0" class="btn btn-default" type="submit" ng-click="deleteClientRole()">
|
||||||
|
<i class="fa fa-angle-double-left"></i> Remove selected
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<label class="control-label" for="client-composite">Effective Roles <span tooltip-placement="right" tooltip-trigger="mouseover mouseout" tooltip="Role mappings for this client. Some roles here might be inherited from a mapped composite role." class="fa fa-info-circle"></span></label>
|
||||||
|
<select id="client-composite" class="form-control" multiple size=5
|
||||||
|
disabled="true"
|
||||||
|
ng-model="dummymodel"
|
||||||
|
ng-options="r.name for r in clientComposite">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<kc-menu></kc-menu>
|
|
@ -40,7 +40,8 @@
|
||||||
<div class="nav-category" data-ng-show="current.realm">
|
<div class="nav-category" data-ng-show="current.realm">
|
||||||
<h2>Manage</h2>
|
<h2>Manage</h2>
|
||||||
<ul class="nav nav-pills nav-stacked">
|
<ul class="nav nav-pills nav-stacked">
|
||||||
<li data-ng-show="access.viewUsers" data-ng-class="(path[2] == 'users' || path[1] == 'user') && 'active'"><a href="#/realms/{{realm.realm}}/users"><span class="pficon pficon-users"></span> Users</a></li>
|
<li data-ng-show="access.viewUsers" data-ng-class="(path[2] == 'groups' || path[1] == 'group') && 'active'"><a href="#/realms/{{realm.realm}}/groups"><span class="pficon pficon-users"></span> Groups</a></li>
|
||||||
|
<li data-ng-show="access.viewUsers" data-ng-class="(path[2] == 'users' || path[1] == 'user') && 'active'"><a href="#/realms/{{realm.realm}}/users"><span class="pficon pficon-user"></span> Users</a></li>
|
||||||
<li data-ng-show="access.viewRealm" data-ng-class="(path[2] == 'sessions') && 'active'"><a href="#/realms/{{realm.realm}}/sessions/realm"><i class="fa fa-clock-o"></i> Sessions</a></li>
|
<li data-ng-show="access.viewRealm" data-ng-class="(path[2] == 'sessions') && 'active'"><a href="#/realms/{{realm.realm}}/sessions/realm"><i class="fa fa-clock-o"></i> Sessions</a></li>
|
||||||
<li data-ng-show="access.viewEvents" data-ng-class="(path[2] == 'events' || path[2] == 'events-settings') && 'active'"><a href="#/realms/{{realm.realm}}/events"><i class="fa fa-calendar"></i> Events</a></li>
|
<li data-ng-show="access.viewEvents" data-ng-class="(path[2] == 'events' || path[2] == 'events-settings') && 'active'"><a href="#/realms/{{realm.realm}}/events"><i class="fa fa-calendar"></i> Events</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
<div data-ng-controller="GroupTabCtrl">
|
||||||
|
<h1>
|
||||||
|
{{group.name|capitalize}}
|
||||||
|
<i id="removeGroup" class="pficon pficon-delete clickable" data-ng-show="access.manageUsers" data-ng-click="removeGroup()"></i>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li ng-class="{active: !path[4]}"><a href="#/realms/{{realm.realm}}/groups/{{group.id}}">Settings</a></li>
|
||||||
|
<li ng-class="{active: path[4] == 'attributes'}"><a href="#/realms/{{realm.realm}}/groups/{{group.id}}/attributes">Attributes</a></li>
|
||||||
|
<li ng-class="{active: path[4] == 'role-mappings'}" ><a href="#/realms/{{realm.realm}}/groups/{{group.id}}/role-mappings">Role Mappings</a></li>
|
||||||
|
<li ng-class="{active: path[4] == 'members'}"><a href="#/realms/{{realm.realm}}/groups/{{group.id}}/members">Members</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
2
forms/common-themes/src/main/resources/theme/keycloak/admin/theme.properties
Normal file → Executable file
2
forms/common-themes/src/main/resources/theme/keycloak/admin/theme.properties
Normal file → Executable file
|
@ -1,3 +1,3 @@
|
||||||
parent=base
|
parent=base
|
||||||
import=common/keycloak
|
import=common/keycloak
|
||||||
styles=lib/patternfly/css/patternfly.css lib/select2-3.4.1/select2.css css/styles.css
|
styles=lib/patternfly/css/patternfly.css lib/select2-3.4.1/select2.css css/styles.css lib/angular/treeview/css/angular.treeview.css
|
|
@ -0,0 +1,20 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013 Steve
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,122 @@
|
||||||
|
Angular Treeview
|
||||||
|
================
|
||||||
|
|
||||||
|
Pure [AngularJS](http://www.angularjs.org) based tree menu directive.
|
||||||
|
|
||||||
|
[![ScreenShot](https://github.com/eu81273/angular.treeview/raw/master/img/preview.png)](http://jsfiddle.net/eu81273/8LWUc/)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Copy the script and css into your project and add a script and link tag to your page.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script type="text/javascript" src="/angular.treeview.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="css/angular.treeview.css">
|
||||||
|
```
|
||||||
|
|
||||||
|
Add a dependency to your application module.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
angular.module('myApp', ['angularTreeview']);
|
||||||
|
```
|
||||||
|
|
||||||
|
Add a tree to your application. See [Usage](#usage).
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Attributes of angular treeview are below.
|
||||||
|
|
||||||
|
- angular-treeview: the treeview directive.
|
||||||
|
- tree-id : each tree's unique id.
|
||||||
|
- tree-model : the tree model on $scope.
|
||||||
|
- node-id : each node's id.
|
||||||
|
- node-label : each node's label.
|
||||||
|
- node-children: each node's children.
|
||||||
|
|
||||||
|
Here is a simple example.
|
||||||
|
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div
|
||||||
|
data-angular-treeview="true"
|
||||||
|
data-tree-id="abc"
|
||||||
|
data-tree-model="treedata"
|
||||||
|
data-node-id="id"
|
||||||
|
data-node-label="label"
|
||||||
|
data-node-children="children" >
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
Example model:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$scope.treedata =
|
||||||
|
[
|
||||||
|
{ "label" : "User", "id" : "role1", "children" : [
|
||||||
|
{ "label" : "subUser1", "id" : "role11", "children" : [] },
|
||||||
|
{ "label" : "subUser2", "id" : "role12", "children" : [
|
||||||
|
{ "label" : "subUser2-1", "id" : "role121", "children" : [
|
||||||
|
{ "label" : "subUser2-1-1", "id" : "role1211", "children" : [] },
|
||||||
|
{ "label" : "subUser2-1-2", "id" : "role1212", "children" : [] }
|
||||||
|
]}
|
||||||
|
]}
|
||||||
|
]},
|
||||||
|
{ "label" : "Admin", "id" : "role2", "children" : [] },
|
||||||
|
{ "label" : "Guest", "id" : "role3", "children" : [] }
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
## Selection
|
||||||
|
|
||||||
|
If tree node is selected, then that selected tree node is saved to $scope."TREE ID".currentNode. By using $watch, the controller can recognize the tree selection.
|
||||||
|
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$scope.$watch( 'abc.currentNode', function( newObj, oldObj ) {
|
||||||
|
if( $scope.abc && angular.isObject($scope.abc.currentNode) ) {
|
||||||
|
console.log( 'Node Selected!!' );
|
||||||
|
console.log( $scope.abc.currentNode );
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
#### Basic example
|
||||||
|
[![ScreenShot](https://github.com/eu81273/angular.treeview/raw/master/img/jsfiddle01.png)](http://jsfiddle.net/eu81273/8LWUc/)
|
||||||
|
|
||||||
|
[jsFiddle - http://jsfiddle.net/eu81273/8LWUc/](http://jsfiddle.net/eu81273/8LWUc/)
|
||||||
|
|
||||||
|
#### Multiple treeview example
|
||||||
|
[![ScreenShot](https://github.com/eu81273/angular.treeview/raw/master/img/jsfiddle02.png)](http://jsfiddle.net/eu81273/b9Pnw/)
|
||||||
|
|
||||||
|
[jsFiddle - http://jsfiddle.net/eu81273/b9Pnw/](http://jsfiddle.net/eu81273/b9Pnw/)
|
||||||
|
|
||||||
|
## Browser Compatibility
|
||||||
|
|
||||||
|
Same with AngularJS. Safari, Chrome, Firefox, Opera, IE8, IE9 and mobile browsers (Android, Chrome Mobile, iOS Safari).
|
||||||
|
|
||||||
|
## Changelogs
|
||||||
|
|
||||||
|
#### version 0.1.6
|
||||||
|
- Fixed the bug that 'null' string appears before each 'div' generated. (Thanks to Iaac)
|
||||||
|
|
||||||
|
#### version 0.1.5
|
||||||
|
- support multiple treeview. (Thanks to Axel Pesme)
|
||||||
|
|
||||||
|
#### version 0.1.4
|
||||||
|
- prevented memory leaks.
|
||||||
|
|
||||||
|
#### version 0.1.3
|
||||||
|
- removed unnecessary codes.
|
||||||
|
|
||||||
|
#### version 0.1.2
|
||||||
|
- removed some jQuery dependency. (Issue #2)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
The MIT License.
|
||||||
|
|
||||||
|
Copyright ⓒ 2013 AHN JAE-HA.
|
||||||
|
|
||||||
|
See [LICENSE](https://github.com/eu81273/angular.treeview/blob/master/LICENSE)
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
@license Angular Treeview version 0.1.6
|
||||||
|
ⓒ 2013 AHN JAE-HA http://github.com/eu81273/angular.treeview
|
||||||
|
License: MIT
|
||||||
|
|
||||||
|
|
||||||
|
[TREE attribute]
|
||||||
|
angular-treeview: the treeview directive
|
||||||
|
tree-id : each tree's unique id.
|
||||||
|
tree-model : the tree model on $scope.
|
||||||
|
node-id : each node's id
|
||||||
|
node-label : each node's label
|
||||||
|
node-children: each node's children
|
||||||
|
|
||||||
|
<div
|
||||||
|
data-angular-treeview="true"
|
||||||
|
data-tree-id="tree"
|
||||||
|
data-tree-model="roleList"
|
||||||
|
data-node-id="roleId"
|
||||||
|
data-node-label="roleName"
|
||||||
|
data-node-children="children" >
|
||||||
|
</div>
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function ( angular ) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module( 'angularTreeview', [] ).directive( 'treeModel', ['$compile', function( $compile ) {
|
||||||
|
return {
|
||||||
|
restrict: 'A',
|
||||||
|
link: function ( scope, element, attrs ) {
|
||||||
|
//tree id
|
||||||
|
var treeId = attrs.treeId;
|
||||||
|
|
||||||
|
//tree model
|
||||||
|
var treeModel = attrs.treeModel;
|
||||||
|
|
||||||
|
//node id
|
||||||
|
var nodeId = attrs.nodeId || 'id';
|
||||||
|
|
||||||
|
//node label
|
||||||
|
var nodeLabel = attrs.nodeLabel || 'label';
|
||||||
|
|
||||||
|
//children
|
||||||
|
var nodeChildren = attrs.nodeChildren || 'children';
|
||||||
|
|
||||||
|
//tree template
|
||||||
|
|
||||||
|
var template =
|
||||||
|
'<ul>' +
|
||||||
|
'<li data-ng-repeat="node in ' + treeModel + '">' +
|
||||||
|
'<i ng-class="getGroupClass(node)" data-ng-click="' + treeId + '.selectNodeHead(node)"></i>' +
|
||||||
|
'<span data-ng-class="getSelectedClass(node)" ng-dblclick="edit(node)" data-ng-click="' + treeId + '.selectNodeLabel(node)">{{node.' + nodeLabel + '}}</span>' +
|
||||||
|
'<div data-ng-hide="node.collapsed" data-tree-id="' + treeId + '" data-tree-model="node.' + nodeChildren + '" data-node-id=' + nodeId + ' data-node-label=' + nodeLabel + ' data-node-children=' + nodeChildren + '></div>' +
|
||||||
|
'</li>' +
|
||||||
|
'</ul>';
|
||||||
|
|
||||||
|
|
||||||
|
//check tree id, tree model
|
||||||
|
if( treeId && treeModel ) {
|
||||||
|
//root node
|
||||||
|
if( attrs.angularTreeview ) {
|
||||||
|
|
||||||
|
//create tree object if not exists
|
||||||
|
scope[treeId] = scope[treeId] || {};
|
||||||
|
|
||||||
|
//if node head clicks,
|
||||||
|
scope[treeId].selectNodeHead = scope[treeId].selectNodeHead || function( selectedNode ){
|
||||||
|
|
||||||
|
//Collapse or Expand
|
||||||
|
selectedNode.collapsed = !selectedNode.collapsed;
|
||||||
|
scope[treeId].selectNodeLabel(selectedNode);
|
||||||
|
};
|
||||||
|
|
||||||
|
//if node label clicks,
|
||||||
|
scope[treeId].selectNodeLabel = scope[treeId].selectNodeLabel || function( selectedNode ){
|
||||||
|
|
||||||
|
//remove highlight from previous node
|
||||||
|
if( scope[treeId].currentNode && scope[treeId].currentNode.selected ) {
|
||||||
|
scope[treeId].currentNode.selected = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
//set highlight to selected node
|
||||||
|
selectedNode.selected = 'selected';
|
||||||
|
|
||||||
|
//set currentNode
|
||||||
|
scope[treeId].currentNode = selectedNode;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//Rendering template.
|
||||||
|
element.html('').append( $compile( template )( scope ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}]);
|
||||||
|
})( angular );
|
|
@ -0,0 +1,9 @@
|
||||||
|
/*
|
||||||
|
@license Angular Treeview version 0.1.6
|
||||||
|
ⓒ 2013 AHN JAE-HA http://github.com/eu81273/angular.treeview
|
||||||
|
License: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(f){f.module("angularTreeview",[]).directive("treeModel",function($compile){return{restrict:"A",link:function(b,h,c){var a=c.treeId,g=c.treeModel,e=c.nodeLabel||"label",d=c.nodeChildren||"children",e='<ul><li data-ng-repeat="node in '+g+'"><i class="collapsed" data-ng-show="node.'+d+'.length && node.collapsed" data-ng-click="'+a+'.selectNodeHead(node)"></i><i class="expanded" data-ng-show="node.'+d+'.length && !node.collapsed" data-ng-click="'+a+'.selectNodeHead(node)"></i><i class="normal" data-ng-hide="node.'+
|
||||||
|
d+'.length"></i> <span data-ng-class="node.selected" data-ng-click="'+a+'.selectNodeLabel(node)">{{node.'+e+'}}</span><div data-ng-hide="node.collapsed" data-tree-id="'+a+'" data-tree-model="node.'+d+'" data-node-id='+(c.nodeId||"id")+" data-node-label="+e+" data-node-children="+d+"></div></li></ul>";a&&g&&(c.angularTreeview&&(b[a]=b[a]||{},b[a].selectNodeHead=b[a].selectNodeHead||function(a){a.collapsed=!a.collapsed},b[a].selectNodeLabel=b[a].selectNodeLabel||function(c){b[a].currentNode&&b[a].currentNode.selected&&
|
||||||
|
(b[a].currentNode.selected=void 0);c.selected="selected";b[a].currentNode=c}),h.html('').append($compile(e)(b)))}}})})(angular);
|
|
@ -0,0 +1,99 @@
|
||||||
|
div[angular-treeview] {
|
||||||
|
/* prevent user selection */
|
||||||
|
-moz-user-select: -moz-none;
|
||||||
|
-khtml-user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
/* default */
|
||||||
|
font-family: Tahoma;
|
||||||
|
font-size:13px;
|
||||||
|
color: #555;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
div[tree-model] ul {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
border: none;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
div[tree-model] li {
|
||||||
|
position: relative;
|
||||||
|
padding: 0 0 0 20px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div[tree-model] li .expanded {
|
||||||
|
padding: 1px 10px;
|
||||||
|
background-image: url("../img/folder.png");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
div[tree-model] li .collapsed {
|
||||||
|
padding: 1px 10px;
|
||||||
|
background-image: url("../img/folder-closed.png");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
div[tree-model] li .normal {
|
||||||
|
padding: 1px 10px;
|
||||||
|
background-image: url("../img/file.png");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
div[tree-model] li i, div[tree-model] li span {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
div[tree-model] li .selected {
|
||||||
|
background-color: #aaddff;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 1px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div[tree-model] li .cut {
|
||||||
|
font-weight: bold;
|
||||||
|
color: gray
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
.angular-ui-tree-handle {
|
||||||
|
cursor: grab;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: bold;
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
min-height: 20px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
.angular-ui-tree-handle {
|
||||||
|
/* background: #f8faff; */
|
||||||
|
/*
|
||||||
|
color: #7c9eb2; */
|
||||||
|
border: 1px solid #dae2ea;
|
||||||
|
padding: 10px 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expanded-folder {
|
||||||
|
padding: 1px 10px;
|
||||||
|
background-image: url("../img/folder.png");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsed-folder {
|
||||||
|
padding: 1px 10px;
|
||||||
|
background-image: url("../img/folder-closed.png");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 263 B |
Binary file not shown.
After Width: | Height: | Size: 281 B |
Binary file not shown.
After Width: | Height: | Size: 289 B |
|
@ -8,7 +8,7 @@ import java.util.Set;
|
||||||
* @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 interface GroupModel {
|
public interface GroupModel extends RoleMapperModel {
|
||||||
String getId();
|
String getId();
|
||||||
|
|
||||||
String getName();
|
String getName();
|
||||||
|
@ -41,18 +41,12 @@ public interface GroupModel {
|
||||||
|
|
||||||
Map<String, List<String>> getAttributes();
|
Map<String, List<String>> getAttributes();
|
||||||
|
|
||||||
Set<RoleModel> getRealmRoleMappings();
|
|
||||||
Set<RoleModel> getClientRoleMappings(ClientModel app);
|
|
||||||
boolean hasRole(RoleModel role);
|
|
||||||
void grantRole(RoleModel role);
|
|
||||||
Set<RoleModel> getRoleMappings();
|
|
||||||
void deleteRoleMapping(RoleModel role);
|
|
||||||
|
|
||||||
GroupModel getParent();
|
GroupModel getParent();
|
||||||
|
String getParentId();
|
||||||
Set<GroupModel> getSubGroups();
|
Set<GroupModel> getSubGroups();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* You must also call joinGroup on the parent group.
|
* You must also call addChild on the parent group, addChild on RealmModel if there is no parent group
|
||||||
*
|
*
|
||||||
* @param group
|
* @param group
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -329,11 +329,18 @@ public interface RealmModel extends RoleContainerModel {
|
||||||
String getDefaultLocale();
|
String getDefaultLocale();
|
||||||
void setDefaultLocale(String locale);
|
void setDefaultLocale(String locale);
|
||||||
|
|
||||||
|
GroupModel createGroup(String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move Group to top realm level. Basically just sets group parent to null. You need to call this though
|
||||||
|
* to make sure caches are set properly
|
||||||
|
*
|
||||||
|
* @param subGroup
|
||||||
|
*/
|
||||||
|
void addTopLevelGroup(GroupModel subGroup);
|
||||||
GroupModel getGroupById(String id);
|
GroupModel getGroupById(String id);
|
||||||
List<GroupModel> getGroups();
|
List<GroupModel> getGroups();
|
||||||
List<GroupModel> getTopLevelGroups();
|
List<GroupModel> getTopLevelGroups();
|
||||||
boolean removeGroup(GroupModel group);
|
boolean removeGroup(GroupModel group);
|
||||||
|
void moveGroup(GroupModel group, GroupModel toParent);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
21
model/api/src/main/java/org/keycloak/models/RoleMapperModel.java
Executable file
21
model/api/src/main/java/org/keycloak/models/RoleMapperModel.java
Executable file
|
@ -0,0 +1,21 @@
|
||||||
|
package org.keycloak.models;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public interface RoleMapperModel {
|
||||||
|
Set<RoleModel> getRealmRoleMappings();
|
||||||
|
|
||||||
|
Set<RoleModel> getClientRoleMappings(ClientModel app);
|
||||||
|
|
||||||
|
boolean hasRole(RoleModel role);
|
||||||
|
|
||||||
|
void grantRole(RoleModel role);
|
||||||
|
|
||||||
|
Set<RoleModel> getRoleMappings();
|
||||||
|
|
||||||
|
void deleteRoleMapping(RoleModel role);
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ import java.util.Set;
|
||||||
* @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 interface UserModel {
|
public interface UserModel extends RoleMapperModel {
|
||||||
String USERNAME = "username";
|
String USERNAME = "username";
|
||||||
String LAST_NAME = "lastName";
|
String LAST_NAME = "lastName";
|
||||||
String FIRST_NAME = "firstName";
|
String FIRST_NAME = "firstName";
|
||||||
|
@ -94,13 +94,6 @@ public interface UserModel {
|
||||||
|
|
||||||
void updateCredentialDirectly(UserCredentialValueModel cred);
|
void updateCredentialDirectly(UserCredentialValueModel cred);
|
||||||
|
|
||||||
Set<RoleModel> getRealmRoleMappings();
|
|
||||||
Set<RoleModel> getClientRoleMappings(ClientModel app);
|
|
||||||
boolean hasRole(RoleModel role);
|
|
||||||
void grantRole(RoleModel role);
|
|
||||||
Set<RoleModel> getRoleMappings();
|
|
||||||
void deleteRoleMapping(RoleModel role);
|
|
||||||
|
|
||||||
Set<GroupModel> getGroups();
|
Set<GroupModel> getGroups();
|
||||||
void joinGroup(GroupModel group);
|
void joinGroup(GroupModel group);
|
||||||
void leaveGroup(GroupModel group);
|
void leaveGroup(GroupModel group);
|
||||||
|
|
|
@ -6,6 +6,7 @@ import org.keycloak.models.AuthenticatorConfigModel;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.ClientSessionModel;
|
import org.keycloak.models.ClientSessionModel;
|
||||||
import org.keycloak.models.FederatedIdentityModel;
|
import org.keycloak.models.FederatedIdentityModel;
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.IdentityProviderMapperModel;
|
import org.keycloak.models.IdentityProviderMapperModel;
|
||||||
import org.keycloak.models.IdentityProviderModel;
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
import org.keycloak.models.ModelException;
|
import org.keycloak.models.ModelException;
|
||||||
|
@ -30,6 +31,7 @@ import org.keycloak.representations.idm.AuthenticatorConfigRepresentation;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||||
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
|
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
|
||||||
|
import org.keycloak.representations.idm.GroupRepresentation;
|
||||||
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
||||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||||
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
||||||
|
@ -58,6 +60,59 @@ import java.util.Set;
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class ModelToRepresentation {
|
public class ModelToRepresentation {
|
||||||
|
public static GroupRepresentation toRepresentation(GroupModel group, boolean full) {
|
||||||
|
GroupRepresentation rep = new GroupRepresentation();
|
||||||
|
rep.setId(group.getId());
|
||||||
|
rep.setName(group.getName());
|
||||||
|
if (!full) return rep;
|
||||||
|
// Role mappings
|
||||||
|
Set<RoleModel> roles = group.getRoleMappings();
|
||||||
|
List<String> realmRoleNames = new ArrayList<>();
|
||||||
|
Map<String, List<String>> clientRoleNames = new HashMap<>();
|
||||||
|
for (RoleModel role : roles) {
|
||||||
|
if (role.getContainer() instanceof RealmModel) {
|
||||||
|
realmRoleNames.add(role.getName());
|
||||||
|
} else {
|
||||||
|
ClientModel client = (ClientModel)role.getContainer();
|
||||||
|
String clientId = client.getClientId();
|
||||||
|
List<String> currentClientRoles = clientRoleNames.get(clientId);
|
||||||
|
if (currentClientRoles == null) {
|
||||||
|
currentClientRoles = new ArrayList<>();
|
||||||
|
clientRoleNames.put(clientId, currentClientRoles);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentClientRoles.add(role.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rep.setRealmRoles(realmRoleNames);
|
||||||
|
rep.setClientRoles(clientRoleNames);
|
||||||
|
Map<String, List<String>> attributes = group.getAttributes();
|
||||||
|
rep.setAttributes(attributes);
|
||||||
|
return rep;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<GroupRepresentation> toGroupHierarchy(RealmModel realm, boolean full) {
|
||||||
|
List<GroupRepresentation> hierarchy = new LinkedList<>();
|
||||||
|
List<GroupModel> groups = realm.getTopLevelGroups();
|
||||||
|
if (groups == null) return hierarchy;
|
||||||
|
for (GroupModel group : groups) {
|
||||||
|
GroupRepresentation rep = toGroupHierarchy(group, full);
|
||||||
|
hierarchy.add(rep);
|
||||||
|
}
|
||||||
|
return hierarchy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GroupRepresentation toGroupHierarchy(GroupModel group, boolean full) {
|
||||||
|
GroupRepresentation rep = toRepresentation(group, full);
|
||||||
|
List<GroupRepresentation> subGroups = new LinkedList<>();
|
||||||
|
for (GroupModel subGroup : group.getSubGroups()) {
|
||||||
|
subGroups.add(toGroupHierarchy(subGroup, full));
|
||||||
|
}
|
||||||
|
rep.setSubGroups(subGroups);
|
||||||
|
return rep;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static UserRepresentation toRepresentation(UserModel user) {
|
public static UserRepresentation toRepresentation(UserModel user) {
|
||||||
UserRepresentation rep = new UserRepresentation();
|
UserRepresentation rep = new UserRepresentation();
|
||||||
rep.setId(user.getId());
|
rep.setId(user.getId());
|
||||||
|
|
|
@ -177,6 +177,11 @@ public class GroupAdapter implements GroupModel {
|
||||||
return realm.getGroupById(group.getParentId());
|
return realm.getGroupById(group.getParentId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getParentId() {
|
||||||
|
return group.getParentId();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<GroupModel> getSubGroups() {
|
public Set<GroupModel> getSubGroups() {
|
||||||
Set<GroupModel> subGroups = new HashSet<>();
|
Set<GroupModel> subGroups = new HashSet<>();
|
||||||
|
|
|
@ -610,6 +610,15 @@ public class RealmAdapter implements RealmModel {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void moveGroup(GroupModel group, GroupModel toParent) {
|
||||||
|
if (group.getParentId() != null) {
|
||||||
|
group.getParent().removeChild(group);
|
||||||
|
}
|
||||||
|
group.setParent(toParent);
|
||||||
|
if (toParent != null) toParent.addChild(group);
|
||||||
|
else addTopLevelGroup(group);
|
||||||
|
}
|
||||||
@Override
|
@Override
|
||||||
public List<GroupModel> getGroups() {
|
public List<GroupModel> getGroups() {
|
||||||
List<GroupModel> list = new LinkedList<>();
|
List<GroupModel> list = new LinkedList<>();
|
||||||
|
@ -1819,4 +1828,13 @@ public class RealmAdapter implements RealmModel {
|
||||||
return mapper;
|
return mapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
@Override
|
||||||
|
public GroupModel createGroup(String name) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addTopLevelGroup(GroupModel subGroup) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -197,6 +197,12 @@ public class GroupAdapter implements GroupModel {
|
||||||
return keycloakSession.realms().getGroupById(cached.getParentId(), realm);
|
return keycloakSession.realms().getGroupById(cached.getParentId(), realm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getParentId() {
|
||||||
|
if (updated != null) return updated.getParentId();
|
||||||
|
return cached.getParentId();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<GroupModel> getSubGroups() {
|
public Set<GroupModel> getSubGroups() {
|
||||||
if (updated != null) return updated.getSubGroups();
|
if (updated != null) return updated.getSubGroups();
|
||||||
|
@ -214,6 +220,8 @@ public class GroupAdapter implements GroupModel {
|
||||||
return subGroups;
|
return subGroups;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setParent(GroupModel group) {
|
public void setParent(GroupModel group) {
|
||||||
getDelegateForUpdate();
|
getDelegateForUpdate();
|
||||||
|
|
|
@ -1272,7 +1272,7 @@ public class RealmAdapter implements RealmModel {
|
||||||
@Override
|
@Override
|
||||||
public List<GroupModel> getGroups() {
|
public List<GroupModel> getGroups() {
|
||||||
if (updated != null) return updated.getGroups();
|
if (updated != null) return updated.getGroups();
|
||||||
if (cached.getGroups().isEmpty()) return null;
|
if (cached.getGroups().isEmpty()) return Collections.EMPTY_LIST;
|
||||||
List<GroupModel> list = new LinkedList<>();
|
List<GroupModel> list = new LinkedList<>();
|
||||||
for (String id : cached.getGroups()) {
|
for (String id : cached.getGroups()) {
|
||||||
GroupModel group = cacheSession.getGroupById(id, this);
|
GroupModel group = cacheSession.getGroupById(id, this);
|
||||||
|
@ -1300,4 +1300,23 @@ public class RealmAdapter implements RealmModel {
|
||||||
getDelegateForUpdate();
|
getDelegateForUpdate();
|
||||||
return updated.removeGroup(group);
|
return updated.removeGroup(group);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GroupModel createGroup(String name) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
return updated.createGroup(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addTopLevelGroup(GroupModel subGroup) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.addTopLevelGroup(subGroup);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void moveGroup(GroupModel group, GroupModel toParent) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.moveGroup(group, toParent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ public class CachedGroup implements Serializable {
|
||||||
this.id = group.getId();
|
this.id = group.getId();
|
||||||
this.realm = realm.getId();
|
this.realm = realm.getId();
|
||||||
this.name = group.getName();
|
this.name = group.getName();
|
||||||
if (group.getParent() != null) this.parentId = group.getParent().getId();
|
this.parentId = group.getParentId();
|
||||||
|
|
||||||
this.attributes.putAll(group.getAttributes());
|
this.attributes.putAll(group.getAttributes());
|
||||||
for (RoleModel role : group.getRoleMappings()) {
|
for (RoleModel role : group.getRoleMappings()) {
|
||||||
|
|
|
@ -87,6 +87,13 @@ public class GroupAdapter implements GroupModel {
|
||||||
return realm.getGroupById(parent.getId());
|
return realm.getGroupById(parent.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getParentId() {
|
||||||
|
GroupEntity parent = group.getParent();
|
||||||
|
if (parent == null) return null;
|
||||||
|
return parent.getId();
|
||||||
|
}
|
||||||
|
|
||||||
public static GroupEntity toEntity(GroupModel model, EntityManager em) {
|
public static GroupEntity toEntity(GroupModel model, EntityManager em) {
|
||||||
if (model instanceof GroupAdapter) {
|
if (model instanceof GroupAdapter) {
|
||||||
return ((GroupAdapter)model).getGroup();
|
return ((GroupAdapter)model).getGroup();
|
||||||
|
@ -95,21 +102,22 @@ public class GroupAdapter implements GroupModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setParent(GroupModel group) {
|
public void setParent(GroupModel parent) {
|
||||||
GroupEntity parent = toEntity(group, em);
|
if (parent == null) group.setParent(null);
|
||||||
group.setParent(group);
|
else {
|
||||||
|
GroupEntity parentEntity = toEntity(parent, em);
|
||||||
|
group.setParent(parentEntity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addChild(GroupModel subGroup) {
|
public void addChild(GroupModel subGroup) {
|
||||||
subGroup.setParent(this);
|
subGroup.setParent(this);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeChild(GroupModel subGroup) {
|
public void removeChild(GroupModel subGroup) {
|
||||||
subGroup.setParent(null);
|
subGroup.setParent(null);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -131,7 +131,7 @@ public class JpaRealmProvider implements RealmProvider {
|
||||||
public GroupModel getGroupById(String id, RealmModel realm) {
|
public GroupModel getGroupById(String id, RealmModel realm) {
|
||||||
GroupEntity groupEntity = em.find(GroupEntity.class, id);
|
GroupEntity groupEntity = em.find(GroupEntity.class, id);
|
||||||
if (groupEntity == null) return null;
|
if (groupEntity == null) return null;
|
||||||
if (groupEntity.getRealm().getId().equals(realm.getId())) return null;
|
if (!groupEntity.getRealm().getId().equals(realm.getId())) return null;
|
||||||
return new GroupAdapter(realm, em, groupEntity);
|
return new GroupAdapter(realm, em, groupEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1947,12 +1947,19 @@ public class RealmAdapter implements RealmModel {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void moveGroup(GroupModel group, GroupModel toParent) {
|
||||||
|
if (group.getParentId() != null) {
|
||||||
|
group.getParent().removeChild(group);
|
||||||
|
}
|
||||||
|
group.setParent(toParent);
|
||||||
|
if (toParent != null) toParent.addChild(group);
|
||||||
|
else addTopLevelGroup(group);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GroupModel getGroupById(String id) {
|
public GroupModel getGroupById(String id) {
|
||||||
GroupEntity groupEntity = em.find(GroupEntity.class, id);
|
return session.realms().getGroupById(id, this);
|
||||||
if (groupEntity == null) return null;
|
|
||||||
if (groupEntity.getRealm().getId().equals(getId())) return null;
|
|
||||||
return new GroupAdapter(this, em, groupEntity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1994,12 +2001,29 @@ public class RealmAdapter implements RealmModel {
|
||||||
|
|
||||||
|
|
||||||
session.users().preRemove(this, group);
|
session.users().preRemove(this, group);
|
||||||
|
moveGroup(group, null);
|
||||||
realm.getGroups().remove(groupEntity);
|
realm.getGroups().remove(groupEntity);
|
||||||
em.createNamedQuery("deleteGroupAttributesByGroup").setParameter("group", group).executeUpdate();
|
em.createNamedQuery("deleteGroupAttributesByGroup").setParameter("group", groupEntity).executeUpdate();
|
||||||
em.createNamedQuery("deleteGroupRoleMappingsByGroup").setParameter("group", group).executeUpdate();
|
em.createNamedQuery("deleteGroupRoleMappingsByGroup").setParameter("group", groupEntity).executeUpdate();
|
||||||
em.remove(groupEntity);
|
em.remove(groupEntity);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GroupModel createGroup(String name) {
|
||||||
|
GroupEntity groupEntity = new GroupEntity();
|
||||||
|
groupEntity.setId(KeycloakModelUtils.generateId());
|
||||||
|
groupEntity.setName(name);
|
||||||
|
groupEntity.setRealm(realm);
|
||||||
|
em.persist(groupEntity);
|
||||||
|
|
||||||
|
return new GroupAdapter(this, em, groupEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addTopLevelGroup(GroupModel subGroup) {
|
||||||
|
subGroup.setParent(null);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -191,6 +191,11 @@ public class GroupAdapter extends AbstractMongoAdapter<MongoGroupEntity> impleme
|
||||||
return realm.getGroupById(group.getParentId());
|
return realm.getGroupById(group.getParentId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getParentId() {
|
||||||
|
return group.getParentId();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<GroupModel> getSubGroups() {
|
public Set<GroupModel> getSubGroups() {
|
||||||
Set<GroupModel> subGroups = new HashSet<>();
|
Set<GroupModel> subGroups = new HashSet<>();
|
||||||
|
|
|
@ -609,6 +609,34 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
||||||
return model.getRoleById(id, this);
|
return model.getRoleById(id, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GroupModel createGroup(String name) {
|
||||||
|
MongoGroupEntity group = new MongoGroupEntity();
|
||||||
|
group.setId(KeycloakModelUtils.generateId());
|
||||||
|
group.setName(name);
|
||||||
|
group.setRealmId(getId());
|
||||||
|
|
||||||
|
getMongoStore().insertEntity(group, invocationContext);
|
||||||
|
|
||||||
|
return new GroupAdapter(session, this, group, invocationContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addTopLevelGroup(GroupModel subGroup) {
|
||||||
|
subGroup.setParent(null);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void moveGroup(GroupModel group, GroupModel toParent) {
|
||||||
|
if (group.getParentId() != null) {
|
||||||
|
group.getParent().removeChild(group);
|
||||||
|
}
|
||||||
|
group.setParent(toParent);
|
||||||
|
if (toParent != null) toParent.addChild(group);
|
||||||
|
else addTopLevelGroup(group);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GroupModel getGroupById(String id) {
|
public GroupModel getGroupById(String id) {
|
||||||
return model.getGroupById(id, this);
|
return model.getGroupById(id, this);
|
||||||
|
@ -646,7 +674,11 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean removeGroup(GroupModel group) {
|
public boolean removeGroup(GroupModel group) {
|
||||||
|
for (GroupModel subGroup : group.getSubGroups()) {
|
||||||
|
removeGroup(subGroup);
|
||||||
|
}
|
||||||
session.users().preRemove(this, group);
|
session.users().preRemove(this, group);
|
||||||
|
moveGroup(group, null);
|
||||||
return getMongoStore().removeEntity(MongoGroupEntity.class, group.getId(), invocationContext);
|
return getMongoStore().removeEntity(MongoGroupEntity.class, group.getId(), invocationContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import org.jboss.resteasy.spi.NotFoundException;
|
||||||
import org.keycloak.events.admin.OperationType;
|
import org.keycloak.events.admin.OperationType;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.RoleMapperModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.utils.ModelToRepresentation;
|
import org.keycloak.models.utils.ModelToRepresentation;
|
||||||
|
@ -29,17 +30,17 @@ import java.util.Set;
|
||||||
* @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 UserClientRoleMappingsResource {
|
public class ClientRoleMappingsResource {
|
||||||
protected static final Logger logger = Logger.getLogger(UserClientRoleMappingsResource.class);
|
protected static final Logger logger = Logger.getLogger(ClientRoleMappingsResource.class);
|
||||||
|
|
||||||
protected RealmModel realm;
|
protected RealmModel realm;
|
||||||
protected RealmAuth auth;
|
protected RealmAuth auth;
|
||||||
protected UserModel user;
|
protected RoleMapperModel user;
|
||||||
protected ClientModel client;
|
protected ClientModel client;
|
||||||
protected AdminEventBuilder adminEvent;
|
protected AdminEventBuilder adminEvent;
|
||||||
private UriInfo uriInfo;
|
private UriInfo uriInfo;
|
||||||
|
|
||||||
public UserClientRoleMappingsResource(UriInfo uriInfo, RealmModel realm, RealmAuth auth, UserModel user, ClientModel client, AdminEventBuilder adminEvent) {
|
public ClientRoleMappingsResource(UriInfo uriInfo, RealmModel realm, RealmAuth auth, RoleMapperModel user, ClientModel client, AdminEventBuilder adminEvent) {
|
||||||
this.uriInfo = uriInfo;
|
this.uriInfo = uriInfo;
|
||||||
this.realm = realm;
|
this.realm = realm;
|
||||||
this.auth = auth;
|
this.auth = auth;
|
||||||
|
@ -105,10 +106,10 @@ public class UserClientRoleMappingsResource {
|
||||||
return getAvailableRoles(user, available);
|
return getAvailableRoles(user, available);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<RoleRepresentation> getAvailableRoles(UserModel user, Set<RoleModel> available) {
|
public static List<RoleRepresentation> getAvailableRoles(RoleMapperModel mapper, Set<RoleModel> available) {
|
||||||
Set<RoleModel> roles = new HashSet<RoleModel>();
|
Set<RoleModel> roles = new HashSet<RoleModel>();
|
||||||
for (RoleModel roleModel : available) {
|
for (RoleModel roleModel : available) {
|
||||||
if (user.hasRole(roleModel)) continue;
|
if (mapper.hasRole(roleModel)) continue;
|
||||||
roles.add(roleModel);
|
roles.add(roleModel);
|
||||||
}
|
}
|
||||||
|
|
261
services/src/main/java/org/keycloak/services/resources/admin/GroupResource.java
Executable file
261
services/src/main/java/org/keycloak/services/resources/admin/GroupResource.java
Executable file
|
@ -0,0 +1,261 @@
|
||||||
|
package org.keycloak.services.resources.admin;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||||
|
import org.jboss.resteasy.spi.NotFoundException;
|
||||||
|
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.utils.ModelToRepresentation;
|
||||||
|
import org.keycloak.representations.idm.GroupRepresentation;
|
||||||
|
|
||||||
|
import javax.ws.rs.Consumes;
|
||||||
|
import javax.ws.rs.DELETE;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.PUT;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.UriBuilder;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Bill Burke
|
||||||
|
*/
|
||||||
|
public class GroupResource {
|
||||||
|
|
||||||
|
private static Logger logger = Logger.getLogger(GroupResource.class);
|
||||||
|
|
||||||
|
private final RealmModel realm;
|
||||||
|
private final KeycloakSession session;
|
||||||
|
private final RealmAuth auth;
|
||||||
|
private final AdminEventBuilder adminEvent;
|
||||||
|
|
||||||
|
public GroupResource(RealmModel realm, KeycloakSession session, RealmAuth auth, AdminEventBuilder adminEvent) {
|
||||||
|
this.realm = realm;
|
||||||
|
this.session = session;
|
||||||
|
this.auth = auth;
|
||||||
|
this.adminEvent = adminEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Context private UriInfo uriInfo;
|
||||||
|
|
||||||
|
public GroupResource(RealmAuth auth, RealmModel realm, KeycloakSession session, AdminEventBuilder adminEvent) {
|
||||||
|
this.realm = realm;
|
||||||
|
this.session = session;
|
||||||
|
this.auth = auth;
|
||||||
|
this.adminEvent = adminEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get group hierarchy. Only name and ids are returned.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@NoCache
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public List<GroupRepresentation> getGroups() {
|
||||||
|
this.auth.requireView();
|
||||||
|
return ModelToRepresentation.toGroupHierarchy(realm, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set or create child as a top level group. This will update the group and set the parent if it exists. Create it and set the parent
|
||||||
|
* if the group doesn't exist.
|
||||||
|
*
|
||||||
|
* @param rep
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Path("{id}")
|
||||||
|
@NoCache
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
public Response addRealmGroup(@PathParam("id") String parentId, GroupRepresentation rep) {
|
||||||
|
GroupModel parentModel = realm.getGroupById(parentId);
|
||||||
|
Response.ResponseBuilder builder = Response.status(204);
|
||||||
|
if (parentModel == null) {
|
||||||
|
throw new NotFoundException("Could not find parent by id");
|
||||||
|
}
|
||||||
|
GroupModel child = null;
|
||||||
|
if (rep.getId() != null) {
|
||||||
|
child = realm.getGroupById(rep.getId());
|
||||||
|
if (child == null) {
|
||||||
|
throw new NotFoundException("Could not find child by id");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
child = realm.createGroup(rep.getName());
|
||||||
|
updateGroup(rep, child);
|
||||||
|
URI uri = uriInfo.getBaseUriBuilder()
|
||||||
|
.path(uriInfo.getMatchedURIs().get(1))
|
||||||
|
.path(child.getId()).build();
|
||||||
|
builder.status(201).location(uri);
|
||||||
|
|
||||||
|
}
|
||||||
|
child.setParent(parentModel);
|
||||||
|
GroupRepresentation childRep = ModelToRepresentation.toRepresentation(child, true);
|
||||||
|
return builder.type(MediaType.APPLICATION_JSON_TYPE).entity(childRep).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does not expand hierarchy. Subgroups will not be set.
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Path("{id}")
|
||||||
|
@NoCache
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public GroupRepresentation getGroupById(@PathParam("id") String id) {
|
||||||
|
this.auth.requireView();
|
||||||
|
GroupModel group = realm.getGroupById(id);
|
||||||
|
if (group == null) {
|
||||||
|
throw new NotFoundException("Could not find group by id");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ModelToRepresentation.toRepresentation(group, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update group
|
||||||
|
*
|
||||||
|
* @param rep
|
||||||
|
*/
|
||||||
|
@PUT
|
||||||
|
@Path("{id}")
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
public void updateGroup(@PathParam("id") String id, GroupRepresentation rep) {
|
||||||
|
GroupModel model = realm.getGroupById(id);
|
||||||
|
if (model == null) {
|
||||||
|
throw new NotFoundException("Could not find group by id");
|
||||||
|
}
|
||||||
|
|
||||||
|
updateGroup(rep, model);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@DELETE
|
||||||
|
@Path("{id}")
|
||||||
|
public void deleteGroup(@PathParam("id") String id) {
|
||||||
|
GroupModel model = realm.getGroupById(id);
|
||||||
|
if (model == null) {
|
||||||
|
throw new NotFoundException("Could not find group by id");
|
||||||
|
}
|
||||||
|
realm.removeGroup(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set or create child. This will just set the parent if it exists. Create it and set the parent
|
||||||
|
* if the group doesn't exist.
|
||||||
|
*
|
||||||
|
* @param rep
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Path("{id}/children")
|
||||||
|
@NoCache
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
public Response addGroup(@PathParam("id") String parentId, GroupRepresentation rep) {
|
||||||
|
GroupModel parentModel = realm.getGroupById(parentId);
|
||||||
|
Response.ResponseBuilder builder = Response.status(204);
|
||||||
|
if (parentModel == null) {
|
||||||
|
throw new NotFoundException("Could not find parent by id");
|
||||||
|
}
|
||||||
|
GroupModel child = null;
|
||||||
|
if (rep.getId() != null) {
|
||||||
|
child = realm.getGroupById(rep.getId());
|
||||||
|
if (child == null) {
|
||||||
|
throw new NotFoundException("Could not find child by id");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
child = realm.createGroup(rep.getName());
|
||||||
|
updateGroup(rep, child);
|
||||||
|
URI uri = uriInfo.getBaseUriBuilder()
|
||||||
|
.path(uriInfo.getMatchedURIs().get(1))
|
||||||
|
.path(child.getId()).build();
|
||||||
|
builder.status(201).location(uri);
|
||||||
|
|
||||||
|
}
|
||||||
|
realm.moveGroup(child, parentModel);
|
||||||
|
GroupRepresentation childRep = ModelToRepresentation.toRepresentation(child, true);
|
||||||
|
return builder.type(MediaType.APPLICATION_JSON_TYPE).entity(childRep).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create or add a top level realm groupSet or create child. This will update the group and set the parent if it exists. Create it and set the parent
|
||||||
|
* if the group doesn't exist.
|
||||||
|
*
|
||||||
|
* @param rep
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
public Response addTopLevelGroup(GroupRepresentation rep) {
|
||||||
|
GroupModel child = null;
|
||||||
|
Response.ResponseBuilder builder = Response.status(204);
|
||||||
|
if (rep.getId() != null) {
|
||||||
|
child = realm.getGroupById(rep.getId());
|
||||||
|
if (child == null) {
|
||||||
|
throw new NotFoundException("Could not find child by id");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
child = realm.createGroup(rep.getName());
|
||||||
|
updateGroup(rep, child);
|
||||||
|
URI uri = uriInfo.getAbsolutePathBuilder()
|
||||||
|
.path(child.getId()).build();
|
||||||
|
builder.status(201).location(uri);
|
||||||
|
}
|
||||||
|
realm.moveGroup(child, null);
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateGroup(GroupRepresentation rep, GroupModel model) {
|
||||||
|
if (rep.getName() != null) model.setName(rep.getName());
|
||||||
|
|
||||||
|
if (rep.getAttributes() != null) {
|
||||||
|
Set<String> attrsToRemove = new HashSet<>(model.getAttributes().keySet());
|
||||||
|
attrsToRemove.removeAll(rep.getAttributes().keySet());
|
||||||
|
for (Map.Entry<String, List<String>> attr : rep.getAttributes().entrySet()) {
|
||||||
|
model.setAttribute(attr.getKey(), attr.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String attr : attrsToRemove) {
|
||||||
|
model.removeAttribute(attr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Path("{id}/role-mappings")
|
||||||
|
public RoleMapperResource getRoleMappings(@PathParam("id") String id) {
|
||||||
|
|
||||||
|
GroupModel group = session.realms().getGroupById(id, realm);
|
||||||
|
if (group == null) {
|
||||||
|
throw new NotFoundException("Group not found");
|
||||||
|
}
|
||||||
|
auth.init(RealmAuth.Resource.USER);
|
||||||
|
|
||||||
|
RoleMapperResource resource = new RoleMapperResource(realm, auth, group, adminEvent);
|
||||||
|
ResteasyProviderFactory.getInstance().injectProperties(resource);
|
||||||
|
return resource;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -619,4 +619,11 @@ public class RealmAdminResource {
|
||||||
return new IdentityProvidersResource(realm, session, this.auth, adminEvent);
|
return new IdentityProvidersResource(realm, session, this.auth, adminEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Path("groups")
|
||||||
|
public GroupResource getGroups() {
|
||||||
|
GroupResource resource = new GroupResource(realm, session, this.auth, adminEvent);
|
||||||
|
ResteasyProviderFactory.getInstance().injectProperties(resource);
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,271 @@
|
||||||
|
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.common.ClientConnection;
|
||||||
|
import org.keycloak.email.EmailException;
|
||||||
|
import org.keycloak.email.EmailProvider;
|
||||||
|
import org.keycloak.events.admin.OperationType;
|
||||||
|
import org.keycloak.models.ClientModel;
|
||||||
|
import org.keycloak.models.ClientSessionModel;
|
||||||
|
import org.keycloak.models.Constants;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.ModelReadOnlyException;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.RoleMapperModel;
|
||||||
|
import org.keycloak.models.RoleModel;
|
||||||
|
import org.keycloak.models.UserCredentialModel;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.UserSessionModel;
|
||||||
|
import org.keycloak.models.utils.ModelToRepresentation;
|
||||||
|
import org.keycloak.models.utils.RepresentationToModel;
|
||||||
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
|
import org.keycloak.protocol.oidc.TokenManager;
|
||||||
|
import org.keycloak.protocol.oidc.utils.RedirectUtils;
|
||||||
|
import org.keycloak.representations.idm.ClientMappingsRepresentation;
|
||||||
|
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||||
|
import org.keycloak.representations.idm.MappingsRepresentation;
|
||||||
|
import org.keycloak.representations.idm.RoleRepresentation;
|
||||||
|
import org.keycloak.services.ErrorResponse;
|
||||||
|
import org.keycloak.services.Urls;
|
||||||
|
import org.keycloak.services.managers.BruteForceProtector;
|
||||||
|
import org.keycloak.services.managers.ClientSessionCode;
|
||||||
|
import org.keycloak.services.managers.RealmManager;
|
||||||
|
|
||||||
|
import javax.ws.rs.Consumes;
|
||||||
|
import javax.ws.rs.DELETE;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.PUT;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.QueryParam;
|
||||||
|
import javax.ws.rs.WebApplicationException;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.UriBuilder;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base resource for managing users
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class RoleMapperResource {
|
||||||
|
protected static final Logger logger = Logger.getLogger(RoleMapperResource.class);
|
||||||
|
|
||||||
|
protected RealmModel realm;
|
||||||
|
|
||||||
|
private RealmAuth auth;
|
||||||
|
|
||||||
|
private RoleMapperModel roleMapper;
|
||||||
|
|
||||||
|
private AdminEventBuilder adminEvent;
|
||||||
|
|
||||||
|
@Context
|
||||||
|
protected ClientConnection clientConnection;
|
||||||
|
|
||||||
|
@Context
|
||||||
|
protected UriInfo uriInfo;
|
||||||
|
|
||||||
|
@Context
|
||||||
|
protected KeycloakSession session;
|
||||||
|
|
||||||
|
@Context
|
||||||
|
protected HttpHeaders headers;
|
||||||
|
|
||||||
|
@Context
|
||||||
|
protected BruteForceProtector protector;
|
||||||
|
|
||||||
|
public RoleMapperResource(RealmModel realm, RealmAuth auth, RoleMapperModel roleMapper, AdminEventBuilder adminEvent) {
|
||||||
|
this.auth = auth;
|
||||||
|
this.realm = realm;
|
||||||
|
this.adminEvent = adminEvent;
|
||||||
|
this.roleMapper = roleMapper;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get role mappings
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@NoCache
|
||||||
|
public MappingsRepresentation getRoleMappings() {
|
||||||
|
auth.requireView();
|
||||||
|
|
||||||
|
MappingsRepresentation all = new MappingsRepresentation();
|
||||||
|
Set<RoleModel> realmMappings = roleMapper.getRoleMappings();
|
||||||
|
RealmManager manager = new RealmManager(session);
|
||||||
|
if (realmMappings.size() > 0) {
|
||||||
|
List<RoleRepresentation> realmRep = new ArrayList<RoleRepresentation>();
|
||||||
|
for (RoleModel roleModel : realmMappings) {
|
||||||
|
realmRep.add(ModelToRepresentation.toRepresentation(roleModel));
|
||||||
|
}
|
||||||
|
all.setRealmMappings(realmRep);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ClientModel> clients = realm.getClients();
|
||||||
|
if (clients.size() > 0) {
|
||||||
|
Map<String, ClientMappingsRepresentation> appMappings = new HashMap<String, ClientMappingsRepresentation>();
|
||||||
|
for (ClientModel client : clients) {
|
||||||
|
Set<RoleModel> roleMappings = roleMapper.getClientRoleMappings(client);
|
||||||
|
if (roleMappings.size() > 0) {
|
||||||
|
ClientMappingsRepresentation mappings = new ClientMappingsRepresentation();
|
||||||
|
mappings.setId(client.getId());
|
||||||
|
mappings.setClient(client.getClientId());
|
||||||
|
List<RoleRepresentation> roles = new ArrayList<RoleRepresentation>();
|
||||||
|
mappings.setMappings(roles);
|
||||||
|
for (RoleModel role : roleMappings) {
|
||||||
|
roles.add(ModelToRepresentation.toRepresentation(role));
|
||||||
|
}
|
||||||
|
appMappings.put(client.getClientId(), mappings);
|
||||||
|
all.setClientMappings(appMappings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return all;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get realm-level role mappings
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Path("realm")
|
||||||
|
@GET
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@NoCache
|
||||||
|
public List<RoleRepresentation> getRealmRoleMappings() {
|
||||||
|
auth.requireView();
|
||||||
|
|
||||||
|
Set<RoleModel> realmMappings = roleMapper.getRealmRoleMappings();
|
||||||
|
List<RoleRepresentation> realmMappingsRep = new ArrayList<RoleRepresentation>();
|
||||||
|
for (RoleModel roleModel : realmMappings) {
|
||||||
|
realmMappingsRep.add(ModelToRepresentation.toRepresentation(roleModel));
|
||||||
|
}
|
||||||
|
return realmMappingsRep;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get effective realm-level role mappings
|
||||||
|
*
|
||||||
|
* This will recurse all composite roles to get the result.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Path("realm/composite")
|
||||||
|
@GET
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@NoCache
|
||||||
|
public List<RoleRepresentation> getCompositeRealmRoleMappings() {
|
||||||
|
auth.requireView();
|
||||||
|
|
||||||
|
Set<RoleModel> roles = realm.getRoles();
|
||||||
|
List<RoleRepresentation> realmMappingsRep = new ArrayList<RoleRepresentation>();
|
||||||
|
for (RoleModel roleModel : roles) {
|
||||||
|
if (roleMapper.hasRole(roleModel)) {
|
||||||
|
realmMappingsRep.add(ModelToRepresentation.toRepresentation(roleModel));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return realmMappingsRep;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get realm-level roles that can be mapped
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Path("realm/available")
|
||||||
|
@GET
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@NoCache
|
||||||
|
public List<RoleRepresentation> getAvailableRealmRoleMappings() {
|
||||||
|
auth.requireView();
|
||||||
|
|
||||||
|
Set<RoleModel> available = realm.getRoles();
|
||||||
|
return ClientRoleMappingsResource.getAvailableRoles(roleMapper, available);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add realm-level role mappings to the user
|
||||||
|
*
|
||||||
|
* @param roles Roles to add
|
||||||
|
*/
|
||||||
|
@Path("realm")
|
||||||
|
@POST
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
public void addRealmRoleMappings(List<RoleRepresentation> roles) {
|
||||||
|
auth.requireManage();
|
||||||
|
|
||||||
|
logger.debugv("** addRealmRoleMappings: {0}", roles);
|
||||||
|
|
||||||
|
for (RoleRepresentation role : roles) {
|
||||||
|
RoleModel roleModel = realm.getRole(role.getName());
|
||||||
|
if (roleModel == null || !roleModel.getId().equals(role.getId())) {
|
||||||
|
throw new NotFoundException("Role not found");
|
||||||
|
}
|
||||||
|
roleMapper.grantRole(roleModel);
|
||||||
|
adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo, role.getId()).representation(roles).success();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete realm-level role mappings
|
||||||
|
*
|
||||||
|
* @param roles
|
||||||
|
*/
|
||||||
|
@Path("realm")
|
||||||
|
@DELETE
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
public void deleteRealmRoleMappings(List<RoleRepresentation> roles) {
|
||||||
|
auth.requireManage();
|
||||||
|
|
||||||
|
logger.debug("deleteRealmRoleMappings");
|
||||||
|
if (roles == null) {
|
||||||
|
Set<RoleModel> roleModels = roleMapper.getRealmRoleMappings();
|
||||||
|
for (RoleModel roleModel : roleModels) {
|
||||||
|
roleMapper.deleteRoleMapping(roleModel);
|
||||||
|
}
|
||||||
|
adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo).representation(roles).success();
|
||||||
|
} else {
|
||||||
|
for (RoleRepresentation role : roles) {
|
||||||
|
RoleModel roleModel = realm.getRole(role.getName());
|
||||||
|
if (roleModel == null || !roleModel.getId().equals(role.getId())) {
|
||||||
|
throw new NotFoundException("Role not found");
|
||||||
|
}
|
||||||
|
roleMapper.deleteRoleMapping(roleModel);
|
||||||
|
|
||||||
|
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo, role.getId()).representation(roles).success();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Path("clients/{client}")
|
||||||
|
public ClientRoleMappingsResource getUserClientRoleMappingsResource(@PathParam("client") String client) {
|
||||||
|
ClientModel clientModel = realm.getClientById(client);
|
||||||
|
if (clientModel == null) {
|
||||||
|
throw new NotFoundException("Client not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ClientRoleMappingsResource(uriInfo, realm, auth, roleMapper, clientModel, adminEvent);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import org.jboss.logging.Logger;
|
||||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||||
import org.jboss.resteasy.spi.BadRequestException;
|
import org.jboss.resteasy.spi.BadRequestException;
|
||||||
import org.jboss.resteasy.spi.NotFoundException;
|
import org.jboss.resteasy.spi.NotFoundException;
|
||||||
|
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||||
import org.keycloak.common.ClientConnection;
|
import org.keycloak.common.ClientConnection;
|
||||||
import org.keycloak.authentication.RequiredActionProvider;
|
import org.keycloak.authentication.RequiredActionProvider;
|
||||||
import org.keycloak.email.EmailException;
|
import org.keycloak.email.EmailException;
|
||||||
|
@ -137,7 +138,7 @@ public class UsersResource {
|
||||||
throw new NotFoundException("User not found");
|
throw new NotFoundException("User not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<String> attrsToRemove;
|
Set<String> attrsToRemove;
|
||||||
if (rep.getAttributes() != null) {
|
if (rep.getAttributes() != null) {
|
||||||
attrsToRemove = new HashSet<>(user.getAttributes().keySet());
|
attrsToRemove = new HashSet<>(user.getAttributes().keySet());
|
||||||
attrsToRemove.removeAll(rep.getAttributes().keySet());
|
attrsToRemove.removeAll(rep.getAttributes().keySet());
|
||||||
|
@ -660,214 +661,18 @@ public class UsersResource {
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get role mappings for the user
|
|
||||||
*
|
|
||||||
* @param id User id
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Path("{id}/role-mappings")
|
@Path("{id}/role-mappings")
|
||||||
@GET
|
public RoleMapperResource getRoleMappings(@PathParam("id") String id) {
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
|
||||||
@NoCache
|
|
||||||
public MappingsRepresentation getRoleMappings(@PathParam("id") String id) {
|
|
||||||
auth.requireView();
|
|
||||||
|
|
||||||
UserModel user = session.users().getUserById(id, realm);
|
UserModel user = session.users().getUserById(id, realm);
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new NotFoundException("User not found");
|
throw new NotFoundException("User not found");
|
||||||
}
|
}
|
||||||
|
auth.init(RealmAuth.Resource.USER);
|
||||||
|
|
||||||
MappingsRepresentation all = new MappingsRepresentation();
|
RoleMapperResource resource = new RoleMapperResource(realm, auth, user, adminEvent);
|
||||||
Set<RoleModel> realmMappings = user.getRoleMappings();
|
ResteasyProviderFactory.getInstance().injectProperties(resource);
|
||||||
RealmManager manager = new RealmManager(session);
|
return resource;
|
||||||
if (realmMappings.size() > 0) {
|
|
||||||
List<RoleRepresentation> realmRep = new ArrayList<RoleRepresentation>();
|
|
||||||
for (RoleModel roleModel : realmMappings) {
|
|
||||||
realmRep.add(ModelToRepresentation.toRepresentation(roleModel));
|
|
||||||
}
|
|
||||||
all.setRealmMappings(realmRep);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<ClientModel> clients = realm.getClients();
|
|
||||||
if (clients.size() > 0) {
|
|
||||||
Map<String, ClientMappingsRepresentation> appMappings = new HashMap<String, ClientMappingsRepresentation>();
|
|
||||||
for (ClientModel client : clients) {
|
|
||||||
Set<RoleModel> roleMappings = user.getClientRoleMappings(client);
|
|
||||||
if (roleMappings.size() > 0) {
|
|
||||||
ClientMappingsRepresentation mappings = new ClientMappingsRepresentation();
|
|
||||||
mappings.setId(client.getId());
|
|
||||||
mappings.setClient(client.getClientId());
|
|
||||||
List<RoleRepresentation> roles = new ArrayList<RoleRepresentation>();
|
|
||||||
mappings.setMappings(roles);
|
|
||||||
for (RoleModel role : roleMappings) {
|
|
||||||
roles.add(ModelToRepresentation.toRepresentation(role));
|
|
||||||
}
|
|
||||||
appMappings.put(client.getClientId(), mappings);
|
|
||||||
all.setClientMappings(appMappings);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return all;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get realm-level role mappings for the user
|
|
||||||
*
|
|
||||||
* @param id User id
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Path("{id}/role-mappings/realm")
|
|
||||||
@GET
|
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
|
||||||
@NoCache
|
|
||||||
public List<RoleRepresentation> getRealmRoleMappings(@PathParam("id") String id) {
|
|
||||||
auth.requireView();
|
|
||||||
|
|
||||||
UserModel user = session.users().getUserById(id, realm);
|
|
||||||
if (user == null) {
|
|
||||||
throw new NotFoundException("User not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<RoleModel> realmMappings = user.getRealmRoleMappings();
|
|
||||||
List<RoleRepresentation> realmMappingsRep = new ArrayList<RoleRepresentation>();
|
|
||||||
for (RoleModel roleModel : realmMappings) {
|
|
||||||
realmMappingsRep.add(ModelToRepresentation.toRepresentation(roleModel));
|
|
||||||
}
|
|
||||||
return realmMappingsRep;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get effective realm-level role mappings for the user
|
|
||||||
*
|
|
||||||
* This will recurse all composite roles to get the result.
|
|
||||||
*
|
|
||||||
* @param id User id
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Path("{id}/role-mappings/realm/composite")
|
|
||||||
@GET
|
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
|
||||||
@NoCache
|
|
||||||
public List<RoleRepresentation> getCompositeRealmRoleMappings(@PathParam("id") String id) {
|
|
||||||
auth.requireView();
|
|
||||||
|
|
||||||
UserModel user = session.users().getUserById(id, realm);
|
|
||||||
if (user == null) {
|
|
||||||
throw new NotFoundException("User not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<RoleModel> roles = realm.getRoles();
|
|
||||||
List<RoleRepresentation> realmMappingsRep = new ArrayList<RoleRepresentation>();
|
|
||||||
for (RoleModel roleModel : roles) {
|
|
||||||
if (user.hasRole(roleModel)) {
|
|
||||||
realmMappingsRep.add(ModelToRepresentation.toRepresentation(roleModel));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return realmMappingsRep;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get realm-level roles that can be mapped to this user
|
|
||||||
*
|
|
||||||
* @param id User id
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Path("{id}/role-mappings/realm/available")
|
|
||||||
@GET
|
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
|
||||||
@NoCache
|
|
||||||
public List<RoleRepresentation> getAvailableRealmRoleMappings(@PathParam("id") String id) {
|
|
||||||
auth.requireView();
|
|
||||||
|
|
||||||
UserModel user = session.users().getUserById(id, realm);
|
|
||||||
if (user == null) {
|
|
||||||
throw new NotFoundException("User not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<RoleModel> available = realm.getRoles();
|
|
||||||
return UserClientRoleMappingsResource.getAvailableRoles(user, available);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add realm-level role mappings to the user
|
|
||||||
*
|
|
||||||
* @param id User id
|
|
||||||
* @param roles Roles to add
|
|
||||||
*/
|
|
||||||
@Path("{id}/role-mappings/realm")
|
|
||||||
@POST
|
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
|
||||||
public void addRealmRoleMappings(@PathParam("id") String id, List<RoleRepresentation> roles) {
|
|
||||||
auth.requireManage();
|
|
||||||
|
|
||||||
logger.debugv("** addRealmRoleMappings: {0}", roles);
|
|
||||||
UserModel user = session.users().getUserById(id, realm);
|
|
||||||
if (user == null) {
|
|
||||||
throw new NotFoundException("User not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (RoleRepresentation role : roles) {
|
|
||||||
RoleModel roleModel = realm.getRole(role.getName());
|
|
||||||
if (roleModel == null || !roleModel.getId().equals(role.getId())) {
|
|
||||||
throw new NotFoundException("Role not found");
|
|
||||||
}
|
|
||||||
user.grantRole(roleModel);
|
|
||||||
adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo, role.getId()).representation(roles).success();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete realm-level role mappings
|
|
||||||
*
|
|
||||||
* @param id User id
|
|
||||||
* @param roles
|
|
||||||
*/
|
|
||||||
@Path("{id}/role-mappings/realm")
|
|
||||||
@DELETE
|
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
|
||||||
public void deleteRealmRoleMappings(@PathParam("id") String id, List<RoleRepresentation> roles) {
|
|
||||||
auth.requireManage();
|
|
||||||
|
|
||||||
logger.debug("deleteRealmRoleMappings");
|
|
||||||
UserModel user = session.users().getUserById(id, realm);
|
|
||||||
if (user == null) {
|
|
||||||
throw new NotFoundException("User not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (roles == null) {
|
|
||||||
Set<RoleModel> roleModels = user.getRealmRoleMappings();
|
|
||||||
for (RoleModel roleModel : roleModels) {
|
|
||||||
user.deleteRoleMapping(roleModel);
|
|
||||||
}
|
|
||||||
adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo).representation(roles).success();
|
|
||||||
} else {
|
|
||||||
for (RoleRepresentation role : roles) {
|
|
||||||
RoleModel roleModel = realm.getRole(role.getName());
|
|
||||||
if (roleModel == null || !roleModel.getId().equals(role.getId())) {
|
|
||||||
throw new NotFoundException("Role not found");
|
|
||||||
}
|
|
||||||
user.deleteRoleMapping(roleModel);
|
|
||||||
|
|
||||||
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo, role.getId()).representation(roles).success();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Path("{id}/role-mappings/clients/{client}")
|
|
||||||
public UserClientRoleMappingsResource getUserClientRoleMappingsResource(@PathParam("id") String id, @PathParam("client") String client) {
|
|
||||||
UserModel user = session.users().getUserById(id, realm);
|
|
||||||
if (user == null) {
|
|
||||||
throw new NotFoundException("User not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
ClientModel clientModel = realm.getClientById(client);
|
|
||||||
if (clientModel == null) {
|
|
||||||
throw new NotFoundException("Client not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
return new UserClientRoleMappingsResource(uriInfo, realm, auth, user, clientModel, adminEvent);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -161,6 +161,9 @@ public class KeycloakServer {
|
||||||
|
|
||||||
if (!System.getProperties().containsKey("keycloak.theme.dir")) {
|
if (!System.getProperties().containsKey("keycloak.theme.dir")) {
|
||||||
System.setProperty("keycloak.theme.dir", file(dir.getAbsolutePath(), "forms", "common-themes", "src", "main", "resources", "theme").getAbsolutePath());
|
System.setProperty("keycloak.theme.dir", file(dir.getAbsolutePath(), "forms", "common-themes", "src", "main", "resources", "theme").getAbsolutePath());
|
||||||
|
} else {
|
||||||
|
String foo = System.getProperty("keycloak.theme.dir");
|
||||||
|
System.out.println(foo);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!System.getProperties().containsKey("keycloak.theme.cacheTemplates")) {
|
if (!System.getProperties().containsKey("keycloak.theme.cacheTemplates")) {
|
||||||
|
|
Loading…
Reference in a new issue