KEYCLOAK-11472 Pagination support for clients

Co-authored-by: stianst <stianst@gmail.com>
This commit is contained in:
Cristian Schuszter 2019-09-18 00:04:03 +02:00 committed by Stian Thorgersen
parent a141aba85f
commit 5c7ce775cf
44 changed files with 753 additions and 379 deletions

View file

@ -50,10 +50,16 @@ public interface ClientsResource {
@Produces(MediaType.APPLICATION_JSON)
List<ClientRepresentation> findAll(@QueryParam("viewableOnly") boolean viewableOnly);
@GET
@Produces(MediaType.APPLICATION_JSON)
List<ClientRepresentation> 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<ClientRepresentation> findByClientId(@QueryParam("clientId") String clientId);
}

View file

@ -693,7 +693,6 @@ public class RealmAdapter implements CachedRealmModel {
@Override
public List<ClientModel> getClients() {
return cacheSession.getClients(this);
}
@Override
@ -722,6 +721,21 @@ public class RealmAdapter implements CachedRealmModel {
return cacheSession.getClientByClientId(clientId, this);
}
@Override
public List<ClientModel> searchClientByClientId(String clientId, Integer firstResult, Integer maxResults) {
return cacheSession.searchClientsByClientId(clientId, firstResult, maxResults, this);
}
@Override
public List<ClientModel> getClients(Integer firstResult, Integer maxResults) {
return cacheSession.getClients(this, firstResult, maxResults);
}
@Override
public Long getClientsCount() {
return cacheSession.getClientsCount(this);
}
@Override
public void updateRequiredCredentials(Set<String> creds) {
getDelegateForUpdate();

View file

@ -536,43 +536,16 @@ public class RealmCacheSession implements CacheRealmProvider {
return container + "." + name + ROLES_QUERY_SUFFIX;
}
@Override
public List<ClientModel> getClients(RealmModel realm, Integer firstResult, Integer maxResults) {
return getClientDelegate().getClients(realm, firstResult, maxResults);
}
@Override
public List<ClientModel> 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<ClientModel> model = getClientDelegate().getClients(realm);
if (model == null) return null;
Set<String> 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<ClientModel> 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;
}
@Override
public boolean removeClient(String id, RealmModel 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<ClientModel> searchClientsByClientId(String clientId, Integer firstResult, Integer maxResults, RealmModel realm) {
return getClientDelegate().searchClientsByClientId(clientId, firstResult, maxResults, realm);
}
@Override
public ClientModel getClientByClientId(String clientId, RealmModel realm) {

View file

@ -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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -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<ClientModel> getClients(RealmModel realm) {
public List<ClientModel> getClients(RealmModel realm, Integer firstResult, Integer maxResults) {
TypedQuery<String> 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<String> 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<ClientModel> 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<ClientModel> searchClientsByClientId(String clientId, Integer firstResult, Integer maxResults, RealmModel realm) {
TypedQuery<String> 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<String> 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);

View file

@ -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<RealmEntity> {
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<RealmEntity> {
}
}
@Override
public List<ClientModel> getClients() {
return session.realms().getClients(this);
}
@Override
public List<ClientModel> 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<RealmEntity> {
return session.realms().getClientByClientId(clientId, this);
}
@Override
public List<ClientModel> 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

View file

@ -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 {

View file

@ -27,6 +27,8 @@ import java.util.Set;
* @version $Revision: 1 $
*/
public interface ClientProvider extends ClientLookupProvider, Provider {
List<ClientModel> getClients(RealmModel realm, Integer firstResult, Integer maxResults);
List<ClientModel> getClients(RealmModel realm);
ClientModel addClient(RealmModel realm, String clientId);

View file

@ -252,6 +252,8 @@ public interface RealmModel extends RoleContainerModel {
void removeDefaultGroup(GroupModel group);
List<ClientModel> getClients();
List<ClientModel> getClients(Integer firstResult, Integer maxResults);
Long getClientsCount();
ClientModel addClient(String name);
@ -261,6 +263,7 @@ public interface RealmModel extends RoleContainerModel {
ClientModel getClientById(String id);
ClientModel getClientByClientId(String clientId);
List<ClientModel> searchClientByClientId(String clientId, Integer firstResult, Integer maxResults);
void updateRequiredCredentials(Set<String> creds);

View file

@ -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<GroupModel> getGroupsByRole(RealmModel realm, RoleModel role, int firstResult, int maxResults);

View file

@ -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<ClientModel> searchClientsByClientId(String clientId, Integer firstResult, Integer maxResults, RealmModel realm);
}

View file

@ -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<ClientRepresentation> getClients(@QueryParam("clientId") String clientId, @QueryParam("viewableOnly") @DefaultValue("false") boolean viewableOnly) {
public List<ClientRepresentation> 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<ClientRepresentation> rep = new ArrayList<>();
if (clientId == null || clientId.trim().equals("")) {
List<ClientModel> clientModels = realm.getClients();
List<ClientModel> clientModels = realm.getClients(firstResult, maxResults);
auth.clients().requireList();
boolean view = auth.clients().canView();
for (ClientModel clientModel : clientModels) {
@ -116,8 +121,17 @@ public class ClientsResource {
}
}
} else {
ClientModel clientModel = realm.getClientByClientId(clientId);
if (clientModel != null) {
List<ClientModel> 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));
@ -128,12 +142,12 @@ public class ClientsResource {
client.setClientId(clientModel.getClientId());
client.setDescription(clientModel.getDescription());
rep.add(client);
} else {
throw new ForbiddenException();
}
}
}
}
return rep;
}

View file

@ -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<ClientModel> searchClientsByClientId(String clientId, Integer firstResult, Integer maxResults, RealmModel realm) {
List<ClientModel> 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,6 +176,11 @@ public class ClientStorageManager implements ClientProvider {
@Override
public List<ClientModel> getClients(RealmModel realm, Integer firstResult, Integer maxResults) {
return session.clientLocalStorage().getClients(realm, firstResult, maxResults);
}
@Override
public List<ClientModel> getClients(RealmModel realm) {
return session.clientLocalStorage().getClients(realm);

View file

@ -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 <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
@ -73,6 +75,12 @@ public class OpenshiftClientStorageProvider implements ClientStorageProvider {
return new OpenshiftSAClientAdapter(clientId, resource, client, session, realm, providerModel);
}
@Override
public List<ClientModel> 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() {

View file

@ -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<ClientModel> 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) {

View file

@ -51,6 +51,9 @@ public class DataTable {
@FindBy(xpath = "(//table)[1]/tbody/tr[@class='ng-scope']")
private List<WebElement> 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<WebElement> rows() {
waitForPageToLoad();
pause(500); // wait a bit to ensure the table is no more changing

View file

@ -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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@ -154,6 +159,35 @@ public class ClientTest extends AbstractAdminTest {
assertFalse(allClients.isEmpty());
}
@Test
public void getAllClientsSearchAndPagination() {
Set<String> 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<ClientRepresentation> actual) {
List<String> expected = new LinkedList<>();
for (int i = start; i <= end; i++) {
expected.add("ccx-" + (i < 10 ? "0" + i : i));
}
List<String> a = actual.stream().map(rep -> rep.getClientId()).collect(Collectors.toList());
assertThat(a, is(expected));
}
@Test
public void getClientById() {
createClient();

View file

@ -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 <a href="mailto:psilva@redhat.com">Pedro Igor</a>

View file

@ -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);
}

View file

@ -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<WebElement> getSelectedElements() {
return getRoot().findElements(By.xpath("(//select[@id='available-client'])/option"));
}
@Override
protected Function<WebElement, String> representation() {
return webElement -> getTextFromElement(webElement.findElements(tagName("td")).get(0));
}
}

View file

@ -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<String> 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);
}

View file

@ -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;
@ -103,6 +104,9 @@ public class CreateClientMappersForm extends Form {
@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:

View file

@ -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,14 +42,12 @@ 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);
@ -53,6 +55,13 @@ public class ClientsTest extends AbstractClientTest {
clientSettingsPage.setId(found.getId());
}
private void create100Clients() {
List<ClientRepresentation> 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() {
//create
@ -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();
}
}

View file

@ -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

View file

@ -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',

View file

@ -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 = [];

View file

@ -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,27 +851,50 @@ module.controller('ClientImportCtrl', function($scope, $location, $upload, realm
});
module.controller('ClientListCtrl', function($scope, realm, Client, serverInfo, $route, Dialog, Notifications, filterFilter) {
module.controller('ClientListCtrl', function($scope, realm, Client, ClientListSearchState, $route, Dialog, Notifications) {
$scope.init = function() {
$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'];
$scope.searchLoaded = true;
Client.query({realm: realm.realm, viewableOnly: true}).$promise.then(function(clients) {
$scope.numberOfPages = Math.ceil(clients.length/$scope.pageSize);
$scope.clients = clients;
ClientListSearchState.query.realm = realm.realm;
$scope.query = ClientListSearchState.query;
if (!ClientListSearchState.isFirstSearch) {
$scope.searchQuery();
} else {
$scope.query.clientId = null;
$scope.firstPage();
}
};
$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.$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.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() {
@ -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,
@ -1662,7 +1676,17 @@ 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();
});

View file

@ -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);
});

View file

@ -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);
});

View file

@ -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) {

View file

@ -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
}
});
});

View file

@ -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,
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++) {

View file

@ -1,4 +1,5 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2" ng-init="init()">
<kc-tabs-clients></kc-tabs-clients>
<table class="datatable table table-striped table-bordered dataTable no-footer">
@ -8,9 +9,9 @@
<div class="form-inline">
<div class="form-group">
<div class="input-group">
<input type="text" placeholder="{{:: 'search.placeholder' | translate}}" data-ng-model="search.clientId" class="form-control search" onkeydown="if(event.keyCode === 13) document.getElementById('clientSearch').click()">
<input type="text" placeholder="{{:: 'search.placeholder' | translate}}" data-ng-model="query.clientId" class="form-control search" onkeydown="if (event.keyCode == 13) document.getElementById('clientSearch').click()">
<div class="input-group-addon">
<i class="fa fa-search" id="clientSearch" data-ng-click="searchClient()"></i>
<i class="fa fa-search" id="clientSearch" data-ng-click="firstPage()"></i>
</div>
</div>
</div>
@ -20,6 +21,9 @@
<a id="importClient" class="btn btn-default" href="#/import/client/{{realm.realm}}" data-ng-show="importButton">{{:: 'import' | translate}}</a>
</div>
</div>
<div data-ng-show="!searchLoaded" class="form-inline">
{{:: 'search.loading' | translate }}
</div>
</th>
</tr>
<tr data-ng-hide="clients.length == 0">
@ -29,8 +33,19 @@
<th colspan="3">{{:: 'actions' | translate}}</th>
</tr>
</thead>
<tfoot data-ng-show="clients && (clients.length >= query.max || query.first > 0)">
<tr>
<td colspan="6">
<div class="table-nav">
<button data-ng-click="firstPage()" class="first" ng-disabled="query.first == 0">{{:: 'first-page' | translate}}</button>
<button data-ng-click="previousPage()" class="prev" ng-disabled="query.first == 0">{{:: 'previous-page' | translate}}</button>
<button data-ng-click="nextPage()" class="next" ng-disabled="clients.length < query.max">{{:: 'next-page' | translate}}</button>
</div>
</td>
</tr>
</tfoot>
<tbody>
<tr ng-repeat="client in clients| filter:search | orderBy:'clientId' | startFrom:(currentPage - 1) * pageSize | limitTo:pageSize">
<tr ng-repeat="client in clients">
<td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{client.clientId}}</a></td>
<td translate="{{client.enabled}}"></td>
<td ng-class="{'text-muted': !client.baseUrl}">
@ -41,13 +56,13 @@
<td class="kc-action-cell" data-ng-click="exportClient(client)">{{:: 'export' | translate}}</td>
<td class="kc-action-cell" data-ng-show="client.access.manage" data-ng-click="removeClient(client)">{{:: 'delete' | translate}}</td>
</tr>
<tr data-ng-show="(clients | filter:search).length == 0">
<td class="text-muted" colspan="4" data-ng-show="search.clientId">{{:: 'no-results' | translate}}</td>
<td class="text-muted" colspan="4" data-ng-hide="search.clientId">{{:: 'no-clients-available' | translate}}</td>
<tr data-ng-show="!clients || clients.length == 0">
<td class="text-muted" data-ng-show="!clients" colspan="6">{{:: 'clients.instruction' | translate}}</td>
<td class="text-muted" data-ng-show="searchLoaded && clients.length == 0 && lastSearch != null">{{:: 'no-results' | translate}}</td>
<td class="text-muted" data-ng-show="searchLoaded && clients.length == 0 && lastSearch == null">{{:: 'no-clients-available' | translate}}</td>
</tr>
</tbody>
</table>
<kc-paging current-page='currentPage' number-of-pages='numberOfPages' current-page-input='currentPageInput'></kc-paging>
</div>
<kc-menu></kc-menu>

