Details
diff --git a/server-spi-private/src/main/java/org/keycloak/broker/provider/util/SimpleHttp.java b/server-spi-private/src/main/java/org/keycloak/broker/provider/util/SimpleHttp.java
index 24ebb8a..a3d2968 100755
--- a/server-spi-private/src/main/java/org/keycloak/broker/provider/util/SimpleHttp.java
+++ b/server-spi-private/src/main/java/org/keycloak/broker/provider/util/SimpleHttp.java
@@ -48,6 +48,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPInputStream;
+import org.apache.http.client.methods.HttpDelete;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -72,6 +73,14 @@ public class SimpleHttp {
this.method = method;
}
+ public static SimpleHttp doDelete(String url, KeycloakSession session) {
+ return doDelete(url, session.getProvider(HttpClientProvider.class).getHttpClient());
+ }
+
+ public static SimpleHttp doDelete(String url, HttpClient client) {
+ return new SimpleHttp(url, "DELETE", client);
+ }
+
public static SimpleHttp doGet(String url, KeycloakSession session) {
return doGet(url, session.getProvider(HttpClientProvider.class).getHttpClient());
}
@@ -157,12 +166,17 @@ public class SimpleHttp {
private Response makeRequest() throws IOException {
boolean get = method.equals("GET");
boolean post = method.equals("POST");
+ boolean delete = method.equals("DELETE");
HttpRequestBase httpRequest = new HttpPost(url);
if (get) {
httpRequest = new HttpGet(appendParameterToUrl(url));
}
+ if (delete) {
+ httpRequest = new HttpDelete(appendParameterToUrl(url));
+ }
+
if (post) {
if (params != null) {
((HttpPost) httpRequest).setEntity(getFormEntityFromParameter());
diff --git a/services/src/main/java/org/keycloak/services/resources/account/AccountRestService.java b/services/src/main/java/org/keycloak/services/resources/account/AccountRestService.java
index 57b808c..7a71167 100755
--- a/services/src/main/java/org/keycloak/services/resources/account/AccountRestService.java
+++ b/services/src/main/java/org/keycloak/services/resources/account/AccountRestService.java
@@ -39,14 +39,7 @@ import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.Cors;
import org.keycloak.storage.ReadOnlyException;
-import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.GET;
-import javax.ws.rs.OPTIONS;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
+import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
@@ -261,12 +254,30 @@ public class AccountRestService {
return Cors.add(request, Response.ok()).auth().allowedOrigins(auth.getToken()).build();
}
+ /**
+ * Remove a specific session
+ *
+ * @param id a specific session to remove
+ * @return
+ */
+ @Path("/session")
+ @DELETE
+ @Produces(MediaType.APPLICATION_JSON)
+ @NoCache
+ public Response sessionLogout(@QueryParam("id") String id) {
+ UserSessionModel userSession = session.sessions().getUserSession(realm, id);
+ if (userSession != null && userSession.getUser().equals(user)) {
+ AuthenticationManager.backchannelLogout(session, userSession, true);
+ }
+ return Cors.add(request, Response.ok()).auth().allowedOrigins(auth.getToken()).build();
+ }
+
@Path("/credentials")
public AccountCredentialResource credentials() {
return new AccountCredentialResource(session, event, user);
}
- // TODO Federated identities
+ // TODO Federated identities
// TODO Applications
// TODO Logs
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountRestServiceTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountRestServiceTest.java
index 01bf87b..509665f 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountRestServiceTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountRestServiceTest.java
@@ -250,6 +250,29 @@ public class AccountRestServiceTest extends AbstractTestRealmKeycloakTest {
assertEquals(expectedStatus, status);
}
+ public void testDeleteSessions() throws IOException {
+ TokenUtil viewToken = new TokenUtil("view-account-access", "password");
+ oauth.doLogin("view-account-access", "password");
+ List<SessionRepresentation> sessions = SimpleHttp.doGet(getAccountUrl("sessions"), client).auth(viewToken.getToken()).asJson(new TypeReference<List<SessionRepresentation>>() {});
+ assertEquals(2, sessions.size());
+ int status = SimpleHttp.doDelete(getAccountUrl("sessions?current=false"), client).acceptJson().auth(viewToken.getToken()).asStatus();
+ assertEquals(200, status);
+ sessions = SimpleHttp.doGet(getAccountUrl("sessions"), client).auth(viewToken.getToken()).asJson(new TypeReference<List<SessionRepresentation>>() {});
+ assertEquals(1, sessions.size());
+ }
+
+ @Test
+ public void testDeleteSession() throws IOException {
+ TokenUtil viewToken = new TokenUtil("view-account-access", "password");
+ String sessionId = oauth.doLogin("view-account-access", "password").getSessionState();
+ List<SessionRepresentation> sessions = SimpleHttp.doGet(getAccountUrl("sessions"), client).auth(viewToken.getToken()).asJson(new TypeReference<List<SessionRepresentation>>() {});
+ assertEquals(2, sessions.size());
+ int status = SimpleHttp.doDelete(getAccountUrl("session?id=" + sessionId), client).acceptJson().auth(viewToken.getToken()).asStatus();
+ assertEquals(200, status);
+ sessions = SimpleHttp.doGet(getAccountUrl("sessions"), client).auth(viewToken.getToken()).asJson(new TypeReference<List<SessionRepresentation>>() {});
+ assertEquals(1, sessions.size());
+ }
+
private String getAccountUrl(String resource) {
return suiteContext.getAuthServerInfo().getContextRoot().toString() + "/auth/realms/test/account" + (resource != null ? "/" + resource : "");
}