diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.7.0.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.7.0.xml old mode 100644 new mode 100755 index c5d7d5252d..1120252613 --- a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.7.0.xml +++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.7.0.xml @@ -1,6 +1,42 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -9,5 +45,19 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/connections/jpa/src/main/resources/META-INF/persistence.xml b/connections/jpa/src/main/resources/META-INF/persistence.xml index b8592a365c..e415488be3 100755 --- a/connections/jpa/src/main/resources/META-INF/persistence.xml +++ b/connections/jpa/src/main/resources/META-INF/persistence.xml @@ -31,6 +31,10 @@ org.keycloak.models.jpa.entities.RequiredActionProviderEntity org.keycloak.models.jpa.session.PersistentUserSessionEntity org.keycloak.models.jpa.session.PersistentClientSessionEntity + org.keycloak.models.jpa.entities.GroupEntity + org.keycloak.models.jpa.entities.GroupAttributeEntity + org.keycloak.models.jpa.entities.GroupRoleMappingEntity + org.keycloak.models.jpa.entities.UserGroupMembershipEntity org.keycloak.events.jpa.EventEntity diff --git a/core/src/main/java/org/keycloak/representations/idm/GroupRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/GroupRepresentation.java new file mode 100755 index 0000000000..cd5292f071 --- /dev/null +++ b/core/src/main/java/org/keycloak/representations/idm/GroupRepresentation.java @@ -0,0 +1,76 @@ +package org.keycloak.representations.idm; + +import org.codehaus.jackson.annotate.JsonIgnore; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class GroupRepresentation { + protected String id; + protected String name; + protected Map> attributes; + protected List realmRoles; + protected Map> clientRoles; + protected List subGroups; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getRealmRoles() { + return realmRoles; + } + + public void setRealmRoles(List realmRoles) { + this.realmRoles = realmRoles; + } + + public Map> getClientRoles() { + return clientRoles; + } + + public void setClientRoles(Map> clientRoles) { + this.clientRoles = clientRoles; + } + + + public Map> getAttributes() { + return attributes; + } + + public void setAttributes(Map> attributes) { + this.attributes = attributes; + } + + public GroupRepresentation singleAttribute(String name, String value) { + if (this.attributes == null) attributes = new HashMap<>(); + attributes.put(name, Arrays.asList(value)); + return this; + } + + public List getSubGroups() { + return subGroups; + } + + public void setSubGroups(List subGroups) { + this.subGroups = subGroups; + } +} diff --git a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java index 387ab7e783..e1333e9280 100755 --- a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java @@ -47,6 +47,7 @@ public class RealmRepresentation { protected String certificate; protected String codeSecret; protected RolesRepresentation roles; + protected List groups; protected List defaultRoles; @Deprecated protected Set requiredCredentials; @@ -775,4 +776,12 @@ public class RealmRepresentation { public void setClientAuthenticationFlow(String clientAuthenticationFlow) { this.clientAuthenticationFlow = clientAuthenticationFlow; } + + public List getGroups() { + return groups; + } + + public void setGroups(List groups) { + this.groups = groups; + } } diff --git a/core/src/main/java/org/keycloak/representations/idm/UserRoleMappingRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/UserRoleMappingRepresentation.java deleted file mode 100755 index a1a1e1c21e..0000000000 --- a/core/src/main/java/org/keycloak/representations/idm/UserRoleMappingRepresentation.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.keycloak.representations.idm; - -import java.util.HashSet; -import java.util.Set; - -/** - * @author Bill Burke - * @version $Revision: 1 $ - */ -public class UserRoleMappingRepresentation { - protected String self; // link - protected String username; - protected Set 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 getRoles() { - return roles; - } - - public void setRoles(Set roles) { - this.roles = roles; - } - - public UserRoleMappingRepresentation role(String role) { - if (this.roles == null) this.roles = new HashSet(); - this.roles.add(role); - return this; - } - -} diff --git a/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/BasePropertiesFederationProvider.java b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/BasePropertiesFederationProvider.java index f6228959c8..d6475aaa61 100755 --- a/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/BasePropertiesFederationProvider.java +++ b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/BasePropertiesFederationProvider.java @@ -1,6 +1,7 @@ package org.keycloak.examples.federation.properties; import org.keycloak.models.CredentialValidationOutput; +import org.keycloak.models.GroupModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; @@ -106,6 +107,12 @@ public abstract class BasePropertiesFederationProvider implements UserFederation } + @Override + public void preRemove(RealmModel realm, GroupModel group) { + // complete we dont'care if a role is removed + + } + /** * See if the user is still in the properties file * diff --git a/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/ClasspathPropertiesFederationProvider.java b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/ClasspathPropertiesFederationProvider.java index 164cf79cdb..5467a425f0 100755 --- a/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/ClasspathPropertiesFederationProvider.java +++ b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/ClasspathPropertiesFederationProvider.java @@ -63,4 +63,6 @@ public class ClasspathPropertiesFederationProvider extends BasePropertiesFederat throw new IllegalStateException("Remove not supported"); } + + } diff --git a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java old mode 100644 new mode 100755 index ec1a905010..09a4da75b6 --- a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java +++ b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java @@ -12,6 +12,7 @@ import org.jboss.logging.Logger; import org.keycloak.federation.kerberos.impl.KerberosUsernamePasswordAuthenticator; import org.keycloak.federation.kerberos.impl.SPNEGOAuthenticator; import org.keycloak.models.CredentialValidationOutput; +import org.keycloak.models.GroupModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; @@ -105,6 +106,11 @@ public class KerberosFederationProvider implements UserFederationProvider { } + @Override + public void preRemove(RealmModel realm, GroupModel group) { + + } + @Override public boolean isValid(RealmModel realm, UserModel local) { // KerberosUsernamePasswordAuthenticator.isUserAvailable is an overhead, so avoid it for now diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java index 322155b61a..3704f7626f 100755 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java @@ -12,6 +12,7 @@ import org.keycloak.federation.ldap.idm.store.ldap.LDAPIdentityStore; import org.keycloak.federation.ldap.kerberos.LDAPProviderKerberosConfig; import org.keycloak.federation.ldap.mappers.LDAPFederationMapper; import org.keycloak.models.CredentialValidationOutput; +import org.keycloak.models.GroupModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.LDAPConstants; import org.keycloak.models.ModelDuplicateException; @@ -319,6 +320,11 @@ public class LDAPFederationProvider implements UserFederationProvider { // TODO: Maybe mappers callback to ensure role deletion propagated to LDAP by RoleLDAPFederationMapper? } + @Override + public void preRemove(RealmModel realm, GroupModel group) { + + } + public boolean validPassword(RealmModel realm, UserModel user, String password) { if (kerberosConfig.isAllowKerberosAuthentication() && kerberosConfig.isUseKerberosForPasswordAuthentication()) { // Use Kerberos JAAS (Krb5LoginModule) diff --git a/forms/common-themes/src/main/resources/theme/base/admin/index.ftl b/forms/common-themes/src/main/resources/theme/base/admin/index.ftl index 1584d0d93a..0cdadfb0cb 100755 --- a/forms/common-themes/src/main/resources/theme/base/admin/index.ftl +++ b/forms/common-themes/src/main/resources/theme/base/admin/index.ftl @@ -25,6 +25,7 @@ + @@ -37,6 +38,7 @@ + diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js index 7fbbc808e1..2fd5382eb6 100755 --- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js @@ -7,7 +7,7 @@ var configUrl = consoleBaseUrl + "/config"; 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 loadingTimer = -1; @@ -583,6 +583,73 @@ module.config([ '$routeProvider', function($routeProvider) { }, 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', { templateUrl : resourceUrl + '/partials/client-role-detail.html', @@ -1871,6 +1938,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 () { return { scope: true, diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/groups.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/groups.js new file mode 100755 index 0000000000..7e82f32b36 --- /dev/null +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/groups.js @@ -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 = []; + }; + + + +}); diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js index e21b50ef83..6cd5beaaf1 100755 --- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js @@ -1993,3 +1993,7 @@ module.controller('AuthenticationConfigCreateCtrl', function($scope, realm, flow + + + + diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js index 7706f0fda8..bcd98b1486 100755 --- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js @@ -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 + } + }); +}); + + diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js index ec0d475bf7..b9e4705c7e 100755 --- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js @@ -1442,6 +1442,78 @@ module.service('SelectRoleDialog', function($modal) { 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" + }); +}); + + + diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/create-group.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/create-group.html new file mode 100755 index 0000000000..6e4788016c --- /dev/null +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/create-group.html @@ -0,0 +1,25 @@ +
+
+

Create Group

+
+
+
+
+ +
+ +
+
+
+ +
+
+ + +
+
+
+
+ + \ No newline at end of file diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/group-attributes.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/group-attributes.html new file mode 100755 index 0000000000..9fcfc8b741 --- /dev/null +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/group-attributes.html @@ -0,0 +1,45 @@ +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + +
KeyValueActions
{{key}} + +
+ +
+ +
+
+ + +
+
+
+
+ + diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/group-detail.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/group-detail.html new file mode 100755 index 0000000000..3dc41aa59e --- /dev/null +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/group-detail.html @@ -0,0 +1,28 @@ +
+ + + +
+
+
+ +
+ +
+
+
+ +
+
+ + +
+
+
+
+ + \ No newline at end of file diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/group-list.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/group-list.html new file mode 100755 index 0000000000..14acbaecba --- /dev/null +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/group-list.html @@ -0,0 +1,40 @@ +
+

+ User Groups + User groups +

+ + + + + + + + + + + + +
+
+
+ + + + + +
+
+
+
+
+ +
+ + \ No newline at end of file diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/group-role-mappings.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/group-role-mappings.html new file mode 100755 index 0000000000..22916e1217 --- /dev/null +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/group-role-mappings.html @@ -0,0 +1,101 @@ +
+ + + + +
+
+ + +
+
+
+ + + + Realm roles that can be assigned to the group. +
+
+ + Realm roles mapped to the group + + +
+
+ + All realm role mappings. Some roles here might be inherited from a mapped composite role. + +
+
+
+
+ +
+ +
+
+
Select client to view roles for client
+
+
+
+ + Assignable roles from this client. + + +
+
+ + Role mappings for this client. + + +
+
+ + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html index baa3b45d5e..8da0e56148 100755 --- a/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html @@ -40,7 +40,8 @@