View file

@ -92,17 +92,16 @@
</div>
</div>
<div class="form-group" ng-show="clients.length > 0 && !create && (compositeSwitch || role.composite)">
<label class="col-md-2 control-label" class="control-label">
<span>{{:: 'client-roles' | translate}}</span>
<select class="form-control" id="clients" name="clients" ng-change="changeClient()" ng-model="compositeClient" ng-options="a.clientId for a in clients | orderBy:'clientId'" ng-disabled="false"></select>
</label>
<div class="col-md-10">
<div class="row" data-ng-hide="compositeClient">
<div class="col-md-4"><span class="text-muted">{{:: 'select-client-to-view-roles' | translate}}</span></div>
<div class="form-group" ng-show="!create && (compositeSwitch || role.composite)">
<label class="col-md-2 control-label" for="clients">{{:: 'client-roles' | translate}}</label>
<div class="col-md-6">
<input type="hidden" ui-select2="clientsUiSelect" id="clients" data-ng-model="selectedClient" data-ng-change="changeClient(selectedClient);" data-placeholder="{{:: 'authz-select-client' | translate}}...">
</input>
</div>
<div class="row" data-ng-show="compositeClient">
<div class="col-md-10 col-md-push-2">
<div class="row" data-ng-show="selectedClient">
<div class="col-md-4">
<label class="control-label" for="available-client">{{:: 'available-roles' | translate}}</label>
<kc-tooltip>{{:: 'available-roles.tooltip' | translate}}</kc-tooltip>

