[KEYCLOAK-10653] Manage Consent via the Account API

This commit is contained in:
Leon Graser 2019-08-20 11:24:44 +02:00 committed by Bruno Oliveira da Silva
parent 3f2a38936c
commit 0ce10a3249
7 changed files with 839 additions and 4 deletions

View file

@ -0,0 +1,62 @@
/*
* Copyright 2019 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.representations.account;
import java.util.List;
public class ConsentRepresentation {
private List<ConsentScopeRepresentation> scopes;
private Long createdDate;
private Long lastUpdatedDate;
public ConsentRepresentation() {
}
public ConsentRepresentation(List<ConsentScopeRepresentation> scopes, Long createdDate, Long lastUpdatedDate) {
this.scopes = scopes;
this.createdDate = createdDate;
this.lastUpdatedDate = lastUpdatedDate;
}
public List<ConsentScopeRepresentation> getScopes() {
return scopes;
}
public void setScopes(List<ConsentScopeRepresentation> scopes) {
this.scopes = scopes;
}
public Long getCreatedDate() {
return createdDate;
}
public void setCreatedDate(Long createdDate) {
this.createdDate = createdDate;
}
public Long getLastUpdatedDate() {
return lastUpdatedDate;
}
public void setLastUpdatedDate(Long lastUpdatedDate) {
this.lastUpdatedDate = lastUpdatedDate;
}
}

View file

@ -0,0 +1,60 @@
/*
* Copyright 2019 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.representations.account;
public class ConsentScopeRepresentation {
private String id;
private String name;
private String displayTest;
public ConsentScopeRepresentation() {
}
public ConsentScopeRepresentation(String id, String name, String displayTest) {
this.id = id;
this.name = name;
this.displayTest = displayTest;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDisplayTest() {
return displayTest;
}
public void setDisplayTest(String displayTest) {
this.displayTest = displayTest;
}
}

View file

@ -67,6 +67,10 @@ public enum EventType {
REMOVE_TOTP(true),
REMOVE_TOTP_ERROR(true),
GRANT_CONSENT(true),
GRANT_CONSENT_ERROR(true),
UPDATE_CONSENT(true),
UPDATE_CONSENT_ERROR(true),
REVOKE_GRANT(true),
REVOKE_GRANT_ERROR(true),

View file

@ -26,6 +26,9 @@ public interface AccountRoles {
String MANAGE_ACCOUNT = "manage-account";
String INITIATE_ACTION = "initiate-action";
String MANAGE_ACCOUNT_LINKS = "manage-account-links";
String VIEW_APPLICATIONS = "view-applications";
String VIEW_CONSENT = "view-consent";
String MANAGE_CONSENT = "manage-consent";
String[] ALL = {VIEW_PROFILE, MANAGE_ACCOUNT};

View file

@ -19,17 +19,22 @@ package org.keycloak.services.resources.account;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.common.ClientConnection;
import org.keycloak.common.Profile;
import org.keycloak.events.Details;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventStoreProvider;
import org.keycloak.events.EventType;
import org.keycloak.models.AccountRoles;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserConsentModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.representations.account.ClientRepresentation;
import org.keycloak.representations.account.ConsentRepresentation;
import org.keycloak.representations.account.ConsentScopeRepresentation;
import org.keycloak.representations.account.SessionRepresentation;
import org.keycloak.representations.account.UserRepresentation;
import org.keycloak.services.ErrorResponse;
@ -39,16 +44,30 @@ import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.resources.account.resources.ResourcesService;
import org.keycloak.storage.ReadOnlyException;
import org.keycloak.theme.Theme;
import javax.ws.rs.*;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.OPTIONS;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.keycloak.common.Profile;
import java.util.Properties;
import java.util.stream.Collectors;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@ -70,6 +89,7 @@ public class AccountRestService {
private final RealmModel realm;
private final UserModel user;
private final Locale locale;
public AccountRestService(KeycloakSession session, Auth auth, ClientModel client, EventBuilder event) {
this.session = session;
@ -78,6 +98,7 @@ public class AccountRestService {
this.user = auth.getUser();
this.client = client;
this.event = event;
this.locale = session.getContext().resolveLocale(user);
}
public void init() {
@ -296,8 +317,231 @@ public class AccountRestService {
return new ResourcesService(session, user, auth, request);
}
// TODO Federated identities
// TODO Applications
// 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.
*
* @param clientId client id to search for
* @return application with the provided id
*/
@Path("/applications/{clientId}")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getApplication(final @PathParam("clientId") String clientId) {
checkAccountApiEnabled();
auth.require(AccountRoles.VIEW_APPLICATIONS);
ClientModel client = realm.getClientByClientId(clientId);
if (client == null) {
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();
}
private ClientRepresentation modelToRepresentation(ClientModel model) {
ClientRepresentation representation = new ClientRepresentation();
representation.setClientId(model.getClientId());
representation.setClientName(getTranslationOrDefault(model.getName()));
return representation;
}
private ConsentRepresentation modelToRepresentation(UserConsentModel model) {
List<ConsentScopeRepresentation> scopes = model.getGrantedClientScopes().stream()
.map(m -> new ConsentScopeRepresentation(m.getId(), m.getName(), getTranslationOrDefault(m.getConsentScreenText())))
.collect(Collectors.toList());
return new ConsentRepresentation(scopes, model.getCreatedDate(), model.getLastUpdatedDate());
}
private String getTranslationOrDefault(String key) {
if (key == null) {
return null;
}
String defaultValue = key;
if (key.startsWith("${")) {
key = key.substring(2, key.length() - 1);
}
try {
Properties messages = session.theme().getTheme(Theme.Type.ACCOUNT).getMessages(locale);
return messages.getProperty(key, defaultValue);
} catch (IOException e) {
return key;
}
}
/**
* Returns the consent for the client with the given client id.
*
* @param clientId client id to return the consent for
* @return consent of the client
*/
@Path("/applications/{clientId}/consent")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getConsent(final @PathParam("clientId") String clientId) {
checkAccountApiEnabled();
auth.requireOneOf(AccountRoles.VIEW_CONSENT, AccountRoles.MANAGE_CONSENT);
ClientModel client = realm.getClientByClientId(clientId);
if (client == null) {
return Cors.add(request, Response.status(Response.Status.NOT_FOUND).entity("No client with clientId: " + clientId + " found.")).build();
}
UserConsentModel consent = session.users().getConsentByClient(realm, user.getId(), client.getId());
if (consent == null) {
return Cors.add(request, Response.noContent()).build();
}
return Cors.add(request, Response.ok(modelToRepresentation(consent))).build();
}
/**
* Deletes the consent for the client with the given client id.
*
* @param clientId client id to delete a consent for
* @return returns 202 if deleted
*/
@Path("/applications/{clientId}/consent")
@DELETE
public Response revokeConsent(final @PathParam("clientId") String clientId) {
checkAccountApiEnabled();
auth.require(AccountRoles.MANAGE_CONSENT);
event.event(EventType.REVOKE_GRANT);
ClientModel client = realm.getClientByClientId(clientId);
if (client == null) {
event.event(EventType.REVOKE_GRANT_ERROR);
String msg = String.format("No client with clientId: %s found.", clientId);
event.error(msg);
return Cors.add(request, Response.status(Response.Status.NOT_FOUND).entity(msg)).build();
}
session.users().revokeConsentForClient(realm, user.getId(), client.getId());
event.success();
return Cors.add(request, Response.accepted()).build();
}
/**
* Creates or updates the consent of the given, requested consent for
* the client with the given client id. Returns the appropriate REST response.
*
* @param clientId client id to set a consent for
* @param consent requested consent for the client
* @return the created or updated consent
*/
@Path("/applications/{clientId}/consent")
@POST
@Produces(MediaType.APPLICATION_JSON)
public Response grantConsent(final @PathParam("clientId") String clientId,
final ConsentRepresentation consent) {
return upsert(clientId, consent);
}
/**
* Creates or updates the consent of the given, requested consent for
* the client with the given client id. Returns the appropriate REST response.
*
* @param clientId client id to set a consent for
* @param consent requested consent for the client
* @return the created or updated consent
*/
@Path("/applications/{clientId}/consent")
@PUT
@Produces(MediaType.APPLICATION_JSON)
public Response updateConsent(final @PathParam("clientId") String clientId,
final ConsentRepresentation consent) {
return upsert(clientId, consent);
}
/**
* Creates or updates the consent of the given, requested consent for
* the client with the given client id. Returns the appropriate REST response.
*
* @param clientId client id to set a consent for
* @param consent requested consent for the client
* @return response to return to the caller
*/
private Response upsert(String clientId, ConsentRepresentation consent) {
checkAccountApiEnabled();
auth.require(AccountRoles.MANAGE_CONSENT);
event.event(EventType.GRANT_CONSENT);
ClientModel client = realm.getClientByClientId(clientId);
if (client == null) {
event.event(EventType.GRANT_CONSENT_ERROR);
String msg = String.format("No client with clientId: %s found.", clientId);
event.error(msg);
return Cors.add(request, Response.status(Response.Status.NOT_FOUND).entity(msg)).build();
}
try {
UserConsentModel grantedConsent = createConsent(client, consent);
if (session.users().getConsentByClient(realm, user.getId(), client.getId()) == null) {
session.users().addConsent(realm, user.getId(), grantedConsent);
} else {
session.users().updateConsent(realm, user.getId(), grantedConsent);
}
event.success();
grantedConsent = session.users().getConsentByClient(realm, user.getId(), client.getId());
return Cors.add(request, Response.ok(modelToRepresentation(grantedConsent))).build();
} catch (IllegalArgumentException e) {
return Cors.add(request, Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage())).build();
}
}
/**
* Create a new consent model object from the requested consent object
* for the given client model.
*
* @param client client to create a consent for
* @param requested list of client scopes that the new consent should contain
* @return newly created consent model
* @throws IllegalArgumentException throws an exception if the scope id is not available
*/
private UserConsentModel createConsent(ClientModel client, ConsentRepresentation requested) throws IllegalArgumentException {
UserConsentModel consent = new UserConsentModel(client);
Map<String, ClientScopeModel> availableGrants = realm.getClientScopes().stream().collect(Collectors.toMap(ClientScopeModel::getId, s -> s));
if (client.isConsentRequired()) {
availableGrants.put(client.getId(), client);
}
for (ConsentScopeRepresentation scopeRepresentation : requested.getScopes()) {
ClientScopeModel scopeModel = availableGrants.get(scopeRepresentation.getId());
if (scopeModel == null) {
String msg = String.format("Scope id %s does not exist for client %s.", scopeRepresentation, consent.getClient().getName());
event.error(msg);
throw new IllegalArgumentException(msg);
} else {
consent.addGrantedClientScope(scopeModel);
}
}
return consent;
}
// TODO Logs
private static void checkAccountApiEnabled() {

View file

@ -87,6 +87,9 @@ public abstract class AbstractRestServiceTest extends AbstractTestRealmKeycloakT
public void configureTestRealm(RealmRepresentation testRealm) {
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-applications-access").role("account", "view-applications").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());
}
protected String getAccountUrl(String resource) {

View file

@ -19,8 +19,12 @@ package org.keycloak.testsuite.account;
import com.fasterxml.jackson.core.type.TypeReference;
import org.junit.Test;
import org.keycloak.broker.provider.util.SimpleHttp;
import org.keycloak.representations.account.ClientRepresentation;
import org.keycloak.representations.account.ConsentRepresentation;
import org.keycloak.representations.account.ConsentScopeRepresentation;
import org.keycloak.representations.account.SessionRepresentation;
import org.keycloak.representations.account.UserRepresentation;
import org.keycloak.representations.idm.ClientScopeRepresentation;
import org.keycloak.representations.idm.ErrorRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.messages.Messages;
@ -333,4 +337,459 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
sessions = SimpleHttp.doGet(getAccountUrl("sessions"), httpClient).auth(tokenUtil.getToken()).asJson(new TypeReference<List<SessionRepresentation>>() {});
assertEquals(1, sessions.size());
}
@Test
public void listApplications() throws IOException {
assumeFeatureEnabled(ACCOUNT_API);
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());
}
@Test
public void listApplicationsWithoutPermission() throws IOException {
assumeFeatureEnabled(ACCOUNT_API);
TokenUtil token = new TokenUtil("view-account-access", "password");
SimpleHttp.Response response = SimpleHttp
.doGet(getAccountUrl("applications"), httpClient)
.header("Accept", "application/json")
.auth(token.getToken())
.asResponse();
assertEquals(403, response.getStatus());
}
@Test
public void getWebConsoleApplication() throws IOException {
assumeFeatureEnabled(ACCOUNT_API);
TokenUtil token = new TokenUtil("view-applications-access", "password");
String appId = "security-admin-console";
ClientRepresentation webConsole = SimpleHttp
.doGet(getAccountUrl("applications/" + appId), httpClient)
.header("Accept", "application/json")
.auth(token.getToken())
.asJson(ClientRepresentation.class);
assertEquals(appId, webConsole.getClientId());
}
@Test
public void getWebConsoleApplicationWithoutPermission() throws IOException {
assumeFeatureEnabled(ACCOUNT_API);
TokenUtil token = new TokenUtil("view-account-access", "password");
String appId = "security-admin-console";
SimpleHttp.Response response = SimpleHttp
.doGet(getAccountUrl("applications/" + appId), httpClient)
.header("Accept", "application/json")
.auth(token.getToken())
.asResponse();
assertEquals(403, response.getStatus());
}
@Test
public void getNotExistingApplication() throws IOException {
assumeFeatureEnabled(ACCOUNT_API);
TokenUtil token = new TokenUtil("view-applications-access", "password");
String appId = "not-existing";
SimpleHttp.Response response = SimpleHttp
.doGet(getAccountUrl("applications/" + appId), httpClient)
.header("Accept", "application/json")
.auth(token.getToken())
.asResponse();
assertEquals(404, response.getStatus());
}
@Test
public void createConsentForClient() throws IOException {
assumeFeatureEnabled(ACCOUNT_API);
TokenUtil token = new TokenUtil("manage-consent-access", "password");
String appId = "security-admin-console";
ClientScopeRepresentation clientScopeRepresentation = testRealm().clientScopes().findAll().get(0);
ConsentScopeRepresentation consentScopeRepresentation = new ConsentScopeRepresentation();
consentScopeRepresentation.setId(clientScopeRepresentation.getId());
ConsentRepresentation requestedConsent = new ConsentRepresentation();
requestedConsent.setScopes(Collections.singletonList(consentScopeRepresentation));
ConsentRepresentation consentRepresentation = SimpleHttp
.doPost(getAccountUrl("applications/" + appId + "/consent"), httpClient)
.header("Accept", "application/json")
.json(requestedConsent)
.auth(token.getToken())
.asJson(ConsentRepresentation.class);
assertTrue(consentRepresentation.getCreatedDate() > 0);
assertTrue(consentRepresentation.getLastUpdatedDate() > 0);
assertEquals(1, consentRepresentation.getScopes().size());
assertEquals(consentScopeRepresentation.getId(), consentRepresentation.getScopes().get(0).getId());
}
@Test
public void updateConsentForClient() throws IOException {
assumeFeatureEnabled(ACCOUNT_API);
TokenUtil token = new TokenUtil("manage-consent-access", "password");
String appId = "security-admin-console";
ClientScopeRepresentation clientScopeRepresentation = testRealm().clientScopes().findAll().get(0);
ConsentScopeRepresentation consentScopeRepresentation = new ConsentScopeRepresentation();
consentScopeRepresentation.setId(clientScopeRepresentation.getId());
ConsentRepresentation requestedConsent = new ConsentRepresentation();
requestedConsent.setScopes(Collections.singletonList(consentScopeRepresentation));
ConsentRepresentation consentRepresentation = SimpleHttp
.doPost(getAccountUrl("applications/" + appId + "/consent"), httpClient)
.header("Accept", "application/json")
.json(requestedConsent)
.auth(token.getToken())
.asJson(ConsentRepresentation.class);
assertTrue(consentRepresentation.getCreatedDate() > 0);
assertTrue(consentRepresentation.getLastUpdatedDate() > 0);
assertEquals(1, consentRepresentation.getScopes().size());
assertEquals(consentScopeRepresentation.getId(), consentRepresentation.getScopes().get(0).getId());
clientScopeRepresentation = testRealm().clientScopes().findAll().get(1);
consentScopeRepresentation = new ConsentScopeRepresentation();
consentScopeRepresentation.setId(clientScopeRepresentation.getId());
requestedConsent = new ConsentRepresentation();
requestedConsent.setScopes(Collections.singletonList(consentScopeRepresentation));
ConsentRepresentation consentRepresentation2 = SimpleHttp
.doPost(getAccountUrl("applications/" + appId + "/consent"), httpClient)
.header("Accept", "application/json")
.json(requestedConsent)
.auth(token.getToken())
.asJson(ConsentRepresentation.class);
assertTrue(consentRepresentation2.getCreatedDate() > 0);
assertEquals(consentRepresentation.getCreatedDate(), consentRepresentation2.getCreatedDate());
assertTrue(consentRepresentation2.getLastUpdatedDate() > 0);
assertTrue(consentRepresentation2.getLastUpdatedDate() > consentRepresentation.getLastUpdatedDate());
assertEquals(1, consentRepresentation2.getScopes().size());
assertEquals(consentScopeRepresentation.getId(), consentRepresentation2.getScopes().get(0).getId());
}
@Test
public void createConsentForNotExistingClient() throws IOException {
assumeFeatureEnabled(ACCOUNT_API);
TokenUtil token = new TokenUtil("manage-consent-access", "password");
String appId = "not-existing";
ClientScopeRepresentation clientScopeRepresentation = testRealm().clientScopes().findAll().get(0);
ConsentScopeRepresentation consentScopeRepresentation = new ConsentScopeRepresentation();
consentScopeRepresentation.setId(clientScopeRepresentation.getId());
ConsentRepresentation requestedConsent = new ConsentRepresentation();
requestedConsent.setScopes(Collections.singletonList(consentScopeRepresentation));
SimpleHttp.Response response = SimpleHttp
.doPost(getAccountUrl("applications/" + appId + "/consent"), httpClient)
.header("Accept", "application/json")
.json(requestedConsent)
.auth(token.getToken())
.asResponse();
assertEquals(404, response.getStatus());
}
@Test
public void createConsentForClientWithoutPermission() throws IOException {
assumeFeatureEnabled(ACCOUNT_API);
TokenUtil token = new TokenUtil("view-consent-access", "password");
String appId = "security-admin-console";
ClientScopeRepresentation clientScopeRepresentation = testRealm().clientScopes().findAll().get(0);
ConsentScopeRepresentation consentScopeRepresentation = new ConsentScopeRepresentation();
consentScopeRepresentation.setId(clientScopeRepresentation.getId());
ConsentRepresentation requestedConsent = new ConsentRepresentation();
requestedConsent.setScopes(Collections.singletonList(consentScopeRepresentation));
SimpleHttp.Response response = SimpleHttp
.doPost(getAccountUrl("applications/" + appId + "/consent"), httpClient)
.header("Accept", "application/json")
.json(requestedConsent)
.auth(token.getToken())
.asResponse();
assertEquals(403, response.getStatus());
}
@Test
public void createConsentForClientWithPut() throws IOException {
assumeFeatureEnabled(ACCOUNT_API);
TokenUtil token = new TokenUtil("manage-consent-access", "password");
String appId = "security-admin-console";
ClientScopeRepresentation clientScopeRepresentation = testRealm().clientScopes().findAll().get(0);
ConsentScopeRepresentation consentScopeRepresentation = new ConsentScopeRepresentation();
consentScopeRepresentation.setId(clientScopeRepresentation.getId());
ConsentRepresentation requestedConsent = new ConsentRepresentation();
requestedConsent.setScopes(Collections.singletonList(consentScopeRepresentation));
ConsentRepresentation consentRepresentation = SimpleHttp
.doPut(getAccountUrl("applications/" + appId + "/consent"), httpClient)
.header("Accept", "application/json")
.json(requestedConsent)
.auth(token.getToken())
.asJson(ConsentRepresentation.class);
assertTrue(consentRepresentation.getCreatedDate() > 0);
assertTrue(consentRepresentation.getLastUpdatedDate() > 0);
assertEquals(1, consentRepresentation.getScopes().size());
assertEquals(consentScopeRepresentation.getId(), consentRepresentation.getScopes().get(0).getId());
}
@Test
public void updateConsentForClientWithPut() throws IOException {
assumeFeatureEnabled(ACCOUNT_API);
TokenUtil token = new TokenUtil("manage-consent-access", "password");
String appId = "security-admin-console";
ClientScopeRepresentation clientScopeRepresentation = testRealm().clientScopes().findAll().get(0);
ConsentScopeRepresentation consentScopeRepresentation = new ConsentScopeRepresentation();
consentScopeRepresentation.setId(clientScopeRepresentation.getId());
ConsentRepresentation requestedConsent = new ConsentRepresentation();
requestedConsent.setScopes(Collections.singletonList(consentScopeRepresentation));
ConsentRepresentation consentRepresentation = SimpleHttp
.doPut(getAccountUrl("applications/" + appId + "/consent"), httpClient)
.header("Accept", "application/json")
.json(requestedConsent)
.auth(token.getToken())
.asJson(ConsentRepresentation.class);
assertTrue(consentRepresentation.getCreatedDate() > 0);
assertTrue(consentRepresentation.getLastUpdatedDate() > 0);
assertEquals(1, consentRepresentation.getScopes().size());
assertEquals(consentScopeRepresentation.getId(), consentRepresentation.getScopes().get(0).getId());
clientScopeRepresentation = testRealm().clientScopes().findAll().get(1);
consentScopeRepresentation = new ConsentScopeRepresentation();
consentScopeRepresentation.setId(clientScopeRepresentation.getId());
requestedConsent = new ConsentRepresentation();
requestedConsent.setScopes(Collections.singletonList(consentScopeRepresentation));
ConsentRepresentation consentRepresentation2 = SimpleHttp
.doPut(getAccountUrl("applications/" + appId + "/consent"), httpClient)
.header("Accept", "application/json")
.json(requestedConsent)
.auth(token.getToken())
.asJson(ConsentRepresentation.class);
assertTrue(consentRepresentation2.getCreatedDate() > 0);
assertEquals(consentRepresentation.getCreatedDate(), consentRepresentation2.getCreatedDate());
assertTrue(consentRepresentation2.getLastUpdatedDate() > 0);
assertTrue(consentRepresentation2.getLastUpdatedDate() > consentRepresentation.getLastUpdatedDate());
assertEquals(1, consentRepresentation2.getScopes().size());
assertEquals(consentScopeRepresentation.getId(), consentRepresentation2.getScopes().get(0).getId());
}
@Test
public void createConsentForNotExistingClientWithPut() throws IOException {
assumeFeatureEnabled(ACCOUNT_API);
TokenUtil token = new TokenUtil("manage-consent-access", "password");
String appId = "not-existing";
ClientScopeRepresentation clientScopeRepresentation = testRealm().clientScopes().findAll().get(0);
ConsentScopeRepresentation consentScopeRepresentation = new ConsentScopeRepresentation();
consentScopeRepresentation.setId(clientScopeRepresentation.getId());
ConsentRepresentation requestedConsent = new ConsentRepresentation();
requestedConsent.setScopes(Collections.singletonList(consentScopeRepresentation));
SimpleHttp.Response response = SimpleHttp
.doPut(getAccountUrl("applications/" + appId + "/consent"), httpClient)
.header("Accept", "application/json")
.json(requestedConsent)
.auth(token.getToken())
.asResponse();
assertEquals(404, response.getStatus());
}
@Test
public void createConsentForClientWithoutPermissionWithPut() throws IOException {
assumeFeatureEnabled(ACCOUNT_API);
TokenUtil token = new TokenUtil("view-consent-access", "password");
String appId = "security-admin-console";
ClientScopeRepresentation clientScopeRepresentation = testRealm().clientScopes().findAll().get(0);
ConsentScopeRepresentation consentScopeRepresentation = new ConsentScopeRepresentation();
consentScopeRepresentation.setId(clientScopeRepresentation.getId());
ConsentRepresentation requestedConsent = new ConsentRepresentation();
requestedConsent.setScopes(Collections.singletonList(consentScopeRepresentation));
SimpleHttp.Response response = SimpleHttp
.doPut(getAccountUrl("applications/" + appId + "/consent"), httpClient)
.header("Accept", "application/json")
.json(requestedConsent)
.auth(token.getToken())
.asResponse();
assertEquals(403, response.getStatus());
}
@Test
public void getConsentForClient() throws IOException {
assumeFeatureEnabled(ACCOUNT_API);
TokenUtil token = new TokenUtil("manage-consent-access", "password");
String appId = "security-admin-console";
ClientScopeRepresentation clientScopeRepresentation = testRealm().clientScopes().findAll().get(0);
ConsentScopeRepresentation consentScopeRepresentation = new ConsentScopeRepresentation();
consentScopeRepresentation.setId(clientScopeRepresentation.getId());
ConsentRepresentation requestedConsent = new ConsentRepresentation();
requestedConsent.setScopes(Collections.singletonList(consentScopeRepresentation));
ConsentRepresentation consentRepresentation1 = SimpleHttp
.doPost(getAccountUrl("applications/" + appId + "/consent"), httpClient)
.header("Accept", "application/json")
.json(requestedConsent)
.auth(token.getToken())
.asJson(ConsentRepresentation.class);
assertTrue(consentRepresentation1.getCreatedDate() > 0);
assertTrue(consentRepresentation1.getLastUpdatedDate() > 0);
assertEquals(1, consentRepresentation1.getScopes().size());
assertEquals(consentScopeRepresentation.getId(), consentRepresentation1.getScopes().get(0).getId());
ConsentRepresentation consentRepresentation2 = SimpleHttp
.doGet(getAccountUrl("applications/" + appId + "/consent"), httpClient)
.header("Accept", "application/json")
.auth(token.getToken())
.asJson(ConsentRepresentation.class);
assertEquals(consentRepresentation1.getLastUpdatedDate(), consentRepresentation2.getLastUpdatedDate());
assertEquals(consentRepresentation1.getCreatedDate(), consentRepresentation2.getCreatedDate());
assertEquals(consentRepresentation1.getScopes().get(0).getId(), consentRepresentation2.getScopes().get(0).getId());
}
@Test
public void getConsentForNotExistingClient() throws IOException {
assumeFeatureEnabled(ACCOUNT_API);
TokenUtil token = new TokenUtil("view-consent-access", "password");
String appId = "not-existing";
SimpleHttp.Response response = SimpleHttp
.doGet(getAccountUrl("applications/" + appId + "/consent"), httpClient)
.header("Accept", "application/json")
.auth(token.getToken())
.asResponse();
assertEquals(404, response.getStatus());
}
@Test
public void getNotExistingConsentForClient() throws IOException {
assumeFeatureEnabled(ACCOUNT_API);
TokenUtil token = new TokenUtil("view-consent-access", "password");
String appId = "security-admin-console";
SimpleHttp.Response response = SimpleHttp
.doGet(getAccountUrl("applications/" + appId + "/consent"), httpClient)
.header("Accept", "application/json")
.auth(token.getToken())
.asResponse();
assertEquals(204, response.getStatus());
}
@Test
public void getConsentWithoutPermission() throws IOException {
assumeFeatureEnabled(ACCOUNT_API);
TokenUtil token = new TokenUtil("view-applications-access", "password");
String appId = "security-admin-console";
SimpleHttp.Response response = SimpleHttp
.doGet(getAccountUrl("applications/" + appId + "/consent"), httpClient)
.header("Accept", "application/json")
.auth(token.getToken())
.asResponse();
assertEquals(403, response.getStatus());
}
@Test
public void deleteConsentForClient() throws IOException {
assumeFeatureEnabled(ACCOUNT_API);
TokenUtil token = new TokenUtil("manage-consent-access", "password");
String appId = "security-admin-console";
ClientScopeRepresentation clientScopeRepresentation = testRealm().clientScopes().findAll().get(0);
ConsentScopeRepresentation consentScopeRepresentation = new ConsentScopeRepresentation();
consentScopeRepresentation.setId(clientScopeRepresentation.getId());
ConsentRepresentation requestedConsent = new ConsentRepresentation();
requestedConsent.setScopes(Collections.singletonList(consentScopeRepresentation));
ConsentRepresentation consentRepresentation = SimpleHttp
.doPost(getAccountUrl("applications/" + appId + "/consent"), httpClient)
.header("Accept", "application/json")
.json(requestedConsent)
.auth(token.getToken())
.asJson(ConsentRepresentation.class);
assertTrue(consentRepresentation.getCreatedDate() > 0);
assertTrue(consentRepresentation.getLastUpdatedDate() > 0);
assertEquals(1, consentRepresentation.getScopes().size());
assertEquals(consentScopeRepresentation.getId(), consentRepresentation.getScopes().get(0).getId());
SimpleHttp.Response response = SimpleHttp
.doDelete(getAccountUrl("applications/" + appId + "/consent"), httpClient)
.header("Accept", "application/json")
.auth(token.getToken())
.asResponse();
assertEquals(202, response.getStatus());
response = SimpleHttp
.doDelete(getAccountUrl("applications/" + appId + "/consent"), httpClient)
.header("Accept", "application/json")
.auth(token.getToken())
.asResponse();
assertEquals(202, response.getStatus());
}
@Test
public void deleteConsentForNotExistingClient() throws IOException {
assumeFeatureEnabled(ACCOUNT_API);
TokenUtil token = new TokenUtil("manage-consent-access", "password");
String appId = "not-existing";
SimpleHttp.Response response = SimpleHttp
.doDelete(getAccountUrl("applications/" + appId + "/consent"), httpClient)
.header("Accept", "application/json")
.auth(token.getToken())
.asResponse();
assertEquals(404, response.getStatus());
}
@Test
public void deleteConsentWithoutPermission() throws IOException {
assumeFeatureEnabled(ACCOUNT_API);
TokenUtil token = new TokenUtil("view-consent-access", "password");
String appId = "security-admin-console";
SimpleHttp.Response response = SimpleHttp
.doDelete(getAccountUrl("applications/" + appId + "/consent"), httpClient)
.header("Accept", "application/json")
.auth(token.getToken())
.asResponse();
assertEquals(403, response.getStatus());
}
}