KEYCLOAK-2589 Copy AssertEvents to Arquillian testsuite and modify to pull events from admin endpoints

This commit is contained in:
Marko Strukelj 2016-03-24 16:08:36 +01:00
parent 7bdf9f4ba1
commit 95d222348d
18 changed files with 1181 additions and 4 deletions

View file

@ -105,4 +105,37 @@ public class EventRepresentation {
public void setDetails(Map<String, String> details) {
this.details = details;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
EventRepresentation that = (EventRepresentation) o;
if (time != that.time) return false;
if (type != null ? !type.equals(that.type) : that.type != null) return false;
if (realmId != null ? !realmId.equals(that.realmId) : that.realmId != null) return false;
if (clientId != null ? !clientId.equals(that.clientId) : that.clientId != null) return false;
if (userId != null ? !userId.equals(that.userId) : that.userId != null) return false;
if (sessionId != null ? !sessionId.equals(that.sessionId) : that.sessionId != null) return false;
if (ipAddress != null ? !ipAddress.equals(that.ipAddress) : that.ipAddress != null) return false;
if (error != null ? !error.equals(that.error) : that.error != null) return false;
return !(details != null ? !details.equals(that.details) : that.details != null);
}
@Override
public int hashCode() {
int result = (int) (time ^ (time >>> 32));
result = 31 * result + (type != null ? type.hashCode() : 0);
result = 31 * result + (realmId != null ? realmId.hashCode() : 0);
result = 31 * result + (clientId != null ? clientId.hashCode() : 0);
result = 31 * result + (userId != null ? userId.hashCode() : 0);
result = 31 * result + (sessionId != null ? sessionId.hashCode() : 0);
result = 31 * result + (ipAddress != null ? ipAddress.hashCode() : 0);
result = 31 * result + (error != null ? error.hashCode() : 0);
result = 31 * result + (details != null ? details.hashCode() : 0);
return result;
}
}

View file

@ -0,0 +1,42 @@
<!--
~ Copyright 2016 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.
-->
<project name="inject-provider" basedir="." default="inject-provider">
<scriptdef name="inject-provider" language="javascript" manager="bsf">
<attribute name="path"/>
<![CDATA[
importClass(Packages.java.io.File);
importClass(Packages.org.keycloak.util.JsonSerialization);
path = attributes.get("path");
file = new File(path);
root = JsonSerialization.mapper.readTree(file);
// inject provider
providers = root.withArray("providers");
providers.add("module:org.keycloak.testsuite.integration-arquillian-event-queue");
// save file
JsonSerialization.prettyMapper.writeValue(file, root);
]]>
</scriptdef>
<target name="inject-provider">
<inject-provider path="${auth.server.home}/standalone/configuration/keycloak-server.json"/>
</target>
</project>

View file

@ -97,13 +97,102 @@
</artifactItems>
</configuration>
</execution>
<execution>
<id>copy-event-queue-provider</id>
<phase>generate-resources</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.keycloak.testsuite</groupId>
<artifactId>integration-arquillian-event-queue</artifactId>
<version>${project.version}</version>
<type>jar</type>
<overWrite>false</overWrite>
<outputDirectory>${auth.server.home}/modules/org/keycloak/testsuite/integration-arquillian-event-queue/main</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
<execution>
<id>install-event-queue-module</id>
<phase>generate-resources</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.keycloak.testsuite</groupId>
<artifactId>integration-arquillian-event-queue</artifactId>
<version>${project.version}</version>
<type>jar</type>
<outputDirectory>${auth.server.home}/modules</outputDirectory>
<includes>**/module.xml</includes>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<id>inject-into-keycloak-server-json</id>
<phase>process-resources</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<ant antfile="../build.xml" inheritRefs="true">
<target name="inject-provider"/>
</ant>
</target>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>ant-contrib</groupId>
<artifactId>ant-contrib</artifactId>
<version>1.0b3</version>
<exclusions>
<exclusion>
<groupId>ant</groupId>
<artifactId>ant</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant-apache-bsf</artifactId>
<version>1.9.3</version>
</dependency>
<dependency>
<groupId>org.apache.bsf</groupId>
<artifactId>bsf-api</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>rhino</groupId>
<artifactId>js</artifactId>
<version>1.7R2</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<artifactId>maven-enforcer-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>