View file

@ -71,16 +71,14 @@
</div>
<div class="form-group">
<label class="col-md-2 control-label" class="control-label">
<span>{{:: 'client-roles' | translate}}</span>
<select class="form-control" id="clients" name="clients" ng-change="changeClient()" ng-model="targetClient" ng-options="a.clientId for a in clients | orderBy:'clientId'" ng-disabled="false"></select>
</label>
<div class="col-md-10">
<div class="row" data-ng-hide="targetClient">
<div class="col-md-4"><span class="text-muted">{{:: 'select-client-roles.tooltip' | translate}}</span></div>
<label class="col-md-2 control-label" for="clients">{{:: 'client-roles' | translate}}</label>
<div class="col-md-6">
<input type="hidden" ui-select2="clientsUiSelect" id="clients" data-ng-model="selectedClient" data-ng-change="selectClient(selectedClient);" data-placeholder="{{:: 'authz-select-client' | translate}}...">
</input>
</div>
<div class="row" data-ng-show="targetClient">
<div class="col-md-10 col-md-offset-2">
<div class="row" data-ng-show="selectedClient">
<div class="col-md-3">
<label class="control-label" for="available-client">{{:: 'available-roles' | translate}}</label>
<kc-tooltip>{{:: 'assign.available-roles.tooltip' | translate}}</kc-tooltip>

