Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
2cc3588be5
8 changed files with 168 additions and 12 deletions
|
@ -21,7 +21,7 @@ import java.util.List;
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for partial import of users, clients, roles, and identity providers.
|
* Used for partial import of users, groups, clients, roles, and identity providers.
|
||||||
*
|
*
|
||||||
* @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
|
* @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
|
||||||
*/
|
*/
|
||||||
|
@ -32,6 +32,7 @@ public class PartialImportRepresentation {
|
||||||
protected Policy policy = Policy.FAIL;
|
protected Policy policy = Policy.FAIL;
|
||||||
protected String ifResourceExists = "";
|
protected String ifResourceExists = "";
|
||||||
protected List<UserRepresentation> users;
|
protected List<UserRepresentation> users;
|
||||||
|
protected List<GroupRepresentation> groups;
|
||||||
protected List<ClientRepresentation> clients;
|
protected List<ClientRepresentation> clients;
|
||||||
protected List<IdentityProviderRepresentation> identityProviders;
|
protected List<IdentityProviderRepresentation> identityProviders;
|
||||||
protected RolesRepresentation roles;
|
protected RolesRepresentation roles;
|
||||||
|
@ -40,6 +41,10 @@ public class PartialImportRepresentation {
|
||||||
return (users != null) && !users.isEmpty();
|
return (users != null) && !users.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasGroups() {
|
||||||
|
return (groups != null) && !groups.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasClients() {
|
public boolean hasClients() {
|
||||||
return (clients != null) && !clients.isEmpty();
|
return (clients != null) && !clients.isEmpty();
|
||||||
}
|
}
|
||||||
|
@ -81,6 +86,14 @@ public class PartialImportRepresentation {
|
||||||
return clients;
|
return clients;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<GroupRepresentation> getGroups() {
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroups(List<GroupRepresentation> groups) {
|
||||||
|
this.groups = groups;
|
||||||
|
}
|
||||||
|
|
||||||
public void setClients(List<ClientRepresentation> clients) {
|
public void setClients(List<ClientRepresentation> clients) {
|
||||||
this.clients = clients;
|
this.clients = clients;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
|
||||||
|
* as indicated by the @author tags. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
|
* the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.keycloak.partialimport;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
import org.keycloak.models.utils.RepresentationToModel;
|
||||||
|
import org.keycloak.representations.idm.GroupRepresentation;
|
||||||
|
import org.keycloak.representations.idm.PartialImportRepresentation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Partial import handler for Groups.
|
||||||
|
*
|
||||||
|
* @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
|
||||||
|
*/
|
||||||
|
public class GroupsPartialImport extends AbstractPartialImport<GroupRepresentation> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GroupRepresentation> getRepList(PartialImportRepresentation partialImportRep) {
|
||||||
|
return partialImportRep.getGroups();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName(GroupRepresentation group) {
|
||||||
|
return group.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
private GroupModel findGroupModel(RealmModel realm, GroupRepresentation groupRep) {
|
||||||
|
return KeycloakModelUtils.findGroupByPath(realm, groupRep.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getModelId(RealmModel realm, KeycloakSession session, GroupRepresentation groupRep) {
|
||||||
|
return findGroupModel(realm, groupRep).getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean exists(RealmModel realm, KeycloakSession session, GroupRepresentation groupRep) {
|
||||||
|
return findGroupModel(realm, groupRep) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String existsMessage(GroupRepresentation groupRep) {
|
||||||
|
return "Group '" + groupRep.getPath() + "' already exists";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceType getResourceType() {
|
||||||
|
return ResourceType.GROUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(RealmModel realm, KeycloakSession session, GroupRepresentation groupRep) {
|
||||||
|
GroupModel group = realm.getGroupById(getModelId(realm, session, groupRep));
|
||||||
|
realm.removeGroup(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void create(RealmModel realm, KeycloakSession session, GroupRepresentation groupRep) {
|
||||||
|
RepresentationToModel.importGroup(realm, null, groupRep);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -50,6 +50,7 @@ public class PartialImportManager {
|
||||||
partialImports.add(new ClientsPartialImport());
|
partialImports.add(new ClientsPartialImport());
|
||||||
partialImports.add(new RolesPartialImport());
|
partialImports.add(new RolesPartialImport());
|
||||||
partialImports.add(new IdentityProvidersPartialImport());
|
partialImports.add(new IdentityProvidersPartialImport());
|
||||||
|
partialImports.add(new GroupsPartialImport());
|
||||||
partialImports.add(new UsersPartialImport());
|
partialImports.add(new UsersPartialImport());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,8 +79,8 @@ public class PartialImportManager {
|
||||||
|
|
||||||
for (PartialImportResult result : results.getResults()) {
|
for (PartialImportResult result : results.getResults()) {
|
||||||
switch (result.getAction()) {
|
switch (result.getAction()) {
|
||||||
case ADDED : addedEvent(result); break;
|
case ADDED : fireCreatedEvent(result); break;
|
||||||
case OVERWRITTEN: overwrittenEvent(result); break;
|
case OVERWRITTEN: fireUpdateEvent(result); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,14 +91,14 @@ public class PartialImportManager {
|
||||||
return Response.ok(results).build();
|
return Response.ok(results).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addedEvent(PartialImportResult result) {
|
private void fireCreatedEvent(PartialImportResult result) {
|
||||||
adminEvent.operation(OperationType.CREATE)
|
adminEvent.operation(OperationType.CREATE)
|
||||||
.resourcePath(result.getResourceType().getPath(), result.getId())
|
.resourcePath(result.getResourceType().getPath(), result.getId())
|
||||||
.representation(result.getRepresentation())
|
.representation(result.getRepresentation())
|
||||||
.success();
|
.success();
|
||||||
};
|
};
|
||||||
|
|
||||||
private void overwrittenEvent(PartialImportResult result) {
|
private void fireUpdateEvent(PartialImportResult result) {
|
||||||
adminEvent.operation(OperationType.UPDATE)
|
adminEvent.operation(OperationType.UPDATE)
|
||||||
.resourcePath(result.getResourceType().getPath(), result.getId())
|
.resourcePath(result.getResourceType().getPath(), result.getId())
|
||||||
.representation(result.getRepresentation())
|
.representation(result.getRepresentation())
|
||||||
|
|
|
@ -23,7 +23,7 @@ package org.keycloak.partialimport;
|
||||||
* @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
|
* @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
|
||||||
*/
|
*/
|
||||||
public enum ResourceType {
|
public enum ResourceType {
|
||||||
USER, CLIENT, IDP, REALM_ROLE, CLIENT_ROLE;
|
USER, GROUP, CLIENT, IDP, REALM_ROLE, CLIENT_ROLE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to create the admin path in events.
|
* Used to create the admin path in events.
|
||||||
|
@ -33,6 +33,7 @@ public enum ResourceType {
|
||||||
public String getPath() {
|
public String getPath() {
|
||||||
switch(this) {
|
switch(this) {
|
||||||
case USER: return "users";
|
case USER: return "users";
|
||||||
|
case GROUP: return "groups";
|
||||||
case CLIENT: return "clients";
|
case CLIENT: return "clients";
|
||||||
case IDP: return "identity-provider-settings";
|
case IDP: return "identity-provider-settings";
|
||||||
case REALM_ROLE: return "realms";
|
case REALM_ROLE: return "realms";
|
||||||
|
@ -45,6 +46,7 @@ public enum ResourceType {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
switch(this) {
|
switch(this) {
|
||||||
case USER: return "User";
|
case USER: return "User";
|
||||||
|
case GROUP: return "Group";
|
||||||
case CLIENT: return "Client";
|
case CLIENT: return "Client";
|
||||||
case IDP: return "Identity Provider";
|
case IDP: return "Identity Provider";
|
||||||
case REALM_ROLE: return "Realm Role";
|
case REALM_ROLE: return "Realm Role";
|
||||||
|
|
|
@ -32,6 +32,7 @@ import org.keycloak.admin.client.resource.UserResource;
|
||||||
import org.keycloak.partialimport.PartialImportResult;
|
import org.keycloak.partialimport.PartialImportResult;
|
||||||
import org.keycloak.partialimport.PartialImportResults;
|
import org.keycloak.partialimport.PartialImportResults;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
|
import org.keycloak.representations.idm.GroupRepresentation;
|
||||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||||
import org.keycloak.representations.idm.PartialImportRepresentation;
|
import org.keycloak.representations.idm.PartialImportRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
@ -56,9 +57,10 @@ import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
*/
|
*/
|
||||||
public class PartialImportTest extends AbstractAuthTest {
|
public class PartialImportTest extends AbstractAuthTest {
|
||||||
|
|
||||||
private static final int NUM_RESOURCE_TYPES = 5;
|
private static final int NUM_RESOURCE_TYPES = 6;
|
||||||
private static final String CLIENT_ROLES_CLIENT = "clientRolesClient";
|
private static final String CLIENT_ROLES_CLIENT = "clientRolesClient";
|
||||||
private static final String USER_PREFIX = "user";
|
private static final String USER_PREFIX = "user";
|
||||||
|
private static final String GROUP_PREFIX = "group";
|
||||||
private static final String CLIENT_PREFIX = "client";
|
private static final String CLIENT_PREFIX = "client";
|
||||||
private static final String REALM_ROLE_PREFIX = "realmRole";
|
private static final String REALM_ROLE_PREFIX = "realmRole";
|
||||||
private static final String CLIENT_ROLE_PREFIX = "clientRole";
|
private static final String CLIENT_ROLE_PREFIX = "clientRole";
|
||||||
|
@ -94,6 +96,14 @@ public class PartialImportTest extends AbstractAuthTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void removeGroups() {
|
||||||
|
List<GroupRepresentation> toRemove = testRealmResource().groups().groups();
|
||||||
|
for (GroupRepresentation group: toRemove) {
|
||||||
|
testRealmResource().groups().group(group.getId()).remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void removeClients() {
|
public void removeClients() {
|
||||||
List<ClientRepresentation> toRemove = testRealmResource().clients().findAll();
|
List<ClientRepresentation> toRemove = testRealmResource().clients().findAll();
|
||||||
|
@ -164,6 +174,19 @@ public class PartialImportTest extends AbstractAuthTest {
|
||||||
piRep.setUsers(users);
|
piRep.setUsers(users);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addGroups() {
|
||||||
|
List<GroupRepresentation> groups = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int i=0; i < NUM_ENTITIES; i++) {
|
||||||
|
GroupRepresentation group = new GroupRepresentation();
|
||||||
|
group.setName(GROUP_PREFIX + i);
|
||||||
|
group.setPath("/" + GROUP_PREFIX + i);
|
||||||
|
groups.add(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
piRep.setGroups(groups);
|
||||||
|
}
|
||||||
|
|
||||||
private void addClients() {
|
private void addClients() {
|
||||||
List<ClientRepresentation> clients = new ArrayList<>();
|
List<ClientRepresentation> clients = new ArrayList<>();
|
||||||
|
|
||||||
|
@ -322,6 +345,12 @@ public class PartialImportTest extends AbstractAuthTest {
|
||||||
testFail();
|
testFail();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddGroupsFail() {
|
||||||
|
addGroups();
|
||||||
|
testFail();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddClientsFail() {
|
public void testAddClientsFail() {
|
||||||
addClients();
|
addClients();
|
||||||
|
@ -361,6 +390,12 @@ public class PartialImportTest extends AbstractAuthTest {
|
||||||
testSkip();
|
testSkip();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddGroupsSkip() {
|
||||||
|
addGroups();
|
||||||
|
testSkip();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddClientsSkip() {
|
public void testAddClientsSkip() {
|
||||||
addClients();
|
addClients();
|
||||||
|
@ -400,6 +435,12 @@ public class PartialImportTest extends AbstractAuthTest {
|
||||||
testOverwrite();
|
testOverwrite();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddGroupsOverwrite() {
|
||||||
|
addGroups();
|
||||||
|
testOverwrite();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddClientsOverwrite() {
|
public void testAddClientsOverwrite() {
|
||||||
addClients();
|
addClients();
|
||||||
|
@ -427,6 +468,7 @@ public class PartialImportTest extends AbstractAuthTest {
|
||||||
|
|
||||||
private void importEverything() {
|
private void importEverything() {
|
||||||
addUsers();
|
addUsers();
|
||||||
|
addGroups();
|
||||||
addClients();
|
addClients();
|
||||||
addProviders();
|
addProviders();
|
||||||
addRealmRoles();
|
addRealmRoles();
|
||||||
|
|
|
@ -548,6 +548,7 @@ file=File
|
||||||
exported-json-file=Exported json file
|
exported-json-file=Exported json file
|
||||||
import-from-realm=Import from realm
|
import-from-realm=Import from realm
|
||||||
import-users=Import users
|
import-users=Import users
|
||||||
|
import-groups=Import groups
|
||||||
import-clients=Import clients
|
import-clients=Import clients
|
||||||
import-identity-providers=Import identity providers
|
import-identity-providers=Import identity providers
|
||||||
import-realm-roles=Import realm roles
|
import-realm-roles=Import realm roles
|
||||||
|
|
|
@ -2116,6 +2116,7 @@ module.controller('RealmImportCtrl', function($scope, realm, $route,
|
||||||
$scope.overwrite = false;
|
$scope.overwrite = false;
|
||||||
$scope.skip = false;
|
$scope.skip = false;
|
||||||
$scope.importUsers = false;
|
$scope.importUsers = false;
|
||||||
|
$scope.importGroups = false;
|
||||||
$scope.importClients = false;
|
$scope.importClients = false;
|
||||||
$scope.importIdentityProviders = false;
|
$scope.importIdentityProviders = false;
|
||||||
$scope.importRealmRoles = false;
|
$scope.importRealmRoles = false;
|
||||||
|
@ -2146,11 +2147,7 @@ module.controller('RealmImportCtrl', function($scope, realm, $route,
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.importing = true;
|
$scope.importing = true;
|
||||||
$scope.importUsers = $scope.hasArray('users');
|
setOnOffSwitchDefaults();
|
||||||
$scope.importClients = $scope.hasArray('clients');
|
|
||||||
$scope.importIdentityProviders = $scope.hasArray('identityProviders');
|
|
||||||
$scope.importRealmRoles = $scope.hasRealmRoles();
|
|
||||||
$scope.importClientRoles = $scope.hasClientRoles();
|
|
||||||
$scope.results = {};
|
$scope.results = {};
|
||||||
if (!$scope.hasResources()) {
|
if (!$scope.hasResources()) {
|
||||||
$scope.nothingToImport();
|
$scope.nothingToImport();
|
||||||
|
@ -2179,6 +2176,15 @@ module.controller('RealmImportCtrl', function($scope, realm, $route,
|
||||||
return endIndex;
|
return endIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setOnOffSwitchDefaults() {
|
||||||
|
$scope.importUsers = $scope.hasArray('users');
|
||||||
|
$scope.importGroups = $scope.hasArray('groups');
|
||||||
|
$scope.importClients = $scope.hasArray('clients');
|
||||||
|
$scope.importIdentityProviders = $scope.hasArray('identityProviders');
|
||||||
|
$scope.importRealmRoles = $scope.hasRealmRoles();
|
||||||
|
$scope.importClientRoles = $scope.hasClientRoles();
|
||||||
|
}
|
||||||
|
|
||||||
$scope.setFirstPage = function() {
|
$scope.setFirstPage = function() {
|
||||||
$scope.currentPage = 0;
|
$scope.currentPage = 0;
|
||||||
}
|
}
|
||||||
|
@ -2255,6 +2261,7 @@ module.controller('RealmImportCtrl', function($scope, realm, $route,
|
||||||
|
|
||||||
$scope.hasResources = function() {
|
$scope.hasResources = function() {
|
||||||
return ($scope.importUsers && $scope.hasArray('users')) ||
|
return ($scope.importUsers && $scope.hasArray('users')) ||
|
||||||
|
($scope.importGroups && $scope.hasArray('groups')) ||
|
||||||
($scope.importClients && $scope.hasArray('clients')) ||
|
($scope.importClients && $scope.hasArray('clients')) ||
|
||||||
($scope.importIdentityProviders && $scope.hasArray('identityProviders')) ||
|
($scope.importIdentityProviders && $scope.hasArray('identityProviders')) ||
|
||||||
($scope.importRealmRoles && $scope.hasRealmRoles()) ||
|
($scope.importRealmRoles && $scope.hasRealmRoles()) ||
|
||||||
|
@ -2269,6 +2276,7 @@ module.controller('RealmImportCtrl', function($scope, realm, $route,
|
||||||
if (!angular.equals($scope.fileContent, oldCopy)) {
|
if (!angular.equals($scope.fileContent, oldCopy)) {
|
||||||
$scope.changed = true;
|
$scope.changed = true;
|
||||||
}
|
}
|
||||||
|
setOnOffSwitchDefaults();
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
$scope.successMessage = function() {
|
$scope.successMessage = function() {
|
||||||
|
@ -2286,6 +2294,7 @@ module.controller('RealmImportCtrl', function($scope, realm, $route,
|
||||||
var json = angular.copy($scope.fileContent);
|
var json = angular.copy($scope.fileContent);
|
||||||
json.ifResourceExists = $scope.ifResourceExists;
|
json.ifResourceExists = $scope.ifResourceExists;
|
||||||
if (!$scope.importUsers) delete json.users;
|
if (!$scope.importUsers) delete json.users;
|
||||||
|
if (!$scope.importGroups) delete json.groups;
|
||||||
if (!$scope.importIdentityProviders) delete json.identityProviders;
|
if (!$scope.importIdentityProviders) delete json.identityProviders;
|
||||||
if (!$scope.importClients) delete json.clients;
|
if (!$scope.importClients) delete json.clients;
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,13 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" data-ng-show="importing && hasArray('groups') && !hasResults()">
|
||||||
|
<label class="col-md-2 control-label" for="importGroups">{{:: 'import-groups' | translate}} ({{itemCount('groups')}})</label>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<input ng-model="importGroups" name="importGroups" id="importGroups" onoffswitch on-text="{{:: 'onText'| translate}}" off-text="{{:: 'offText'| translate}}"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group" data-ng-show="importing && hasArray('clients') && !hasResults()">
|
<div class="form-group" data-ng-show="importing && hasArray('clients') && !hasResults()">
|
||||||
<label class="col-md-2 control-label" for="importClients">{{:: 'import-clients' | translate}} ({{itemCount('clients')}})</label>
|
<label class="col-md-2 control-label" for="importClients">{{:: 'import-clients' | translate}} ({{itemCount('clients')}})</label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
|
|
Loading…
Reference in a new issue