diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientsResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientsResource.java index ce30a75e62..4e65111f3b 100755 --- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientsResource.java +++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientsResource.java @@ -50,10 +50,16 @@ public interface ClientsResource { @Produces(MediaType.APPLICATION_JSON) List findAll(@QueryParam("viewableOnly") boolean viewableOnly); + @GET + @Produces(MediaType.APPLICATION_JSON) + List findAll(@QueryParam("clientId") String clientId, + @QueryParam("viewableOnly") Boolean viewableOnly, + @QueryParam("search") Boolean search, + @QueryParam("first") Integer firstResult, + @QueryParam("max") Integer maxResults); + @GET @Produces(MediaType.APPLICATION_JSON) List findByClientId(@QueryParam("clientId") String clientId); - - } diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java index a94720d783..72dfc6d2c1 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java @@ -693,7 +693,6 @@ public class RealmAdapter implements CachedRealmModel { @Override public List getClients() { return cacheSession.getClients(this); - } @Override @@ -722,6 +721,21 @@ public class RealmAdapter implements CachedRealmModel { return cacheSession.getClientByClientId(clientId, this); } + @Override + public List searchClientByClientId(String clientId, Integer firstResult, Integer maxResults) { + return cacheSession.searchClientsByClientId(clientId, firstResult, maxResults, this); + } + + @Override + public List getClients(Integer firstResult, Integer maxResults) { + return cacheSession.getClients(this, firstResult, maxResults); + } + + @Override + public Long getClientsCount() { + return cacheSession.getClientsCount(this); + } + @Override public void updateRequiredCredentials(Set creds) { getDelegateForUpdate(); diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java index db251d33a8..2c14954c84 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java @@ -536,41 +536,14 @@ public class RealmCacheSession implements CacheRealmProvider { return container + "." + name + ROLES_QUERY_SUFFIX; } + @Override + public List getClients(RealmModel realm, Integer firstResult, Integer maxResults) { + return getClientDelegate().getClients(realm, firstResult, maxResults); + } + @Override public List getClients(RealmModel realm) { - String cacheKey = getRealmClientsQueryCacheKey(realm.getId()); - boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(realm.getId()); - if (queryDB) { - return getClientDelegate().getClients(realm); - } - - ClientListQuery query = cache.get(cacheKey, ClientListQuery.class); - if (query != null) { - logger.tracev("getClients cache hit: {0}", realm.getName()); - } - - if (query == null) { - Long loaded = cache.getCurrentRevision(cacheKey); - List model = getClientDelegate().getClients(realm); - if (model == null) return null; - Set ids = new HashSet<>(); - for (ClientModel client : model) ids.add(client.getId()); - query = new ClientListQuery(loaded, cacheKey, realm, ids); - logger.tracev("adding realm clients cache miss: realm {0} key {1}", realm.getName(), cacheKey); - cache.addRevisioned(query, startupRevision); - return model; - } - List list = new LinkedList<>(); - for (String id : query.getClients()) { - ClientModel client = session.realms().getClientById(id, realm); - if (client == null) { - // TODO: Handle with cluster invalidations too - invalidations.add(cacheKey); - return getRealmDelegate().getClients(realm); - } - list.add(client); - } - return list; + return getClientDelegate().getClients(realm); } @@ -874,6 +847,11 @@ public class RealmCacheSession implements CacheRealmProvider { return getRealmDelegate().getGroupsCount(realm, onlyTopGroups); } + @Override + public Long getClientsCount(RealmModel realm) { + return getRealmDelegate().getClientsCount(realm); + } + @Override public Long getGroupsCountByNameContaining(RealmModel realm, String search) { return getRealmDelegate().getGroupsCountByNameContaining(realm, search); @@ -1112,6 +1090,10 @@ public class RealmCacheSession implements CacheRealmProvider { return adapter; } + @Override + public List searchClientsByClientId(String clientId, Integer firstResult, Integer maxResults, RealmModel realm) { + return getClientDelegate().searchClientsByClientId(clientId, firstResult, maxResults, realm); + } @Override public ClientModel getClientByClientId(String clientId, RealmModel realm) { diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java index cd22a46fa9..c74f711ccc 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java @@ -40,10 +40,11 @@ import org.keycloak.models.jpa.entities.RoleEntity; import org.keycloak.models.utils.KeycloakModelUtils; import javax.persistence.EntityManager; +import javax.persistence.LockModeType; import javax.persistence.TypedQuery; + import java.util.*; import java.util.stream.Collectors; -import javax.persistence.LockModeType; /** * @author Bill Burke @@ -291,7 +292,6 @@ public class JpaRealmProvider implements RealmProvider { list.add(session.realms().getRoleById(id, realm)); } return list; - } @Override @@ -383,6 +383,13 @@ public class JpaRealmProvider implements RealmProvider { return count; } + @Override + public Long getClientsCount(RealmModel realm) { + return em.createNamedQuery("getRealmClientsCount", Long.class) + .setParameter("realm", realm.getId()) + .getSingleResult(); + } + @Override public Long getGroupsCountByNameContaining(RealmModel realm, String search) { return (long) searchForGroupByName(realm, search, null, null).size(); @@ -543,8 +550,14 @@ public class JpaRealmProvider implements RealmProvider { } @Override - public List getClients(RealmModel realm) { + public List getClients(RealmModel realm, Integer firstResult, Integer maxResults) { TypedQuery query = em.createNamedQuery("getClientIdsByRealm", String.class); + if (firstResult != null && firstResult > 0) { + query.setFirstResult(firstResult); + } + if (maxResults != null && maxResults > 0) { + query.setMaxResults(maxResults); + } query.setParameter("realm", realm.getId()); List clients = query.getResultList(); if (clients.isEmpty()) return Collections.EMPTY_LIST; @@ -554,7 +567,11 @@ public class JpaRealmProvider implements RealmProvider { if (client != null) list.add(client); } return Collections.unmodifiableList(list); + } + @Override + public List getClients(RealmModel realm) { + return this.getClients(realm, null, null); } @Override @@ -578,6 +595,22 @@ public class JpaRealmProvider implements RealmProvider { return session.realms().getClientById(id, realm); } + @Override + public List searchClientsByClientId(String clientId, Integer firstResult, Integer maxResults, RealmModel realm) { + TypedQuery query = em.createNamedQuery("searchClientsByClientId", String.class); + if (firstResult != null && firstResult > 0) { + query.setFirstResult(firstResult); + } + if (maxResults != null && maxResults > 0) { + query.setMaxResults(maxResults); + } + query.setParameter("clientId", clientId); + query.setParameter("realm", realm.getId()); + List results = query.getResultList(); + if (results.isEmpty()) return Collections.EMPTY_LIST; + return results.stream().map(id -> session.realms().getClientById(id, realm)).collect(Collectors.toList()); + } + @Override public boolean removeClient(String id, RealmModel realm) { final ClientModel client = getClientById(id, realm); diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java index aeae1adcda..992632b619 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java @@ -28,13 +28,12 @@ import org.keycloak.models.utils.ComponentUtil; import org.keycloak.models.utils.KeycloakModelUtils; import javax.persistence.EntityManager; +import javax.persistence.LockModeType; import javax.persistence.TypedQuery; - import java.util.*; import java.util.function.Predicate; import java.util.stream.Collectors; -import javax.persistence.LockModeType; import static java.util.Objects.nonNull; /** @@ -46,6 +45,12 @@ public class RealmAdapter implements RealmModel, JpaModel { protected RealmEntity realm; protected EntityManager em; protected KeycloakSession session; + + @Override + public Long getClientsCount() { + return session.realms().getClientsCount(this); + } + private PasswordPolicy passwordPolicy; private OTPPolicy otpPolicy; @@ -782,12 +787,16 @@ public class RealmAdapter implements RealmModel, JpaModel { } } - @Override public List getClients() { return session.realms().getClients(this); } + @Override + public List getClients(Integer firstResult, Integer maxResults) { + return session.realms().getClients(this, firstResult, maxResults); + } + @Override public ClientModel addClient(String name) { return session.realms().addClient(this, name); @@ -816,6 +825,11 @@ public class RealmAdapter implements RealmModel, JpaModel { return session.realms().getClientByClientId(clientId, this); } + @Override + public List searchClientByClientId(String clientId, Integer firstResult, Integer maxResults) { + return session.realms().searchClientsByClientId(clientId, firstResult, maxResults, this); + } + private static final String BROWSER_HEADER_PREFIX = "_browser_header."; @Override diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java index 874bc8251d..6b39068777 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java @@ -55,6 +55,8 @@ import java.util.Set; @NamedQuery(name="getClientById", query="select client from ClientEntity client where client.id = :id and client.realm.id = :realm"), @NamedQuery(name="getClientIdsByRealm", query="select client.id from ClientEntity client where client.realm.id = :realm"), @NamedQuery(name="findClientIdByClientId", query="select client.id from ClientEntity client where client.clientId = :clientId and client.realm.id = :realm"), + @NamedQuery(name="searchClientsByClientId", query="select client.id from ClientEntity client where lower(client.clientId) like lower(concat('%',:clientId,'%')) and client.realm.id = :realm"), + @NamedQuery(name="getRealmClientsCount", query="select count(client) from ClientEntity client where client.realm.id = :realm"), @NamedQuery(name="findClientByClientId", query="select client from ClientEntity client where client.clientId = :clientId and client.realm.id = :realm"), }) public class ClientEntity { diff --git a/server-spi/src/main/java/org/keycloak/models/ClientProvider.java b/server-spi/src/main/java/org/keycloak/models/ClientProvider.java index 29dc415013..d02522d8e7 100644 --- a/server-spi/src/main/java/org/keycloak/models/ClientProvider.java +++ b/server-spi/src/main/java/org/keycloak/models/ClientProvider.java @@ -27,6 +27,8 @@ import java.util.Set; * @version $Revision: 1 $ */ public interface ClientProvider extends ClientLookupProvider, Provider { + List getClients(RealmModel realm, Integer firstResult, Integer maxResults); + List getClients(RealmModel realm); ClientModel addClient(RealmModel realm, String clientId); diff --git a/server-spi/src/main/java/org/keycloak/models/RealmModel.java b/server-spi/src/main/java/org/keycloak/models/RealmModel.java index 62e0a9e91e..9d10645c22 100755 --- a/server-spi/src/main/java/org/keycloak/models/RealmModel.java +++ b/server-spi/src/main/java/org/keycloak/models/RealmModel.java @@ -252,6 +252,8 @@ public interface RealmModel extends RoleContainerModel { void removeDefaultGroup(GroupModel group); List getClients(); + List getClients(Integer firstResult, Integer maxResults); + Long getClientsCount(); ClientModel addClient(String name); @@ -261,7 +263,8 @@ public interface RealmModel extends RoleContainerModel { ClientModel getClientById(String id); ClientModel getClientByClientId(String clientId); - + List searchClientByClientId(String clientId, Integer firstResult, Integer maxResults); + void updateRequiredCredentials(Set creds); Map getBrowserSecurityHeaders(); diff --git a/server-spi/src/main/java/org/keycloak/models/RealmProvider.java b/server-spi/src/main/java/org/keycloak/models/RealmProvider.java index 96c41c45e3..f7406960f2 100755 --- a/server-spi/src/main/java/org/keycloak/models/RealmProvider.java +++ b/server-spi/src/main/java/org/keycloak/models/RealmProvider.java @@ -42,6 +42,8 @@ public interface RealmProvider extends Provider, ClientProvider { Long getGroupsCount(RealmModel realm, Boolean onlyTopGroups); + Long getClientsCount(RealmModel realm); + Long getGroupsCountByNameContaining(RealmModel realm, String search); List getGroupsByRole(RealmModel realm, RoleModel role, int firstResult, int maxResults); diff --git a/server-spi/src/main/java/org/keycloak/storage/client/ClientLookupProvider.java b/server-spi/src/main/java/org/keycloak/storage/client/ClientLookupProvider.java index 7f04e5f427..edfda976dc 100644 --- a/server-spi/src/main/java/org/keycloak/storage/client/ClientLookupProvider.java +++ b/server-spi/src/main/java/org/keycloak/storage/client/ClientLookupProvider.java @@ -19,6 +19,8 @@ package org.keycloak.storage.client; import org.keycloak.models.ClientModel; import org.keycloak.models.RealmModel; +import java.util.List; + /** * Abstraction interface for lookoup of clients by id and clientId. These methods required for participating in login flows. * @@ -28,4 +30,5 @@ import org.keycloak.models.RealmModel; public interface ClientLookupProvider { ClientModel getClientById(String id, RealmModel realm); ClientModel getClientByClientId(String clientId, RealmModel realm); + List searchClientsByClientId(String clientId, Integer firstResult, Integer maxResults, RealmModel realm); } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java index a87dbeee7a..1d77f91da7 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java @@ -16,17 +16,13 @@ */ package org.keycloak.services.resources.admin; -import static java.lang.Boolean.TRUE; - import org.jboss.logging.Logger; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.keycloak.authorization.admin.AuthorizationService; -import org.keycloak.common.Profile; import org.keycloak.events.admin.OperationType; import org.keycloak.events.admin.ResourceType; import org.keycloak.models.ClientModel; -import org.keycloak.models.Constants; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.RealmModel; @@ -56,11 +52,13 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Properties; +import static java.lang.Boolean.TRUE; + /** * Base resource class for managing a realm's clients. * @@ -91,15 +89,22 @@ public class ClientsResource { * * @param clientId filter by clientId * @param viewableOnly filter clients that cannot be viewed in full by admin + * @param search whether this is a search query or a getClientById query + * @param firstResult the first result + * @param maxResults the max results to return */ @GET @Produces(MediaType.APPLICATION_JSON) @NoCache - public List getClients(@QueryParam("clientId") String clientId, @QueryParam("viewableOnly") @DefaultValue("false") boolean viewableOnly) { + public List getClients(@QueryParam("clientId") String clientId, + @QueryParam("viewableOnly") @DefaultValue("false") boolean viewableOnly, + @QueryParam("search") @DefaultValue("false") boolean search, + @QueryParam("first") Integer firstResult, + @QueryParam("max") Integer maxResults) { List rep = new ArrayList<>(); if (clientId == null || clientId.trim().equals("")) { - List clientModels = realm.getClients(); + List clientModels = realm.getClients(firstResult, maxResults); auth.clients().requireList(); boolean view = auth.clients().canView(); for (ClientModel clientModel : clientModels) { @@ -116,21 +121,30 @@ public class ClientsResource { } } } else { - ClientModel clientModel = realm.getClientByClientId(clientId); - if (clientModel != null) { - if (auth.clients().canView(clientModel)) { - ClientRepresentation representation = ModelToRepresentation.toRepresentation(clientModel, session); - representation.setAccess(auth.clients().getAccess(clientModel)); - rep.add(representation); - } else if (!viewableOnly && auth.clients().canView(clientModel)){ - ClientRepresentation client = new ClientRepresentation(); - client.setId(clientModel.getId()); - client.setClientId(clientModel.getClientId()); - client.setDescription(clientModel.getDescription()); - rep.add(client); - - } else { - throw new ForbiddenException(); + List clientModels = Collections.emptyList(); + if(search) { + clientModels = realm.searchClientByClientId(clientId, firstResult, maxResults); + } else { + ClientModel client = realm.getClientByClientId(clientId); + if(client != null) { + clientModels = Collections.singletonList(client); + } + } + if (clientModels != null) { + for(ClientModel clientModel : clientModels) { + if (auth.clients().canView(clientModel)) { + ClientRepresentation representation = ModelToRepresentation.toRepresentation(clientModel, session); + representation.setAccess(auth.clients().getAccess(clientModel)); + rep.add(representation); + } else if (!viewableOnly && auth.clients().canView(clientModel)){ + ClientRepresentation client = new ClientRepresentation(); + client.setId(clientModel.getId()); + client.setClientId(clientModel.getClientId()); + client.setDescription(clientModel.getDescription()); + rep.add(client); + } else { + throw new ForbiddenException(); + } } } } diff --git a/services/src/main/java/org/keycloak/storage/ClientStorageManager.java b/services/src/main/java/org/keycloak/storage/ClientStorageManager.java index d60d2f4a22..8da754f582 100644 --- a/services/src/main/java/org/keycloak/storage/ClientStorageManager.java +++ b/services/src/main/java/org/keycloak/storage/ClientStorageManager.java @@ -31,7 +31,6 @@ import org.keycloak.storage.client.ClientStorageProviderFactory; import org.keycloak.storage.client.ClientStorageProviderModel; import java.util.Collections; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; @@ -151,6 +150,18 @@ public class ClientStorageManager implements ClientProvider { return null; } + @Override + public List searchClientsByClientId(String clientId, Integer firstResult, Integer maxResults, RealmModel realm) { + List clients = session.clientLocalStorage().searchClientsByClientId(clientId, firstResult, maxResults, realm); + if (clients != null) { + return clients; + } + for (ClientLookupProvider provider : getEnabledStorageProviders(session, realm, ClientLookupProvider.class)) { + clients = provider.searchClientsByClientId(clientId, firstResult, maxResults, realm); + if (clients != null) return clients; + } + return null; + } @Override public ClientModel addClient(RealmModel realm, String clientId) { @@ -165,9 +176,14 @@ public class ClientStorageManager implements ClientProvider { + @Override + public List getClients(RealmModel realm, Integer firstResult, Integer maxResults) { + return session.clientLocalStorage().getClients(realm, firstResult, maxResults); + } + @Override public List getClients(RealmModel realm) { - return session.clientLocalStorage().getClients(realm); + return session.clientLocalStorage().getClients(realm); } @Override diff --git a/services/src/main/java/org/keycloak/storage/openshift/OpenshiftClientStorageProvider.java b/services/src/main/java/org/keycloak/storage/openshift/OpenshiftClientStorageProvider.java index 41ec8144d1..2a0573db36 100644 --- a/services/src/main/java/org/keycloak/storage/openshift/OpenshiftClientStorageProvider.java +++ b/services/src/main/java/org/keycloak/storage/openshift/OpenshiftClientStorageProvider.java @@ -16,8 +16,6 @@ */ package org.keycloak.storage.openshift; -import java.util.regex.Matcher; - import com.openshift.restclient.IClient; import com.openshift.restclient.NotFoundException; import com.openshift.restclient.model.IResource; @@ -28,6 +26,10 @@ import org.keycloak.storage.StorageId; import org.keycloak.storage.client.ClientStorageProvider; import org.keycloak.storage.client.ClientStorageProviderModel; +import java.util.Collections; +import java.util.List; +import java.util.regex.Matcher; + /** * @author Pedro Igor */ @@ -73,6 +75,12 @@ public class OpenshiftClientStorageProvider implements ClientStorageProvider { return new OpenshiftSAClientAdapter(clientId, resource, client, session, realm, providerModel); } + @Override + public List searchClientsByClientId(String clientId, Integer firstResult, Integer maxResults, RealmModel realm) { + // TODO not sure about this, but I don't see this implementation using the search now + return Collections.singletonList(getClientByClientId(clientId, realm)); + } + @Override public void close() { diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/HardcodedClientStorageProvider.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/HardcodedClientStorageProvider.java index a1e9edf64b..d9bed02bea 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/HardcodedClientStorageProvider.java +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/HardcodedClientStorageProvider.java @@ -23,7 +23,7 @@ import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; import org.keycloak.models.utils.KeycloakModelUtils; -import org.keycloak.protocol.oidc.OIDCLoginProtocol; + import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory; import org.keycloak.storage.StorageId; import org.keycloak.storage.client.AbstractReadOnlyClientStorageAdapter; @@ -36,6 +36,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.List; import java.util.stream.Collectors; /** @@ -76,6 +77,14 @@ public class HardcodedClientStorageProvider implements ClientStorageProvider, Cl } + @Override + public List searchClientsByClientId(String clientId, Integer firstResult, Integer maxResults, RealmModel realm) { + if (clientId != null && this.clientId.toLowerCase().contains(clientId.toLowerCase())) { + return Collections.singletonList(new ClientAdapter(realm)); + } + return Collections.EMPTY_LIST; + } + public class ClientAdapter extends AbstractReadOnlyClientStorageAdapter { public ClientAdapter(RealmModel realm) { diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/DataTable.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/DataTable.java index d5cf11241f..a8c3557bde 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/DataTable.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/DataTable.java @@ -50,6 +50,9 @@ public class DataTable { private WebElement body; @FindBy(xpath = "(//table)[1]/tbody/tr[@class='ng-scope']") private List rows; + + @FindBy(tagName = "tfoot") + private WebElement footer; @FindBy private WebElement infoRow; @@ -71,6 +74,11 @@ public class DataTable { return body; } + public WebElement footer() { + return footer; + } + + public List rows() { waitForPageToLoad(); pause(500); // wait a bit to ensure the table is no more changing diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java index 4c67407bf2..6af3d03b03 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java @@ -53,11 +53,16 @@ import javax.ws.rs.core.Response; import java.io.IOException; import java.util.Collections; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.*; +import static org.keycloak.testsuite.auth.page.login.Login.OIDC; /** * @author Stian Thorgersen @@ -154,6 +159,35 @@ public class ClientTest extends AbstractAdminTest { assertFalse(allClients.isEmpty()); } + @Test + public void getAllClientsSearchAndPagination() { + Set ids = new HashSet<>(); + try { + for (int i = 1; i <= 10; i++) { + ClientRepresentation c = ClientBuilder.create().clientId("ccx-" + (i < 10 ? "0" + i : i)).build(); + Response response = realm.clients().create(c); + ids.add(ApiUtil.getCreatedId(response)); + response.close(); + } + + assertPaginatedClients(1, 10, realm.clients().findAll("ccx-", null, true, 0, 100)); + assertPaginatedClients(1, 5, realm.clients().findAll("ccx-", null, true, 0, 5)); + assertPaginatedClients(6, 10, realm.clients().findAll("ccx-", null, true, 5, 5)); + } finally { + ids.stream().forEach(id -> realm.clients().get(id).remove()); + } + } + + private void assertPaginatedClients(int start, int end, List actual) { + List expected = new LinkedList<>(); + for (int i = start; i <= end; i++) { + expected.add("ccx-" + (i < 10 ? "0" + i : i)); + } + List a = actual.stream().map(rep -> rep.getClientId()).collect(Collectors.toList()); + assertThat(a, is(expected)); + + } + @Test public void getClientById() { createClient(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourceServerManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourceServerManagementTest.java index 6affcee41f..546f5a2c4a 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourceServerManagementTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourceServerManagementTest.java @@ -17,13 +17,6 @@ package org.keycloak.testsuite.admin.client.authorization; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.util.List; - import org.junit.Test; import org.keycloak.admin.client.resource.AuthorizationResource; import org.keycloak.admin.client.resource.ClientsResource; @@ -32,6 +25,15 @@ import org.keycloak.representations.idm.authorization.DecisionStrategy; import org.keycloak.representations.idm.authorization.PolicyEnforcementMode; import org.keycloak.util.JsonSerialization; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + + + /** * * @author Pedro Igor diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/Clients.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/Clients.java index 2bf4ed19af..26359e5b94 100644 --- a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/Clients.java +++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/Clients.java @@ -20,6 +20,7 @@ package org.keycloak.testsuite.console.page.clients; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.testsuite.console.page.AdminConsoleRealm; import org.keycloak.testsuite.console.page.fragment.DataTable; +import org.openqa.selenium.By; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; @@ -81,6 +82,22 @@ public class Clients extends AdminConsoleRealm { clickRowActionButton(getRowByLinkText(clientId), EDIT); } + private void clickFooterButton(int index) { + footer().findElements(By.tagName("button")).get(index).click(); + } + + public void clickNextPage() { + clickFooterButton(2); + } + + public void clickPrevPage() { + clickFooterButton(1); + } + + public void clickFirstPage() { + clickFooterButton(0); + } + public void deleteClient(String clientId) { clickRowActionButton(getRowByLinkText(clientId), DELETE); } diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/ClientSelectModal.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/ClientSelectModal.java new file mode 100644 index 0000000000..28221f24c3 --- /dev/null +++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/ClientSelectModal.java @@ -0,0 +1,24 @@ +package org.keycloak.testsuite.console.page.clients.authorization.policy; + +import org.keycloak.testsuite.console.page.fragment.MultipleStringSelect2; +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; + +import java.util.List; +import java.util.function.Function; + +import static org.keycloak.testsuite.util.UIUtils.getTextFromElement; +import static org.openqa.selenium.By.tagName; + +public class ClientSelectModal extends MultipleStringSelect2 { + + @Override + protected List getSelectedElements() { + return getRoot().findElements(By.xpath("(//select[@id='available-client'])/option")); + } + + @Override + protected Function representation() { + return webElement -> getTextFromElement(webElement.findElements(tagName("td")).get(0)); + } +} diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/clientscopes/ClientScopesEvaluateForm.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/clientscopes/ClientScopesEvaluateForm.java index a525f27536..f364fcc3db 100644 --- a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/clientscopes/ClientScopesEvaluateForm.java +++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/clientscopes/ClientScopesEvaluateForm.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Set; import java.util.stream.Collectors; +import org.keycloak.testsuite.console.page.clients.authorization.policy.ClientSelectModal; import org.keycloak.testsuite.console.page.fragment.DataTable; import org.keycloak.testsuite.page.Form; import org.keycloak.testsuite.util.WaitUtils; @@ -81,6 +82,10 @@ public class ClientScopesEvaluateForm extends Form { @FindBy(tagName = "textarea") private WebElement accessTokenTextArea; + @FindBy(id = "s2id_users") + private ClientSelectModal clientsInput; + + public Set getAvailableClientScopes() { return ClientScopesSetupForm.getSelectValues(availableClientScopesSelect); @@ -101,10 +106,7 @@ public class ClientScopesEvaluateForm extends Form { public void selectUser(String username) { - // TODO: Should be probably better way how to work with the "ui-select2" component - driver.findElement(By.id("select2-chosen-1")).click(); - driver.findElement(By.className("select2-input")).sendKeys(username); - driver.findElement(By.className("select2-result-label")).click(); + clientsInput.select(username); } diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/mappers/CreateClientMappersForm.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/mappers/CreateClientMappersForm.java index 09f8cd08f8..c8826b5069 100644 --- a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/mappers/CreateClientMappersForm.java +++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/mappers/CreateClientMappersForm.java @@ -1,6 +1,7 @@ package org.keycloak.testsuite.console.page.clients.mappers; import org.jboss.arquillian.drone.api.annotation.Drone; +import org.keycloak.testsuite.console.page.clients.authorization.policy.ClientSelectModal; import org.keycloak.testsuite.console.page.fragment.OnOffSwitch; import org.keycloak.testsuite.page.Form; import org.keycloak.testsuite.util.UIUtils; @@ -102,7 +103,10 @@ public class CreateClientMappersForm extends Form { private WebElement selectClientRoleButton; @FindBy(xpath = ".//button[@class='close']") private WebElement closeButton; - + + @FindBy(id = "s2id_clients") + private ClientSelectModal clientsInput; + public void closeRoleSelectorModalDialog() { closeButton.click(); } @@ -117,14 +121,18 @@ public class CreateClientMappersForm extends Form { } public void selectClientRole(String clientName, String roleName) { + WaitUtils.pause(100); if (roleName != null || clientName != null) { - clientSelect.selectByVisibleText(clientName); clientAvailable.selectByVisibleText(roleName); } WaitUtils.pause(1000); selectClientRoleButton.click(); WaitUtils.waitForModalFadeOut(); } + + public void selectClient(String clientName) { + clientsInput.select(clientName); + } } public void selectRole(String roleType, String roleName, String clientName) { @@ -134,6 +142,7 @@ public class CreateClientMappersForm extends Form { roleSelectorModalDialog.selectRealmRole(roleName); break; case CLIENT_ROLE: + roleSelectorModalDialog.selectClient(clientName); roleSelectorModalDialog.selectClientRole(clientName, roleName); break; default: diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/clients/ClientsTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/clients/ClientsTest.java index cf0969c902..1cb41a2e35 100644 --- a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/clients/ClientsTest.java +++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/clients/ClientsTest.java @@ -27,6 +27,10 @@ import org.junit.Test; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.testsuite.console.page.clients.settings.ClientSettings; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.IntStream; + import static org.junit.Assert.*; import static org.keycloak.testsuite.auth.page.login.Login.OIDC; import static org.keycloak.testsuite.console.clients.AbstractClientTest.createClientRep; @@ -38,20 +42,25 @@ import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlEquals; */ public class ClientsTest extends AbstractClientTest { - private ClientRepresentation newClient; - @Page private ClientSettings clientSettingsPage; @Before public void beforeClientsTest() { - newClient = createClientRep(TEST_CLIENT_ID, OIDC); + ClientRepresentation newClient = createClientRep(TEST_CLIENT_ID, OIDC); testRealmResource().clients().create(newClient).close(); ClientRepresentation found = findClientByClientId(TEST_CLIENT_ID); assertNotNull("Client " + TEST_CLIENT_ID + " was not found.", found); clientSettingsPage.setId(found.getId()); } + + private void create100Clients() { + List clients = new ArrayList<>(); + IntStream.range(0, 100) + .mapToObj(i -> createClientRep(TEST_CLIENT_ID + i, OIDC)) + .forEach(rep -> testRealmResource().clients().create(rep).close()); + } @Test public void clientsCRUD() { @@ -73,4 +82,42 @@ public class ClientsTest extends AbstractClientTest { ClientRepresentation found = findClientByClientId(TEST_CLIENT_ID); assertNull("Deleted client " + TEST_CLIENT_ID + " was found.", found); } + + + @Test + public void clientsNavigationTest() { + //create 100 clients + create100Clients(); + String firstPageClient = TEST_CLIENT_ID + 0; + String secondPageClient = TEST_CLIENT_ID + 22; + String thirdPageClient = TEST_CLIENT_ID + 41; + + //edit on the 2nd page then go back + clientsPage.navigateTo(); + clientsPage.table().clickNextPage(); + clientsPage.table().editClient(secondPageClient); + assertEquals(secondPageClient, clientSettingsPage.form().getClientId()); + + //go to the main page and delete + clientsPage.navigateTo(); + clientsPage.table().clickPrevPage(); + clientsPage.table().deleteClient(TEST_CLIENT_ID); + modalDialog.confirmDeletion(); + ClientRepresentation found = findClientByClientId(TEST_CLIENT_ID); + assertNull("Deleted client " + TEST_CLIENT_ID + " was found.", found); + + // go forward two pages then main page + clientsPage.navigateTo(); + clientsPage.table().clickNextPage(); + clientsPage.table().clickNextPage(); + clientsPage.table().editClient(thirdPageClient); + assertEquals(thirdPageClient, clientSettingsPage.form().getClientId()); + clientsPage.navigateTo(); + + clientsPage.table().clickFirstPage(); + clientsPage.table().editClient(firstPageClient); + assertEquals(firstPageClient, clientSettingsPage.form().getClientId()); + clientsPage.navigateTo(); + + } } \ No newline at end of file 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 abeca2f35b..0b2b34ee1a 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 @@ -262,6 +262,7 @@ included.custom.audience.tooltip=This is used just if 'Included Client Audience' # client details clients.tooltip=Clients are trusted browser apps and web services in a realm. These clients can request a login. You can also define client specific roles. search.placeholder=Search... +search.loading=Searching... create=Create import=Import client-id=Client ID @@ -1252,6 +1253,7 @@ view-all-groups=View all groups unlock-users=Unlock users no-users-available=No users available users.instruction=Please enter a search, or click on view all users +clients.instruction=Please enter a search consents=Consents started=Started logout-all-sessions=Log out all sessions @@ -1315,8 +1317,8 @@ authz-any-resource=Any resource authz-any-scope=Any scope authz-any-role=Any role authz-policy-evaluation=Policy Evaluation -authz-select-client=Select a client authz-select-user=Select a user +authz-select-client=Select a client authz-entitlements=Entitlements authz-no-resources=No resources authz-result=Result 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 059432056a..1bfa0abb10 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 @@ -507,9 +507,6 @@ module.config([ '$routeProvider', function($routeProvider) { realm : function(RealmLoader) { return RealmLoader(); }, - clients : function(ClientListLoader) { - return ClientListLoader(); - }, roles : function(RoleListLoader) { return RoleListLoader(); } @@ -2692,7 +2689,7 @@ module.controller('RoleSelectorModalCtrl', function($scope, realm, config, confi } $scope.selectClientRole = function() { - config[configName] = $scope.client.selected.clientId + "." + $scope.selectedClientRole.role.name; + config[configName] = $scope.selectedClient.clientId + "." + $scope.selectedClientRole.role.name; $modalInstance.close(); } @@ -2700,9 +2697,18 @@ module.controller('RoleSelectorModalCtrl', function($scope, realm, config, confi $modalInstance.dismiss(); } - $scope.changeClient = function() { - if ($scope.client.selected) { - ClientRole.query({realm: realm.realm, client: $scope.client.selected.id}, function (data) { + clientSelectControl($scope, realm.realm, Client); + + $scope.selectedClient = null; + + $scope.changeClient = function(client) { + $scope.selectedClient = client; + if (!client || !client.id) { + $scope.selectedClient = null; + return; + } + if ($scope.selectedClient) { + ClientRole.query({realm: realm.realm, client: $scope.selectedClient.id}, function (data) { $scope.clientRoles = data; }); } else { @@ -2710,20 +2716,16 @@ module.controller('RoleSelectorModalCtrl', function($scope, realm, config, confi $scope.clientRoles = null; } + $scope.selectedClient = client; } + RealmRoles.query({realm: realm.realm}, function(data) { $scope.realmRoles = data; }) - Client.query({realm: realm.realm}, function(data) { - $scope.clients = data; - if (data.length > 0) { - $scope.client.selected = data[0]; - $scope.changeClient(); - } - }) }); -module.controller('ProviderConfigCtrl', function ($modal, $scope, ComponentUtils) { +module.controller('ProviderConfigCtrl', function ($modal, $scope, $route, ComponentUtils, Client) { + clientSelectControl($scope, $route.current.params.realm, Client); $scope.fileNames = {}; // KEYCLOAK-4463 @@ -2731,6 +2733,18 @@ module.controller('ProviderConfigCtrl', function ($modal, $scope, ComponentUtils editor.$blockScrolling = Infinity; // suppress warning message }; + $scope.initSelectedClient = function(configName, config) { + if(config[configName]) { + $scope.selectedClient = null; + Client.query({realm: $route.current.params.realm, search: false, clientId: config[configName], max: 1}, function(data) { + if(data.length > 0) { + $scope.selectedClient = angular.copy(data[0]); + $scope.selectedClient.text = $scope.selectedClient.clientId; + } + }); + } + } + $scope.openRoleSelector = function (configName, config) { $modal.open({ templateUrl: resourceUrl + '/partials/modal/role-selector.html', @@ -2749,6 +2763,16 @@ module.controller('ProviderConfigCtrl', function ($modal, $scope, ComponentUtils }) } + $scope.changeClient = function(configName, config, client) { + if (!client || !client.id) { + config[configName] = null; + $scope.selectedClient = null; + return; + } + $scope.selectedClient = client; + config[configName] = client.clientId; + }; + ComponentUtils.convertAllMultivaluedStringValuesToList($scope.properties, $scope.config); ComponentUtils.addLastEmptyValueToMultivaluedLists($scope.properties, $scope.config); @@ -2844,7 +2868,31 @@ module.controller('ComponentRoleSelectorModalCtrl', function($scope, realm, conf }) }); -module.controller('ComponentConfigCtrl', function ($modal, $scope) { +module.controller('ComponentConfigCtrl', function ($modal, $scope, $route, Client) { + + $scope.initSelectedClient = function(configName, config) { + if(config[configName]) { + $scope.selectedClient = null; + Client.query({realm: $route.current.params.realm, search: false, clientId: config[configName], max: 1}, function(data) { + if(data.length > 0) { + $scope.selectedClient = angular.copy(data[0]); + $scope.selectedClient.text = $scope.selectedClient.clientId; + } + }); + } + } + + $scope.changeClient = function(configName, config, client) { + if (!client || !client.id) { + config[configName] = null; + $scope.selectedClient = null; + return; + } + $scope.selectedClient = client; + config[configName] = client.clientId; + }; + + $scope.openRoleSelector = function (configName, config) { $modal.open({ templateUrl: resourceUrl + '/partials/modal/component-role-selector.html', diff --git a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js index 07f0612975..7f28d9f6e7 100644 --- a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js @@ -1518,29 +1518,7 @@ module.controller('ResourceServerPolicyClientDetailCtrl', function($scope, $rout }, onInit : function() { - $scope.clientsUiSelect = { - minimumInputLength: 1, - delay: 500, - allowClear: true, - query: function (query) { - var data = {results: []}; - if ('' == query.term.trim()) { - query.callback(data); - return; - } - Client.query({realm: $route.current.params.realm, search: query.term.trim(), max: 20}, function(response) { - for (i = 0; i < response.length; i++) { - if (response[i].clientId.indexOf(query.term) != -1) { - data.results.push(response[i]); - } - } - query.callback(data); - }); - }, - formatResult: function(object, container, query) { - return object.clientId; - } - }; + clientSelectControl($scope, $route.current.params.realm, Client); $scope.selectedClients = []; 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 0e8cc3a777..b94fc71436 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 @@ -674,7 +674,7 @@ module.controller('ClientOfflineSessionsCtrl', function($scope, realm, offlineSe }; }); -module.controller('ClientRoleDetailCtrl', function($scope, realm, client, role, roles, clients, +module.controller('ClientRoleDetailCtrl', function($scope, $route, realm, client, role, roles, Client, Role, ClientRole, RoleById, RoleRealmComposites, RoleClientComposites, $http, $location, Dialog, Notifications, ComponentUtils) { $scope.realm = realm; @@ -752,7 +752,7 @@ module.controller('ClientRoleDetailCtrl', function($scope, realm, client, role, } } - roleControl($scope, realm, role, roles, clients, + roleControl($scope, $route, realm, role, roles, Client, ClientRole, RoleById, RoleRealmComposites, RoleClientComposites, $http, $location, Notifications, Dialog, ComponentUtils); @@ -851,28 +851,51 @@ module.controller('ClientImportCtrl', function($scope, $location, $upload, realm }); -module.controller('ClientListCtrl', function($scope, realm, Client, serverInfo, $route, Dialog, Notifications, filterFilter) { - $scope.realm = realm; - $scope.clients = []; - $scope.currentPage = 1; - $scope.currentPageInput = 1; - $scope.numberOfPages = 1; - $scope.pageSize = 20; - $scope.clientStorageProviders = serverInfo.componentTypes['org.keycloak.storage.client.ClientStorageProvider']; +module.controller('ClientListCtrl', function($scope, realm, Client, ClientListSearchState, $route, Dialog, Notifications) { + $scope.init = function() { + $scope.realm = realm; + $scope.searchLoaded = true; + + ClientListSearchState.query.realm = realm.realm; + $scope.query = ClientListSearchState.query; + + if (!ClientListSearchState.isFirstSearch) { + $scope.searchQuery(); + } else { + $scope.query.clientId = null; + $scope.firstPage(); + } + }; - Client.query({realm: realm.realm, viewableOnly: true}).$promise.then(function(clients) { - $scope.numberOfPages = Math.ceil(clients.length/$scope.pageSize); - $scope.clients = clients; - }); - - $scope.$watch('search', function (newVal, oldVal) { - $scope.filtered = filterFilter($scope.clients, newVal); - $scope.totalItems = $scope.filtered.length; - $scope.numberOfPages = Math.ceil($scope.totalItems/$scope.pageSize); - $scope.currentPage = 1; - $scope.currentPageInput = 1; - }, true); - + $scope.searchQuery = function() { + console.log("query.search: ", $scope.query); + $scope.searchLoaded = false; + + $scope.clients = Client.query($scope.query, function() { + $scope.searchLoaded = true; + $scope.lastSearch = $scope.query.search; + ClientListSearchState.isFirstSearch = false; + }); + }; + + $scope.firstPage = function() { + $scope.query.first = 0; + $scope.searchQuery(); + } + + $scope.previousPage = function() { + $scope.query.first -= parseInt($scope.query.max); + if ($scope.query.first < 0) { + $scope.query.first = 0; + } + $scope.searchQuery(); + } + + $scope.nextPage = function() { + $scope.query.first += parseInt($scope.query.max); + $scope.searchQuery(); + } + $scope.removeClient = function(client) { Dialog.confirmDelete(client.clientId, 'client', function() { Client.remove({ @@ -885,15 +908,6 @@ module.controller('ClientListCtrl', function($scope, realm, Client, serverInfo, }); }; - $scope.searchClient = function() { - console.log('searchQuery!!! ' + $scope.search.clientId); - Client.query({realm: realm.realm, viewableOnly: true, clientId: $scope.search.clientId}).$promise.then(function(clients) { - $scope.numberOfPages = Math.ceil(clients.length/$scope.pageSize); - $scope.clients = clients; - }); - - }; - $scope.exportClient = function(client) { var clientCopy = angular.copy(client); delete clientCopy.id; @@ -1626,7 +1640,7 @@ module.controller('CreateClientCtrl', function($scope, realm, client, $route, se }; }); -module.controller('ClientScopeMappingCtrl', function($scope, $http, realm, client, clients, Notifications, +module.controller('ClientScopeMappingCtrl', function($scope, $http, realm, $route, client, clients, Notifications, Client, ClientScope, ClientRealmScopeMapping, ClientClientScopeMapping, ClientRole, ClientAvailableRealmScopeMapping, ClientAvailableClientScopeMapping, @@ -1661,8 +1675,18 @@ module.controller('ClientScopeMappingCtrl', function($scope, $http, realm, clien }); } + + $scope.selectedClient = null; + $scope.selectClient = function(client) { + if (!client || !client.id) { + $scope.selectedClient = null; + return; + } + $scope.selectedClient = client; + updateClientRoles(); + } function updateRealmRoles() { $scope.realmRoles = ClientAvailableRealmScopeMapping.query({realm : realm.realm, client : client.id}); @@ -1671,10 +1695,10 @@ module.controller('ClientScopeMappingCtrl', function($scope, $http, realm, clien } function updateClientRoles() { - if ($scope.targetClient) { - $scope.clientRoles = ClientAvailableClientScopeMapping.query({realm : realm.realm, client : client.id, targetClient : $scope.targetClient.id}); - $scope.clientMappings = ClientClientScopeMapping.query({realm : realm.realm, client : client.id, targetClient : $scope.targetClient.id}); - $scope.clientComposite = ClientCompositeClientScopeMapping.query({realm : realm.realm, client : client.id, targetClient : $scope.targetClient.id}); + if ($scope.selectedClient) { + $scope.clientRoles = ClientAvailableClientScopeMapping.query({realm : realm.realm, client : client.id, targetClient : $scope.selectedClient.id}); + $scope.clientMappings = ClientClientScopeMapping.query({realm : realm.realm, client : client.id, targetClient : $scope.selectedClient.id}); + $scope.clientComposite = ClientCompositeClientScopeMapping.query({realm : realm.realm, client : client.id, targetClient : $scope.selectedClient.id}); } else { $scope.clientRoles = null; $scope.clientMappings = null; @@ -1682,10 +1706,6 @@ module.controller('ClientScopeMappingCtrl', function($scope, $http, realm, clien } } - $scope.changeClient = function() { - updateClientRoles(); - }; - $scope.addRealmRole = function() { $scope.selectedRealmRolesToAdd = JSON.parse('[' + $scope.selectedRealmRoles + ']'); $scope.selectedRealmRoles = []; @@ -1711,7 +1731,7 @@ module.controller('ClientScopeMappingCtrl', function($scope, $http, realm, clien $scope.addClientRole = function() { $scope.selectedClientRolesToAdd = JSON.parse('[' + $scope.selectedClientRoles + ']'); $scope.selectedClientRoles = []; - $http.post(authUrl + '/admin/realms/' + realm.realm + '/clients/' + client.id + '/scope-mappings/clients/' + $scope.targetClient.id, + $http.post(authUrl + '/admin/realms/' + realm.realm + '/clients/' + client.id + '/scope-mappings/clients/' + $scope.selectedClient.id, $scope.selectedClientRolesToAdd).then(function () { updateClientRoles(); $scope.selectedClientRolesToAdd = []; @@ -1722,7 +1742,7 @@ module.controller('ClientScopeMappingCtrl', function($scope, $http, realm, clien $scope.deleteClientRole = function() { $scope.selectedClientMappingsToRemove = JSON.parse('[' + $scope.selectedClientMappings + ']'); $scope.selectedClientMappings = []; - $http.delete(authUrl + '/admin/realms/' + realm.realm + '/clients/' + client.id + '/scope-mappings/clients/' + $scope.targetClient.id, + $http.delete(authUrl + '/admin/realms/' + realm.realm + '/clients/' + client.id + '/scope-mappings/clients/' + $scope.selectedClient.id, {data : $scope.selectedClientMappingsToRemove, headers : {"content-type" : "application/json"}}).then(function () { updateClientRoles(); $scope.selectedClientMappingsToRemove = []; @@ -1730,6 +1750,7 @@ module.controller('ClientScopeMappingCtrl', function($scope, $http, realm, clien }); }; + clientSelectControl($scope, $route.current.params.realm, Client); updateRealmRoles(); }); @@ -2115,6 +2136,7 @@ module.controller('ClientProtocolMapperCreateCtrl', function($scope, realm, serv changed: false, mapperTypes: serverInfo.protocolMapperTypes[protocol] }; + console.log("mapper types: ", $scope.model.mapperTypes); // apply default configurations on change for selected protocolmapper type. $scope.$watch('model.mapperType', function() { @@ -2296,7 +2318,8 @@ module.controller('ClientClientScopesSetupCtrl', function($scope, realm, Realm, module.controller('ClientClientScopesEvaluateCtrl', function($scope, Realm, User, ClientEvaluateProtocolMappers, ClientEvaluateGrantedRoles, ClientEvaluateNotGrantedRoles, ClientEvaluateGenerateExampleToken, realm, client, clients, clientScopes, serverInfo, - ComponentUtils, clientOptionalClientScopes, clientDefaultClientScopes, $route, $routeParams, $http, Notifications, $location) { + ComponentUtils, clientOptionalClientScopes, clientDefaultClientScopes, $route, $routeParams, $http, Notifications, $location, + Client) { console.log('ClientClientScopesEvaluateCtrl'); @@ -2434,6 +2457,21 @@ module.controller('ClientClientScopesEvaluateCtrl', function($scope, Realm, User $scope.userId = user.id; } + clientSelectControl($scope, $route.current.params.realm, Client); + + $scope.selectedClient = null; + + $scope.selectClient = function(client) { + console.log("selected client: ", client); + if (!client || !client.id) { + $scope.selectedClient = null; + return; + } + + $scope.selectedClient = client; + updateScopeClientRoles(); + } + $scope.sendEvaluationRequest = function () { @@ -2527,17 +2565,17 @@ module.controller('ClientClientScopesEvaluateCtrl', function($scope, Realm, User } function updateScopeClientRoles() { - if ($scope.targetClient) { + if ($scope.selectedClient) { $scope.grantedClientRoles = ClientEvaluateGrantedRoles.query({ realm: realm.realm, client: client.id, - roleContainer: $scope.targetClient.id, + roleContainer: $scope.selectedClient.id, scopeParam: $scope.scopeParam }); $scope.notGrantedClientRoles = ClientEvaluateNotGrantedRoles.query({ realm: realm.realm, client: client.id, - roleContainer: $scope.targetClient.id, + roleContainer: $scope.selectedClient.id, scopeParam: $scope.scopeParam }); } else { @@ -2545,10 +2583,6 @@ module.controller('ClientClientScopesEvaluateCtrl', function($scope, Realm, User $scope.notGrantedClientRoles = null; } } - - $scope.changeClient = function() { - updateScopeClientRoles(); - }; }); @@ -3063,8 +3097,8 @@ module.controller('ClientScopeAddBuiltinProtocolMapperCtrl', function($scope, re }); -module.controller('ClientScopeScopeMappingCtrl', function($scope, $http, realm, clientScope, clients, Notifications, - ClientScope, +module.controller('ClientScopeScopeMappingCtrl', function($scope, $http, $route, realm, clientScope, Notifications, + ClientScope, Client, ClientScopeRealmScopeMapping, ClientScopeClientScopeMapping, ClientRole, ClientScopeAvailableRealmScopeMapping, ClientScopeAvailableClientScopeMapping, ClientScopeCompositeRealmScopeMapping, ClientScopeCompositeClientScopeMapping) { @@ -3073,13 +3107,13 @@ module.controller('ClientScopeScopeMappingCtrl', function($scope, $http, realm, $scope.selectedRealmRoles = []; $scope.selectedRealmMappings = []; $scope.realmMappings = []; - $scope.clients = clients; $scope.clientRoles = []; $scope.clientComposite = []; $scope.selectedClientRoles = []; $scope.selectedClientMappings = []; $scope.clientMappings = []; $scope.dummymodel = []; + $scope.selectedClient = null; function updateScopeRealmRoles() { $scope.realmRoles = ClientScopeAvailableRealmScopeMapping.query({realm : realm.realm, clientScope : clientScope.id}); @@ -3088,10 +3122,10 @@ module.controller('ClientScopeScopeMappingCtrl', function($scope, $http, realm, } function updateScopeClientRoles() { - if ($scope.targetClient) { - $scope.clientRoles = ClientScopeAvailableClientScopeMapping.query({realm : realm.realm, clientScope : clientScope.id, targetClient : $scope.targetClient.id}); - $scope.clientMappings = ClientScopeClientScopeMapping.query({realm : realm.realm, clientScope : clientScope.id, targetClient : $scope.targetClient.id}); - $scope.clientComposite = ClientScopeCompositeClientScopeMapping.query({realm : realm.realm, clientScope : clientScope.id, targetClient : $scope.targetClient.id}); + if ($scope.selectedClient) { + $scope.clientRoles = ClientScopeAvailableClientScopeMapping.query({realm : realm.realm, clientScope : clientScope.id, targetClient : $scope.selectedClient.id}); + $scope.clientMappings = ClientScopeClientScopeMapping.query({realm : realm.realm, clientScope : clientScope.id, targetClient : $scope.selectedClient.id}); + $scope.clientComposite = ClientScopeCompositeClientScopeMapping.query({realm : realm.realm, clientScope : clientScope.id, targetClient : $scope.selectedClient.id}); } else { $scope.clientRoles = null; $scope.clientMappings = null; @@ -3099,7 +3133,12 @@ module.controller('ClientScopeScopeMappingCtrl', function($scope, $http, realm, } } - $scope.changeClient = function() { + $scope.changeClient = function(client) { + if (!client || !client.id) { + $scope.selectedClient = null; + return; + } + $scope.selectedClient = client; updateScopeClientRoles(); }; @@ -3128,7 +3167,7 @@ module.controller('ClientScopeScopeMappingCtrl', function($scope, $http, realm, $scope.addClientRole = function() { $scope.selectedClientRolesToAdd = JSON.parse('[' + $scope.selectedClientRoles + ']'); $scope.selectedClientRoles = []; - $http.post(authUrl + '/admin/realms/' + realm.realm + '/client-scopes/' + clientScope.id + '/scope-mappings/clients/' + $scope.targetClient.id, + $http.post(authUrl + '/admin/realms/' + realm.realm + '/client-scopes/' + clientScope.id + '/scope-mappings/clients/' + $scope.selectedClient.id, $scope.selectedClientRolesToAdd).then(function () { updateScopeClientRoles(); $scope.selectedClientRolesToAdd = []; @@ -3139,7 +3178,7 @@ module.controller('ClientScopeScopeMappingCtrl', function($scope, $http, realm, $scope.deleteClientRole = function() { $scope.selectedClientMappingsToRemove = JSON.parse('[' + $scope.selectedClientMappings + ']'); $scope.selectedClientMappings = []; - $http.delete(authUrl + '/admin/realms/' + realm.realm + '/client-scopes/' + clientScope.id + '/scope-mappings/clients/' + $scope.targetClient.id, + $http.delete(authUrl + '/admin/realms/' + realm.realm + '/client-scopes/' + clientScope.id + '/scope-mappings/clients/' + $scope.selectedClient.id, {data : $scope.selectedClientMappingsToRemove, headers : {"content-type" : "application/json"}}).then(function () { updateScopeClientRoles(); $scope.selectedClientMappingsToRemove = []; @@ -3147,6 +3186,7 @@ module.controller('ClientScopeScopeMappingCtrl', function($scope, $http, realm, }); }; + clientSelectControl($scope, $route.current.params.realm, Client); updateScopeRealmRoles(); }); diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/groups.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/groups.js index f36ec4cd96..d55fb266e4 100755 --- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/groups.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/groups.js @@ -308,7 +308,7 @@ module.controller('GroupDetailCtrl', function(Dialog, $scope, realm, group, Grou } }); -module.controller('GroupRoleMappingCtrl', function($scope, $http, realm, group, clients, client, Notifications, GroupRealmRoleMapping, +module.controller('GroupRoleMappingCtrl', function($scope, $http, $route, realm, group, clients, client, Client, Notifications, GroupRealmRoleMapping, GroupClientRoleMapping, GroupAvailableRealmRoleMapping, GroupAvailableClientRoleMapping, GroupCompositeRealmRoleMapping, GroupCompositeClientRoleMapping) { $scope.realm = realm; @@ -339,11 +339,11 @@ module.controller('GroupRoleMappingCtrl', function($scope, $http, realm, group, $scope.realmComposite = GroupCompositeRealmRoleMapping.query({realm : realm.realm, groupId : group.id}); $scope.selectedRealmMappings = []; $scope.selectRealmRoles = []; - if ($scope.targetClient) { + if ($scope.selectedClient) { console.log('load available'); - $scope.clientComposite = GroupCompositeClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.targetClient.id}); - $scope.clientRoles = GroupAvailableClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.targetClient.id}); - $scope.clientMappings = GroupClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.targetClient.id}); + $scope.clientComposite = GroupCompositeClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.selectedClient.id}); + $scope.clientRoles = GroupAvailableClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.selectedClient.id}); + $scope.clientMappings = GroupClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.selectedClient.id}); $scope.selectedClientRoles = []; $scope.selectedClientMappings = []; } @@ -362,11 +362,11 @@ module.controller('GroupRoleMappingCtrl', function($scope, $http, realm, group, $scope.realmComposite = GroupCompositeRealmRoleMapping.query({realm : realm.realm, groupId : group.id}); $scope.selectedRealmMappings = []; $scope.selectRealmRoles = []; - if ($scope.targetClient) { + if ($scope.selectedClient) { console.log('load available'); - $scope.clientComposite = GroupCompositeClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.targetClient.id}); - $scope.clientRoles = GroupAvailableClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.targetClient.id}); - $scope.clientMappings = GroupClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.targetClient.id}); + $scope.clientComposite = GroupCompositeClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.selectedClient.id}); + $scope.clientRoles = GroupAvailableClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.selectedClient.id}); + $scope.clientMappings = GroupClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.selectedClient.id}); $scope.selectedClientRoles = []; $scope.selectedClientMappings = []; } @@ -377,11 +377,11 @@ module.controller('GroupRoleMappingCtrl', function($scope, $http, realm, group, $scope.addClientRole = function() { $scope.selectedClientRolesToAdd = JSON.parse('[' + $scope.selectedClientRoles + ']'); - $http.post(authUrl + '/admin/realms/' + realm.realm + '/groups/' + group.id + '/role-mappings/clients/' + $scope.targetClient.id, + $http.post(authUrl + '/admin/realms/' + realm.realm + '/groups/' + group.id + '/role-mappings/clients/' + $scope.selectedClient.id, $scope.selectedClientRolesToAdd).then(function() { - $scope.clientMappings = GroupClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.targetClient.id}); - $scope.clientRoles = GroupAvailableClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.targetClient.id}); - $scope.clientComposite = GroupCompositeClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.targetClient.id}); + $scope.clientMappings = GroupClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.selectedClient.id}); + $scope.clientRoles = GroupAvailableClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.selectedClient.id}); + $scope.clientComposite = GroupCompositeClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.selectedClient.id}); $scope.selectedClientRoles = []; $scope.selectedClientMappings = []; $scope.realmComposite = GroupCompositeRealmRoleMapping.query({realm : realm.realm, groupId : group.id}); @@ -393,11 +393,11 @@ module.controller('GroupRoleMappingCtrl', function($scope, $http, realm, group, $scope.deleteClientRole = function() { $scope.selectedClientMappingsToRemove = JSON.parse('[' + $scope.selectedClientMappings + ']'); - $http.delete(authUrl + '/admin/realms/' + realm.realm + '/groups/' + group.id + '/role-mappings/clients/' + $scope.targetClient.id, + $http.delete(authUrl + '/admin/realms/' + realm.realm + '/groups/' + group.id + '/role-mappings/clients/' + $scope.selectedClient.id, {data : $scope.selectedClientMappingsToRemove, headers : {"content-type" : "application/json"}}).then(function() { - $scope.clientMappings = GroupClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.targetClient.id}); - $scope.clientRoles = GroupAvailableClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.targetClient.id}); - $scope.clientComposite = GroupCompositeClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.targetClient.id}); + $scope.clientMappings = GroupClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.selectedClient.id}); + $scope.clientRoles = GroupAvailableClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.selectedClient.id}); + $scope.clientComposite = GroupCompositeClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.selectedClient.id}); $scope.selectedClientRoles = []; $scope.selectedClientMappings = []; $scope.realmComposite = GroupCompositeRealmRoleMapping.query({realm : realm.realm, groupId : group.id}); @@ -408,21 +408,25 @@ module.controller('GroupRoleMappingCtrl', function($scope, $http, realm, group, }; - $scope.changeClient = function() { - if ($scope.targetClient) { - $scope.clientComposite = GroupCompositeClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.targetClient.id}); - $scope.clientRoles = GroupAvailableClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.targetClient.id}); - $scope.clientMappings = GroupClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.targetClient.id}); - } else { + $scope.changeClient = function(client) { + $scope.selectedClient = client; + if (!client || !client.id) { + $scope.selectedClient = null; $scope.clientRoles = null; $scope.clientMappings = null; $scope.clientComposite = null; + return; + } + if ($scope.selectedClient) { + $scope.clientComposite = GroupCompositeClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.selectedClient.id}); + $scope.clientRoles = GroupAvailableClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.selectedClient.id}); + $scope.clientMappings = GroupClientRoleMapping.query({realm : realm.realm, groupId : group.id, client : $scope.selectedClient.id}); } $scope.selectedClientRoles = []; $scope.selectedClientMappings = []; }; - + clientSelectControl($scope, $route.current.params.realm, Client); }); diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js index a66428d4fb..5d0e56b340 100644 --- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js @@ -582,7 +582,7 @@ module.controller('RealmPasswordPolicyCtrl', function($scope, Realm, realm, $htt }; }); -module.controller('RealmDefaultRolesCtrl', function ($scope, Realm, realm, clients, roles, Notifications, ClientRole, Client) { +module.controller('RealmDefaultRolesCtrl', function ($scope, $route, Realm, realm, roles, Notifications, ClientRole, Client) { console.log('RealmDefaultRolesCtrl'); @@ -592,14 +592,6 @@ module.controller('RealmDefaultRolesCtrl', function ($scope, Realm, realm, clien $scope.selectedRealmRoles = []; $scope.selectedRealmDefRoles = []; - $scope.clients = angular.copy(clients); - for (var i = 0; i < clients.length; i++) { - if (clients[i].name == 'account') { - $scope.client = $scope.clients[i]; - break; - } - } - $scope.availableClientRoles = []; $scope.selectedClientRoles = []; $scope.selectedClientDefRoles = []; @@ -660,24 +652,28 @@ module.controller('RealmDefaultRolesCtrl', function ($scope, Realm, realm, clien }); }; - $scope.changeClient = function () { - + $scope.changeClient = function (client) { + $scope.selectedClient = client; $scope.selectedClientRoles = []; $scope.selectedClientDefRoles = []; + if (!client || !client.id) { + $scope.selectedClient = null; + return; + } // Populate available roles for selected client - if ($scope.client) { - var appDefaultRoles = ClientRole.query({realm: $scope.realm.realm, client: $scope.client.id}, function () { - - if (!$scope.client.hasOwnProperty('defaultRoles') || $scope.client.defaultRoles === null) { - $scope.client.defaultRoles = []; + if ($scope.selectedClient) { + ClientRole.query({realm: $scope.realm.realm, client: $scope.selectedClient.id}, function (appDefaultRoles) { + if (!$scope.selectedClient.hasOwnProperty('defaultRoles') || $scope.selectedClient.defaultRoles === null) { + $scope.selectedClient.defaultRoles = []; } $scope.availableClientRoles = []; - + console.log('default roles', appDefaultRoles); for (var i = 0; i < appDefaultRoles.length; i++) { + var roleName = appDefaultRoles[i].name; - if ($scope.client.defaultRoles.indexOf(roleName) < 0) { + if ($scope.selectedClient.defaultRoles.indexOf(roleName) < 0) { $scope.availableClientRoles.push(roleName); } } @@ -693,9 +689,9 @@ module.controller('RealmDefaultRolesCtrl', function ($scope, Realm, realm, clien for (var i = 0; i < $scope.selectedClientRoles.length; i++) { var role = $scope.selectedClientRoles[i]; - var idx = $scope.client.defaultRoles.indexOf(role); + var idx = $scope.selectedClient.defaultRoles.indexOf(role); if (idx < 0) { - $scope.client.defaultRoles.push(role); + $scope.selectedClient.defaultRoles.push(role); } idx = $scope.availableClientRoles.indexOf(role); @@ -708,11 +704,16 @@ module.controller('RealmDefaultRolesCtrl', function ($scope, Realm, realm, clien $scope.selectedClientRoles = []; // Update/save the selected client with new default roles. + delete $scope.selectedClient.text; Client.update({ realm: $scope.realm.realm, - client: $scope.client.id - }, $scope.client, function () { + client: $scope.selectedClient.id + }, $scope.selectedClient, function () { Notifications.success("Your changes have been saved to the client."); + Client.get({realm: realm.realm, client: $scope.selectedClient.id}, function(response) { + response.text = response.clientId; + $scope.changeClient(response); + }); }); }; @@ -721,9 +722,9 @@ module.controller('RealmDefaultRolesCtrl', function ($scope, Realm, realm, clien // Remove selected roles from the app default roles and add them to app available roles (move from right to left). for (var i = 0; i < $scope.selectedClientDefRoles.length; i++) { var role = $scope.selectedClientDefRoles[i]; - var idx = $scope.client.defaultRoles.indexOf(role); + var idx = $scope.selectedClient.defaultRoles.indexOf(role); if (idx != -1) { - $scope.client.defaultRoles.splice(idx, 1); + $scope.selectedClient.defaultRoles.splice(idx, 1); } idx = $scope.availableClientRoles.indexOf(role); if (idx < 0) { @@ -734,14 +735,20 @@ module.controller('RealmDefaultRolesCtrl', function ($scope, Realm, realm, clien $scope.selectedClientDefRoles = []; // Update/save the selected client with new default roles. + delete $scope.selectedClient.text; Client.update({ realm: $scope.realm.realm, - client: $scope.client.id - }, $scope.client, function () { + client: $scope.selectedClient.id + }, $scope.selectedClient, function () { Notifications.success("Your changes have been saved to the client."); + Client.get({realm: realm.realm, client: $scope.selectedClient.id}, function(response) { + response.text = response.clientId; + $scope.changeClient(response); + }); }); }; + clientSelectControl($scope, $route.current.params.realm, Client); }); @@ -1473,7 +1480,7 @@ module.controller('RoleListCtrl', function($scope, $route, Dialog, Notifications }); -module.controller('RoleDetailCtrl', function($scope, realm, role, roles, clients, +module.controller('RoleDetailCtrl', function($scope, realm, role, roles, Client, $route, Role, ClientRole, RoleById, RoleRealmComposites, RoleClientComposites, $http, $location, Dialog, Notifications, RealmRoleRemover, ComponentUtils) { $scope.realm = realm; @@ -1542,7 +1549,7 @@ module.controller('RoleDetailCtrl', function($scope, realm, role, roles, clients } } - roleControl($scope, realm, role, roles, clients, + roleControl($scope, $route, realm, role, roles, Client, ClientRole, RoleById, RoleRealmComposites, RoleClientComposites, $http, $location, Notifications, Dialog, ComponentUtils); }); 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 6ca4988886..f909a47059 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 @@ -1,4 +1,4 @@ -module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, clients, client, Notifications, RealmRoleMapping, +module.controller('UserRoleMappingCtrl', function($scope, $http, $route, realm, user, client, Client, Notifications, RealmRoleMapping, ClientRoleMapping, AvailableRealmRoleMapping, AvailableClientRoleMapping, CompositeRealmRoleMapping, CompositeClientRoleMapping) { $scope.realm = realm; @@ -6,7 +6,6 @@ module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, cl $scope.selectedRealmRoles = []; $scope.selectedRealmMappings = []; $scope.realmMappings = []; - $scope.clients = clients; $scope.client = client; $scope.clientRoles = []; $scope.clientComposite = []; @@ -14,6 +13,8 @@ module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, cl $scope.selectedClientMappings = []; $scope.clientMappings = []; $scope.dummymodel = []; + $scope.selectedClient = null; + $scope.realmMappings = RealmRoleMapping.query({realm : realm.realm, userId : user.id}); $scope.realmRoles = AvailableRealmRoleMapping.query({realm : realm.realm, userId : user.id}); @@ -29,11 +30,11 @@ module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, cl $scope.realmComposite = CompositeRealmRoleMapping.query({realm : realm.realm, userId : user.id}); $scope.selectedRealmMappings = []; $scope.selectRealmRoles = []; - if ($scope.targetClient) { + if ($scope.selectedClient) { console.log('load available'); - $scope.clientComposite = CompositeClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.targetClient.id}); - $scope.clientRoles = AvailableClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.targetClient.id}); - $scope.clientMappings = ClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.targetClient.id}); + $scope.clientComposite = CompositeClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.selectedClient.id}); + $scope.clientRoles = AvailableClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.selectedClient.id}); + $scope.clientMappings = ClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.selectedClient.id}); $scope.selectedClientRoles = []; $scope.selectedClientMappings = []; } @@ -51,11 +52,11 @@ module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, cl $scope.realmComposite = CompositeRealmRoleMapping.query({realm : realm.realm, userId : user.id}); $scope.selectedRealmMappings = []; $scope.selectRealmRoles = []; - if ($scope.targetClient) { + if ($scope.selectedClient) { console.log('load available'); - $scope.clientComposite = CompositeClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.targetClient.id}); - $scope.clientRoles = AvailableClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.targetClient.id}); - $scope.clientMappings = ClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.targetClient.id}); + $scope.clientComposite = CompositeClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.selectedClient.id}); + $scope.clientRoles = AvailableClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.selectedClient.id}); + $scope.clientMappings = ClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.selectedClient.id}); $scope.selectedClientRoles = []; $scope.selectedClientMappings = []; } @@ -65,11 +66,11 @@ module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, cl $scope.addClientRole = function() { $scope.clientRolesToAdd = JSON.parse('[' + $scope.selectedClientRoles + ']'); - $http.post(authUrl + '/admin/realms/' + realm.realm + '/users/' + user.id + '/role-mappings/clients/' + $scope.targetClient.id, + $http.post(authUrl + '/admin/realms/' + realm.realm + '/users/' + user.id + '/role-mappings/clients/' + $scope.selectedClient.id, $scope.clientRolesToAdd).then(function() { - $scope.clientMappings = ClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.targetClient.id}); - $scope.clientRoles = AvailableClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.targetClient.id}); - $scope.clientComposite = CompositeClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.targetClient.id}); + $scope.clientMappings = ClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.selectedClient.id}); + $scope.clientRoles = AvailableClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.selectedClient.id}); + $scope.clientComposite = CompositeClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.selectedClient.id}); $scope.selectedClientRoles = []; $scope.selectedClientMappings = []; $scope.realmComposite = CompositeRealmRoleMapping.query({realm : realm.realm, userId : user.id}); @@ -80,11 +81,11 @@ module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, cl $scope.deleteClientRole = function() { $scope.clientRolesToRemove = JSON.parse('[' + $scope.selectedClientMappings + ']'); - $http.delete(authUrl + '/admin/realms/' + realm.realm + '/users/' + user.id + '/role-mappings/clients/' + $scope.targetClient.id, + $http.delete(authUrl + '/admin/realms/' + realm.realm + '/users/' + user.id + '/role-mappings/clients/' + $scope.selectedClient.id, {data : $scope.clientRolesToRemove, headers : {"content-type" : "application/json"}}).then(function() { - $scope.clientMappings = ClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.targetClient.id}); - $scope.clientRoles = AvailableClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.targetClient.id}); - $scope.clientComposite = CompositeClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.targetClient.id}); + $scope.clientMappings = ClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.selectedClient.id}); + $scope.clientRoles = AvailableClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.selectedClient.id}); + $scope.clientComposite = CompositeClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.selectedClient.id}); $scope.selectedClientRoles = []; $scope.selectedClientMappings = []; $scope.realmComposite = CompositeRealmRoleMapping.query({realm : realm.realm, userId : user.id}); @@ -93,14 +94,19 @@ module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, cl }); }; - - $scope.changeClient = function() { - console.log('changeClient'); - if ($scope.targetClient) { + $scope.changeClient = function(client) { + console.log("selected client: ", client); + if (!client || !client.id) { + $scope.selectedClient = null; + return; + } else { + $scope.selectedClient = client; + } + if ($scope.selectedClient) { console.log('load available'); - $scope.clientComposite = CompositeClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.targetClient.id}); - $scope.clientRoles = AvailableClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.targetClient.id}); - $scope.clientMappings = ClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.targetClient.id}); + $scope.clientComposite = CompositeClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.selectedClient.id}); + $scope.clientRoles = AvailableClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.selectedClient.id}); + $scope.clientMappings = ClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.selectedClient.id}); } else { $scope.clientRoles = null; $scope.clientMappings = null; @@ -110,8 +116,7 @@ module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, cl $scope.selectedClientMappings = []; }; - - + clientSelectControl($scope, $route.current.params.realm, Client); }); module.controller('UserSessionsCtrl', function($scope, realm, user, sessions, UserSessions, UserLogout, UserSessionLogout, Notifications) { diff --git a/themes/src/main/resources/theme/base/admin/resources/js/loaders.js b/themes/src/main/resources/theme/base/admin/resources/js/loaders.js index af2691177d..d67da8dc1f 100755 --- a/themes/src/main/resources/theme/base/admin/resources/js/loaders.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/loaders.js @@ -327,7 +327,9 @@ module.factory('ClientLoader', function(Loader, Client, $route, $q) { module.factory('ClientListLoader', function(Loader, Client, $route, $q) { return Loader.query(Client, function() { return { - realm : $route.current.params.realm + realm : $route.current.params.realm, + first: 0, + max: 20 } }); }); 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 ca1e632a4c..4329a38a46 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 @@ -557,6 +557,15 @@ module.service('UserSearchState', function() { }; }); +module.service('ClientListSearchState', function() { + this.isFirstSearch = true; + this.query = { + max : 20, + first : 0, + search: true + }; +}); + // Service tracks the last flow selected in Authentication-->Flows tab module.service('LastFlowSelected', function() { this.alias = null; @@ -883,11 +892,32 @@ module.factory('RoleClientComposites', function($resource) { }); }); +function clientSelectControl($scope, realm, Client) { + $scope.clientsUiSelect = { + minimumInputLength: 1, + delay: 500, + allowClear: true, + query: function (query) { + var data = {results: []}; + if ('' == query.term.trim()) { + query.callback(data); + return; + } + Client.query({realm: realm, search: true, clientId: query.term.trim(), max: 20}, function(response) { + data.results = response; + query.callback(data); + }); + }, + formatResult: function(object, container, query) { + object.text = object.clientId; + return object.clientId; + } + }; +} -function roleControl($scope, realm, role, roles, clients, - ClientRole, RoleById, RoleRealmComposites, RoleClientComposites, - $http, $location, Notifications, Dialog, ComponentUtils) { - +function roleControl($scope, $route, realm, role, roles, Client, + ClientRole, RoleById, RoleRealmComposites, RoleClientComposites, + $http, $location, Notifications, Dialog, ComponentUtils) { $scope.$watch(function () { return $location.path(); }, function () { @@ -924,7 +954,6 @@ function roleControl($scope, realm, role, roles, clients, $scope.selectedRealmRoles = []; $scope.selectedRealmMappings = []; $scope.realmMappings = []; - $scope.clients = clients; $scope.clientRoles = []; $scope.selectedClientRoles = []; $scope.selectedClientMappings = []; @@ -940,6 +969,11 @@ function roleControl($scope, realm, role, roles, clients, } + clientSelectControl($scope, $route.current.params.realm, Client); + + $scope.selectedClient = null; + + $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]; @@ -1033,9 +1067,15 @@ function roleControl($scope, realm, role, roles, clients, }; - $scope.changeClient = function() { - $scope.clientRoles = ClientRole.query({realm : realm.realm, client : $scope.compositeClient.id}, function() { - $scope.clientMappings = RoleClientComposites.query({realm : realm.realm, role : role.id, client : $scope.compositeClient.id}, function(){ + $scope.changeClient = function(client) { + console.log("selected client: ", client); + if (!client || !client.id) { + $scope.selectedClient = null; + return; + } + $scope.selectedClient = client; + $scope.clientRoles = ClientRole.query({realm : realm.realm, client : client.id}, function() { + $scope.clientMappings = RoleClientComposites.query({realm : realm.realm, role : role.id, client : client.id}, function(){ for (var i = 0; i < $scope.clientMappings.length; i++) { var role = $scope.clientMappings[i]; for (var j = 0; j < $scope.clientRoles.length; j++) { 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 4fb919bb41..6640d5a9fa 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,4 +1,5 @@ -
+
+ @@ -8,18 +9,21 @@ +
+ {{:: 'search.loading' | translate }} +
@@ -29,8 +33,19 @@ + + + + + - + - - - + + + + -
\ No newline at end of file diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-role-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-role-detail.html index 9b6fd7d9ab..8877dbeea8 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-role-detail.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-role-detail.html @@ -92,17 +92,16 @@
-
- +
+ +
+ + +
-
-
-
{{:: 'select-client-to-view-roles' | translate}}
-
-
+ +
+
{{:: 'available-roles.tooltip' | translate}} diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-scope-mappings.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-scope-mappings.html index bde92a1359..1343f1b4c0 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-scope-mappings.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-scope-mappings.html @@ -71,16 +71,14 @@
- + +
+ + +
-
-
-
{{:: 'select-client-roles.tooltip' | translate}}
-
-
+
+
{{:: 'assign.available-roles.tooltip' | translate}} diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-scope-scope-mappings.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-scope-scope-mappings.html index 2a52ac0d11..3180826b7d 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-scope-scope-mappings.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-scope-scope-mappings.html @@ -60,16 +60,14 @@
- + +
+ + +
-
-
-
{{:: 'select-client-roles.tooltip' | translate}}
-
-
+
+
{{:: 'assign.available-roles.tooltip' | translate}} diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-scopes-evaluate.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-scopes-evaluate.html index fab5d52918..cd8785ebb4 100644 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-scopes-evaluate.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-scopes-evaluate.html @@ -208,16 +208,14 @@
- + +
+ + +
-
-
-
{{:: 'select-client-roles.tooltip' | translate}}
-
-
+
+
{{:: 'client-scopes.evaluate.not-granted-roles.tooltip' | translate}} diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-service-account-roles.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-service-account-roles.html index 3bee1686fd..bc17f1b242 100644 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-service-account-roles.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-service-account-roles.html @@ -63,16 +63,14 @@
- + +
+ + +
-
-
-
{{:: 'select-client-to-view-roles' | translate}}
-
-
+
+
{{:: 'assign.available-roles.tooltip' | translate}} diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/group-role-mappings.html b/themes/src/main/resources/theme/base/admin/resources/partials/group-role-mappings.html index 4e3ab97f0d..fc3f6ee361 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/group-role-mappings.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/group-role-mappings.html @@ -56,15 +56,13 @@
- -
-
-
{{:: 'select-client-to-view-roles' | translate}}
-
-
+ +
+ + +
+
+
{{:: 'group.available-roles.tooltip' | translate}} @@ -107,7 +105,6 @@
-
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/modal/role-selector.html b/themes/src/main/resources/theme/base/admin/resources/partials/modal/role-selector.html index d93484c149..b4dd43fe73 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/modal/role-selector.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/modal/role-selector.html @@ -19,13 +19,12 @@ {{:: 'select-realm-role' | translate}}
-
-