View file

@ -60,16 +60,14 @@
</div>
<div class="form-group">
<label class="col-md-2 control-label" class="control-label">
<span>{{:: 'client-roles' | translate}}</span>
<select class="form-control" id="clients" name="clients" ng-change="changeClient()" ng-model="targetClient" ng-options="a.clientId for a in clients | orderBy:'clientId'" ng-disabled="false"></select>
</label>
<div class="col-md-10">
<div class="row" data-ng-hide="targetClient">
<div class="col-md-4"><span class="text-muted">{{:: 'select-client-roles.tooltip' | translate}}</span></div>
<label class="col-md-2 control-label" for="clients">{{:: 'client-roles' | translate}}</label>
<div class="col-md-6">
<input type="hidden" ui-select2="clientsUiSelect" id="clients" data-ng-model="selectedClient" data-ng-change="changeClient(selectedClient);" data-placeholder="{{:: 'authz-select-client' | translate}}...">
</input>
</div>
<div class="row" data-ng-show="targetClient">
<div class="col-md-10 col-md-push-2">
<div class="row" data-ng-show="selectedClient">
<div class="col-md-3">
<label class="control-label" for="available-client">{{:: 'available-roles' | translate}}</label>
<kc-tooltip>{{:: 'assign.available-roles.tooltip' | translate}}</kc-tooltip>

