keycloak-uncached
Changes
testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserPropertyFileStorage.java 2(+1 -1)
testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserPropertyFileStorageFactory.java 18(+17 -1)
Details
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 8270ed4..b58fbe5 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -207,6 +207,18 @@ public class RealmAdminResource {
}
/**
+ * Base path for managing components under this realm.
+ *
+ * @return
+ */
+ @Path("components")
+ public ComponentResource getComponents() {
+ ComponentResource resource = new ComponentResource(realm, auth, adminEvent);
+ ResteasyProviderFactory.getInstance().injectProperties(resource);
+ return resource;
+ }
+
+ /**
* base path for managing realm-level roles of this realm
*
* @return
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserPropertyFileStorage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserPropertyFileStorage.java
index 644dcb8..489dccd 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserPropertyFileStorage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserPropertyFileStorage.java
@@ -52,7 +52,7 @@ public class UserPropertyFileStorage implements UserLookupProvider, UserStorageP
this.session = session;
this.model = model;
this.userPasswords = userPasswords;
- this.federatedStorageEnabled = model.getConfig().containsKey("USER_FEDERATED_STORAGE");
+ this.federatedStorageEnabled = model.getConfig().containsKey("federatedStorage") && Boolean.valueOf(model.getConfig().getFirst("federatedStorage")).booleanValue();
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserPropertyFileStorageFactory.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserPropertyFileStorageFactory.java
index 0dec2bb..291f084 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserPropertyFileStorageFactory.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserPropertyFileStorageFactory.java
@@ -20,9 +20,12 @@ import org.keycloak.Config;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.storage.UserStorageProviderFactory;
import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Properties;
/**
@@ -38,7 +41,7 @@ public class UserPropertyFileStorageFactory implements UserStorageProviderFactor
public UserPropertyFileStorage create(KeycloakSession session, ComponentModel model) {
Properties props = new Properties();
try {
- props.load(getClass().getResourceAsStream(model.getConfig().getFirst("property.file")));
+ props.load(getClass().getResourceAsStream(model.getConfig().getFirst("propertyFile")));
} catch (IOException e) {
throw new RuntimeException(e);
}
@@ -50,6 +53,19 @@ public class UserPropertyFileStorageFactory implements UserStorageProviderFactor
return PROVIDER_ID;
}
+ static List<ProviderConfigProperty> OPTIONS = new LinkedList<>();
+ static {
+ ProviderConfigProperty prop = new ProviderConfigProperty("propertyFile", "Property File", "file that contains name value pairs", ProviderConfigProperty.STRING_TYPE, null);
+ OPTIONS.add(prop);
+ prop = new ProviderConfigProperty("federatedStorage", "User Federated Storage", "use federated storage?", ProviderConfigProperty.BOOLEAN_TYPE, null);
+ OPTIONS.add(prop);
+
+ }
+ @Override
+ public List<ProviderConfigProperty> getConfigProperties() {
+ return OPTIONS;
+ }
+
@Override
public void init(Config.Scope config) {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageTest.java
index aa648b6..3e75d64 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageTest.java
@@ -67,15 +67,15 @@ public class UserStorageTest {
model.setPriority(1);
model.setProviderId(UserPropertyFileStorageFactory.PROVIDER_ID);
model.setParentId(appRealm.getId());
- model.getConfig().putSingle("property.file", "/storage-test/read-only-user-password.properties");
+ model.getConfig().putSingle("propertyFile", "/storage-test/read-only-user-password.properties");
appRealm.addComponentModel(model);
model = new UserStorageProviderModel();
model.setName("user-props");
model.setPriority(2);
model.setParentId(appRealm.getId());
model.setProviderId(UserPropertyFileStorageFactory.PROVIDER_ID);
- model.getConfig().putSingle("property.file", "/storage-test/user-password.properties");
- model.getConfig().putSingle("USER_FEDERATED_STORAGE", "true");
+ model.getConfig().putSingle("propertyFile", "/storage-test/user-password.properties");
+ model.getConfig().putSingle("federatedStorage", "true");
appRealm.addComponentModel(model);
}
});
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 1f39b06..9efe97e 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
@@ -585,6 +585,7 @@ add-client-template=Add client template
manage=Manage
authentication=Authentication
user-federation=User Federation
+user-storage=User Storage
events=Events
realm-settings=Realm Settings
configure=Configure
@@ -687,6 +688,7 @@ create-user-federation-mapper=Create user federation mapper
add-user-federation-mapper=Add user federation mapper
provider-name=Provider Name
no-user-federation-providers-configured=No user federation providers configured
+no-user-storage-providers-configured=No user storage providers configured
add-identity-provider=Add identity provider
add-identity-provider-link=Add identity provider link
identity-provider=Identity Provider
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 1f6f5a8..2609e81 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
@@ -1338,6 +1338,18 @@ module.config([ '$routeProvider', function($routeProvider) {
},
controller : 'RealmSessionStatsCtrl'
})
+ .when('/realms/:realm/user-storage', {
+ templateUrl : resourceUrl + '/partials/user-storage.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ serverInfo : function(ServerInfoLoader) {
+ return ServerInfoLoader();
+ }
+ },
+ controller : 'UserStorageCtrl'
+ })
.when('/realms/:realm/user-federation', {
templateUrl : resourceUrl + '/partials/user-federation.html',
resolve : {
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
index 8fb01cf..8f07ffd 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
@@ -592,6 +592,34 @@ module.controller('UserCredentialsCtrl', function($scope, realm, user, RequiredA
};
});
+module.controller('UserStorageCtrl', function($scope, $location, $route, realm, serverInfo, Components, Notifications, Dialog) {
+ console.log('UserStorageCtrl ++++****');
+ $scope.realm = realm;
+ $scope.providers = serverInfo.componentTypes['org.keycloak.storage.UserStorageProvider'];
+
+ $scope.addProvider = function(provider) {
+ console.log('Add provider: ' + provider.id);
+ $location.url("/create/user-storage/" + realm.realm + "/providers/" + provider.id);
+ };
+
+ $scope.instances = Components.query({realm: realm.realm,
+ parent: realm.id,
+ type: 'org.keycloak.storage.UserStorageProvider'
+ });
+
+ $scope.removeUserStorage = function(instance) {
+ Dialog.confirmDelete(instance.name, 'user storage provider', function() {
+ Components.remove({
+ realm : realm.realm,
+ componentId : instance.id
+ }, function() {
+ $route.reload();
+ Notifications.success("The provider has been deleted.");
+ });
+ });
+ };
+});
+
module.controller('UserFederationCtrl', function($scope, $location, $route, realm, UserFederationProviders, UserFederationInstances, Notifications, Dialog) {
console.log('UserFederationCtrl ++++****');
$scope.realm = realm;
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/services.js b/themes/src/main/resources/theme/base/admin/resources/js/services.js
index c49a8d5..7e4df38 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/services.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/services.js
@@ -1629,3 +1629,16 @@ module.factory('DefaultGroups', function($resource) {
}
});
});
+
+module.factory('Components', function($resource) {
+ return $resource(authUrl + '/admin/realms/:realm/components/:componentId', {
+ realm : '@realm',
+ componentId : '@componentId'
+ }, {
+ update : {
+ method : 'PUT'
+ }
+ });
+});
+
+
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/user-storage.html b/themes/src/main/resources/theme/base/admin/resources/partials/user-storage.html
new file mode 100755
index 0000000..fb6b87a
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/user-storage.html
@@ -0,0 +1,43 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+ <h1>
+ <span>{{:: 'user-federation' | translate}}</span>
+ </h1>
+
+ <table class="table table-striped table-bordered">
+ <thead>
+ <tr ng-show="providers.length > 0 && access.manageUsers">
+ <th colspan="5" class="kc-table-actions">
+ <div class="pull-right">
+ <div>
+ <select class="form-control" ng-model="selectedProvider"
+ ng-options="p.id for p in providers"
+ data-ng-change="addProvider(selectedProvider); selectedProvider = null">
+ <option value="" disabled selected>{{:: 'add-provider.placeholder' | translate}}</option>
+ </select>
+ </div>
+ </div>
+ </th>
+ </tr>
+ <tr data-ng-show="instances && instances.length > 0">
+ <th>{{:: 'id' | translate}}</th>
+ <th>{{:: 'provider-name' | translate}}</th>
+ <th>{{:: 'priority' | translate}}</th>
+ <th colspan="2">{{:: 'actions' | translate}}</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="instance in instances">
+ <td><a href="#/realms/{{realm.realm}}/user-storage/providers/{{instance.providerId}}/{{instance.id}}">{{instance.name}}</a></td>
+ <td>{{instance.providerId|capitalize}}</td>
+ <td>{{instance.config['priority'][0]}}</td>
+ <td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/user-storage/providers/{{instance.providerId}}/{{instance.id}}">{{:: 'edit' | translate}}</td>
+ <td class="kc-action-cell" data-ng-click="removeUserStorage(instance)">{{:: 'delete' | translate}}</td>
+ </tr>
+ <tr data-ng-show="!instances || instances.length == 0">
+ <td class="text-muted">{{:: 'no-user-storage-providers-configured' | translate}}</td>
+ </tr>
+ </tbody>
+ </table>
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html
index e766bcb..926b43c 100755
--- a/themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html
+++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html
@@ -34,6 +34,7 @@
<li data-ng-show="access.viewRealm" data-ng-class="(path[2] == 'roles' || path[2] == 'default-roles' || (path[1] == 'role' && path[3] != 'clients')) && 'active'"><a href="#/realms/{{realm.realm}}/roles"><i class="fa fa-tasks"></i> {{:: 'roles' | translate}}</a></li>
<li data-ng-show="access.viewIdentityProviders" data-ng-class="(path[2] == 'identity-provider-settings' || path[2] == 'identity-provider-mappers') && 'active'"><a href="#/realms/{{realm.realm}}/identity-provider-settings"><i class="fa fa-exchange"></i> {{:: 'identity-providers' | translate}}</a></li>
<li data-ng-show="access.viewUsers" data-ng-class="(path[1] == 'user-federation' || path[2] == 'user-federation') && 'active'"><a href="#/realms/{{realm.realm}}/user-federation"><i class="fa fa-database"></i> {{:: 'user-federation' | translate}}</a></li>
+ <li data-ng-show="access.viewUsers" data-ng-class="(path[1] == 'user-storage' || path[2] == 'user-storage') && 'active'"><a href="#/realms/{{realm.realm}}/user-storage"><i class="fa fa-database"></i> {{:: 'user-storage' | translate}}</a></li>
<li data-ng-show="access.viewRealm" data-ng-class="(path[1] == 'authentication' || path[2] == 'authentication') && 'active'"><a href="#/realms/{{realm.realm}}/authentication/flows"><i class="fa fa-lock"></i> {{:: 'authentication' | translate}}</a></li>
</ul>
</div>