[KEYCLOAK-5628] Added application endpoint

This commit is contained in:
Douglas Palmer 2019-06-26 13:20:37 -07:00 committed by Bruno Oliveira da Silva
parent 66f5d1259f
commit f9fa5b551d
7 changed files with 298 additions and 91 deletions

View file

@ -97,6 +97,9 @@ public final class StringPropertyReplacer
*/ */
public static String replaceProperties(final String string, final Properties props) public static String replaceProperties(final String string, final Properties props)
{ {
if(string == null) {
return null;
}
final char[] chars = string.toCharArray(); final char[] chars = string.toCharArray();
StringBuilder buffer = new StringBuilder(); StringBuilder buffer = new StringBuilder();
boolean properties = false; boolean properties = false;

View file

@ -6,6 +6,12 @@ package org.keycloak.representations.account;
public class ClientRepresentation { public class ClientRepresentation {
private String clientId; private String clientId;
private String clientName; private String clientName;
private String description;
private boolean userConsentRequired;
private boolean inUse;
private boolean offlineAccess;
private String baseUrl;
private ConsentRepresentation consent;
public String getClientId() { public String getClientId() {
return clientId; return clientId;
@ -22,4 +28,52 @@ public class ClientRepresentation {
public void setClientName(String clientName) { public void setClientName(String clientName) {
this.clientName = clientName; this.clientName = clientName;
} }
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public boolean isUserConsentRequired() {
return userConsentRequired;
}
public void setUserConsentRequired(boolean userConsentRequired) {
this.userConsentRequired = userConsentRequired;
}
public boolean isInUse() {
return inUse;
}
public void setInUse(boolean inUse) {
this.inUse = inUse;
}
public boolean isOfflineAccess() {
return offlineAccess;
}
public void setOfflineAccess(boolean offlineAccess) {
this.offlineAccess = offlineAccess;
}
public String getBaseUrl() {
return baseUrl;
}
public void setBaseUrl(String baseUrl) {
this.baseUrl = baseUrl;
}
public ConsentRepresentation getConsent() {
return consent;
}
public void setConsent(ConsentRepresentation consent) {
this.consent = consent;
}
} }

View file

@ -21,7 +21,7 @@ import java.util.List;
public class ConsentRepresentation { public class ConsentRepresentation {
private List<ConsentScopeRepresentation> scopes; private List<ConsentScopeRepresentation> grantedScopes;
private Long createdDate; private Long createdDate;
@ -30,18 +30,18 @@ public class ConsentRepresentation {
public ConsentRepresentation() { public ConsentRepresentation() {
} }
public ConsentRepresentation(List<ConsentScopeRepresentation> scopes, Long createdDate, Long lastUpdatedDate) { public ConsentRepresentation(List<ConsentScopeRepresentation> grantedScopes, Long createdDate, Long lastUpdatedDate) {
this.scopes = scopes; this.grantedScopes = grantedScopes;
this.createdDate = createdDate; this.createdDate = createdDate;
this.lastUpdatedDate = lastUpdatedDate; this.lastUpdatedDate = lastUpdatedDate;
} }
public List<ConsentScopeRepresentation> getScopes() { public List<ConsentScopeRepresentation> getGrantedScopes() {
return scopes; return grantedScopes;
} }
public void setScopes(List<ConsentScopeRepresentation> scopes) { public void setGrantedScopes(List<ConsentScopeRepresentation> grantedScopes) {
this.scopes = scopes; this.grantedScopes = grantedScopes;
} }
public Long getCreatedDate() { public Long getCreatedDate() {

View file

@ -19,17 +19,20 @@ package org.keycloak.services.resources.account;
import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.common.ClientConnection; import org.keycloak.common.ClientConnection;
import org.keycloak.common.util.StringPropertyReplacer;
import org.keycloak.events.Details; import org.keycloak.events.Details;
import org.keycloak.events.EventBuilder; import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventStoreProvider; import org.keycloak.events.EventStoreProvider;
import org.keycloak.events.EventType; import org.keycloak.events.EventType;
import org.keycloak.models.AccountRoles; import org.keycloak.models.AccountRoles;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientScopeModel; import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.UserConsentModel; import org.keycloak.models.UserConsentModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.representations.account.ClientRepresentation; import org.keycloak.representations.account.ClientRepresentation;
import org.keycloak.representations.account.ConsentRepresentation; import org.keycloak.representations.account.ConsentRepresentation;
import org.keycloak.representations.account.ConsentScopeRepresentation; import org.keycloak.representations.account.ConsentScopeRepresentation;
@ -56,10 +59,15 @@ import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import java.io.IOException; import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.keycloak.common.Profile; import org.keycloak.common.Profile;
@ -242,28 +250,6 @@ public class AccountRestService {
// TODO Federated identities // TODO Federated identities
/**
* Returns the list of available applications in the specified
* realm.
*
* @return list of applications in that realm
*/
@Path("/applications")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getApplications() {
checkAccountApiEnabled();
auth.require(AccountRoles.VIEW_APPLICATIONS);
List<ClientModel> clients = realm.getClients();
List<ClientRepresentation> clientRepresentations = clients.stream()
.map(this::modelToRepresentation)
.collect(Collectors.toList());
return Cors.add(request, Response.ok(clientRepresentations)).build();
}
/** /**
* Returns the applications with the given id in the specified realm. * Returns the applications with the given id in the specified realm.
* *
@ -275,42 +261,56 @@ public class AccountRestService {
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response getApplication(final @PathParam("clientId") String clientId) { public Response getApplication(final @PathParam("clientId") String clientId) {
checkAccountApiEnabled(); checkAccountApiEnabled();
auth.require(AccountRoles.VIEW_APPLICATIONS); auth.requireOneOf(AccountRoles.MANAGE_ACCOUNT, AccountRoles.VIEW_APPLICATIONS);
ClientModel client = realm.getClientByClientId(clientId); ClientModel client = realm.getClientByClientId(clientId);
if (client == null) { if (client == null || client.isBearerOnly() || client.getBaseUrl() == null) {
return Cors.add(request, Response.status(Response.Status.NOT_FOUND).entity("No client with clientId: " + clientId + " found.")).build(); return Cors.add(request, Response.status(Response.Status.NOT_FOUND).entity("No client with clientId: " + clientId + " found.")).build();
} }
return Cors.add(request, Response.ok(modelToRepresentation(client))).build(); List<String> inUseClients = new LinkedList<>();
if(!session.sessions().getUserSessions(realm, client).isEmpty()) {
inUseClients.add(clientId);
}
List<String> offlineClients = new LinkedList<>();
if(session.sessions().getOfflineSessionsCount(realm, client) > 0) {
offlineClients.add(clientId);
}
UserConsentModel consentModel = session.users().getConsentByClient(realm, user.getId(), client.getId());
Map<String, UserConsentModel> consentModels = Collections.singletonMap(client.getClientId(), consentModel);
return Cors.add(request, Response.ok(modelToRepresentation(client, inUseClients, offlineClients, consentModels))).build();
} }
private ClientRepresentation modelToRepresentation(ClientModel model) { private ClientRepresentation modelToRepresentation(ClientModel model, List<String> inUseClients, List<String> offlineClients, Map<String, UserConsentModel> consents) {
ClientRepresentation representation = new ClientRepresentation(); ClientRepresentation representation = new ClientRepresentation();
representation.setClientId(model.getClientId()); representation.setClientId(model.getClientId());
representation.setClientName(getTranslationOrDefault(model.getName())); representation.setClientName(StringPropertyReplacer.replaceProperties(model.getName(), getProperties()));
representation.setDescription(model.getDescription());
representation.setUserConsentRequired(model.isConsentRequired());
representation.setInUse(inUseClients.contains(model.getClientId()));
representation.setOfflineAccess(offlineClients.contains(model.getClientId()));
representation.setBaseUrl(model.getBaseUrl());
UserConsentModel consentModel = consents.get(client.getClientId());
if(consentModel != null) {
representation.setConsent(modelToRepresentation(consentModel));
}
return representation; return representation;
} }
private ConsentRepresentation modelToRepresentation(UserConsentModel model) { private ConsentRepresentation modelToRepresentation(UserConsentModel model) {
List<ConsentScopeRepresentation> scopes = model.getGrantedClientScopes().stream() List<ConsentScopeRepresentation> grantedScopes = model.getGrantedClientScopes().stream()
.map(m -> new ConsentScopeRepresentation(m.getId(), m.getName(), getTranslationOrDefault(m.getConsentScreenText()))) .map(m -> new ConsentScopeRepresentation(m.getId(), m.getName(), StringPropertyReplacer.replaceProperties(m.getConsentScreenText(), getProperties())))
.collect(Collectors.toList()); .collect(Collectors.toList());
return new ConsentRepresentation(scopes, model.getCreatedDate(), model.getLastUpdatedDate()); return new ConsentRepresentation(grantedScopes, model.getCreatedDate(), model.getLastUpdatedDate());
} }
private String getTranslationOrDefault(String key) { private Properties getProperties() {
if (key == null) {
return null;
}
String defaultValue = key;
if (key.startsWith("${")) {
key = key.substring(2, key.length() - 1);
}
try { try {
Properties messages = session.theme().getTheme(Theme.Type.ACCOUNT).getMessages(locale); return session.theme().getTheme(Theme.Type.ACCOUNT).getMessages(locale);
return messages.getProperty(key, defaultValue);
} catch (IOException e) { } catch (IOException e) {
return key; return null;
} }
} }
@ -325,7 +325,7 @@ public class AccountRestService {
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response getConsent(final @PathParam("clientId") String clientId) { public Response getConsent(final @PathParam("clientId") String clientId) {
checkAccountApiEnabled(); checkAccountApiEnabled();
auth.requireOneOf(AccountRoles.VIEW_CONSENT, AccountRoles.MANAGE_CONSENT); auth.requireOneOf(AccountRoles.MANAGE_ACCOUNT, AccountRoles.VIEW_CONSENT, AccountRoles.MANAGE_CONSENT);
ClientModel client = realm.getClientByClientId(clientId); ClientModel client = realm.getClientByClientId(clientId);
if (client == null) { if (client == null) {
@ -350,7 +350,7 @@ public class AccountRestService {
@DELETE @DELETE
public Response revokeConsent(final @PathParam("clientId") String clientId) { public Response revokeConsent(final @PathParam("clientId") String clientId) {
checkAccountApiEnabled(); checkAccountApiEnabled();
auth.require(AccountRoles.MANAGE_CONSENT); auth.requireOneOf(AccountRoles.MANAGE_ACCOUNT, AccountRoles.MANAGE_CONSENT);
event.event(EventType.REVOKE_GRANT); event.event(EventType.REVOKE_GRANT);
ClientModel client = realm.getClientByClientId(clientId); ClientModel client = realm.getClientByClientId(clientId);
@ -420,7 +420,7 @@ public class AccountRestService {
*/ */
private Response upsert(String clientId, ConsentRepresentation consent) { private Response upsert(String clientId, ConsentRepresentation consent) {
checkAccountApiEnabled(); checkAccountApiEnabled();
auth.require(AccountRoles.MANAGE_CONSENT); auth.requireOneOf(AccountRoles.MANAGE_ACCOUNT, AccountRoles.MANAGE_CONSENT);
event.event(EventType.GRANT_CONSENT); event.event(EventType.GRANT_CONSENT);
ClientModel client = realm.getClientByClientId(clientId); ClientModel client = realm.getClientByClientId(clientId);
@ -463,7 +463,7 @@ public class AccountRestService {
availableGrants.put(client.getId(), client); availableGrants.put(client.getId(), client);
} }
for (ConsentScopeRepresentation scopeRepresentation : requested.getScopes()) { for (ConsentScopeRepresentation scopeRepresentation : requested.getGrantedScopes()) {
ClientScopeModel scopeModel = availableGrants.get(scopeRepresentation.getId()); ClientScopeModel scopeModel = availableGrants.get(scopeRepresentation.getId());
if (scopeModel == null) { if (scopeModel == null) {
String msg = String.format("Scope id %s does not exist for client %s.", scopeRepresentation, consent.getClient().getName()); String msg = String.format("Scope id %s does not exist for client %s.", scopeRepresentation, consent.getClient().getName());
@ -481,6 +481,54 @@ public class AccountRestService {
return new LinkedAccountsResource(session, request, client, auth, event, user); return new LinkedAccountsResource(session, request, client, auth, event, user);
} }
@Path("/applications")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public Response applications() {
checkAccountApiEnabled();
auth.requireOneOf(AccountRoles.MANAGE_ACCOUNT, AccountRoles.VIEW_APPLICATIONS);
Set<ClientModel> clients = new HashSet<ClientModel>();
List<String> inUseClients = new LinkedList<String>();
List<UserSessionModel> sessions = session.sessions().getUserSessions(realm, user);
for(UserSessionModel s : sessions) {
for (AuthenticatedClientSessionModel a : s.getAuthenticatedClientSessions().values()) {
ClientModel client = a.getClient();
clients.add(client);
inUseClients.add(client.getClientId());
}
}
List<String> offlineClients = new LinkedList<String>();
List<UserSessionModel> offlineSessions = session.sessions().getOfflineUserSessions(realm, user);
for(UserSessionModel s : offlineSessions) {
for(AuthenticatedClientSessionModel a : s.getAuthenticatedClientSessions().values()) {
ClientModel client = a.getClient();
clients.add(client);
offlineClients.add(client.getClientId());
}
}
Map<String, UserConsentModel> consentModels = new HashMap<String, UserConsentModel>();
List<UserConsentModel> consents = session.users().getConsents(realm, user.getId());
for (UserConsentModel consent : consents) {
ClientModel client = consent.getClient();
clients.add(client);
consentModels.put(client.getClientId(), consent);
}
List<ClientRepresentation> apps = new LinkedList<ClientRepresentation>();
for (ClientModel client : clients) {
if (client.isBearerOnly() || client.getBaseUrl() == null) {
continue;
}
apps.add(modelToRepresentation(client, inUseClients, offlineClients, consentModels));
}
return Cors.add(request, Response.ok(apps)).auth().allowedOrigins(auth.getToken()).build();
}
// TODO Logs // TODO Logs
private static void checkAccountApiEnabled() { private static void checkAccountApiEnabled() {

View file

@ -19,8 +19,8 @@ package org.keycloak.testsuite.account;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static org.keycloak.common.Profile.Feature.ACCOUNT_API; import static org.keycloak.common.Profile.Feature.ACCOUNT_API;
import static org.keycloak.testsuite.util.OAuthClient.APP_ROOT;
import javax.ws.rs.core.Response;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
@ -32,12 +32,14 @@ import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.keycloak.broker.provider.util.SimpleHttp; import org.keycloak.broker.provider.util.SimpleHttp;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.representations.account.SessionRepresentation; import org.keycloak.representations.account.SessionRepresentation;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest; import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.arquillian.annotation.DisableFeature; import org.keycloak.testsuite.arquillian.annotation.DisableFeature;
import org.keycloak.testsuite.arquillian.annotation.EnableFeature; import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
import org.keycloak.testsuite.util.ClientBuilder;
import org.keycloak.testsuite.util.TokenUtil; import org.keycloak.testsuite.util.TokenUtil;
import org.keycloak.testsuite.util.UserBuilder; import org.keycloak.testsuite.util.UserBuilder;
@ -55,6 +57,10 @@ public abstract class AbstractRestServiceTest extends AbstractTestRealmKeycloakT
protected CloseableHttpClient httpClient; protected CloseableHttpClient httpClient;
protected String inUseClientAppUri = APP_ROOT + "/in-use-client";
protected String offlineClientAppUri = APP_ROOT + "/offline-client";
@Before @Before
public void before() { public void before() {
httpClient = HttpClientBuilder.create().build(); httpClient = HttpClientBuilder.create().build();
@ -73,9 +79,25 @@ public abstract class AbstractRestServiceTest extends AbstractTestRealmKeycloakT
public void configureTestRealm(RealmRepresentation testRealm) { public void configureTestRealm(RealmRepresentation testRealm) {
testRealm.getUsers().add(UserBuilder.create().username("no-account-access").password("password").build()); testRealm.getUsers().add(UserBuilder.create().username("no-account-access").password("password").build());
testRealm.getUsers().add(UserBuilder.create().username("view-account-access").role("account", "view-profile").password("password").build()); testRealm.getUsers().add(UserBuilder.create().username("view-account-access").role("account", "view-profile").password("password").build());
testRealm.getUsers().add(UserBuilder.create().username("view-applications-access").role("account", "view-applications").password("password").build()); testRealm.getUsers().add(UserBuilder.create().username("view-applications-access").addRoles("user", "offline_access").role("account", "view-applications").role("account", "manage-consent").password("password").build());
testRealm.getUsers().add(UserBuilder.create().username("view-consent-access").role("account", "view-consent").password("password").build()); testRealm.getUsers().add(UserBuilder.create().username("view-consent-access").role("account", "view-consent").password("password").build());
testRealm.getUsers().add(UserBuilder.create().username("manage-consent-access").role("account", "manage-consent").password("password").build()); testRealm.getUsers().add(UserBuilder.create().username("manage-consent-access").role("account", "manage-consent").password("password").build());
org.keycloak.representations.idm.ClientRepresentation inUseApp = ClientBuilder.create().clientId("in-use-client")
.id(KeycloakModelUtils.generateId())
.name("In Use Client")
.baseUrl(inUseClientAppUri)
.directAccessGrants()
.secret("secret1").build();
testRealm.getClients().add(inUseApp);
org.keycloak.representations.idm.ClientRepresentation offlineApp = ClientBuilder.create().clientId("offline-client")
.id(KeycloakModelUtils.generateId())
.name("Offline Client")
.baseUrl(offlineClientAppUri)
.directAccessGrants()
.secret("secret1").build();
testRealm.getClients().add(offlineApp);
} }
protected String getAccountUrl(String resource) { protected String getAccountUrl(String resource) {

View file

@ -17,7 +17,9 @@
package org.keycloak.testsuite.account; package org.keycloak.testsuite.account;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.broker.provider.util.SimpleHttp; import org.keycloak.broker.provider.util.SimpleHttp;
import org.keycloak.representations.account.ClientRepresentation; import org.keycloak.representations.account.ClientRepresentation;
import org.keycloak.representations.account.ConsentRepresentation; import org.keycloak.representations.account.ConsentRepresentation;
@ -30,7 +32,7 @@ import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.messages.Messages; import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.account.AccountCredentialResource; import org.keycloak.services.resources.account.AccountCredentialResource;
import org.keycloak.services.resources.account.AccountCredentialResource.PasswordUpdate; import org.keycloak.services.resources.account.AccountCredentialResource.PasswordUpdate;
import org.keycloak.testsuite.arquillian.annotation.EnableFeature; import org.keycloak.testsuite.util.OAuthClient;
import org.keycloak.testsuite.util.TokenUtil; import org.keycloak.testsuite.util.TokenUtil;
import java.io.IOException; import java.io.IOException;
@ -38,16 +40,15 @@ import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.keycloak.common.Profile.Feature.ACCOUNT_API;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/ */
public class AccountRestServiceTest extends AbstractRestServiceTest { public class AccountRestServiceTest extends AbstractRestServiceTest {
@Test @Test
public void testGetProfile() throws IOException { public void testGetProfile() throws IOException {
UserRepresentation user = SimpleHttp.doGet(getAccountUrl(null), httpClient).auth(tokenUtil.getToken()).asJson(UserRepresentation.class); UserRepresentation user = SimpleHttp.doGet(getAccountUrl(null), httpClient).auth(tokenUtil.getToken()).asJson(UserRepresentation.class);
@ -285,7 +286,11 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
} }
@Test @Test
public void listApplications() throws IOException { public void listApplications() throws Exception {
oauth.clientId("in-use-client");
OAuthClient.AccessTokenResponse tokenResponse = oauth.doGrantAccessTokenRequest("secret1", "view-applications-access", "password");
Assert.assertNull(tokenResponse.getErrorDescription());
TokenUtil token = new TokenUtil("view-applications-access", "password"); TokenUtil token = new TokenUtil("view-applications-access", "password");
List<ClientRepresentation> applications = SimpleHttp List<ClientRepresentation> applications = SimpleHttp
.doGet(getAccountUrl("applications"), httpClient) .doGet(getAccountUrl("applications"), httpClient)
@ -294,11 +299,86 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
.asJson(new TypeReference<List<ClientRepresentation>>() { .asJson(new TypeReference<List<ClientRepresentation>>() {
}); });
assertFalse(applications.isEmpty()); assertFalse(applications.isEmpty());
Map<String, ClientRepresentation> apps = applications.stream().collect(Collectors.toMap(x -> x.getClientId(), x -> x));
Assert.assertThat(apps.keySet(), containsInAnyOrder("in-use-client"));
assertClientRep(apps.get("in-use-client"), "In Use Client", null, false, true, false, inUseClientAppUri);
}
@Test
public void listApplicationsOfflineAccess() throws Exception {
oauth.scope(OAuth2Constants.OFFLINE_ACCESS);
oauth.clientId("offline-client");
OAuthClient.AccessTokenResponse offlineTokenResponse = oauth.doGrantAccessTokenRequest("secret1", "view-applications-access", "password");
Assert.assertNull(offlineTokenResponse.getErrorDescription());
TokenUtil token = new TokenUtil("view-applications-access", "password");
List<ClientRepresentation> applications = SimpleHttp
.doGet(getAccountUrl("applications"), httpClient)
.header("Accept", "application/json")
.auth(token.getToken())
.asJson(new TypeReference<List<ClientRepresentation>>() {
});
assertFalse(applications.isEmpty());
Map<String, ClientRepresentation> apps = applications.stream().collect(Collectors.toMap(x -> x.getClientId(), x -> x));
Assert.assertThat(apps.keySet(), containsInAnyOrder("offline-client"));
assertClientRep(apps.get("offline-client"), "Offline Client", null, false, true, true, offlineClientAppUri);
}
@Test
public void listApplicationsThirdParty() throws Exception {
String appId = "third-party";
TokenUtil token = new TokenUtil("view-applications-access", "password");
ClientScopeRepresentation clientScopeRepresentation = testRealm().clientScopes().findAll().get(0);
ConsentScopeRepresentation consentScopeRepresentation = new ConsentScopeRepresentation();
consentScopeRepresentation.setId(clientScopeRepresentation.getId());
ConsentRepresentation requestedConsent = new ConsentRepresentation();
requestedConsent.setGrantedScopes(Collections.singletonList(consentScopeRepresentation));
SimpleHttp
.doPost(getAccountUrl("applications/" + appId + "/consent"), httpClient)
.header("Accept", "application/json")
.json(requestedConsent)
.auth(token.getToken())
.asJson(ConsentRepresentation.class);
List<ClientRepresentation> applications = SimpleHttp
.doGet(getAccountUrl("applications"), httpClient)
.header("Accept", "application/json")
.auth(token.getToken())
.asJson(new TypeReference<List<ClientRepresentation>>() {
});
assertFalse(applications.isEmpty());
SimpleHttp
.doDelete(getAccountUrl("applications/" + appId + "/consent"), httpClient)
.header("Accept", "application/json")
.auth(token.getToken())
.asResponse();
Map<String, ClientRepresentation> apps = applications.stream().collect(Collectors.toMap(x -> x.getClientId(), x -> x));
Assert.assertThat(apps.keySet(), containsInAnyOrder(appId));
assertClientRep(apps.get(appId), null, "A third party application", true, false, false, "http://localhost:8180/auth/realms/master/app/auth");
}
private void assertClientRep(ClientRepresentation clientRep, String name, String description, boolean userConsentRequired, boolean inUse, boolean offlineAccess, String baseUrl) {
assertNotNull(clientRep);
assertEquals(name, clientRep.getClientName());
assertEquals(description, clientRep.getDescription());
assertEquals(userConsentRequired, clientRep.isUserConsentRequired());
assertEquals(inUse, clientRep.isInUse());
assertEquals(offlineAccess, clientRep.isOfflineAccess());
assertEquals(baseUrl, clientRep.getBaseUrl());
} }
@Test @Test
public void listApplicationsWithoutPermission() throws IOException { public void listApplicationsWithoutPermission() throws IOException {
TokenUtil token = new TokenUtil("view-account-access", "password"); TokenUtil token = new TokenUtil("no-account-access", "password");
SimpleHttp.Response response = SimpleHttp SimpleHttp.Response response = SimpleHttp
.doGet(getAccountUrl("applications"), httpClient) .doGet(getAccountUrl("applications"), httpClient)
.header("Accept", "application/json") .header("Accept", "application/json")
@ -321,7 +401,7 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
@Test @Test
public void getWebConsoleApplicationWithoutPermission() throws IOException { public void getWebConsoleApplicationWithoutPermission() throws IOException {
TokenUtil token = new TokenUtil("view-account-access", "password"); TokenUtil token = new TokenUtil("no-account-access", "password");
String appId = "security-admin-console"; String appId = "security-admin-console";
SimpleHttp.Response response = SimpleHttp SimpleHttp.Response response = SimpleHttp
.doGet(getAccountUrl("applications/" + appId), httpClient) .doGet(getAccountUrl("applications/" + appId), httpClient)
@ -353,7 +433,7 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
consentScopeRepresentation.setId(clientScopeRepresentation.getId()); consentScopeRepresentation.setId(clientScopeRepresentation.getId());
ConsentRepresentation requestedConsent = new ConsentRepresentation(); ConsentRepresentation requestedConsent = new ConsentRepresentation();
requestedConsent.setScopes(Collections.singletonList(consentScopeRepresentation)); requestedConsent.setGrantedScopes(Collections.singletonList(consentScopeRepresentation));
ConsentRepresentation consentRepresentation = SimpleHttp ConsentRepresentation consentRepresentation = SimpleHttp
.doPost(getAccountUrl("applications/" + appId + "/consent"), httpClient) .doPost(getAccountUrl("applications/" + appId + "/consent"), httpClient)
@ -363,8 +443,8 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
.asJson(ConsentRepresentation.class); .asJson(ConsentRepresentation.class);
assertTrue(consentRepresentation.getCreatedDate() > 0); assertTrue(consentRepresentation.getCreatedDate() > 0);
assertTrue(consentRepresentation.getLastUpdatedDate() > 0); assertTrue(consentRepresentation.getLastUpdatedDate() > 0);
assertEquals(1, consentRepresentation.getScopes().size()); assertEquals(1, consentRepresentation.getGrantedScopes().size());
assertEquals(consentScopeRepresentation.getId(), consentRepresentation.getScopes().get(0).getId()); assertEquals(consentScopeRepresentation.getId(), consentRepresentation.getGrantedScopes().get(0).getId());
} }
@Test @Test
@ -377,7 +457,7 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
consentScopeRepresentation.setId(clientScopeRepresentation.getId()); consentScopeRepresentation.setId(clientScopeRepresentation.getId());
ConsentRepresentation requestedConsent = new ConsentRepresentation(); ConsentRepresentation requestedConsent = new ConsentRepresentation();
requestedConsent.setScopes(Collections.singletonList(consentScopeRepresentation)); requestedConsent.setGrantedScopes(Collections.singletonList(consentScopeRepresentation));
ConsentRepresentation consentRepresentation = SimpleHttp ConsentRepresentation consentRepresentation = SimpleHttp
.doPost(getAccountUrl("applications/" + appId + "/consent"), httpClient) .doPost(getAccountUrl("applications/" + appId + "/consent"), httpClient)
@ -387,15 +467,15 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
.asJson(ConsentRepresentation.class); .asJson(ConsentRepresentation.class);
assertTrue(consentRepresentation.getCreatedDate() > 0); assertTrue(consentRepresentation.getCreatedDate() > 0);
assertTrue(consentRepresentation.getLastUpdatedDate() > 0); assertTrue(consentRepresentation.getLastUpdatedDate() > 0);
assertEquals(1, consentRepresentation.getScopes().size()); assertEquals(1, consentRepresentation.getGrantedScopes().size());
assertEquals(consentScopeRepresentation.getId(), consentRepresentation.getScopes().get(0).getId()); assertEquals(consentScopeRepresentation.getId(), consentRepresentation.getGrantedScopes().get(0).getId());
clientScopeRepresentation = testRealm().clientScopes().findAll().get(1); clientScopeRepresentation = testRealm().clientScopes().findAll().get(1);
consentScopeRepresentation = new ConsentScopeRepresentation(); consentScopeRepresentation = new ConsentScopeRepresentation();
consentScopeRepresentation.setId(clientScopeRepresentation.getId()); consentScopeRepresentation.setId(clientScopeRepresentation.getId());
requestedConsent = new ConsentRepresentation(); requestedConsent = new ConsentRepresentation();
requestedConsent.setScopes(Collections.singletonList(consentScopeRepresentation)); requestedConsent.setGrantedScopes(Collections.singletonList(consentScopeRepresentation));
ConsentRepresentation consentRepresentation2 = SimpleHttp ConsentRepresentation consentRepresentation2 = SimpleHttp
.doPost(getAccountUrl("applications/" + appId + "/consent"), httpClient) .doPost(getAccountUrl("applications/" + appId + "/consent"), httpClient)
@ -407,8 +487,8 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
assertEquals(consentRepresentation.getCreatedDate(), consentRepresentation2.getCreatedDate()); assertEquals(consentRepresentation.getCreatedDate(), consentRepresentation2.getCreatedDate());
assertTrue(consentRepresentation2.getLastUpdatedDate() > 0); assertTrue(consentRepresentation2.getLastUpdatedDate() > 0);
assertTrue(consentRepresentation2.getLastUpdatedDate() > consentRepresentation.getLastUpdatedDate()); assertTrue(consentRepresentation2.getLastUpdatedDate() > consentRepresentation.getLastUpdatedDate());
assertEquals(1, consentRepresentation2.getScopes().size()); assertEquals(1, consentRepresentation2.getGrantedScopes().size());
assertEquals(consentScopeRepresentation.getId(), consentRepresentation2.getScopes().get(0).getId()); assertEquals(consentScopeRepresentation.getId(), consentRepresentation2.getGrantedScopes().get(0).getId());
} }
@Test @Test
@ -421,7 +501,7 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
consentScopeRepresentation.setId(clientScopeRepresentation.getId()); consentScopeRepresentation.setId(clientScopeRepresentation.getId());
ConsentRepresentation requestedConsent = new ConsentRepresentation(); ConsentRepresentation requestedConsent = new ConsentRepresentation();
requestedConsent.setScopes(Collections.singletonList(consentScopeRepresentation)); requestedConsent.setGrantedScopes(Collections.singletonList(consentScopeRepresentation));
SimpleHttp.Response response = SimpleHttp SimpleHttp.Response response = SimpleHttp
.doPost(getAccountUrl("applications/" + appId + "/consent"), httpClient) .doPost(getAccountUrl("applications/" + appId + "/consent"), httpClient)
@ -443,7 +523,7 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
consentScopeRepresentation.setId(clientScopeRepresentation.getId()); consentScopeRepresentation.setId(clientScopeRepresentation.getId());
ConsentRepresentation requestedConsent = new ConsentRepresentation(); ConsentRepresentation requestedConsent = new ConsentRepresentation();
requestedConsent.setScopes(Collections.singletonList(consentScopeRepresentation)); requestedConsent.setGrantedScopes(Collections.singletonList(consentScopeRepresentation));
SimpleHttp.Response response = SimpleHttp SimpleHttp.Response response = SimpleHttp
.doPost(getAccountUrl("applications/" + appId + "/consent"), httpClient) .doPost(getAccountUrl("applications/" + appId + "/consent"), httpClient)
@ -465,7 +545,7 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
consentScopeRepresentation.setId(clientScopeRepresentation.getId()); consentScopeRepresentation.setId(clientScopeRepresentation.getId());
ConsentRepresentation requestedConsent = new ConsentRepresentation(); ConsentRepresentation requestedConsent = new ConsentRepresentation();
requestedConsent.setScopes(Collections.singletonList(consentScopeRepresentation)); requestedConsent.setGrantedScopes(Collections.singletonList(consentScopeRepresentation));
ConsentRepresentation consentRepresentation = SimpleHttp ConsentRepresentation consentRepresentation = SimpleHttp
.doPut(getAccountUrl("applications/" + appId + "/consent"), httpClient) .doPut(getAccountUrl("applications/" + appId + "/consent"), httpClient)
@ -475,8 +555,8 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
.asJson(ConsentRepresentation.class); .asJson(ConsentRepresentation.class);
assertTrue(consentRepresentation.getCreatedDate() > 0); assertTrue(consentRepresentation.getCreatedDate() > 0);
assertTrue(consentRepresentation.getLastUpdatedDate() > 0); assertTrue(consentRepresentation.getLastUpdatedDate() > 0);
assertEquals(1, consentRepresentation.getScopes().size()); assertEquals(1, consentRepresentation.getGrantedScopes().size());
assertEquals(consentScopeRepresentation.getId(), consentRepresentation.getScopes().get(0).getId()); assertEquals(consentScopeRepresentation.getId(), consentRepresentation.getGrantedScopes().get(0).getId());
} }
@Test @Test
@ -489,7 +569,7 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
consentScopeRepresentation.setId(clientScopeRepresentation.getId()); consentScopeRepresentation.setId(clientScopeRepresentation.getId());
ConsentRepresentation requestedConsent = new ConsentRepresentation(); ConsentRepresentation requestedConsent = new ConsentRepresentation();
requestedConsent.setScopes(Collections.singletonList(consentScopeRepresentation)); requestedConsent.setGrantedScopes(Collections.singletonList(consentScopeRepresentation));
ConsentRepresentation consentRepresentation = SimpleHttp ConsentRepresentation consentRepresentation = SimpleHttp
.doPut(getAccountUrl("applications/" + appId + "/consent"), httpClient) .doPut(getAccountUrl("applications/" + appId + "/consent"), httpClient)
@ -499,15 +579,15 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
.asJson(ConsentRepresentation.class); .asJson(ConsentRepresentation.class);
assertTrue(consentRepresentation.getCreatedDate() > 0); assertTrue(consentRepresentation.getCreatedDate() > 0);
assertTrue(consentRepresentation.getLastUpdatedDate() > 0); assertTrue(consentRepresentation.getLastUpdatedDate() > 0);
assertEquals(1, consentRepresentation.getScopes().size()); assertEquals(1, consentRepresentation.getGrantedScopes().size());
assertEquals(consentScopeRepresentation.getId(), consentRepresentation.getScopes().get(0).getId()); assertEquals(consentScopeRepresentation.getId(), consentRepresentation.getGrantedScopes().get(0).getId());
clientScopeRepresentation = testRealm().clientScopes().findAll().get(1); clientScopeRepresentation = testRealm().clientScopes().findAll().get(1);
consentScopeRepresentation = new ConsentScopeRepresentation(); consentScopeRepresentation = new ConsentScopeRepresentation();
consentScopeRepresentation.setId(clientScopeRepresentation.getId()); consentScopeRepresentation.setId(clientScopeRepresentation.getId());
requestedConsent = new ConsentRepresentation(); requestedConsent = new ConsentRepresentation();
requestedConsent.setScopes(Collections.singletonList(consentScopeRepresentation)); requestedConsent.setGrantedScopes(Collections.singletonList(consentScopeRepresentation));
ConsentRepresentation consentRepresentation2 = SimpleHttp ConsentRepresentation consentRepresentation2 = SimpleHttp
.doPut(getAccountUrl("applications/" + appId + "/consent"), httpClient) .doPut(getAccountUrl("applications/" + appId + "/consent"), httpClient)
@ -519,8 +599,8 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
assertEquals(consentRepresentation.getCreatedDate(), consentRepresentation2.getCreatedDate()); assertEquals(consentRepresentation.getCreatedDate(), consentRepresentation2.getCreatedDate());
assertTrue(consentRepresentation2.getLastUpdatedDate() > 0); assertTrue(consentRepresentation2.getLastUpdatedDate() > 0);
assertTrue(consentRepresentation2.getLastUpdatedDate() > consentRepresentation.getLastUpdatedDate()); assertTrue(consentRepresentation2.getLastUpdatedDate() > consentRepresentation.getLastUpdatedDate());
assertEquals(1, consentRepresentation2.getScopes().size()); assertEquals(1, consentRepresentation2.getGrantedScopes().size());
assertEquals(consentScopeRepresentation.getId(), consentRepresentation2.getScopes().get(0).getId()); assertEquals(consentScopeRepresentation.getId(), consentRepresentation2.getGrantedScopes().get(0).getId());
} }
@Test @Test
@ -533,7 +613,7 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
consentScopeRepresentation.setId(clientScopeRepresentation.getId()); consentScopeRepresentation.setId(clientScopeRepresentation.getId());
ConsentRepresentation requestedConsent = new ConsentRepresentation(); ConsentRepresentation requestedConsent = new ConsentRepresentation();
requestedConsent.setScopes(Collections.singletonList(consentScopeRepresentation)); requestedConsent.setGrantedScopes(Collections.singletonList(consentScopeRepresentation));
SimpleHttp.Response response = SimpleHttp SimpleHttp.Response response = SimpleHttp
.doPut(getAccountUrl("applications/" + appId + "/consent"), httpClient) .doPut(getAccountUrl("applications/" + appId + "/consent"), httpClient)
@ -555,7 +635,7 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
consentScopeRepresentation.setId(clientScopeRepresentation.getId()); consentScopeRepresentation.setId(clientScopeRepresentation.getId());
ConsentRepresentation requestedConsent = new ConsentRepresentation(); ConsentRepresentation requestedConsent = new ConsentRepresentation();
requestedConsent.setScopes(Collections.singletonList(consentScopeRepresentation)); requestedConsent.setGrantedScopes(Collections.singletonList(consentScopeRepresentation));
SimpleHttp.Response response = SimpleHttp SimpleHttp.Response response = SimpleHttp
.doPut(getAccountUrl("applications/" + appId + "/consent"), httpClient) .doPut(getAccountUrl("applications/" + appId + "/consent"), httpClient)
@ -577,7 +657,7 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
consentScopeRepresentation.setId(clientScopeRepresentation.getId()); consentScopeRepresentation.setId(clientScopeRepresentation.getId());
ConsentRepresentation requestedConsent = new ConsentRepresentation(); ConsentRepresentation requestedConsent = new ConsentRepresentation();
requestedConsent.setScopes(Collections.singletonList(consentScopeRepresentation)); requestedConsent.setGrantedScopes(Collections.singletonList(consentScopeRepresentation));
ConsentRepresentation consentRepresentation1 = SimpleHttp ConsentRepresentation consentRepresentation1 = SimpleHttp
.doPost(getAccountUrl("applications/" + appId + "/consent"), httpClient) .doPost(getAccountUrl("applications/" + appId + "/consent"), httpClient)
@ -587,8 +667,8 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
.asJson(ConsentRepresentation.class); .asJson(ConsentRepresentation.class);
assertTrue(consentRepresentation1.getCreatedDate() > 0); assertTrue(consentRepresentation1.getCreatedDate() > 0);
assertTrue(consentRepresentation1.getLastUpdatedDate() > 0); assertTrue(consentRepresentation1.getLastUpdatedDate() > 0);
assertEquals(1, consentRepresentation1.getScopes().size()); assertEquals(1, consentRepresentation1.getGrantedScopes().size());
assertEquals(consentScopeRepresentation.getId(), consentRepresentation1.getScopes().get(0).getId()); assertEquals(consentScopeRepresentation.getId(), consentRepresentation1.getGrantedScopes().get(0).getId());
ConsentRepresentation consentRepresentation2 = SimpleHttp ConsentRepresentation consentRepresentation2 = SimpleHttp
.doGet(getAccountUrl("applications/" + appId + "/consent"), httpClient) .doGet(getAccountUrl("applications/" + appId + "/consent"), httpClient)
@ -597,7 +677,7 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
.asJson(ConsentRepresentation.class); .asJson(ConsentRepresentation.class);
assertEquals(consentRepresentation1.getLastUpdatedDate(), consentRepresentation2.getLastUpdatedDate()); assertEquals(consentRepresentation1.getLastUpdatedDate(), consentRepresentation2.getLastUpdatedDate());
assertEquals(consentRepresentation1.getCreatedDate(), consentRepresentation2.getCreatedDate()); assertEquals(consentRepresentation1.getCreatedDate(), consentRepresentation2.getCreatedDate());
assertEquals(consentRepresentation1.getScopes().get(0).getId(), consentRepresentation2.getScopes().get(0).getId()); assertEquals(consentRepresentation1.getGrantedScopes().get(0).getId(), consentRepresentation2.getGrantedScopes().get(0).getId());
} }
@Test @Test
@ -626,7 +706,7 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
@Test @Test
public void getConsentWithoutPermission() throws IOException { public void getConsentWithoutPermission() throws IOException {
TokenUtil token = new TokenUtil("view-applications-access", "password"); TokenUtil token = new TokenUtil("no-account-access", "password");
String appId = "security-admin-console"; String appId = "security-admin-console";
SimpleHttp.Response response = SimpleHttp SimpleHttp.Response response = SimpleHttp
.doGet(getAccountUrl("applications/" + appId + "/consent"), httpClient) .doGet(getAccountUrl("applications/" + appId + "/consent"), httpClient)
@ -646,7 +726,7 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
consentScopeRepresentation.setId(clientScopeRepresentation.getId()); consentScopeRepresentation.setId(clientScopeRepresentation.getId());
ConsentRepresentation requestedConsent = new ConsentRepresentation(); ConsentRepresentation requestedConsent = new ConsentRepresentation();
requestedConsent.setScopes(Collections.singletonList(consentScopeRepresentation)); requestedConsent.setGrantedScopes(Collections.singletonList(consentScopeRepresentation));
ConsentRepresentation consentRepresentation = SimpleHttp ConsentRepresentation consentRepresentation = SimpleHttp
.doPost(getAccountUrl("applications/" + appId + "/consent"), httpClient) .doPost(getAccountUrl("applications/" + appId + "/consent"), httpClient)
@ -656,8 +736,8 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
.asJson(ConsentRepresentation.class); .asJson(ConsentRepresentation.class);
assertTrue(consentRepresentation.getCreatedDate() > 0); assertTrue(consentRepresentation.getCreatedDate() > 0);
assertTrue(consentRepresentation.getLastUpdatedDate() > 0); assertTrue(consentRepresentation.getLastUpdatedDate() > 0);
assertEquals(1, consentRepresentation.getScopes().size()); assertEquals(1, consentRepresentation.getGrantedScopes().size());
assertEquals(consentScopeRepresentation.getId(), consentRepresentation.getScopes().get(0).getId()); assertEquals(consentScopeRepresentation.getId(), consentRepresentation.getGrantedScopes().get(0).getId());
SimpleHttp.Response response = SimpleHttp SimpleHttp.Response response = SimpleHttp
.doDelete(getAccountUrl("applications/" + appId + "/consent"), httpClient) .doDelete(getAccountUrl("applications/" + appId + "/consent"), httpClient)
@ -686,7 +766,6 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
assertEquals(404, response.getStatus()); assertEquals(404, response.getStatus());
} }
@Test @Test
public void deleteConsentWithoutPermission() throws IOException { public void deleteConsentWithoutPermission() throws IOException {
TokenUtil token = new TokenUtil("view-consent-access", "password"); TokenUtil token = new TokenUtil("view-consent-access", "password");

View file

@ -214,6 +214,7 @@
}, },
{ {
"clientId" : "third-party", "clientId" : "third-party",
"description" : "A third party application",
"enabled": true, "enabled": true,
"consentRequired": true, "consentRequired": true,