View file

@ -30,6 +30,7 @@
<name>Auth Server</name>
<modules>
<module>services</module>
<module>jboss</module>
<module>undertow</module>
</modules>

View file

@ -0,0 +1,81 @@
<?xml version="1.0"?>
<!--
~ Copyright 2016 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.
-->
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.keycloak.testsuite</groupId>
<artifactId>integration-arquillian-servers-auth-server-services</artifactId>
<version>2.0.0.CR1-SNAPSHOT</version>
</parent>
<artifactId>integration-arquillian-event-queue</artifactId>
<name>Auth Server Services - Event Queue</name>
<dependencies>
<!-- Keycloak deps for tests -->
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-dependencies-server-all</artifactId>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
</dependency>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-core</artifactId>
</dependency>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-servlet</artifactId>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>

View file

@ -0,0 +1,66 @@
/*
* Copyright 2016 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.testsuite.events;
import org.keycloak.events.Event;
import org.keycloak.util.JsonSerialization;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.BlockingQueue;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
public class AssertEventsServletFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
if ("/event-queue".equals(req.getRequestURI().substring(req.getContextPath().length()))) {
BlockingQueue<Event> events = EventsListenerProvider.getInstance();
HttpServletResponse resp = (HttpServletResponse) servletResponse;
resp.setContentType("application/json");
Event event = events.poll();
if (event != null) {
JsonSerialization.writeValueToStream(servletResponse.getOutputStream(), event);
} else {
resp.setStatus(204);
}
} else {
filterChain.doFilter(servletRequest, servletResponse);
}
}
@Override
public void destroy() {
}
}

View file

@ -0,0 +1,52 @@
/*
* Copyright 2016 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.testsuite.events;
import org.keycloak.events.Event;
import org.keycloak.events.EventListenerProvider;
import org.keycloak.events.admin.AdminEvent;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
public class EventsListenerProvider implements EventListenerProvider {
private static final BlockingQueue<Event> events = new LinkedBlockingQueue<Event>();
@Override
public void onEvent(Event event) {
events.add(event);
}
@Override
public void onEvent(AdminEvent event, boolean includeRepresentation) {
// TODO: implement if needed
}
@Override
public void close() {
}
public static BlockingQueue<Event> getInstance() {
return events;
}
}

View file

@ -0,0 +1,58 @@
/*
* Copyright 2016 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.testsuite.events;
import org.keycloak.Config;
import org.keycloak.events.EventListenerProvider;
import org.keycloak.events.EventListenerProviderFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
public class EventsListenerProviderFactory implements EventListenerProviderFactory {
private static final EventsListenerProvider INSTANCE = new EventsListenerProvider();
private EventsServer server = new EventsServer();
@Override
public EventListenerProvider create(KeycloakSession session) {
return INSTANCE;
}
@Override
public void init(Config.Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
server.start();
}
@Override
public void close() {
server.stop();
}
@Override
public String getId() {
return "event-queue";
}
}

View file

@ -0,0 +1,95 @@
/*
* Copyright 2016 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.testsuite.events;
import io.undertow.Undertow;
import io.undertow.server.handlers.PathHandler;
import io.undertow.servlet.Servlets;
import io.undertow.servlet.api.DeploymentInfo;
import io.undertow.servlet.api.DeploymentManager;
import io.undertow.servlet.api.FilterInfo;
import io.undertow.servlet.api.ServletContainer;
import io.undertow.servlet.api.ServletContainer.Factory;
import javax.servlet.DispatcherType;
import javax.servlet.ServletException;
import java.util.logging.Logger;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
public class EventsServer {
private static final Logger log = Logger.getLogger(EventsServer.class.getName());
private String rootPath = "/";
private int port;
private Undertow server;
public EventsServer() {
int eventsPort = Integer.parseInt(System.getProperty("auth.server.events.http.port", "8089"));
int portOffset = Integer.parseInt(System.getProperty("auth.server.port.offset", "0"));
int jbossPortOffset = Integer.parseInt(System.getProperty("jboss.socket.binding.port-offset", "-1"));
log.fine("Configuration:");
log.fine(" auth.server.events.http.port: " + eventsPort);
log.fine(" auth.server.port.offset: " + portOffset);
log.fine(" jboss.socket.binding.port-offset: " + jbossPortOffset);
port = eventsPort + (jbossPortOffset != -1 ? jbossPortOffset : portOffset);
}
public void start() {
PathHandler root = new PathHandler();
this.server = Undertow.builder().addHttpListener(port, "localhost").setHandler(root).build();
this.server.start();
ServletContainer container = Factory.newInstance();
DeploymentInfo di = new DeploymentInfo();
di.setClassLoader(getClass().getClassLoader());
di.setContextPath(rootPath);
di.setDeploymentName("testing-event-queue");
FilterInfo filter = Servlets.filter("EventsFilter", AssertEventsServletFilter.class);
di.addFilter(filter);
di.addFilterUrlMapping("EventsFilter", "/event-queue", DispatcherType.REQUEST);
DeploymentManager manager = container.addDeployment(di);
manager.deploy();
try {
root.addPrefixPath(rootPath, manager.start());
} catch (ServletException e) {
throw new RuntimeException(e);
}
log.info("Started EventsServer on port: " + port);
}
public void stop() {
this.server.stop();
}
public void setRootPath(String rootPath) {
this.rootPath = rootPath;
}
public void setPort(int port) {
this.port = port;
}
}

View file

@ -0,0 +1,35 @@
#
# Copyright 2016 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.
#
#
# Copyright 2016 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.
#
org.keycloak.testsuite.events.EventsListenerProviderFactory

View file

@ -0,0 +1,30 @@
<!--
~ Copyright 2016 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.
-->
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.testsuite.integration-arquillian-event-queue">
<resources>
<resource-root path="integration-arquillian-event-queue-${project.version}.jar"/>
</resources>
<dependencies>
<module name="javax.api"/>
<module name="javax.servlet.api"/>
<module name="io.undertow.core"/>
<module name="io.undertow.servlet"/>
<module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-server-spi"/>
</dependencies>
</module>

View file

@ -0,0 +1,36 @@
<?xml version="1.0"?>
<!--
~ Copyright 2016 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.
-->
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<parent>
<groupId>org.keycloak.testsuite</groupId>
<artifactId>integration-arquillian-servers-auth-server</artifactId>
<version>2.0.0.CR1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>integration-arquillian-servers-auth-server-services</artifactId>
<packaging>pom</packaging>
<name>Auth Server Services</name>
<modules>
<module>event-queue</module>
</modules>
</project>

View file

@ -57,6 +57,11 @@
<artifactId>commons-configuration</artifactId>
<version>1.10</version>
</dependency>
<dependency>
<groupId>org.keycloak.testsuite</groupId>
<artifactId>integration-arquillian-event-queue</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>

View file

@ -0,0 +1,365 @@
/*
* Copyright 2016 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.testsuite;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.hamcrest.CoreMatchers;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Assert;
import org.keycloak.OAuth2Constants;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.authentication.authenticators.client.ClientIdAndSecretAuthenticator;
import org.keycloak.common.util.PemUtils;
import org.keycloak.events.Details;
import org.keycloak.events.EventType;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.EventRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.representations.idm.UserSessionRepresentation;
import org.keycloak.util.JsonSerialization;
import org.keycloak.util.TokenUtil;
import java.io.IOException;
import java.security.PublicKey;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class AssertEvents {
static final String DEFAULT_CLIENT_ID = "test-app";
static final String DEFAULT_IP_ADDRESS = "127.0.0.1";
static final String DEFAULT_REALM = "test";
static final String DEFAULT_USERNAME = "test-user@localhost";
String defaultRedirectUri = "http://localhost:8081/app/auth";
String defaultEventsQueueUri = "http://localhost:8092";
private RealmResource realmResource;
private RealmRepresentation realmRep;
private AbstractKeycloakTest context;
private PublicKey realmPublicKey;
private UserRepresentation defaultUser;
public AssertEvents(AbstractKeycloakTest ctx) throws Exception {
context = ctx;
realmResource = context.adminClient.realms().realm(DEFAULT_REALM);
realmRep = realmResource.toRepresentation();
String pubKeyString = realmRep.getPublicKey();
realmPublicKey = PemUtils.decodePublicKey(pubKeyString);
defaultUser = getUser(DEFAULT_USERNAME);
if (defaultUser == null) {
throw new RuntimeException("Default user does not exist: " + DEFAULT_USERNAME + ". Make sure to add it to your test realm.");
}
defaultEventsQueueUri = getAuthServerEventsQueueUri();
}
String getAuthServerEventsQueueUri() {
int httpPort = Integer.parseInt(System.getProperty("auth.server.event.http.port", "8089"));
int portOffset = Integer.parseInt(System.getProperty("auth.server.port.offset", "0"));
return "http://localhost:" + (httpPort + portOffset);
}
public EventRepresentation poll() {
EventRepresentation event = fetchNextEvent();
Assert.assertNotNull("Event expected", event);
return event;
}
public void clear() {
realmResource.clearEvents();
}
public ExpectedEvent expectRequiredAction(EventType event) {
return expectLogin().event(event).removeDetail(Details.CONSENT).session(isUUID());
}
public ExpectedEvent expectLogin() {
return expect(EventType.LOGIN)
.detail(Details.CODE_ID, isCodeId())
//.detail(Details.USERNAME, DEFAULT_USERNAME)
//.detail(Details.AUTH_METHOD, OIDCLoginProtocol.LOGIN_PROTOCOL)
//.detail(Details.AUTH_TYPE, AuthorizationEndpoint.CODE_AUTH_TYPE)
.detail(Details.REDIRECT_URI, defaultRedirectUri)
.detail(Details.CONSENT, Details.CONSENT_VALUE_NO_CONSENT_REQUIRED)
.session(isUUID());
}
public ExpectedEvent expectClientLogin() {
return expect(EventType.CLIENT_LOGIN)
.detail(Details.CODE_ID, isCodeId())
.detail(Details.CLIENT_AUTH_METHOD, ClientIdAndSecretAuthenticator.PROVIDER_ID)
.detail(Details.GRANT_TYPE, OAuth2Constants.CLIENT_CREDENTIALS)
.removeDetail(Details.CODE_ID)
.session(isUUID());
}
public ExpectedEvent expectSocialLogin() {
return expect(EventType.LOGIN)
.detail(Details.CODE_ID, isCodeId())
.detail(Details.USERNAME, DEFAULT_USERNAME)
.detail(Details.AUTH_METHOD, "form")
.detail(Details.REDIRECT_URI, defaultRedirectUri)
.session(isUUID());
}
public ExpectedEvent expectCodeToToken(String codeId, String sessionId) {
return expect(EventType.CODE_TO_TOKEN)
.detail(Details.CODE_ID, codeId)
.detail(Details.TOKEN_ID, isUUID())
.detail(Details.REFRESH_TOKEN_ID, isUUID())
.detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_REFRESH)
.detail(Details.CLIENT_AUTH_METHOD, ClientIdAndSecretAuthenticator.PROVIDER_ID)
.session(sessionId);
}
public ExpectedEvent expectRefresh(String refreshTokenId, String sessionId) {
return expect(EventType.REFRESH_TOKEN)
.detail(Details.TOKEN_ID, isUUID())
.detail(Details.REFRESH_TOKEN_ID, refreshTokenId)
.detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_REFRESH)
.detail(Details.UPDATED_REFRESH_TOKEN_ID, isUUID())
.detail(Details.CLIENT_AUTH_METHOD, ClientIdAndSecretAuthenticator.PROVIDER_ID)
.session(sessionId);
}
public ExpectedEvent expectLogout(String sessionId) {
return expect(EventType.LOGOUT).client((String) null)
.detail(Details.REDIRECT_URI, defaultRedirectUri)
.session(sessionId);
}
public ExpectedEvent expectRegister(String username, String email) {
UserRepresentation user = username != null ? getUser(username) : null;
return expect(EventType.REGISTER)
.user(user != null ? user.getId() : null)
.detail(Details.USERNAME, username)
.detail(Details.EMAIL, email)
.detail(Details.REGISTER_METHOD, "form")
.detail(Details.REDIRECT_URI, defaultRedirectUri);
}
public ExpectedEvent expectAccount(EventType event) {
return expect(event).client("account");
}
public ExpectedEvent expect(EventType event) {
return new ExpectedEvent()
.realm(realmRep.getId())
.client(DEFAULT_CLIENT_ID)
.user(defaultUser.getId())
.ipAddress(DEFAULT_IP_ADDRESS)
.session((String) null)
.event(event);
}
UserRepresentation getUser(String username) {
List<UserRepresentation> result = realmResource.users().search(username, null, null, null, 0, 1);
return result.size() > 0 ? result.get(0) : null;
}
public PublicKey getRealmPublicKey() {
return realmPublicKey;
}
public class ExpectedEvent {
private EventRepresentation expected = new EventRepresentation();
private Matcher<String> userId;
private Matcher<String> sessionId;
private HashMap<String, Matcher<String>> details;
public ExpectedEvent realm(RealmRepresentation realm) {
expected.setRealmId(realm.getId());
return this;
}
public ExpectedEvent realm(String realmId) {
expected.setRealmId(realmId);
return this;
}
public ExpectedEvent client(ClientRepresentation client) {
expected.setClientId(client.getClientId());
return this;
}
public ExpectedEvent client(String clientId) {
expected.setClientId(clientId);
return this;
}
public ExpectedEvent user(UserRepresentation user) {
return user(user.getId());
}
public ExpectedEvent user(String userId) {
return user(CoreMatchers.equalTo(userId));
}
public ExpectedEvent user(Matcher<String> userId) {
this.userId = userId;
return this;
}
public ExpectedEvent session(UserSessionRepresentation session) {
return session(session.getId());
}
public ExpectedEvent session(String sessionId) {
return session(CoreMatchers.equalTo(sessionId));
}
public ExpectedEvent session(Matcher<String> sessionId) {
this.sessionId = sessionId;
return this;
}
public ExpectedEvent ipAddress(String ipAddress) {
expected.setIpAddress(ipAddress);
return this;
}
public ExpectedEvent event(EventType e) {
expected.setType(e.name());
return this;
}
public ExpectedEvent detail(String key, String value) {
return detail(key, CoreMatchers.equalTo(value));
}
public ExpectedEvent detail(String key, Matcher<String> matcher) {
if (details == null) {
details = new HashMap<String, Matcher<String>>();
}
details.put(key, matcher);
return this;
}
public ExpectedEvent removeDetail(String key) {
if (details != null) {
details.remove(key);
}
return this;
}
public ExpectedEvent clearDetails() {
if (details != null) details.clear();
return this;
}
public ExpectedEvent error(String error) {
expected.setError(error);
return this;
}
public EventRepresentation assertEvent() {
return assertEvent(poll());
}
public EventRepresentation assertEvent(EventRepresentation actual) {
if (expected.getError() != null && !expected.getType().toString().endsWith("_ERROR")) {
expected.setType(expected.getType() + "_ERROR");
}
Assert.assertEquals(expected.getType(), actual.getType());
Assert.assertEquals(expected.getRealmId(), actual.getRealmId());
Assert.assertEquals(expected.getClientId(), actual.getClientId());
Assert.assertEquals(expected.getError(), actual.getError());
Assert.assertEquals(expected.getIpAddress(), actual.getIpAddress());
Assert.assertThat(actual.getUserId(), userId);
Assert.assertThat(actual.getSessionId(), sessionId);
if (details == null || details.isEmpty()) {
// Assert.assertNull(actual.getDetails());
} else {
Assert.assertNotNull(actual.getDetails());
for (Map.Entry<String, Matcher<String>> d : details.entrySet()) {
String actualValue = actual.getDetails().get(d.getKey());
if (!actual.getDetails().containsKey(d.getKey())) {
Assert.fail(d.getKey() + " missing");
}
Assert.assertThat("Unexpected value for " + d.getKey(), actualValue, d.getValue());
}
/*
for (String k : actual.getDetails().keySet()) {
if (!details.containsKey(k)) {
Assert.fail(k + " was not expected");
}
}
*/
}
return actual;
}
}
public static Matcher<String> isCodeId() {
return isUUID();
}
public static Matcher<String> isUUID() {
return new TypeSafeMatcher<String>() {
@Override
protected boolean matchesSafely(String item) {
return 36 == item.length() && item.charAt(8) == '-' && item.charAt(13) == '-' && item.charAt(18) == '-' && item.charAt(23) == '-';
}
@Override
public void describeTo(Description description) {
description.appendText("Not an UUID");
}
};
}
private EventRepresentation fetchNextEvent() {
CloseableHttpClient httpclient = HttpClients.createDefault();
try {
HttpPost post = new HttpPost(defaultEventsQueueUri + "/event-queue");
CloseableHttpResponse response = httpclient.execute(post);
if (response.getStatusLine().getStatusCode() != 200) {
throw new RuntimeException("Failed to retrieve event from " + post.getURI() + ": " + response.getStatusLine().toString() + " / " + IOUtils.toString(response.getEntity().getContent()));
}
return JsonSerialization.readValue(response.getEntity().getContent(), EventRepresentation.class);
} catch (Exception e) {
throw new RuntimeException(e);
}
finally {
try {
httpclient.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}

View file

@ -38,7 +38,7 @@ public abstract class AbstractEventTest extends AbstractAuthTest {
configRep.setAdminEventsDetailsEnabled(false);
configRep.setAdminEventsEnabled(false);
configRep.setEventsEnabled(false);
configRep.setEnabledEventTypes(Collections.EMPTY_LIST); // resets to all types
configRep.setEnabledEventTypes(Collections.<String>emptyList()); // resets to all types
saveConfig();
}

View file

@ -11,7 +11,8 @@
"jboss-logging" : {
"success-level": "debug",
"error-level": "warn"
}
},
"event-queue": {}
},
"realm": {

View file

@ -0,0 +1,186 @@
{
"id": "test",
"realm": "test",
"enabled": true,
"sslRequired": "external",
"registrationAllowed": true,
"resetPasswordAllowed": true,
"editUsernameAllowed" : true,
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials": [ "password" ],
"defaultRoles": [ "user" ],
"smtpServer": {
"from": "auto@keycloak.org",
"host": "localhost",
"port":"3025"
},
"users" : [
{
"username" : "test-user@localhost",
"enabled": true,
"email" : "test-user@localhost",
"firstName": "Tom",
"lastName": "Brady",
"credentials" : [
{ "type" : "password",
"value" : "password" }
],
"realmRoles": ["user", "offline_access"],
"clientRoles": {
"test-app": [ "customer-user" ],
"account": [ "view-profile", "manage-account" ]
}
},
{
"username" : "john-doh@localhost",
"enabled": true,
"email" : "john-doh@localhost",
"firstName": "John",
"lastName": "Doh",
"credentials" : [
{ "type" : "password",
"value" : "password" }
],
"realmRoles": ["user"],
"clientRoles": {
"test-app": [ "customer-user" ],
"account": [ "view-profile", "manage-account" ]
}
},
{
"username" : "keycloak-user@localhost",
"enabled": true,
"email" : "keycloak-user@localhost",
"credentials" : [
{ "type" : "password",
"value" : "password" }
],
"realmRoles": ["user"],
"clientRoles": {
"test-app": [ "customer-user" ],
"account": [ "view-profile", "manage-account" ]
}
},
{
"username" : "topGroupUser",
"enabled": true,
"email" : "top@redhat.com",
"credentials" : [
{ "type" : "password",
"value" : "password" }
],
"groups": [
"/topGroup"
]
},
{
"username" : "level2GroupUser",
"enabled": true,
"email" : "level2@redhat.com",
"credentials" : [
{ "type" : "password",
"value" : "password" }
],
"groups": [
"/topGroup/level2group"
]
}
],
"scopeMappings": [
{
"client": "third-party",
"roles": ["user"]
},
{
"client": "test-app",
"roles": ["user"]
}
],
"clients": [
{
"clientId": "test-app",
"enabled": true,
"baseUrl": "http://localhost:8081/app",
"redirectUris": [
"http://localhost:8081/app/*"
],
"adminUrl": "http://localhost:8081/app/logout",
"secret": "password"
},
{
"clientId" : "third-party",
"enabled": true,
"consentRequired": true,
"redirectUris": [
"http://localhost:8081/app/*"
],
"secret": "password"
}
],
"roles" : {
"realm" : [
{
"name": "user",
"description": "Have User privileges"
},
{
"name": "admin",
"description": "Have Administrator privileges"
}
],
"client" : {
"test-app" : [
{
"name": "customer-user",
"description": "Have Customer User privileges"
},
{
"name": "customer-admin",
"description": "Have Customer Admin privileges"
}
]
}
},
"groups" : [
{
"name": "topGroup",
"attributes": {
"topAttribute": ["true"]
},
"realmRoles": ["user"],
"subGroups": [
{
"name": "level2group",
"realmRoles": ["admin"],
"clientRoles": {
"test-app": ["customer-user"]
},
"attributes": {
"level2Attribute": ["true"]
}
}
]
}
],
"clientScopeMappings": {
"test-app": [
{
"client": "third-party",
"roles": ["customer-user"]
}
]
},
"internationalizationEnabled": true,
"supportedLocales": ["en", "de"],
"defaultLocale": "en",
"eventsListeners": ["jboss-logging", "event-queue"]
}

View file

@ -48,6 +48,7 @@
<auth.server.port.offset>100</auth.server.port.offset>
<auth.server.http.port>8180</auth.server.http.port>
<auth.server.events.http.port>8089</auth.server.events.http.port>
<auth.server.https.port>8543</auth.server.https.port>
<auth.server.management.port>10090</auth.server.management.port>
<auth.server.management.port.jmx>10099</auth.server.management.port.jmx>
@ -140,6 +141,7 @@
<auth.server.port.offset>${auth.server.port.offset}</auth.server.port.offset>
<auth.server.http.port>${auth.server.http.port}</auth.server.http.port>
<auth.server.events.http.port>${auth.server.events.http.port}</auth.server.events.http.port>
<auth.server.https.port>${auth.server.https.port}</auth.server.https.port>
<auth.server.management.port>${auth.server.management.port}</auth.server.management.port>
<auth.server.management.port.jmx>${auth.server.management.port.jmx}</auth.server.management.port.jmx>