View file

@ -208,16 +208,14 @@
</div>
<div class="form-group" data-ng-show="rolesShown()">
<label class="col-md-2 control-label" class="control-label">
<span>{{:: 'client-roles' | translate}}</span>
<select class="form-control" id="clients" name="clients" ng-change="changeClient()" ng-model="targetClient" ng-options="a.clientId for a in clients | orderBy:'clientId'" ng-disabled="false"></select>
</label>
<div class="col-md-10">
<div class="row" data-ng-hide="targetClient">
<div class="col-md-4"><span class="text-muted">{{:: 'select-client-roles.tooltip' | translate}}</span></div>
<label class="col-md-2 control-label" for="clients">{{:: 'client-roles' | translate}}</label>
<div class="col-md-6">
<input type="hidden" ui-select2="clientsUiSelect" id="clients" data-ng-model="selectedClient" data-ng-change="selectClient(selectedClient);" data-placeholder="{{:: 'authz-select-client' | translate}}...">
</input>
</div>
<div class="row" data-ng-show="targetClient">
<div class="col-md-10 col-md-offset-2">
<div class="row" data-ng-show="selectedClient">
<div class="col-md-4">
<label class="control-label" for="available-client">{{:: 'client-scopes.evaluate.not-granted-roles' | translate}}</label>
<kc-tooltip>{{:: 'client-scopes.evaluate.not-granted-roles.tooltip' | translate}}</kc-tooltip>

View file

@ -63,16 +63,14 @@
</div>
<div class="form-group">
<label class="col-md-2 control-label" class="control-label">
<span>{{:: 'client-roles' | translate}}</span>
<select class="form-control" id="clients" name="clients" ng-change="changeClient()" ng-model="targetClient" ng-options="a.clientId for a in clients | orderBy:'clientId'" ng-disabled="false"></select>
</label>
<div class="col-md-10">
<div class="row" data-ng-hide="targetClient">
<div class="col-md-4"><span class="text-muted">{{:: 'select-client-to-view-roles' | translate}}</span></div>
<label class="col-md-2 control-label" for="clients">{{:: 'client-roles' | translate}}</label>
<div class="col-md-6">
<input type="hidden" ui-select2="clientsUiSelect" id="clients" data-ng-model="selectedClient" data-ng-change="changeClient(selectedClient);" data-placeholder="{{:: 'authz-select-client' | translate}}...">
</input>
</div>
<div class="row" data-ng-show="targetClient">
<div class="col-md-10 col-md-push-2">
<div class="row" data-ng-show="selectedClient">
<div class="col-md-3">
<label class="control-label" for="available-client">{{:: 'available-roles' | translate}}</label>
<kc-tooltip>{{:: 'assign.available-roles.tooltip' | translate}}</kc-tooltip>

View file

