From 681256a079666c037b1a6318f4735041093908e1 Mon Sep 17 00:00:00 2001 From: Bill Burke Date: Mon, 26 Feb 2018 15:34:51 -0500 Subject: [PATCH] KEYCLOAK-6622 --- .../messages/admin-messages_en.properties | 28 ++- .../theme/base/admin/resources/js/app.js | 61 +++++- .../admin/resources/js/controllers/clients.js | 207 ++++++++++++++++++ .../admin/resources/partials/client-list.html | 5 +- .../partials/client-storage-generic.html | 207 ++++++++++++++++++ .../partials/client-storage-list.html | 68 ++++++ .../resources/templates/kc-tabs-clients.html | 16 ++ 7 files changed, 585 insertions(+), 7 deletions(-) create mode 100755 themes/src/main/resources/theme/base/admin/resources/partials/client-storage-generic.html create mode 100755 themes/src/main/resources/theme/base/admin/resources/partials/client-storage-list.html create mode 100755 themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-clients.html diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties index ba0412fde0..f2077e227a 100644 --- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties +++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties @@ -744,6 +744,12 @@ configure=Configure select-realm=Select realm add=Add +client-storage=Client Storage +no-client-storage-providers-configured=No client storage providers configured +client-stores.tooltip=Keycloak can retrieve clients and their details from external stores. + + + client-template.name.tooltip=Name of the client template. Must be unique in the realm client-template.description.tooltip=Description of the client template client-template.protocol.tooltip=Which SSO protocol configuration is being supplied by this client template @@ -1335,7 +1341,7 @@ userStorage.cachePolicy.option.EVICT_WEEKLY=EVICT_WEEKLY userStorage.cachePolicy.option.EVICT_DAILY=EVICT_DAILY userStorage.cachePolicy.option.MAX_LIFESPAN=MAX_LIFESPAN userStorage.cachePolicy.option.NO_CACHE=NO_CACHE -userStorage.cachePolicy.tooltip=Cache Policy for this storage provider. 'DEFAULT' is whatever the default settings are for the global user cache. 'EVICT_DAILY' is a time of day every day that the user cache will be invalidated. 'EVICT_WEEKLY' is a day of the week and time the cache will be invalidated. 'MAX-LIFESPAN' is the time in milliseconds that will be the lifespan of a cache entry. +userStorage.cachePolicy.tooltip=Cache Policy for this storage provider. 'DEFAULT' is whatever the default settings are for the global cache. 'EVICT_DAILY' is a time of day every day that the cache will be invalidated. 'EVICT_WEEKLY' is a day of the week and time the cache will be invalidated. 'MAX-LIFESPAN' is the time in milliseconds that will be the lifespan of a cache entry. userStorage.cachePolicy.evictionDay=Eviction Day userStorage.cachePolicy.evictionDay.tooltip=Day of the week the entry will become invalid on userStorage.cachePolicy.evictionHour=Eviction Hour @@ -1343,13 +1349,31 @@ userStorage.cachePolicy.evictionHour.tooltip=Hour of day the entry will become i userStorage.cachePolicy.evictionMinute=Eviction Minute userStorage.cachePolicy.evictionMinute.tooltip=Minute of day the entry will become invalid on. userStorage.cachePolicy.maxLifespan=Max Lifespan -userStorage.cachePolicy.maxLifespan.tooltip=Max lifespan of a user cache entry in milliseconds. +userStorage.cachePolicy.maxLifespan.tooltip=Max lifespan of cache entry in milliseconds. user-origin-link=Storage Origin user-origin.tooltip=UserStorageProvider the user was loaded from user-link.tooltip=UserStorageProvider this locally stored user was imported from. client-origin-link=Storage Origin client-origin.tooltip=Provider the client was loaded from +client-storage-cache-policy=Cache Settings +clientStorage.cachePolicy=Cache Policy +clientStorage.cachePolicy.option.DEFAULT=DEFAULT +clientStorage.cachePolicy.option.EVICT_WEEKLY=EVICT_WEEKLY +clientStorage.cachePolicy.option.EVICT_DAILY=EVICT_DAILY +clientStorage.cachePolicy.option.MAX_LIFESPAN=MAX_LIFESPAN +clientStorage.cachePolicy.option.NO_CACHE=NO_CACHE +clientStorage.cachePolicy.tooltip=Cache Policy for this storage provider. 'DEFAULT' is whatever the default settings are for the global cache. 'EVICT_DAILY' is a time of day every day that the cache will be invalidated. 'EVICT_WEEKLY' is a day of the week and time the cache will be invalidated. 'MAX-LIFESPAN' is the time in milliseconds that will be the lifespan of a cache entry. +clientStorage.cachePolicy.evictionDay=Eviction Day +clientStorage.cachePolicy.evictionDay.tooltip=Day of the week the entry will become invalid on +clientStorage.cachePolicy.evictionHour=Eviction Hour +clientStorage.cachePolicy.evictionHour.tooltip=Hour of day the entry will become invalid on. +clientStorage.cachePolicy.evictionMinute=Eviction Minute +clientStorage.cachePolicy.evictionMinute.tooltip=Minute of day the entry will become invalid on. +clientStorage.cachePolicy.maxLifespan=Max Lifespan +clientStorage.cachePolicy.maxLifespan.tooltip=Max lifespan of cache entry in milliseconds. + + disable=Disable disableable-credential-types=Disableable Types credentials.disableable.tooltip=List of credential types that you can disable diff --git a/themes/src/main/resources/theme/base/admin/resources/js/app.js b/themes/src/main/resources/theme/base/admin/resources/js/app.js index bc71ae810a..3c1f0864b1 100755 --- a/themes/src/main/resources/theme/base/admin/resources/js/app.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/app.js @@ -1451,7 +1451,57 @@ module.config([ '$routeProvider', function($routeProvider) { }, controller : 'ClientImportCtrl' }) - .when('/', { + .when('/realms/:realm/client-stores', { + templateUrl : resourceUrl + '/partials/client-storage-list.html', + resolve : { + realm : function(RealmLoader) { + return RealmLoader(); + }, + serverInfo : function(ServerInfoLoader) { + return ServerInfoLoader(); + } + }, + controller : 'ClientStoresCtrl' + }) + .when('/realms/:realm/client-storage/providers/:provider/:componentId', { + templateUrl : resourceUrl + '/partials/client-storage-generic.html', + resolve : { + realm : function(RealmLoader) { + return RealmLoader(); + }, + instance : function(ComponentLoader) { + return ComponentLoader(); + }, + providerId : function($route) { + return $route.current.params.provider; + }, + serverInfo : function(ServerInfoLoader) { + return ServerInfoLoader(); + } + }, + controller : 'GenericClientStorageCtrl' + }) + .when('/create/client-storage/:realm/providers/:provider', { + templateUrl : resourceUrl + '/partials/client-storage-generic.html', + resolve : { + realm : function(RealmLoader) { + return RealmLoader(); + }, + instance : function() { + return { + + }; + }, + providerId : function($route) { + return $route.current.params.provider; + }, + serverInfo : function(ServerInfoLoader) { + return ServerInfoLoader(); + } + }, + controller : 'GenericClientStorageCtrl' + }) + .when('/', { templateUrl : resourceUrl + '/partials/home.html', controller : 'HomeCtrl' }) @@ -2409,6 +2459,15 @@ module.directive('kcTabsUsers', function () { } }); +module.directive('kcTabsClients', function () { + return { + scope: true, + restrict: 'E', + replace: true, + templateUrl: resourceUrl + '/templates/kc-tabs-clients.html' + } +}); + module.directive('kcTabsGroup', function () { return { scope: true, diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js index 950345c266..04f2a48ab5 100755 --- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js @@ -2388,3 +2388,210 @@ module.controller('ClientTemplateScopeMappingCtrl', function($scope, $http, real updateTemplateRealmRoles(); }); + +module.controller('ClientStoresCtrl', function($scope, $location, $route, realm, serverInfo, Components, Notifications, Dialog) { + console.log('ClientStoresCtrl ++++****'); + $scope.realm = realm; + $scope.providers = serverInfo.componentTypes['org.keycloak.storage.client.ClientStorageProvider']; + $scope.instancesLoaded = false; + + if (!$scope.providers) $scope.providers = []; + + $scope.addProvider = function(provider) { + console.log('Add provider: ' + provider.id); + $location.url("/create/client-storage/" + realm.realm + "/providers/" + provider.id); + }; + + $scope.getInstanceLink = function(instance) { + return "/realms/" + realm.realm + "/client-storage/providers/" + instance.providerId + "/" + instance.id; + } + + $scope.getInstanceName = function(instance) { + return instance.name; + } + $scope.getInstanceProvider = function(instance) { + return instance.providerId; + } + + $scope.isProviderEnabled = function(instance) { + return !instance.config['enabled'] || instance.config['enabled'][0] == 'true'; + } + + $scope.getInstancePriority = function(instance) { + if (!instance.config['priority']) { + return "0"; + } + return instance.config['priority'][0]; + } + + Components.query({realm: realm.realm, + parent: realm.id, + type: 'org.keycloak.storage.client.ClientStorageProvider' + }, function(data) { + $scope.instances = data; + $scope.instancesLoaded = true; + }); + + $scope.removeInstance = function(instance) { + Dialog.confirmDelete(instance.name, 'client storage provider', function() { + Components.remove({ + realm : realm.realm, + componentId : instance.id + }, function() { + $route.reload(); + Notifications.success("The provider has been deleted."); + }); + }); + }; +}); + +module.controller('GenericClientStorageCtrl', function($scope, $location, Notifications, $route, Dialog, realm, + serverInfo, instance, providerId, Components) { + console.log('GenericClientStorageCtrl'); + console.log('providerId: ' + providerId); + $scope.create = !instance.providerId; + console.log('create: ' + $scope.create); + var providers = serverInfo.componentTypes['org.keycloak.storage.client.ClientStorageProvider']; + console.log('providers length ' + providers.length); + var providerFactory = null; + for (var i = 0; i < providers.length; i++) { + var p = providers[i]; + console.log('provider: ' + p.id); + if (p.id == providerId) { + $scope.providerFactory = p; + providerFactory = p; + break; + } + + } + $scope.changed = false; + + console.log("providerFactory: " + providerFactory.id); + + function initClientStorageSettings() { + if ($scope.create) { + $scope.changed = true; + instance.name = providerFactory.id; + instance.providerId = providerFactory.id; + instance.providerType = 'org.keycloak.storage.client.ClientStorageProvider'; + instance.parentId = realm.id; + instance.config = { + + }; + instance.config['priority'] = ["0"]; + instance.config['enabled'] = ["true"]; + + $scope.fullSyncEnabled = false; + $scope.changedSyncEnabled = false; + instance.config['cachePolicy'] = ['DEFAULT']; + instance.config['evictionDay'] = ['']; + instance.config['evictionHour'] = ['']; + instance.config['evictionMinute'] = ['']; + instance.config['maxLifespan'] = ['']; + if (providerFactory.properties) { + + for (var i = 0; i < providerFactory.properties.length; i++) { + var configProperty = providerFactory.properties[i]; + if (configProperty.defaultValue) { + instance.config[configProperty.name] = [configProperty.defaultValue]; + } else { + instance.config[configProperty.name] = ['']; + } + + } + } + + } else { + $scope.changed = false; + if (!instance.config['enabled']) { + instance.config['enabled'] = ['true']; + } + if (!instance.config['cachePolicy']) { + instance.config['cachePolicy'] = ['DEFAULT']; + + } + if (!instance.config['evictionDay']) { + instance.config['evictionDay'] = ['']; + + } + if (!instance.config['evictionHour']) { + instance.config['evictionHour'] = ['']; + + } + if (!instance.config['evictionMinute']) { + instance.config['evictionMinute'] = ['']; + + } + if (!instance.config['maxLifespan']) { + instance.config['maxLifespan'] = ['']; + + } + if (!instance.config['priority']) { + instance.config['priority'] = ['0']; + } + + if (providerFactory.properties) { + for (var i = 0; i < providerFactory.properties.length; i++) { + var configProperty = providerFactory.properties[i]; + if (!instance.config[configProperty.name]) { + instance.config[configProperty.name] = ['']; + } + } + } + + } + } + + initClientStorageSettings(); + $scope.instance = angular.copy(instance); + $scope.realm = realm; + + $scope.$watch('instance', function() { + if (!angular.equals($scope.instance, instance)) { + $scope.changed = true; + } + + }, true); + + $scope.save = function() { + console.log('save provider'); + $scope.changed = false; + if ($scope.create) { + console.log('saving new provider'); + Components.save({realm: realm.realm}, $scope.instance, function (data, headers) { + var l = headers().location; + var id = l.substring(l.lastIndexOf("/") + 1); + + $location.url("/realms/" + realm.realm + "/client-storage/providers/" + $scope.instance.providerId + "/" + id); + Notifications.success("The provider has been created."); + }); + } else { + console.log('update existing provider'); + Components.update({realm: realm.realm, + componentId: instance.id + }, + $scope.instance, function () { + $route.reload(); + Notifications.success("The provider has been updated."); + }); + } + }; + + $scope.reset = function() { + $route.reload(); + }; + + $scope.cancel = function() { + console.log('cancel'); + if ($scope.create) { + $location.url("/realms/" + realm.realm + "/client-stores"); + } else { + $route.reload(); + } + }; + + + +}); + + diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-list.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-list.html index 744e3c484e..1773fec011 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-list.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-list.html @@ -1,8 +1,5 @@
-

- {{:: 'clients' | translate}} - {{:: 'clients.tooltip' | translate}} -

+ diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-storage-generic.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-storage-generic.html new file mode 100755 index 0000000000..90ed088ef2 --- /dev/null +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-storage-generic.html @@ -0,0 +1,207 @@ +
+ + +
+
+ {{:: 'required-settings' | translate}} +
+ +
+ +
+
+
+ +
+ +
+ {{:: 'client-storage.enabled.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'console-display-name.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'priority.tooltip' | translate}} +
+ + + +
+ +
+ {{:: 'client-storage-cache-policy' | translate}} +
+ +
+
+ +
+
+ {{:: 'clientStorage.cachePolicy.tooltip' | translate}} +
+
+ +
+
+ +
+
+ {{:: 'clientStorage.cachePolicy.evictionDay.tooltip' | translate}} +
+
+ +
+
+ +
+
+ {{:: 'clientStorage.cachePolicy.evictionHour.tooltip' | translate}} +
+
+ +
+
+ +
+
+ {{:: 'clientStorage.cachePolicy.evictionMinute.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'clientStorage.cachePolicy.maxLifespan.tooltip' | translate}} +
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ +
+ + \ No newline at end of file diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-storage-list.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-storage-list.html new file mode 100755 index 0000000000..6db54670c5 --- /dev/null +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-storage-list.html @@ -0,0 +1,68 @@ +
+ + + +
+
+ +
+

+ {{:: 'client-storage' | translate}} +

+

Keycloak can federate external client databases. Out of the box we have support for Openshift OAuth clients and service accounts.

+

To get started select a provider from the dropdown below:

+
+
+
+
+ +
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+
{{:: 'id' | translate}}{{:: 'enabled' | translate}}{{:: 'provider-name' | translate}}{{:: 'priority' | translate}}{{:: 'actions' | translate}}
{{getInstanceName(instance)}}{{isProviderEnabled(instance)}}{{getInstanceProvider(instance) | capitalize}}{{getInstancePriority(instance)}}{{:: 'edit' | translate}}{{:: 'delete' | translate}}
{{:: 'no-client-storage-providers-configured' | translate}}
+
+ + \ No newline at end of file diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-clients.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-clients.html new file mode 100755 index 0000000000..cf5a332221 --- /dev/null +++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-clients.html @@ -0,0 +1,16 @@ +
+

+ {{:: 'clients' | translate}} +

+ + +
\ No newline at end of file