From e2222402826fc6b610851465b4de352160675542 Mon Sep 17 00:00:00 2001 From: Bill Burke Date: Fri, 31 Jan 2014 21:39:54 -0500 Subject: [PATCH] composite UI --- .../META-INF/resources/admin/js/app.js | 24 + .../admin/js/controllers/applications.js | 45 +- .../resources/admin/js/controllers/realm.js | 56 +- .../META-INF/resources/admin/js/services.js | 1096 ++++++++++------- .../partials/application-role-detail.html | 70 ++ .../resources/admin/partials/role-detail.html | 70 ++ .../idm/RoleRepresentation.java | 12 + .../resources/admin/ApplicationResource.java | 2 +- .../resources/admin/ApplicationsResource.java | 2 +- .../resources/admin/RealmAdminResource.java | 10 +- .../resources/admin/RoleByIdResource.java | 119 ++ .../admin/RoleContainerResource.java | 197 +-- .../resources/admin/RoleResource.java | 101 ++ 13 files changed, 1187 insertions(+), 617 deletions(-) create mode 100755 services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java create mode 100755 services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js index 401be0c6bc..aa32945a6f 100755 --- a/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js +++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js @@ -187,6 +187,12 @@ module.config([ '$routeProvider', function($routeProvider) { }, role : function() { return {}; + }, + roles : function(RoleListLoader) { + return RoleListLoader(); + }, + applications : function(ApplicationListLoader) { + return ApplicationListLoader(); } }, controller : 'RoleDetailCtrl' @@ -199,6 +205,12 @@ module.config([ '$routeProvider', function($routeProvider) { }, role : function(RoleLoader) { return RoleLoader(); + }, + roles : function(RoleListLoader) { + return RoleListLoader(); + }, + applications : function(ApplicationListLoader) { + return ApplicationListLoader(); } }, controller : 'RoleDetailCtrl' @@ -227,6 +239,12 @@ module.config([ '$routeProvider', function($routeProvider) { }, role : function() { return {}; + }, + roles : function(RoleListLoader) { + return RoleListLoader(); + }, + applications : function(ApplicationListLoader) { + return ApplicationListLoader(); } }, controller : 'ApplicationRoleDetailCtrl' @@ -242,6 +260,12 @@ module.config([ '$routeProvider', function($routeProvider) { }, role : function(ApplicationRoleLoader) { return ApplicationRoleLoader(); + }, + roles : function(RoleListLoader) { + return RoleListLoader(); + }, + applications : function(ApplicationListLoader) { + return ApplicationListLoader(); } }, controller : 'ApplicationRoleDetailCtrl' diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/applications.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/applications.js index df955ad63d..20d596261d 100755 --- a/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/applications.js +++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/applications.js @@ -99,7 +99,9 @@ module.controller('ApplicationSessionsCtrl', function($scope, $location, realm, $scope.application = application; }); -module.controller('ApplicationRoleDetailCtrl', function($scope, realm, application, role, ApplicationRole, $location, Dialog, Notifications) { +module.controller('ApplicationRoleDetailCtrl', function($scope, realm, application, role, roles, applications, + Role, ApplicationRole, RoleById, RoleRealmComposites, RoleApplicationComposites, + $http, $location, Dialog, Notifications) { $scope.realm = realm; $scope.application = application; $scope.role = angular.copy(role); @@ -107,18 +109,6 @@ module.controller('ApplicationRoleDetailCtrl', function($scope, realm, applicati $scope.changed = $scope.create; - $scope.$watch(function() { - return $location.path(); - }, function() { - $scope.path = $location.path().substring(1).split("/"); - }); - - $scope.$watch('role', function() { - if (!angular.equals($scope.role, role)) { - $scope.changed = true; - } - }, true); - $scope.save = function() { if ($scope.create) { ApplicationRole.save({ @@ -134,27 +124,10 @@ module.controller('ApplicationRoleDetailCtrl', function($scope, realm, applicati Notifications.success("The role has been created."); }); } else { - ApplicationRole.update({ - realm : realm.realm, - application : application.name, - role : role.name - }, $scope.role, function() { - $scope.changed = false; - role = angular.copy($scope.role); - Notifications.success("Your changes have been saved to the role."); - }); + $scope.update(); } }; - $scope.reset = function() { - $scope.role = angular.copy(role); - $scope.changed = false; - }; - - $scope.cancel = function() { - $location.url("/realms/" + realm.realm + "/applications/" + application.name + "/roles"); - }; - $scope.remove = function() { Dialog.confirmDelete($scope.role.name, 'role', function() { $scope.role.$remove({ @@ -167,6 +140,16 @@ module.controller('ApplicationRoleDetailCtrl', function($scope, realm, applicati }); }); }; + + $scope.cancel = function () { + $location.url("/realms/" + realm.realm + "/applications/" + application.name + "/roles"); + }; + + + roleControl($scope, realm, role, roles, applications, + ApplicationRole, RoleById, RoleRealmComposites, RoleApplicationComposites, + $http, $location, Notifications, Dialog); + }); module.controller('ApplicationListCtrl', function($scope, realm, applications, Application, $location) { diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js index 3371766ee7..3ff868230a 100755 --- a/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js +++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js @@ -675,26 +675,18 @@ module.controller('RoleListCtrl', function($scope, $location, realm, roles) { }); }); -module.controller('RoleDetailCtrl', function($scope, realm, role, Role, $location, Dialog, Notifications) { + +module.controller('RoleDetailCtrl', function($scope, realm, role, roles, applications, + Role, ApplicationRole, RoleById, RoleRealmComposites, RoleApplicationComposites, + $http, $location, Dialog, Notifications) { $scope.realm = realm; $scope.role = angular.copy(role); $scope.create = !role.name; $scope.changed = $scope.create; - $scope.$watch(function() { - return $location.path(); - }, function() { - $scope.path = $location.path().substring(1).split("/"); - }); - - $scope.$watch('role', function() { - if (!angular.equals($scope.role, role)) { - $scope.changed = true; - } - }, true); - $scope.save = function() { + console.log('save'); if ($scope.create) { Role.save({ realm: realm.realm @@ -708,37 +700,31 @@ module.controller('RoleDetailCtrl', function($scope, realm, role, Role, $locatio Notifications.success("The role has been created."); }); } else { - Role.update({ - realm : realm.realm, - role : role.name - }, $scope.role, function() { - $scope.changed = false; - role = angular.copy($scope.role); - Notifications.success("Your changes have been saved to the role."); - }); + $scope.update(); } }; - $scope.reset = function() { - $scope.role = angular.copy(role); - $scope.changed = false; - }; - - $scope.cancel = function() { - $location.url("/realms/" + realm.realm + "/roles"); - }; - - $scope.remove = function() { - Dialog.confirmDelete($scope.role.name, 'role', function() { + $scope.remove = function () { + Dialog.confirmDelete($scope.role.name, 'role', function () { $scope.role.$remove({ - realm : realm.realm, - role : $scope.role.name - }, function() { + realm: realm.realm, + role: $scope.role.name + }, function () { $location.url("/realms/" + realm.realm + "/roles"); Notifications.success("The role has been deleted."); }); }); }; + + $scope.cancel = function () { + $location.url("/realms/" + realm.realm + "/roles"); + }; + + + + roleControl($scope, realm, role, roles, applications, + ApplicationRole, RoleById, RoleRealmComposites, RoleApplicationComposites, + $http, $location, Notifications, Dialog); }); module.controller('RealmSMTPSettingsCtrl', function($scope, Current, Realm, realm, $http, $location, Dialog, Notifications) { diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/services.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/services.js index 193a8d33d0..33e47a9545 100755 --- a/admin-ui/src/main/resources/META-INF/resources/admin/js/services.js +++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/services.js @@ -1,453 +1,645 @@ -'use strict'; - -var module = angular.module('keycloak.services', [ 'ngResource' ]); - -module.service('Dialog', function($dialog) { - var dialog = {}; - - var escapeHtml = function(str) { - var div = document.createElement('div'); - div.appendChild(document.createTextNode(str)); - return div.innerHTML; - }; - - dialog.confirmDelete = function(name, type, success) { - var title = 'Delete ' + escapeHtml(type.charAt(0).toUpperCase() + type.slice(1)); - var msg = 'Are you sure you want to permanently delete the ' + escapeHtml(type) + ' "' + escapeHtml(name) + '"?' + - 'This action can\'t be undone.'; - var btns = [ { - result : 'cancel', - label : 'Cancel' - }, { - result : 'ok', - label : 'Delete', - cssClass : 'destructive' - } ]; - - $dialog.messageBox(title, msg, btns).open().then(function(result) { - if (result == "ok") { - success(); - } - }); - } - - dialog.confirmGenerateKeys = function(name, type, success) { - var title = 'Generate new keys for realm'; - var msg = 'Are you sure you want to permanently generate new keys for ' + name + '"?' + - 'This action can\'t be undone.'; - var btns = [ { - result : 'cancel', - label : 'Cancel' - }, { - result : 'ok', - label : 'Generate new keys', - cssClass : 'destructive' - } ]; - - $dialog.messageBox(title, msg, btns).open().then(function(result) { - if (result == "ok") { - success(); - } - }); - } - - return dialog -}); - -module.factory('Notifications', function($rootScope, $timeout) { - // time (in ms) the notifications are shown - var delay = 5000; - - var notifications = {}; - - var scheduled = null; - var schedulePop = function() { - if (scheduled) { - $timeout.cancel(scheduled); - } - - scheduled = $timeout(function() { - $rootScope.notification = null; - scheduled = null; - }, delay); - }; - - if (!$rootScope.notifications) { - $rootScope.notifications = []; - } - - notifications.message = function(type, header, message) { - $rootScope.notification = { - type : type, - header: header, - message : message - }; - - schedulePop(); - } - - notifications.info = function(message) { - notifications.message("info", "Info!", message); - }; - - notifications.success = function(message) { - notifications.message("success", "Success!", message); - }; - - notifications.error = function(message) { - notifications.message("error", "Error!", message); - }; - - notifications.warn = function(message) { - notifications.message("warn", "Warning!", message); - }; - - return notifications; -}); - -module.factory('Realm', function($resource) { - return $resource('/auth/rest/admin/realms/:id', { - id : '@realm' - }, { - update : { - method : 'PUT' - }, - create : { - method : 'POST', - params : { id : ''} - } - - }); -}); - -module.factory('User', function($resource) { - return $resource('/auth/rest/admin/realms/:realm/users/:userId', { - realm : '@realm', - userId : '@userId' - }, { - update : { - method : 'PUT' - } - }); -}); - -module.factory('UserCredentials', function($resource) { - return $resource('/auth/rest/admin/realms/:realm/users/:userId/credentials', { - realm : '@realm', - userId : '@userId' - }, { - update : { - method : 'PUT', - isArray : true - } - }); -}); - -module.factory('RealmRoleMapping', function($resource) { - return $resource('/auth/rest/admin/realms/:realm/users/:userId/role-mappings/realm', { - realm : '@realm', - userId : '@userId' - }); -}); - -module.factory('ApplicationRoleMapping', function($resource) { - return $resource('/auth/rest/admin/realms/:realm/users/:userId/role-mappings/applications/:application', { - realm : '@realm', - userId : '@userId', - application : "@application" - }); -}); - -module.factory('ApplicationRealmScopeMapping', function($resource) { - return $resource('/auth/rest/admin/realms/:realm/applications/:application/scope-mappings/realm', { - realm : '@realm', - application : '@application' - }); -}); - -module.factory('ApplicationApplicationScopeMapping', function($resource) { - return $resource('/auth/rest/admin/realms/:realm/applications/:application/scope-mappings/applications/:targetApp', { - realm : '@realm', - application : '@application', - targetApp : '@targetApp' - }); -}); - - - -module.factory('RealmRoles', function($resource) { - return $resource('/auth/rest/admin/realms/:realm/roles', { - realm : '@realm' - }); -}); - - - -module.factory('Role', function($resource) { - return $resource('/auth/rest/admin/realms/:realm/roles/:role', { - realm : '@realm', - role : '@role' - }, { - update : { - method : 'PUT' - } - }); -}); - -module.factory('ApplicationRole', function($resource) { - return $resource('/auth/rest/admin/realms/:realm/applications/:application/roles/:role', { - realm : '@realm', - application : "@application", - role : '@role' - }, { - update : { - method : 'PUT' - } - }); -}); - - -module.factory('Application', function($resource) { - return $resource('/auth/rest/admin/realms/:realm/applications/:application', { - realm : '@realm', - application : '@name' - }, { - update : { - method : 'PUT' - } - }); -}); - -module.factory('ApplicationInstallation', function($resource) { - var url = '/auth/rest/admin/realms/:realm/applications/:application/installation'; - var resource = $resource('/auth/rest/admin/realms/:realm/applications/:application/installation', { - realm : '@realm', - application : '@application' - }, { - update : { - method : 'PUT' - } - }); - resource.url = function(parameters) { - return url.replace(':realm', parameters.realm).replace(':application', parameters.application); - } - return resource; -}); - -module.factory('ApplicationCredentials', function($resource) { - return $resource('/auth/rest/admin/realms/:realm/applications/:application/credentials', { - realm : '@realm', - application : '@application' - }, { - update : { - method : 'PUT', - isArray : true - } - }); -}); - -module.factory('ApplicationOrigins', function($resource) { - return $resource('/auth/rest/admin/realms/:realm/applications/:application/allowed-origins', { - realm : '@realm', - application : '@application' - }, { - update : { - method : 'PUT', - isArray : true - } - }); -}); - -module.factory('OAuthClient', function($resource) { - return $resource('/auth/rest/admin/realms/:realm/oauth-clients/:id', { - realm : '@realm', - id : '@id' - }, { - update : { - method : 'PUT' - } - }); -}); - -module.factory('OAuthClientCredentials', function($resource) { - return $resource('/auth/rest/admin/realms/:realm/oauth-clients/:oauth/credentials', { - realm : '@realm', - oauth : '@oauth' - }, { - update : { - method : 'PUT', - isArray : true - } - }); -}); - -module.factory('OAuthClientRealmScopeMapping', function($resource) { - return $resource('/auth/rest/admin/realms/:realm/oauth-clients/:oauth/scope-mappings/realm', { - realm : '@realm', - oauth : '@oauth' - }); -}); - -module.factory('OAuthClientApplicationScopeMapping', function($resource) { - return $resource('/auth/rest/admin/realms/:realm/oauth-clients/:oauth/scope-mappings/applications/:targetApp', { - realm : '@realm', - oauth : '@oauth', - targetApp : '@targetApp' - }); -}); - -module.factory('OAuthClientInstallation', function($resource) { - var url = '/auth/rest/admin/realms/:realm/oauth-clients/:oauth/installation'; - var resource = $resource('/auth/rest/admin/realms/:realm/oauth-clients/:oauth/installation', { - realm : '@realm', - oauth : '@oauth' - }, { - update : { - method : 'PUT' - } - }); - resource.url = function(parameters) { - return url.replace(':realm', parameters.realm).replace(':oauth', parameters.oauth); - } - return resource; -}); - - -module.factory('Current', function(Realm, $route) { - var current = {}; - - current.realms = {}; - current.realm = null; - current.applications = {}; - current.application = null; - - current.refresh = function() { - current.realm = null; - current.realms = Realm.query(null, function(realms) { - if ($route.current.params.realm) { - for (var i = 0; i < realms.length; i++) { - if (realms[i].realm == $route.current.params.realm) { - current.realm = realms[i]; - } - } - } - }); - } - - current.refresh(); - - return current; -}); - -module.factory('TimeUnit', function() { - var t = {}; - - t.autoUnit = function(time) { - var unit = 'Seconds'; - if (time % 60 == 0) { - unit = 'Minutes'; - time = time / 60; - } - if (time % 60 == 0) { - unit = 'Hours'; - time = time / 60; - } - if (time % 24 == 0) { - unit = 'Days' - time = time / 24; - } - return unit; - } - - t.toSeconds = function(time, unit) { - switch (unit) { - case 'Seconds': return time; - case 'Minutes': return time * 60; - case 'Hours': return time * 360; - case 'Days': return time * 86400; - default: throw 'invalid unit ' + unit; - } - } - - t.toUnit = function(time, unit) { - switch (unit) { - case 'Seconds': return time; - case 'Minutes': return Math.ceil(time / 60); - case 'Hours': return Math.ceil(time / 360); - case 'Days': return Math.ceil(time / 86400); - default: throw 'invalid unit ' + unit; - } - } - - t.convert = function(time, from, to) { - var seconds = t.toSeconds(time, from); - return t.toUnit(seconds, to); - } - - return t; -}); - - -module.factory('PasswordPolicy', function() { - var p = {}; - - p.policyMessages = { - length: "Minimal password length (integer type). Default value is 8.", - digits: "Minimal number (integer type) of digits in password. Default value is 1.", - lowerCase: "Minimal number (integer type) of lowercase characters in password. Default value is 1.", - upperCase: "Minimal number (integer type) of uppercase characters in password. Default value is 1.", - specialChars: "Minimal number (integer type) of special characters in password. Default value is 1." - } - - p.allPolicies = [ - { name: 'length', value: 8 }, - { name: 'digits', value: 1 }, - { name: 'lowerCase', value: 1 }, - { name: 'upperCase', value: 1 }, - { name: 'specialChars', value: 1 } - ]; - - p.parse = function(policyString) { - var policies = []; - - if (!policyString || policyString.length == 0){ - return policies; - } - - var policyArray = policyString.split(" and "); - - for (var i = 0; i < policyArray.length; i ++){ - var policyToken = policyArray[i]; - var re = /(\w+)\(*(\d*)\)*/; - - var policyEntry = re.exec(policyToken); - - policies.push({ name: policyEntry[1], value: parseInt(policyEntry[2]) }); - - } - - return policies; - }; - - p.toString = function(policies) { - if (!policies || policies.length == 0) { - return null; - } - - var policyString = ""; - - for (var i in policies){ - policyString += policies[i].name; - if ( policies[i].value ){ - policyString += '(' + policies[i].value + ')'; - } - policyString += " and "; - } - - policyString = policyString.substring(0, policyString.length - 5); - - return policyString; - }; - - return p; +'use strict'; + +var module = angular.module('keycloak.services', [ 'ngResource' ]); + +module.service('Dialog', function($dialog) { + var dialog = {}; + + var escapeHtml = function(str) { + var div = document.createElement('div'); + div.appendChild(document.createTextNode(str)); + return div.innerHTML; + }; + + dialog.confirmDelete = function(name, type, success) { + var title = 'Delete ' + escapeHtml(type.charAt(0).toUpperCase() + type.slice(1)); + var msg = 'Are you sure you want to permanently delete the ' + escapeHtml(type) + ' "' + escapeHtml(name) + '"?' + + 'This action can\'t be undone.'; + var btns = [ { + result : 'cancel', + label : 'Cancel' + }, { + result : 'ok', + label : 'Delete', + cssClass : 'destructive' + } ]; + + $dialog.messageBox(title, msg, btns).open().then(function(result) { + if (result == "ok") { + success(); + } + }); + } + + dialog.confirmGenerateKeys = function(name, type, success) { + var title = 'Generate new keys for realm'; + var msg = 'Are you sure you want to permanently generate new keys for ' + name + '"?' + + 'This action can\'t be undone.'; + var btns = [ { + result : 'cancel', + label : 'Cancel' + }, { + result : 'ok', + label : 'Generate new keys', + cssClass : 'destructive' + } ]; + + $dialog.messageBox(title, msg, btns).open().then(function(result) { + if (result == "ok") { + success(); + } + }); + } + + return dialog +}); + +module.factory('Notifications', function($rootScope, $timeout) { + // time (in ms) the notifications are shown + var delay = 5000; + + var notifications = {}; + + var scheduled = null; + var schedulePop = function() { + if (scheduled) { + $timeout.cancel(scheduled); + } + + scheduled = $timeout(function() { + $rootScope.notification = null; + scheduled = null; + }, delay); + }; + + if (!$rootScope.notifications) { + $rootScope.notifications = []; + } + + notifications.message = function(type, header, message) { + $rootScope.notification = { + type : type, + header: header, + message : message + }; + + schedulePop(); + } + + notifications.info = function(message) { + notifications.message("info", "Info!", message); + }; + + notifications.success = function(message) { + notifications.message("success", "Success!", message); + }; + + notifications.error = function(message) { + notifications.message("error", "Error!", message); + }; + + notifications.warn = function(message) { + notifications.message("warn", "Warning!", message); + }; + + return notifications; +}); + +module.factory('Realm', function($resource) { + return $resource('/auth/rest/admin/realms/:id', { + id : '@realm' + }, { + update : { + method : 'PUT' + }, + create : { + method : 'POST', + params : { id : ''} + } + + }); +}); + +module.factory('User', function($resource) { + return $resource('/auth/rest/admin/realms/:realm/users/:userId', { + realm : '@realm', + userId : '@userId' + }, { + update : { + method : 'PUT' + } + }); +}); + +module.factory('UserCredentials', function($resource) { + return $resource('/auth/rest/admin/realms/:realm/users/:userId/credentials', { + realm : '@realm', + userId : '@userId' + }, { + update : { + method : 'PUT', + isArray : true + } + }); +}); + +module.factory('RealmRoleMapping', function($resource) { + return $resource('/auth/rest/admin/realms/:realm/users/:userId/role-mappings/realm', { + realm : '@realm', + userId : '@userId' + }); +}); + +module.factory('ApplicationRoleMapping', function($resource) { + return $resource('/auth/rest/admin/realms/:realm/users/:userId/role-mappings/applications/:application', { + realm : '@realm', + userId : '@userId', + application : "@application" + }); +}); + +module.factory('ApplicationRealmScopeMapping', function($resource) { + return $resource('/auth/rest/admin/realms/:realm/applications/:application/scope-mappings/realm', { + realm : '@realm', + application : '@application' + }); +}); + +module.factory('ApplicationApplicationScopeMapping', function($resource) { + return $resource('/auth/rest/admin/realms/:realm/applications/:application/scope-mappings/applications/:targetApp', { + realm : '@realm', + application : '@application', + targetApp : '@targetApp' + }); +}); + + + +module.factory('RealmRoles', function($resource) { + return $resource('/auth/rest/admin/realms/:realm/roles', { + realm : '@realm' + }); +}); + +module.factory('RoleRealmComposites', function($resource) { + return $resource('/auth/rest/admin/realms/:realm/roles-by-id/:role/composites/realm', { + realm : '@realm', + role : '@role' + }); +}); + +module.factory('RoleApplicationComposites', function($resource) { + return $resource('/auth/rest/admin/realms/:realm/roles-by-id/:role/composites/applications/:application', { + realm : '@realm', + role : '@role', + application : "@application" + }); +}); + + +function roleControl($scope, realm, role, roles, applications, + ApplicationRole, RoleById, RoleRealmComposites, RoleApplicationComposites, + $http, $location, Notifications, Dialog) { + + $scope.$watch(function () { + return $location.path(); + }, function () { + $scope.path = $location.path().substring(1).split("/"); + }); + + $scope.$watch('role', function () { + if (!angular.equals($scope.role, role)) { + $scope.changed = true; + } + }, true); + + $scope.update = function () { + RoleById.update({ + realm: realm.realm, + role: role.id + }, $scope.role, function () { + $scope.changed = false; + role = angular.copy($scope.role); + Notifications.success("Your changes have been saved to the role."); + }); + }; + + $scope.reset = function () { + $scope.role = angular.copy(role); + $scope.changed = false; + }; + + if (!role.id) return; + + $scope.realmRoles = angular.copy(roles); + $scope.selectedRealmRoles = []; + $scope.selectedRealmMappings = []; + $scope.realmMappings = []; + $scope.applications = applications; + $scope.applicationRoles = []; + $scope.selectedApplicationRoles = []; + $scope.selectedApplicationMappings = []; + $scope.applicationMappings = []; + + console.log('remove self'); + for (var j = 0; j < $scope.realmRoles.length; j++) { + if ($scope.realmRoles[j].id == role.id) { + var realmRole = $scope.realmRoles[j]; + var idx = $scope.realmRoles.indexOf(realmRole); + $scope.realmRoles.splice(idx, 1); + break; + } + } + + + $scope.realmMappings = RoleRealmComposites.query({realm : realm.realm, role : role.id}, function(){ + for (var i = 0; i < $scope.realmMappings.length; i++) { + var role = $scope.realmMappings[i]; + for (var j = 0; j < $scope.realmRoles.length; j++) { + var realmRole = $scope.realmRoles[j]; + if (realmRole.id == role.id) { + var idx = $scope.realmRoles.indexOf(realmRole); + if (idx != -1) { + $scope.realmRoles.splice(idx, 1); + break; + } + } + } + } + }); + + $scope.addRealmRole = function() { + $http.post('/auth/rest/admin/realms/' + realm.realm + '/roles-by-id/' + role.id + '/composites', + $scope.selectedRealmRoles).success(function() { + for (var i = 0; i < $scope.selectedRealmRoles.length; i++) { + var role = $scope.selectedRealmRoles[i]; + var idx = $scope.realmRoles.indexOf($scope.selectedRealmRoles[i]); + if (idx != -1) { + $scope.realmRoles.splice(idx, 1); + $scope.realmMappings.push(role); + } + } + $scope.selectRealmRoles = []; + }); + }; + + $scope.deleteRealmRole = function() { + $http.delete('/auth/rest/admin/realms/' + realm.realm + '/roles-by-id/' + role.id + '/composites', + {data : $scope.selectedRealmMappings, headers : {"content-type" : "application/json"}}).success(function() { + for (var i = 0; i < $scope.selectedRealmMappings.length; i++) { + var role = $scope.selectedRealmMappings[i]; + var idx = $scope.realmMappings.indexOf($scope.selectedRealmMappings[i]); + if (idx != -1) { + $scope.realmMappings.splice(idx, 1); + $scope.realmRoles.push(role); + } + } + $scope.selectedRealmMappings = []; + }); + }; + + $scope.addApplicationRole = function() { + $http.post('/auth/rest/admin/realms/' + realm.realm + '/roles-by-id/' + role.id + '/composites', + $scope.selectedApplicationRoles).success(function() { + for (var i = 0; i < $scope.selectedApplicationRoles.length; i++) { + var role = $scope.selectedApplicationRoles[i]; + var idx = $scope.applicationRoles.indexOf($scope.selectedApplicationRoles[i]); + if (idx != -1) { + $scope.applicationRoles.splice(idx, 1); + $scope.applicationMappings.push(role); + } + } + $scope.selectedApplicationRoles = []; + }); + }; + + $scope.deleteApplicationRole = function() { + $http.delete('/auth/rest/admin/realms/' + realm.realm + '/roles-by-id/' + role.id + '/composites', + {data : $scope.selectedApplicationMappings, headers : {"content-type" : "application/json"}}).success(function() { + for (var i = 0; i < $scope.selectedApplicationMappings.length; i++) { + var role = $scope.selectedApplicationMappings[i]; + var idx = $scope.applicationMappings.indexOf($scope.selectedApplicationMappings[i]); + if (idx != -1) { + $scope.applicationMappings.splice(idx, 1); + $scope.applicationRoles.push(role); + } + } + $scope.selectedApplicationMappings = []; + }); + }; + + + $scope.changeApplication = function() { + $scope.applicationRoles = ApplicationRole.query({realm : realm.realm, application : $scope.compositeApp.name}, function() { + $scope.applicationMappings = RoleApplicationComposites.query({realm : realm.realm, role : role.id, application : $scope.compositeApp.name}, function(){ + for (var i = 0; i < $scope.applicationMappings.length; i++) { + var role = $scope.applicationMappings[i]; + for (var j = 0; j < $scope.applicationRoles.length; j++) { + var realmRole = $scope.applicationRoles[j]; + if (realmRole.id == role.id) { + var idx = $scope.applicationRoles.indexOf(realmRole); + if (idx != -1) { + $scope.applicationRoles.splice(idx, 1); + break; + } + } + } + } + }); + for (var j = 0; j < $scope.applicationRoles.length; j++) { + if ($scope.applicationRoles[j] == role.id) { + var appRole = $scope.applicationRoles[j]; + var idx = $scope.applicationRoles.indexof(appRole); + $scope.applicationRoles.splice(idx, 1); + break; + } + } + } + ); + }; + + + + +} + + +module.factory('Role', function($resource) { + return $resource('/auth/rest/admin/realms/:realm/roles/:role', { + realm : '@realm', + role : '@role' + }, { + update : { + method : 'PUT' + } + }); +}); + +module.factory('RoleById', function($resource) { + return $resource('/auth/rest/admin/realms/:realm/roles-by-id/:role', { + realm : '@realm', + role : '@role' + }, { + update : { + method : 'PUT' + } + }); +}); + +module.factory('ApplicationRole', function($resource) { + return $resource('/auth/rest/admin/realms/:realm/applications/:application/roles/:role', { + realm : '@realm', + application : "@application", + role : '@role' + }, { + update : { + method : 'PUT' + } + }); +}); + + +module.factory('Application', function($resource) { + return $resource('/auth/rest/admin/realms/:realm/applications/:application', { + realm : '@realm', + application : '@name' + }, { + update : { + method : 'PUT' + } + }); +}); + +module.factory('ApplicationInstallation', function($resource) { + var url = '/auth/rest/admin/realms/:realm/applications/:application/installation'; + var resource = $resource('/auth/rest/admin/realms/:realm/applications/:application/installation', { + realm : '@realm', + application : '@application' + }, { + update : { + method : 'PUT' + } + }); + resource.url = function(parameters) { + return url.replace(':realm', parameters.realm).replace(':application', parameters.application); + } + return resource; +}); + +module.factory('ApplicationCredentials', function($resource) { + return $resource('/auth/rest/admin/realms/:realm/applications/:application/credentials', { + realm : '@realm', + application : '@application' + }, { + update : { + method : 'PUT', + isArray : true + } + }); +}); + +module.factory('ApplicationOrigins', function($resource) { + return $resource('/auth/rest/admin/realms/:realm/applications/:application/allowed-origins', { + realm : '@realm', + application : '@application' + }, { + update : { + method : 'PUT', + isArray : true + } + }); +}); + +module.factory('OAuthClient', function($resource) { + return $resource('/auth/rest/admin/realms/:realm/oauth-clients/:id', { + realm : '@realm', + id : '@id' + }, { + update : { + method : 'PUT' + } + }); +}); + +module.factory('OAuthClientCredentials', function($resource) { + return $resource('/auth/rest/admin/realms/:realm/oauth-clients/:oauth/credentials', { + realm : '@realm', + oauth : '@oauth' + }, { + update : { + method : 'PUT', + isArray : true + } + }); +}); + +module.factory('OAuthClientRealmScopeMapping', function($resource) { + return $resource('/auth/rest/admin/realms/:realm/oauth-clients/:oauth/scope-mappings/realm', { + realm : '@realm', + oauth : '@oauth' + }); +}); + +module.factory('OAuthClientApplicationScopeMapping', function($resource) { + return $resource('/auth/rest/admin/realms/:realm/oauth-clients/:oauth/scope-mappings/applications/:targetApp', { + realm : '@realm', + oauth : '@oauth', + targetApp : '@targetApp' + }); +}); + +module.factory('OAuthClientInstallation', function($resource) { + var url = '/auth/rest/admin/realms/:realm/oauth-clients/:oauth/installation'; + var resource = $resource('/auth/rest/admin/realms/:realm/oauth-clients/:oauth/installation', { + realm : '@realm', + oauth : '@oauth' + }, { + update : { + method : 'PUT' + } + }); + resource.url = function(parameters) { + return url.replace(':realm', parameters.realm).replace(':oauth', parameters.oauth); + } + return resource; +}); + + +module.factory('Current', function(Realm, $route) { + var current = {}; + + current.realms = {}; + current.realm = null; + current.applications = {}; + current.application = null; + + current.refresh = function() { + current.realm = null; + current.realms = Realm.query(null, function(realms) { + if ($route.current.params.realm) { + for (var i = 0; i < realms.length; i++) { + if (realms[i].realm == $route.current.params.realm) { + current.realm = realms[i]; + } + } + } + }); + } + + current.refresh(); + + return current; +}); + +module.factory('TimeUnit', function() { + var t = {}; + + t.autoUnit = function(time) { + var unit = 'Seconds'; + if (time % 60 == 0) { + unit = 'Minutes'; + time = time / 60; + } + if (time % 60 == 0) { + unit = 'Hours'; + time = time / 60; + } + if (time % 24 == 0) { + unit = 'Days' + time = time / 24; + } + return unit; + } + + t.toSeconds = function(time, unit) { + switch (unit) { + case 'Seconds': return time; + case 'Minutes': return time * 60; + case 'Hours': return time * 360; + case 'Days': return time * 86400; + default: throw 'invalid unit ' + unit; + } + } + + t.toUnit = function(time, unit) { + switch (unit) { + case 'Seconds': return time; + case 'Minutes': return Math.ceil(time / 60); + case 'Hours': return Math.ceil(time / 360); + case 'Days': return Math.ceil(time / 86400); + default: throw 'invalid unit ' + unit; + } + } + + t.convert = function(time, from, to) { + var seconds = t.toSeconds(time, from); + return t.toUnit(seconds, to); + } + + return t; +}); + + +module.factory('PasswordPolicy', function() { + var p = {}; + + p.policyMessages = { + length: "Minimal password length (integer type). Default value is 8.", + digits: "Minimal number (integer type) of digits in password. Default value is 1.", + lowerCase: "Minimal number (integer type) of lowercase characters in password. Default value is 1.", + upperCase: "Minimal number (integer type) of uppercase characters in password. Default value is 1.", + specialChars: "Minimal number (integer type) of special characters in password. Default value is 1." + } + + p.allPolicies = [ + { name: 'length', value: 8 }, + { name: 'digits', value: 1 }, + { name: 'lowerCase', value: 1 }, + { name: 'upperCase', value: 1 }, + { name: 'specialChars', value: 1 } + ]; + + p.parse = function(policyString) { + var policies = []; + + if (!policyString || policyString.length == 0){ + return policies; + } + + var policyArray = policyString.split(" and "); + + for (var i = 0; i < policyArray.length; i ++){ + var policyToken = policyArray[i]; + var re = /(\w+)\(*(\d*)\)*/; + + var policyEntry = re.exec(policyToken); + + policies.push({ name: policyEntry[1], value: parseInt(policyEntry[2]) }); + + } + + return policies; + }; + + p.toString = function(policies) { + if (!policies || policies.length == 0) { + return null; + } + + var policyString = ""; + + for (var i in policies){ + policyString += policies[i].name; + if ( policies[i].value ){ + policyString += '(' + policies[i].value + ')'; + } + policyString += " and "; + } + + policyString = policyString.substring(0, policyString.length - 5); + + return policyString; + }; + + return p; }); \ No newline at end of file diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-role-detail.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-role-detail.html index 84a39e3a14..4c6b5b25fc 100755 --- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-role-detail.html +++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-role-detail.html @@ -52,6 +52,10 @@ required> --> +
+ + +
+
+ Composite Realm Roles +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ +
+ Composite Application Roles +
+ +
+
+ +
+
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/role-detail.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/role-detail.html index 50cf51243d..b835fcb7f8 100755 --- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/role-detail.html +++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/role-detail.html @@ -47,6 +47,10 @@ +
+ + +
+
+ Composite Realm Roles +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ +
+ Composite Application Roles +
+ +
+
+ +
+
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ diff --git a/core/src/main/java/org/keycloak/representations/idm/RoleRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/RoleRepresentation.java index 59119a6094..81d2428b92 100755 --- a/core/src/main/java/org/keycloak/representations/idm/RoleRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/RoleRepresentation.java @@ -1,5 +1,8 @@ package org.keycloak.representations.idm; +import java.util.List; +import java.util.Set; + /** * @author Bill Burke * @version $Revision: 1 $ @@ -9,6 +12,7 @@ public class RoleRepresentation { protected String name; protected String description; protected boolean composite; + protected List composites; public RoleRepresentation() { } @@ -49,4 +53,12 @@ public class RoleRepresentation { public void setComposite(boolean composite) { this.composite = composite; } + + public List getComposites() { + return composites; + } + + public void setComposites(List composites) { + this.composites = composites; + } } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java index a64b45dc1a..cfc4c62297 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java @@ -48,7 +48,7 @@ public class ApplicationResource extends RoleContainerResource { } public ApplicationResource(RealmModel realm, ApplicationModel applicationModel, KeycloakSession session) { - super(applicationModel); + super(realm, applicationModel); this.realm = realm; this.application = applicationModel; this.session = session; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationsResource.java index faf90e711b..4f9d5ca7df 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationsResource.java @@ -71,7 +71,7 @@ public class ApplicationsResource { public ApplicationResource getApplication(final @PathParam("app-name") String name) { ApplicationModel applicationModel = realm.getApplicationByName(name); if (applicationModel == null) { - throw new NotFoundException(); + throw new NotFoundException("Could not find application: " + name); } ApplicationResource applicationResource = new ApplicationResource(realm, applicationModel, session); resourceContext.initResource(applicationResource); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java index 09f2fc849b..deb46fb919 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java @@ -29,7 +29,7 @@ public class RealmAdminResource extends RoleContainerResource { protected KeycloakSession session; public RealmAdminResource(UserModel admin, RealmModel realm) { - super(realm); + super(realm, realm); this.admin = admin; this.realm = realm; } @@ -77,6 +77,14 @@ public class RealmAdminResource extends RoleContainerResource { return users; } + @Path("roles-by-id") + public RoleByIdResource rolesById() { + RoleByIdResource resource = new RoleByIdResource(realm); + resourceContext.initResource(resource); + return resource; + + } + } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java new file mode 100755 index 0000000000..98869bfd68 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java @@ -0,0 +1,119 @@ +package org.keycloak.services.resources.admin; + +import org.jboss.resteasy.annotations.cache.NoCache; +import org.keycloak.models.Constants; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleContainerModel; +import org.keycloak.models.RoleModel; +import org.keycloak.representations.idm.RoleRepresentation; +import org.keycloak.services.managers.ModelToRepresentation; +import org.keycloak.services.resources.admin.RoleResource; +import org.keycloak.services.resources.flows.Flows; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.NotFoundException; +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.Response; +import javax.ws.rs.core.UriInfo; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * Sometimes its easier to just interact with roles by their ID instead of container/role-name + * + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class RoleByIdResource extends RoleResource { + public RoleByIdResource(RealmModel realm) { + super(realm); + } + + @Path("{role-id}") + @GET + @NoCache + @Produces("application/json") + public RoleRepresentation getRole(final @PathParam("role-id") String id) { + RoleModel roleModel = getRoleModel(id); + return getRole(roleModel); + } + + protected RoleModel getRoleModel(String id) { + RoleModel roleModel = realm.getRoleById(id); + if (roleModel == null || roleModel.getName().startsWith(Constants.INTERNAL_ROLE)) { + throw new NotFoundException("Could not find role with id: " + id); + } + return roleModel; + } + + @Path("{role-id}") + @DELETE + @NoCache + public void deleteRole(final @PathParam("role-id") String id) { + RoleModel role = getRoleModel(id); + deleteRole(role); + } + + @Path("{role-id}") + @PUT + @Consumes("application/json") + public void updateRole(final @PathParam("role-id") String id, final RoleRepresentation rep) { + RoleModel role = getRoleModel(id); + updateRole(rep, role); + } + + @Path("{role-id}/composites") + @POST + @Consumes("application/json") + public void addComposites(final @PathParam("role-id") String id, List roles) { + RoleModel role = getRoleModel(id); + addComposites(roles, role); + } + + @Path("{role-id}/composites") + @GET + @NoCache + @Produces("application/json") + public Set getRoleComposites(final @PathParam("role-id") String id) { + RoleModel role = getRoleModel(id); + return getRoleComposites(role); + } + + @Path("{role-id}/composites/realm") + @GET + @NoCache + @Produces("application/json") + public Set getRealmRoleComposites(final @PathParam("role-id") String id) { + RoleModel role = getRoleModel(id); + return getRealmRoleComposites(role); + } + + @Path("{role-id}/composites/applications/{app}") + @GET + @NoCache + @Produces("application/json") + public Set getApplicationRoleComposites(final @PathParam("role-id") String id, + final @PathParam("app") String appName) { + RoleModel role = getRoleModel(id); + return getApplicationRoleComposites(appName, role); + } + + + @Path("{role-id}/composites") + @DELETE + @Consumes("application/json") + public void deleteComposites(final @PathParam("role-id") String id, List roles) { + RoleModel role = getRoleModel(id); + deleteComposites(roles, role); + } + + +} diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java index 1971b31c4a..7728d61544 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java @@ -1,7 +1,9 @@ package org.keycloak.services.resources.admin; import org.jboss.resteasy.annotations.cache.NoCache; +import org.keycloak.models.ApplicationModel; import org.keycloak.models.Constants; +import org.keycloak.models.RealmModel; import org.keycloak.models.RoleContainerModel; import org.keycloak.models.RoleModel; import org.keycloak.representations.idm.RoleRepresentation; @@ -22,10 +24,11 @@ import java.util.Set; * @author Bill Burke * @version $Revision: 1 $ */ -public class RoleContainerResource { +public class RoleContainerResource extends RoleResource { protected RoleContainerModel roleContainer; - public RoleContainerResource(RoleContainerModel roleContainer) { + public RoleContainerResource(RealmModel realm, RoleContainerModel roleContainer) { + super(realm); this.roleContainer = roleContainer; } @@ -44,100 +47,6 @@ public class RoleContainerResource { return roles; } - @Path("roles/{role-name}") - @GET - @NoCache - @Produces("application/json") - public RoleRepresentation getRole(final @PathParam("role-name") String roleName) { - RoleModel roleModel = roleContainer.getRole(roleName); - if (roleModel == null || roleModel.getName().startsWith(Constants.INTERNAL_ROLE)) { - throw new NotFoundException("Could not find role: " + roleName); - } - return ModelToRepresentation.toRepresentation(roleModel); - } - - @Path("roles/{role-name}") - @DELETE - @NoCache - public void deleteRole(final @PathParam("role-name") String roleName) { - RoleModel role = roleContainer.getRole(roleName); - if (role == null) { - throw new NotFoundException("Could not find role: " + roleName); - } - if (!roleContainer.removeRoleById(role.getId())) { - throw new NotFoundException(); - } - } - - @Path("roles/{role-name}") - @PUT - @Consumes("application/json") - public void updateRole(final @PathParam("role-name") String roleName, final RoleRepresentation rep) { - RoleModel role = roleContainer.getRole(roleName); - if (role == null || role.getName().startsWith(Constants.INTERNAL_ROLE)) { - throw new NotFoundException("Could not find role: " + roleName); - } - role.setName(rep.getName()); - role.setDescription(rep.getDescription()); - role.setComposite(rep.isComposite()); - } - - @Path("roles/{role-name}/composites") - @POST - @Consumes("application/json") - public void addComposites(final @PathParam("role-name") String roleName, List roles) { - RoleModel role = roleContainer.getRole(roleName); - if (role == null || role.getName().startsWith(Constants.INTERNAL_ROLE)) { - throw new NotFoundException("Could not find role: " + roleName); - } - for (RoleRepresentation rep : roles) { - RoleModel composite = roleContainer.getRole(rep.getName()); - if (role == null || role.getName().startsWith(Constants.INTERNAL_ROLE)) { - throw new NotFoundException("Could not find composite role: " + rep.getName()); - } - if (!role.isComposite()) role.setComposite(true); - role.addCompositeRole(composite); - } - } - - @Path("roles/{role-name}/composites") - @GET - @NoCache - @Produces("application/json") - public Set getRoleComposites(final @PathParam("role-name") String roleName) { - RoleModel role = roleContainer.getRole(roleName); - if (role == null || role.getName().startsWith(Constants.INTERNAL_ROLE)) { - throw new NotFoundException("Could not find role: " + roleName); - } - if (!role.isComposite() || role.getComposites().size() == 0) return Collections.emptySet(); - - Set composites = new HashSet(role.getComposites().size()); - for (RoleModel composite : role.getComposites()) { - composites.add(ModelToRepresentation.toRepresentation(composite)); - } - return composites; - } - - - @Path("roles/{role-name}/composites") - @DELETE - @Consumes("application/json") - public void deleteComposites(final @PathParam("role-name") String roleName, List roles) { - RoleModel role = roleContainer.getRole(roleName); - if (role == null || role.getName().startsWith(Constants.INTERNAL_ROLE)) { - throw new NotFoundException("Could not find role: " + roleName); - } - for (RoleRepresentation rep : roles) { - RoleModel composite = roleContainer.getRole(rep.getName()); - if (role == null || role.getName().startsWith(Constants.INTERNAL_ROLE)) { - throw new NotFoundException("Could not find composite role: " + rep.getName()); - } - role.removeCompositeRole(composite); - } - if (role.getComposites().size() > 0) role.setComposite(false); - } - - @Path("roles") @POST @Consumes("application/json") @@ -153,4 +62,100 @@ public class RoleContainerResource { role.setComposite(rep.isComposite()); return Response.created(uriInfo.getAbsolutePathBuilder().path(role.getName()).build()).build(); } + + @Path("roles/{role-name}") + @GET + @NoCache + @Produces("application/json") + public RoleRepresentation getRole(final @PathParam("role-name") String roleName) { + RoleModel roleModel = roleContainer.getRole(roleName); + if (roleModel == null || roleModel.getName().startsWith(Constants.INTERNAL_ROLE)) { + throw new NotFoundException("Could not find role: " + roleName); + } + return getRole(roleModel); + } + + @Path("roles/{role-name}") + @DELETE + @NoCache + public void deleteRole(final @PathParam("role-name") String roleName) { + RoleModel role = roleContainer.getRole(roleName); + if (role == null) { + throw new NotFoundException("Could not find role: " + roleName); + } + deleteRole(role); + } + + @Path("roles/{role-name}") + @PUT + @Consumes("application/json") + public void updateRole(final @PathParam("role-name") String roleName, final RoleRepresentation rep) { + RoleModel role = roleContainer.getRole(roleName); + if (role == null || role.getName().startsWith(Constants.INTERNAL_ROLE)) { + throw new NotFoundException("Could not find role: " + roleName); + } + updateRole(rep, role); + } + + @Path("roles/{role-name}/composites") + @POST + @Consumes("application/json") + public void addComposites(final @PathParam("role-name") String roleName, List roles) { + RoleModel role = roleContainer.getRole(roleName); + if (role == null || role.getName().startsWith(Constants.INTERNAL_ROLE)) { + throw new NotFoundException("Could not find role: " + roleName); + } + addComposites(roles, role); + } + + @Path("roles/{role-name}/composites") + @GET + @NoCache + @Produces("application/json") + public Set getRoleComposites(final @PathParam("role-name") String roleName) { + RoleModel role = roleContainer.getRole(roleName); + if (role == null || role.getName().startsWith(Constants.INTERNAL_ROLE)) { + throw new NotFoundException("Could not find role: " + roleName); + } + return getRoleComposites(role); + } + + @Path("roles/{role-name}/composites/realm") + @GET + @NoCache + @Produces("application/json") + public Set getRealmRoleComposites(final @PathParam("role-name") String roleName) { + RoleModel role = roleContainer.getRole(roleName); + if (role == null || role.getName().startsWith(Constants.INTERNAL_ROLE)) { + throw new NotFoundException("Could not find role: " + roleName); + } + return getRealmRoleComposites(role); + } + + @Path("roles/{role-name}/composites/application/{app}") + @GET + @NoCache + @Produces("application/json") + public Set getApplicationRoleComposites(final @PathParam("role-name") String roleName, + final @PathParam("app") String appName) { + RoleModel role = roleContainer.getRole(roleName); + if (role == null || role.getName().startsWith(Constants.INTERNAL_ROLE)) { + throw new NotFoundException("Could not find role: " + roleName); + } + return getApplicationRoleComposites(appName, role); + } + + + @Path("roles/{role-name}/composites") + @DELETE + @Consumes("application/json") + public void deleteComposites(final @PathParam("role-name") String roleName, List roles) { + RoleModel role = roleContainer.getRole(roleName); + if (role == null || role.getName().startsWith(Constants.INTERNAL_ROLE)) { + throw new NotFoundException("Could not find role: " + roleName); + } + deleteComposites(roles, role); + } + + } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java new file mode 100755 index 0000000000..8bab186c79 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java @@ -0,0 +1,101 @@ +package org.keycloak.services.resources.admin; + +import org.keycloak.models.ApplicationModel; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleModel; +import org.keycloak.representations.idm.RoleRepresentation; +import org.keycloak.services.managers.ModelToRepresentation; + +import javax.ws.rs.NotFoundException; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class RoleResource { + protected RealmModel realm; + + public RoleResource(RealmModel realm) { + this.realm = realm; + } + + protected RoleRepresentation getRole(RoleModel roleModel) { + return ModelToRepresentation.toRepresentation(roleModel); + } + + protected void deleteRole(RoleModel role) { + if (!role.getContainer().removeRoleById(role.getId())) { + throw new NotFoundException(); + } + } + + protected void updateRole(RoleRepresentation rep, RoleModel role) { + role.setName(rep.getName()); + role.setDescription(rep.getDescription()); + role.setComposite(rep.isComposite()); + } + + protected void addComposites(List roles, RoleModel role) { + for (RoleRepresentation rep : roles) { + RoleModel composite = realm.getRoleById(rep.getId()); + if (composite == null) { + throw new NotFoundException("Could not find composite role: " + rep.getName()); + } + if (!role.isComposite()) role.setComposite(true); + role.addCompositeRole(composite); + } + } + + protected Set getRoleComposites(RoleModel role) { + if (!role.isComposite() || role.getComposites().size() == 0) return Collections.emptySet(); + + Set composites = new HashSet(role.getComposites().size()); + for (RoleModel composite : role.getComposites()) { + composites.add(ModelToRepresentation.toRepresentation(composite)); + } + return composites; + } + + protected Set getRealmRoleComposites(RoleModel role) { + if (!role.isComposite() || role.getComposites().size() == 0) return Collections.emptySet(); + + Set composites = new HashSet(role.getComposites().size()); + for (RoleModel composite : role.getComposites()) { + if (composite.getContainer() instanceof RealmModel) + composites.add(ModelToRepresentation.toRepresentation(composite)); + } + return composites; + } + + protected Set getApplicationRoleComposites(String appName, RoleModel role) { + if (!role.isComposite() || role.getComposites().size() == 0) return Collections.emptySet(); + + ApplicationModel app = realm.getApplicationByName(appName); + if (app == null) { + throw new NotFoundException("Could not find application: " + appName); + + } + + Set composites = new HashSet(role.getComposites().size()); + for (RoleModel composite : role.getComposites()) { + if (composite.getContainer().equals(app)) + composites.add(ModelToRepresentation.toRepresentation(composite)); + } + return composites; + } + + protected void deleteComposites(List roles, RoleModel role) { + for (RoleRepresentation rep : roles) { + RoleModel composite = realm.getRoleById(rep.getId()); + if (composite == null) { + throw new NotFoundException("Could not find composite role: " + rep.getName()); + } + role.removeCompositeRole(composite); + } + if (role.getComposites().size() == 0) role.setComposite(false); + } +}