@ -56,15 +56,13 @@
</div>
<div class="form-group">
<label class="col-md-2 control-label" class="control-label">
<span>{{:: 'client-roles' | translate}}</span>
<select class="form-control" id="clients" name="clients" ng-change="changeClient()" ng-model="targetClient" ng-options="a.clientId for a in clients | orderBy:'clientId'" ng-disabled="false"></select>
</label>
<div class="col-md-10" kc-read-only="!group.access.manage">
<div class="row" data-ng-hide="targetClient">
<div class="col-md-4"><span class="text-muted">{{:: 'select-client-to-view-roles' | translate}}</span></div>
<label class="col-md-2 control-label" for="clients">{{:: 'client-roles' | translate}}</label>
<div class="col-md-6">
<input type="hidden" ui-select2="clientsUiSelect" id="clients" data-ng-model="selectedClient" data-ng-change="changeClient(selectedClient);" data-placeholder="{{:: 'authz-select-client' | translate}}...">
</input>
</div>
<div class="row" data-ng-show="targetClient">
<div class="col-md-10 col-md-push-2">
<div class="row" data-ng-show="selectedClient" >
<div class="col-md-3">
<label class="control-label" for="available-client">{{:: 'available-roles' | translate}}</label>
<kc-tooltip>{{:: 'group.available-roles.tooltip' | translate}}</kc-tooltip>
@ -107,7 +105,6 @@
</div>
</div>
</div>
</div>
</form>
</div>

View file

@ -19,13 +19,12 @@
{{:: 'select-realm-role' | translate}}</i>
</button>
</div>
<div data-ng-show="clients.length > 0">
<label class="control-label">
<span>{{:: 'client-roles' | translate}}</span>
<div>
<label class="control-label" for="clients">{{:: 'client-roles' | translate}}
<kc-tooltip>{{:: 'client-roles.tooltip' | translate}}</kc-tooltip>
<select class="form-control" id="clients" name="clients" ng-change="changeClient()" ng-model="client.selected" ng-options="a.clientId for a in clients" ng-disabled="false">
</select>
</label>
<input type="hidden" style="margin-bottom: 10px;"ui-select2="clientsUiSelect" id="clients" data-ng-model="selectedClient" data-ng-change="changeClient(selectedClient);" data-placeholder="{{:: 'authz-select-client' | translate}}...">
</input>
<select id="available-client" class="form-control" size="5"
ng-dblclick="selectClientRole()"
ng-model="selectedClientRole.role"

View file

@ -44,15 +44,13 @@
</div>
<div class="form-group">
<label class="col-md-2 control-label" class="control-label">
<span>{{:: 'client-roles' | translate}}</span>
<select class="form-control" id="clients" name="clients" ng-change="changeClient()" ng-model="client" ng-options="a.clientId for a in clients | orderBy:'clientId'" ng-disabled="false"></select>
</label>
<div class="col-md-10" data-ng-show="client">
<div class="row" data-ng-hide="client">
<div class="col-md-4"><span class="text-muted">{{:: 'select-client-to-view-roles' | translate}}</span></div>
<label class="col-md-2 control-label" for="clients">{{:: 'client-roles' | translate}}</label>
<div class="col-md-6">
<input type="hidden" ui-select2="clientsUiSelect" id="clients" data-ng-model="selectedClient" data-ng-change="changeClient(selectedClient);" data-placeholder="{{:: 'authz-select-client' | translate}}...">
</input>
</div>
<div class="row" data-ng-show="client">
<div class="col-md-10 col-md-push-2">
<div class="row" data-ng-show="selectedClient">
<div class="col-md-4">
<label class="control-label" for="available-client">{{:: 'available-roles' | translate}}</label>
<kc-tooltip>{{:: 'default.available-roles-client.tooltip' | translate}}</kc-tooltip>
@ -73,7 +71,7 @@
<select id="assigned-client" class="form-control overflow-select" multiple size=5
ng-multiple="true"
ng-model="selectedClientDefRoles">
<option ng-repeat="r in client.defaultRoles | orderBy:'toString()'" value="{{r}}" title="{{r.toString()}}">
<option ng-repeat="r in selectedClient.defaultRoles | orderBy:'toString()'" value="{{r}}" title="{{r.toString()}}">
{{r.toString()}}
</option>
</select>

View file

@ -88,16 +88,15 @@
</div>
<div class="form-group">
<label class="col-md-2 control-label" class="control-label">
<span>{{:: 'client-roles' | translate}}</span>
<select class="form-control" id="clients" name="clients" ng-change="changeClient()" ng-model="compositeClient" ng-options="a.clientId for a in clients | orderBy:'clientId'" ng-disabled="false"></select>
</label>
<div class="col-md-10">
<div class="row" data-ng-hide="compositeClient">
<div class="col-md-4"><span class="text-muted">{{:: 'select-client-to-view-roles' | translate}}</span></div>
<label class="col-md-2 control-label" for="clients">{{:: 'client-roles' | translate}}</label>
<div class="col-md-6">
<input type="hidden" ui-select2="clientsUiSelect" id="clients" data-ng-model="selectedClient" data-ng-change="changeClient(selectedClient);" data-placeholder="{{:: 'authz-select-client' | translate}}...">
</input>
</div>
<div class="row" data-ng-show="compositeClient">
<div class="col-md-10 col-md-push-2">
<div class="row" data-ng-show="selectedClient">
<div class="col-md-4">
<label class="control-label" for="available-client">{{:: 'available-roles' | translate}}</label>
<kc-tooltip>{{:: 'composite.available-roles-client.tooltip' | translate}}</kc-tooltip>

View file

@ -59,15 +59,15 @@
</div>
<div class="form-group">
<label class="col-md-2 control-label" class="control-label">
<span>{{:: 'client-roles' | translate}}</span>
<select class="form-control" id="clients" name="clients" ng-change="changeClient()" ng-model="targetClient" ng-options="a.clientId for a in clients | orderBy:'clientId'" ng-disabled="false"></select>
</label>
<div class="col-md-10" kc-read-only="!user.access.mapRoles">
<div class="row" data-ng-hide="targetClient">
<div class="col-md-4"><span class="text-muted">{{:: 'select-client-to-view-roles' | translate}}</span></div>
<label class="col-md-2 control-label" for="clients">{{:: 'client-roles' | translate}}</label>
<div class="col-md-6">
<input type="hidden" ui-select2="clientsUiSelect" id="clients" data-ng-model="selectedClient" data-ng-change="changeClient(selectedClient);" data-placeholder="{{:: 'authz-select-client' | translate}}...">
</input>
</div>
<div class="row" data-ng-show="targetClient">
<div class="col-md-10 col-md-push-2" kc-read-only="!user.access.mapRoles">
<div class="row" data-ng-show="selectedClient">
<div class="col-md-3">
<label class="control-label" for="available-client">{{:: 'available-roles' | translate}}</label>
<kc-tooltip>{{:: 'user.available-roles.tooltip' | translate}}</kc-tooltip>

View file

@ -30,9 +30,8 @@
</div>
</div>
<div class="col-md-4" data-ng-if="option.type == 'ClientList'">
<select ng-model="config[ option.name ][0]" ng-options="client.clientId as client.clientId for client in clients">
<option value="" selected> {{:: 'selectOne' | translate}} </option>
</select>
<input type="hidden" ui-select2="clientsUiSelect" id="clients" data-ng-init="initSelectedClient(option.name, config)" data-ng-model="selectedClient" data-ng-change="changeClient(option.name, config, selectedClient);" data-placeholder="{{:: 'selectOne' | translate}}...">
</input>
</div>
<div class="col-md-6" data-ng-if="option.type == 'File'">

View file

@ -27,9 +27,8 @@
</div>
</div>
<div class="col-md-4" data-ng-if="option.type == 'ClientList'">
<select ng-model="config[ option.name ]" ng-options="client.clientId as client.clientId for client in clients">
<option value="" selected> {{:: 'selectOne' | translate}} </option>
</select>
<input type="hidden" ui-select2="clientsUiSelect" id="clients" data-ng-init="initSelectedClient(option.name, config)" data-ng-model="selectedClient" data-ng-change="changeClient(option.name, config, selectedClient);" data-placeholder="{{:: 'selectOne' | translate}}...">
</input>
</div>
<div class="col-md-6" data-ng-if="option.type == 'Script'">