KEYCLOAK-836 Refactoring of JaxrsBearerTokenFilter to work with both resteasy and Apache CXF. Added test
This commit is contained in:
parent
ebb795af5a
commit
d1e819cef1
18 changed files with 962 additions and 129 deletions
10
core/src/main/java/org/keycloak/util/GenericConstants.java
Normal file
10
core/src/main/java/org/keycloak/util/GenericConstants.java
Normal file
|
@ -0,0 +1,10 @@
|
|||
package org.keycloak.util;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class GenericConstants {
|
||||
|
||||
public static final String PROTOCOL_CLASSPATH = "classpath:";
|
||||
|
||||
}
|
|
@ -11,12 +11,10 @@ import java.security.KeyStore;
|
|||
*/
|
||||
public class KeystoreUtil {
|
||||
|
||||
private static final String PROTOCOL_CLASSPATH = "classpath:";
|
||||
|
||||
public static KeyStore loadKeyStore(String filename, String password) throws Exception {
|
||||
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
InputStream trustStream = (filename.startsWith(PROTOCOL_CLASSPATH))
|
||||
?KeystoreUtil.class.getResourceAsStream(filename.replace(PROTOCOL_CLASSPATH, ""))
|
||||
InputStream trustStream = (filename.startsWith(GenericConstants.PROTOCOL_CLASSPATH))
|
||||
?KeystoreUtil.class.getResourceAsStream(filename.replace(GenericConstants.PROTOCOL_CLASSPATH, ""))
|
||||
:new FileInputStream(new File(filename));
|
||||
trustStore.load(trustStream, password.toCharArray());
|
||||
trustStream.close();
|
||||
|
|
|
@ -166,7 +166,7 @@ public class PreAuthActionsHandler {
|
|||
if (!facade.getRequest().isSecure() && deployment.getSslRequired().isRequired(facade.getRequest().getRemoteAddr())) {
|
||||
log.warn("SSL is required for adapter admin action");
|
||||
facade.getResponse().sendError(403, "ssl required");
|
||||
|
||||
return null;
|
||||
}
|
||||
String token = StreamUtil.readString(facade.getRequest().getInputStream());
|
||||
if (token == null) {
|
||||
|
|
|
@ -14,11 +14,6 @@
|
|||
<description/>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>jaxrs-api</artifactId>
|
||||
|
@ -43,6 +38,12 @@
|
|||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-adapter-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.jackson</groupId>
|
||||
<artifactId>jackson-core-asl</artifactId>
|
||||
|
|
128
integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilter.java
Executable file → Normal file
128
integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilter.java
Executable file → Normal file
|
@ -1,114 +1,14 @@
|
|||
package org.keycloak.jaxrs;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||
import org.keycloak.KeycloakPrincipal;
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.RSATokenVerifier;
|
||||
import org.keycloak.VerificationException;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
|
||||
import javax.annotation.Priority;
|
||||
import javax.ws.rs.Priorities;
|
||||
import javax.ws.rs.container.ContainerRequestContext;
|
||||
import javax.ws.rs.container.ContainerRequestFilter;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.SecurityContext;
|
||||
import java.io.IOException;
|
||||
import java.security.Principal;
|
||||
import java.security.PublicKey;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
@Priority(Priorities.AUTHENTICATION)
|
||||
public class JaxrsBearerTokenFilter implements ContainerRequestFilter {
|
||||
private static Logger log = Logger.getLogger(JaxrsBearerTokenFilter.class);
|
||||
protected String realm;
|
||||
protected PublicKey realmPublicKey;
|
||||
protected String resourceName;
|
||||
|
||||
public JaxrsBearerTokenFilter(String realm, PublicKey realmPublicKey, String resourceName) {
|
||||
this.realm = realm;
|
||||
this.realmPublicKey = realmPublicKey;
|
||||
this.resourceName = resourceName;
|
||||
}
|
||||
|
||||
protected void challengeResponse(ContainerRequestContext request, String error, String description) {
|
||||
StringBuilder header = new StringBuilder("Bearer realm=\"");
|
||||
header.append(realm).append("\"");
|
||||
if (error != null) {
|
||||
header.append(", error=\"").append(error).append("\"");
|
||||
}
|
||||
if (description != null) {
|
||||
header.append(", error_description=\"").append(description).append("\"");
|
||||
}
|
||||
request.abortWith(Response.status(Response.Status.UNAUTHORIZED).header(HttpHeaders.WWW_AUTHENTICATE, header.toString()).build());
|
||||
return;
|
||||
}
|
||||
|
||||
@Context
|
||||
protected SecurityContext securityContext;
|
||||
|
||||
@Override
|
||||
public void filter(ContainerRequestContext request) throws IOException {
|
||||
String authHeader = request.getHeaderString(HttpHeaders.AUTHORIZATION);
|
||||
if (authHeader == null) {
|
||||
challengeResponse(request, null, null);
|
||||
return;
|
||||
}
|
||||
|
||||
String[] split = authHeader.trim().split("\\s+");
|
||||
if (split == null || split.length != 2) challengeResponse(request, null, null);
|
||||
if (!split[0].equalsIgnoreCase("Bearer")) challengeResponse(request, null, null);
|
||||
|
||||
|
||||
String tokenString = split[1];
|
||||
|
||||
|
||||
try {
|
||||
AccessToken token = RSATokenVerifier.verifyToken(tokenString, realmPublicKey, realm);
|
||||
KeycloakSecurityContext skSession = new KeycloakSecurityContext(tokenString, token, null, null);
|
||||
ResteasyProviderFactory.pushContext(KeycloakSecurityContext.class, skSession);
|
||||
|
||||
final KeycloakPrincipal<KeycloakSecurityContext> principal = new KeycloakPrincipal<KeycloakSecurityContext>(token.getSubject(), skSession);
|
||||
final boolean isSecure = securityContext.isSecure();
|
||||
final AccessToken.Access access;
|
||||
if (resourceName != null) {
|
||||
access = token.getResourceAccess(resourceName);
|
||||
} else {
|
||||
access = token.getRealmAccess();
|
||||
}
|
||||
SecurityContext ctx = new SecurityContext() {
|
||||
@Override
|
||||
public Principal getUserPrincipal() {
|
||||
return principal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUserInRole(String role) {
|
||||
if (access.getRoles() == null) return false;
|
||||
return access.getRoles().contains(role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecure() {
|
||||
return isSecure;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthenticationScheme() {
|
||||
return "OAUTH_BEARER";
|
||||
}
|
||||
};
|
||||
request.setSecurityContext(ctx);
|
||||
} catch (VerificationException e) {
|
||||
log.error("Failed to verify token", e);
|
||||
challengeResponse(request, "invalid_token", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
package org.keycloak.jaxrs;
|
||||
|
||||
import javax.annotation.Priority;
|
||||
import javax.ws.rs.Priorities;
|
||||
import javax.ws.rs.container.ContainerRequestFilter;
|
||||
import javax.ws.rs.container.PreMatching;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@PreMatching
|
||||
@Priority(Priorities.AUTHENTICATION)
|
||||
public interface JaxrsBearerTokenFilter extends ContainerRequestFilter {
|
||||
}
|
||||
|
|
|
@ -0,0 +1,254 @@
|
|||
package org.keycloak.jaxrs;
|
||||
|
||||
import org.keycloak.KeycloakPrincipal;
|
||||
import org.keycloak.adapters.AdapterDeploymentContext;
|
||||
import org.keycloak.adapters.AdapterUtils;
|
||||
import org.keycloak.adapters.AuthChallenge;
|
||||
import org.keycloak.adapters.AuthOutcome;
|
||||
import org.keycloak.adapters.BearerTokenRequestAuthenticator;
|
||||
import org.keycloak.adapters.KeycloakConfigResolver;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.KeycloakDeploymentBuilder;
|
||||
import org.keycloak.adapters.NodesRegistrationManagement;
|
||||
import org.keycloak.adapters.PreAuthActionsHandler;
|
||||
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
||||
import org.keycloak.adapters.UserSessionManagement;
|
||||
import org.keycloak.util.GenericConstants;
|
||||
|
||||
import javax.annotation.Priority;
|
||||
import javax.ws.rs.Priorities;
|
||||
import javax.ws.rs.container.ContainerRequestContext;
|
||||
import javax.ws.rs.container.PreMatching;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.SecurityContext;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.Principal;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
@PreMatching
|
||||
@Priority(Priorities.AUTHENTICATION)
|
||||
public class JaxrsBearerTokenFilterImpl implements JaxrsBearerTokenFilter {
|
||||
|
||||
private final static Logger log = Logger.getLogger("" + JaxrsBearerTokenFilterImpl.class);
|
||||
|
||||
private String keycloakConfigFile;
|
||||
private String keycloakConfigResolverClass;
|
||||
private volatile boolean started;
|
||||
|
||||
protected AdapterDeploymentContext deploymentContext;
|
||||
|
||||
// TODO: Should also handle stop lifecycle for de-registration
|
||||
protected NodesRegistrationManagement nodesRegistrationManagement;
|
||||
protected UserSessionManagement userSessionManagement = new EmptyUserSessionManagement();
|
||||
|
||||
public void setKeycloakConfigFile(String configFile) {
|
||||
this.keycloakConfigFile = configFile;
|
||||
start();
|
||||
}
|
||||
|
||||
public String getKeycloakConfigFile() {
|
||||
return this.keycloakConfigFile;
|
||||
}
|
||||
|
||||
public String getKeycloakConfigResolverClass() {
|
||||
return keycloakConfigResolverClass;
|
||||
}
|
||||
|
||||
public void setKeycloakConfigResolverClass(String keycloakConfigResolverClass) {
|
||||
this.keycloakConfigResolverClass = keycloakConfigResolverClass;
|
||||
start();
|
||||
}
|
||||
|
||||
protected void start() {
|
||||
if (started) {
|
||||
throw new IllegalStateException("Filter already started. Make sure to specify just keycloakConfigResolver or keycloakConfigFile but not both");
|
||||
}
|
||||
|
||||
if (keycloakConfigResolverClass != null) {
|
||||
Class<?> clazz;
|
||||
try {
|
||||
clazz = getClass().getClassLoader().loadClass(keycloakConfigResolverClass);
|
||||
} catch (ClassNotFoundException cnfe) {
|
||||
// Fallback to tccl
|
||||
try {
|
||||
clazz = Thread.currentThread().getContextClassLoader().loadClass(keycloakConfigResolverClass);
|
||||
} catch (ClassNotFoundException cnfe2) {
|
||||
throw new RuntimeException("Unable to find resolver class: " + keycloakConfigResolverClass);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
KeycloakConfigResolver resolver = (KeycloakConfigResolver) clazz.newInstance();
|
||||
log.info("Using " + resolver + " to resolve Keycloak configuration on a per-request basis.");
|
||||
this.deploymentContext = new AdapterDeploymentContext(resolver);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Unable to instantiate resolver " + clazz);
|
||||
}
|
||||
} else {
|
||||
if (keycloakConfigFile == null) {
|
||||
throw new IllegalArgumentException("You need to specify either keycloakConfigResolverClass or keycloakConfigFile in configuration");
|
||||
}
|
||||
InputStream is = readConfigFile();
|
||||
KeycloakDeployment kd = KeycloakDeploymentBuilder.build(is);
|
||||
deploymentContext = new AdapterDeploymentContext(kd);
|
||||
log.info("Keycloak is using a per-deployment configuration loaded from: " + keycloakConfigFile);
|
||||
}
|
||||
|
||||
nodesRegistrationManagement = new NodesRegistrationManagement();
|
||||
started = true;
|
||||
}
|
||||
|
||||
protected InputStream readConfigFile() {
|
||||
if (keycloakConfigFile.startsWith(GenericConstants.PROTOCOL_CLASSPATH)) {
|
||||
String classPathLocation = keycloakConfigFile.replace(GenericConstants.PROTOCOL_CLASSPATH, "");
|
||||
log.fine("Loading config from classpath on location: " + classPathLocation);
|
||||
// Try current class classloader first
|
||||
InputStream is = getClass().getClassLoader().getResourceAsStream(classPathLocation);
|
||||
if (is == null) {
|
||||
is = Thread.currentThread().getContextClassLoader().getResourceAsStream(classPathLocation);
|
||||
}
|
||||
|
||||
if (is != null) {
|
||||
return is;
|
||||
} else {
|
||||
throw new RuntimeException("Unable to find config from classpath: " + keycloakConfigFile);
|
||||
}
|
||||
} else {
|
||||
// Fallback to file
|
||||
try {
|
||||
log.fine("Loading config from file: " + keycloakConfigFile);
|
||||
return new FileInputStream(keycloakConfigFile);
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
log.severe("Config not found on " + keycloakConfigFile);
|
||||
throw new RuntimeException(fnfe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void filter(ContainerRequestContext request) throws IOException {
|
||||
SecurityContext securityContext = getRequestSecurityContext(request);
|
||||
JaxrsHttpFacade facade = new JaxrsHttpFacade(request, securityContext);
|
||||
if (handlePreauth(request, facade)) {
|
||||
return;
|
||||
}
|
||||
|
||||
KeycloakDeployment resolvedDeployment = deploymentContext.resolveDeployment(facade);
|
||||
|
||||
nodesRegistrationManagement.tryRegister(resolvedDeployment);
|
||||
|
||||
bearerAuthentication(facade, request, resolvedDeployment);
|
||||
}
|
||||
|
||||
protected boolean handlePreauth(ContainerRequestContext request, JaxrsHttpFacade facade) {
|
||||
PreAuthActionsHandler handler = new PreAuthActionsHandler(userSessionManagement, deploymentContext, facade);
|
||||
if (handler.handleRequest()) {
|
||||
// Response might be already finished if error was sent
|
||||
if (!facade.isResponseFinished()) {
|
||||
request.abortWith(facade.getResponseBuilder().build());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void bearerAuthentication(JaxrsHttpFacade facade, ContainerRequestContext request, KeycloakDeployment resolvedDeployment) {
|
||||
BearerTokenRequestAuthenticator bearer = new BearerTokenRequestAuthenticator(resolvedDeployment);
|
||||
AuthOutcome outcome = bearer.authenticate(facade);
|
||||
if (outcome == AuthOutcome.FAILED || outcome == AuthOutcome.NOT_ATTEMPTED) {
|
||||
AuthChallenge challenge = bearer.getChallenge();
|
||||
log.fine("Authentication outcome: " + outcome);
|
||||
boolean challengeSent = challenge.challenge(facade);
|
||||
if (!challengeSent) {
|
||||
// Use some default status code
|
||||
facade.getResponse().setStatus(Response.Status.UNAUTHORIZED.getStatusCode());
|
||||
}
|
||||
|
||||
// Send response now
|
||||
if (!facade.isResponseFinished()) {
|
||||
request.abortWith(facade.getResponseBuilder().build());
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
if (verifySslFailed(facade, resolvedDeployment)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
propagateSecurityContext(facade, request, resolvedDeployment, bearer);
|
||||
}
|
||||
|
||||
protected void propagateSecurityContext(JaxrsHttpFacade facade, ContainerRequestContext request, KeycloakDeployment resolvedDeployment, BearerTokenRequestAuthenticator bearer) {
|
||||
RefreshableKeycloakSecurityContext skSession = new RefreshableKeycloakSecurityContext(resolvedDeployment, null, bearer.getTokenString(), bearer.getToken(), null, null, null);
|
||||
|
||||
// Not needed to do resteasy specifics as KeycloakSecurityContext can be always retrieved from SecurityContext by typecast SecurityContext.getUserPrincipal to KeycloakPrincipal
|
||||
// ResteasyProviderFactory.pushContext(KeycloakSecurityContext.class, skSession);
|
||||
|
||||
facade.setSecurityContext(skSession);
|
||||
String principalName = AdapterUtils.getPrincipalName(resolvedDeployment, bearer.getToken());
|
||||
final KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal = new KeycloakPrincipal<RefreshableKeycloakSecurityContext>(principalName, skSession);
|
||||
SecurityContext anonymousSecurityContext = getRequestSecurityContext(request);
|
||||
final boolean isSecure = anonymousSecurityContext.isSecure();
|
||||
final Set<String> roles = AdapterUtils.getRolesFromSecurityContext(skSession);
|
||||
|
||||
SecurityContext ctx = new SecurityContext() {
|
||||
@Override
|
||||
public Principal getUserPrincipal() {
|
||||
return principal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUserInRole(String role) {
|
||||
return roles.contains(role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecure() {
|
||||
return isSecure;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthenticationScheme() {
|
||||
return "OAUTH_BEARER";
|
||||
}
|
||||
};
|
||||
request.setSecurityContext(ctx);
|
||||
}
|
||||
|
||||
protected boolean verifySslFailed(JaxrsHttpFacade facade, KeycloakDeployment deployment) {
|
||||
if (!facade.getRequest().isSecure() && deployment.getSslRequired().isRequired(facade.getRequest().getRemoteAddr())) {
|
||||
log.warning("SSL is required to authenticate, but request is not secured");
|
||||
facade.getResponse().sendError(403, "SSL required!");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected SecurityContext getRequestSecurityContext(ContainerRequestContext request) {
|
||||
return request.getSecurityContext();
|
||||
}
|
||||
|
||||
// We don't have any sessions to manage with pure jaxrs filter
|
||||
private static class EmptyUserSessionManagement implements UserSessionManagement {
|
||||
|
||||
@Override
|
||||
public void logoutAll() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logoutHttpSessions(List<String> ids) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,175 @@
|
|||
package org.keycloak.jaxrs;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.security.cert.X509Certificate;
|
||||
import javax.ws.rs.container.ContainerRequestContext;
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.SecurityContext;
|
||||
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.adapters.HttpFacade;
|
||||
import org.keycloak.util.HostUtils;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class JaxrsHttpFacade implements HttpFacade {
|
||||
|
||||
protected final ContainerRequestContext requestContext;
|
||||
protected final SecurityContext securityContext;
|
||||
protected final RequestFacade requestFacade = new RequestFacade();
|
||||
protected final ResponseFacade responseFacade = new ResponseFacade();
|
||||
protected KeycloakSecurityContext keycloakSecurityContext;
|
||||
protected boolean responseFinished;
|
||||
|
||||
public JaxrsHttpFacade(ContainerRequestContext containerRequestContext, SecurityContext securityContext) {
|
||||
this.requestContext = containerRequestContext;
|
||||
this.securityContext = securityContext;
|
||||
}
|
||||
|
||||
protected class RequestFacade implements HttpFacade.Request {
|
||||
|
||||
@Override
|
||||
public String getMethod() {
|
||||
return requestContext.getMethod();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getURI() {
|
||||
return requestContext.getUriInfo().getRequestUri().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecure() {
|
||||
return securityContext.isSecure();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getQueryParamValue(String param) {
|
||||
MultivaluedMap<String, String> queryParams = requestContext.getUriInfo().getQueryParameters();
|
||||
if (queryParams == null)
|
||||
return null;
|
||||
return queryParams.getFirst(param);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cookie getCookie(String cookieName) {
|
||||
Map<String, javax.ws.rs.core.Cookie> cookies = requestContext.getCookies();
|
||||
if (cookies == null)
|
||||
return null;
|
||||
javax.ws.rs.core.Cookie cookie = cookies.get(cookieName);
|
||||
if (cookie == null)
|
||||
return null;
|
||||
return new Cookie(cookie.getName(), cookie.getValue(), cookie.getVersion(), cookie.getDomain(), cookie.getPath());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHeader(String name) {
|
||||
return requestContext.getHeaderString(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getHeaders(String name) {
|
||||
MultivaluedMap<String, String> headers = requestContext.getHeaders();
|
||||
return (headers == null) ? null : headers.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() {
|
||||
return requestContext.getEntityStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRemoteAddr() {
|
||||
// TODO: implement properly
|
||||
return HostUtils.getIpAddress();
|
||||
}
|
||||
}
|
||||
|
||||
protected class ResponseFacade implements HttpFacade.Response {
|
||||
|
||||
private javax.ws.rs.core.Response.ResponseBuilder responseBuilder = javax.ws.rs.core.Response.status(204);
|
||||
|
||||
@Override
|
||||
public void setStatus(int status) {
|
||||
responseBuilder.status(status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addHeader(String name, String value) {
|
||||
responseBuilder.header(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeader(String name, String value) {
|
||||
responseBuilder.header(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetCookie(String name, String path) {
|
||||
// For now doesn't need to be supported
|
||||
throw new IllegalStateException("Not supported yet");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCookie(String name, String value, String path, String domain, int maxAge, boolean secure, boolean httpOnly) {
|
||||
// For now doesn't need to be supported
|
||||
throw new IllegalStateException("Not supported yet");
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream getOutputStream() {
|
||||
// For now doesn't need to be supported
|
||||
throw new IllegalStateException("Not supported yet");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendError(int code, String message) {
|
||||
javax.ws.rs.core.Response response = responseBuilder.status(code).entity(message).build();
|
||||
requestContext.abortWith(response);
|
||||
responseFinished = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void end() {
|
||||
// For now doesn't need to be supported
|
||||
throw new IllegalStateException("Not supported yet");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeycloakSecurityContext getSecurityContext() {
|
||||
return keycloakSecurityContext;
|
||||
}
|
||||
|
||||
public void setSecurityContext(KeycloakSecurityContext securityContext) {
|
||||
this.keycloakSecurityContext = securityContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request getRequest() {
|
||||
return requestFacade;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response getResponse() {
|
||||
return responseFacade;
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate[] getCertificateChain() {
|
||||
throw new IllegalStateException("Not supported yet");
|
||||
}
|
||||
|
||||
public boolean isResponseFinished() {
|
||||
return responseFinished;
|
||||
}
|
||||
|
||||
public javax.ws.rs.core.Response.ResponseBuilder getResponseBuilder() {
|
||||
return responseFacade.responseBuilder;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
package org.keycloak.jaxrs;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.AbstractOAuthClient;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.representations.AccessTokenResponse;
|
||||
|
@ -18,6 +17,7 @@ import javax.ws.rs.core.UriBuilder;
|
|||
import javax.ws.rs.core.UriInfo;
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Helper code to obtain oauth access tokens via browser redirects
|
||||
|
@ -26,7 +26,7 @@ import java.util.Map;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class JaxrsOAuthClient extends AbstractOAuthClient {
|
||||
protected static final Logger logger = Logger.getLogger(JaxrsOAuthClient.class);
|
||||
private final static Logger logger = Logger.getLogger("" + JaxrsOAuthClient.class);
|
||||
protected Client client;
|
||||
|
||||
/**
|
||||
|
@ -80,8 +80,8 @@ public class JaxrsOAuthClient extends AbstractOAuthClient {
|
|||
URI url = uriBuilder.build();
|
||||
|
||||
NewCookie cookie = new NewCookie(getStateCookieName(), state, getStateCookiePath(uriInfo), null, null, -1, isSecure, true);
|
||||
logger.debug("NewCookie: " + cookie.toString());
|
||||
logger.debug("Oauth Redirect to: " + url);
|
||||
logger.fine("NewCookie: " + cookie.toString());
|
||||
logger.fine("Oauth Redirect to: " + url);
|
||||
return Response.status(302)
|
||||
.location(url)
|
||||
.cookie(cookie).build();
|
||||
|
|
|
@ -124,6 +124,11 @@
|
|||
<artifactId>keycloak-undertow-adapter</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-jaxrs-oauth-client</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>federation-properties-example</artifactId>
|
||||
|
|
|
@ -26,6 +26,7 @@ package org.keycloak.testsuite;
|
|||
*/
|
||||
public class Constants {
|
||||
|
||||
public static String AUTH_SERVER_ROOT = "http://localhost:8081/auth";
|
||||
public static String SERVER_ROOT = "http://localhost:8081";
|
||||
public static String AUTH_SERVER_ROOT = SERVER_ROOT + "/auth";
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,298 @@
|
|||
package org.keycloak.testsuite.jaxrs;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.ws.rs.client.Entity;
|
||||
import javax.ws.rs.core.Form;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
|
||||
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
|
||||
import org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine;
|
||||
import org.junit.Assert;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExternalResource;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.TokenIdGenerator;
|
||||
import org.keycloak.adapters.AdapterConstants;
|
||||
import org.keycloak.adapters.HttpClientBuilder;
|
||||
import org.keycloak.models.ApplicationModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.protocol.oidc.TokenManager;
|
||||
import org.keycloak.representations.adapters.action.PushNotBeforeAction;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.testsuite.Constants;
|
||||
import org.keycloak.testsuite.OAuthClient;
|
||||
import org.keycloak.testsuite.rule.KeycloakRule;
|
||||
import org.keycloak.testsuite.rule.WebResource;
|
||||
import org.keycloak.testsuite.rule.WebRule;
|
||||
import org.keycloak.util.Time;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class JaxrsFilterTest {
|
||||
|
||||
private static final String JAXRS_APP_URL = Constants.SERVER_ROOT + "/jaxrs-simple/res";
|
||||
private static final String JAXRS_APP_PUSN_NOT_BEFORE_URL = Constants.SERVER_ROOT + "/jaxrs-simple/" + AdapterConstants.K_PUSH_NOT_BEFORE;
|
||||
|
||||
public static final String CONFIG_FILE_INIT_PARAM = "config-file";
|
||||
|
||||
@ClassRule
|
||||
public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
|
||||
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
ApplicationModel app = appRealm.addApplication("jaxrs-app");
|
||||
app.setEnabled(true);
|
||||
RoleModel role = app.addRole("jaxrs-app-user");
|
||||
UserModel user = manager.getSession().users().getUserByUsername("test-user@localhost", appRealm);
|
||||
user.grantRole(role);
|
||||
|
||||
JaxrsFilterTest.appRealm = appRealm;
|
||||
}
|
||||
});
|
||||
|
||||
@ClassRule
|
||||
public static ExternalResource clientRule = new ExternalResource() {
|
||||
|
||||
@Override
|
||||
protected void before() throws Throwable {
|
||||
DefaultHttpClient httpClient = (DefaultHttpClient) new HttpClientBuilder().build();
|
||||
ApacheHttpClient4Engine engine = new ApacheHttpClient4Engine(httpClient);
|
||||
client = new ResteasyClientBuilder().httpEngine(engine).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void after() {
|
||||
client.close();
|
||||
}
|
||||
};
|
||||
|
||||
private static ResteasyClient client;
|
||||
|
||||
@Rule
|
||||
public WebRule webRule = new WebRule(this);
|
||||
|
||||
@WebResource
|
||||
protected WebDriver driver;
|
||||
|
||||
// Used for signing admin action
|
||||
protected static RealmModel appRealm;
|
||||
|
||||
|
||||
@Test
|
||||
public void testBasic() {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
Map<String,String> initParams = new TreeMap<String,String>();
|
||||
initParams.put(CONFIG_FILE_INIT_PARAM, "classpath:jaxrs-test/jaxrs-keycloak.json");
|
||||
keycloakRule.deployJaxrsApplication("JaxrsSimpleApp", "/jaxrs-simple", JaxrsTestApplication.class, initParams);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Send GET request without token, it should fail
|
||||
Response getResp = client.target(JAXRS_APP_URL).request().get();
|
||||
Assert.assertEquals(getResp.getStatus(), 401);
|
||||
getResp.close();
|
||||
|
||||
// Send POST request without token, it should fail
|
||||
Response postResp = client.target(JAXRS_APP_URL).request().post(Entity.form(new Form()));
|
||||
Assert.assertEquals(postResp.getStatus(), 401);
|
||||
postResp.close();
|
||||
|
||||
// Retrieve token
|
||||
OAuthClient.AccessTokenResponse accessTokenResp = retrieveAccessToken();
|
||||
String authHeader = "Bearer " + accessTokenResp.getAccessToken();
|
||||
|
||||
// Send GET request with token and assert it's passing
|
||||
JaxrsTestResource.SimpleRepresentation getRep = client.target(JAXRS_APP_URL).request()
|
||||
.header(HttpHeaders.AUTHORIZATION, authHeader)
|
||||
.get(JaxrsTestResource.SimpleRepresentation.class);
|
||||
Assert.assertEquals("get", getRep.getMethod());
|
||||
Assert.assertTrue(getRep.getHasUserRole());
|
||||
Assert.assertFalse(getRep.getHasAdminRole());
|
||||
Assert.assertFalse(getRep.getHasJaxrsAppRole());
|
||||
// Assert that principal is ID of user (should be in UUID format)
|
||||
UUID.fromString(getRep.getPrincipal());
|
||||
|
||||
// Send POST request with token and assert it's passing
|
||||
JaxrsTestResource.SimpleRepresentation postRep = client.target(JAXRS_APP_URL).request()
|
||||
.header(HttpHeaders.AUTHORIZATION, authHeader)
|
||||
.post(Entity.form(new Form()), JaxrsTestResource.SimpleRepresentation.class);
|
||||
Assert.assertEquals("post", postRep.getMethod());
|
||||
Assert.assertEquals(getRep.getPrincipal(), postRep.getPrincipal());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRelativeUriAndPublicKey() {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
Map<String,String> initParams = new TreeMap<String,String>();
|
||||
initParams.put(CONFIG_FILE_INIT_PARAM, "classpath:jaxrs-test/jaxrs-keycloak-relative.json");
|
||||
keycloakRule.deployJaxrsApplication("JaxrsSimpleApp", "/jaxrs-simple", JaxrsTestApplication.class, initParams);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Send GET request without token, it should fail
|
||||
Response getResp = client.target(JAXRS_APP_URL).request().get();
|
||||
Assert.assertEquals(getResp.getStatus(), 401);
|
||||
getResp.close();
|
||||
|
||||
// Retrieve token
|
||||
OAuthClient.AccessTokenResponse accessTokenResp = retrieveAccessToken();
|
||||
String authHeader = "Bearer " + accessTokenResp.getAccessToken();
|
||||
|
||||
// Send GET request with token and assert it's passing
|
||||
JaxrsTestResource.SimpleRepresentation getRep = client.target(JAXRS_APP_URL).request()
|
||||
.header(HttpHeaders.AUTHORIZATION, authHeader)
|
||||
.get(JaxrsTestResource.SimpleRepresentation.class);
|
||||
Assert.assertEquals("get", getRep.getMethod());
|
||||
Assert.assertTrue(getRep.getHasUserRole());
|
||||
Assert.assertFalse(getRep.getHasAdminRole());
|
||||
Assert.assertFalse(getRep.getHasJaxrsAppRole());
|
||||
// Assert that principal is ID of user (should be in UUID format)
|
||||
UUID.fromString(getRep.getPrincipal());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSslRequired() {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
Map<String, String> initParams = new TreeMap<String, String>();
|
||||
initParams.put(CONFIG_FILE_INIT_PARAM, "classpath:jaxrs-test/jaxrs-keycloak-ssl.json");
|
||||
keycloakRule.deployJaxrsApplication("JaxrsSimpleApp", "/jaxrs-simple", JaxrsTestApplication.class, initParams);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Retrieve token
|
||||
OAuthClient.AccessTokenResponse accessTokenResp = retrieveAccessToken();
|
||||
String authHeader = "Bearer " + accessTokenResp.getAccessToken();
|
||||
|
||||
// Fail due to non-https
|
||||
Response getResp = client.target(JAXRS_APP_URL).request()
|
||||
.header(HttpHeaders.AUTHORIZATION, authHeader)
|
||||
.get();
|
||||
Assert.assertEquals(getResp.getStatus(), 403);
|
||||
getResp.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResourceRoleMappings() {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
Map<String, String> initParams = new TreeMap<String, String>();
|
||||
initParams.put(CONFIG_FILE_INIT_PARAM, "classpath:jaxrs-test/jaxrs-keycloak-resource-mappings.json");
|
||||
keycloakRule.deployJaxrsApplication("JaxrsSimpleApp", "/jaxrs-simple", JaxrsTestApplication.class, initParams);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Retrieve token
|
||||
OAuthClient.AccessTokenResponse accessTokenResp = retrieveAccessToken();
|
||||
String authHeader = "Bearer " + accessTokenResp.getAccessToken();
|
||||
|
||||
// Send GET request with token and assert it's passing
|
||||
JaxrsTestResource.SimpleRepresentation getRep = client.target(JAXRS_APP_URL).request()
|
||||
.header(HttpHeaders.AUTHORIZATION, authHeader)
|
||||
.get(JaxrsTestResource.SimpleRepresentation.class);
|
||||
Assert.assertEquals("get", getRep.getMethod());
|
||||
|
||||
// principal is username
|
||||
Assert.assertEquals("test-user@localhost", getRep.getPrincipal());
|
||||
|
||||
// User is in jaxrs-app-user role thanks to use-resource-role-mappings
|
||||
Assert.assertFalse(getRep.getHasUserRole());
|
||||
Assert.assertFalse(getRep.getHasAdminRole());
|
||||
Assert.assertTrue(getRep.getHasJaxrsAppRole());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPushNotBefore() {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
Map<String,String> initParams = new TreeMap<String,String>();
|
||||
initParams.put(CONFIG_FILE_INIT_PARAM, "classpath:jaxrs-test/jaxrs-keycloak.json");
|
||||
keycloakRule.deployJaxrsApplication("JaxrsSimpleApp", "/jaxrs-simple", JaxrsTestApplication.class, initParams);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Retrieve token
|
||||
OAuthClient.AccessTokenResponse accessTokenResp = retrieveAccessToken();
|
||||
String authHeader = "Bearer " + accessTokenResp.getAccessToken();
|
||||
|
||||
// Send GET request with token and assert it's passing
|
||||
JaxrsTestResource.SimpleRepresentation getRep = client.target(JAXRS_APP_URL).request()
|
||||
.header(HttpHeaders.AUTHORIZATION, authHeader)
|
||||
.get(JaxrsTestResource.SimpleRepresentation.class);
|
||||
Assert.assertEquals("get", getRep.getMethod());
|
||||
Assert.assertTrue(getRep.getHasUserRole());
|
||||
|
||||
// Push new notBefore now TODO: should use admin console (admin client) instead..
|
||||
int currentTime = Time.currentTime();
|
||||
PushNotBeforeAction action = new PushNotBeforeAction(TokenIdGenerator.generateId(), currentTime + 30, "jaxrs-app", currentTime + 1);
|
||||
String token = new TokenManager().encodeToken(appRealm, action);
|
||||
Response response = client.target(JAXRS_APP_PUSN_NOT_BEFORE_URL).request().post(Entity.text(token));
|
||||
Assert.assertEquals(204, response.getStatus());
|
||||
response.close();
|
||||
|
||||
// Assert that previous token shouldn't pass anymore
|
||||
response = client.target(JAXRS_APP_URL).request()
|
||||
.header(HttpHeaders.AUTHORIZATION, authHeader)
|
||||
.get();
|
||||
Assert.assertEquals(401, response.getStatus());
|
||||
response.close();
|
||||
}
|
||||
|
||||
// @Test
|
||||
public void testCxfExample() {
|
||||
String uri = "http://localhost:9000/customerservice/customers/123";
|
||||
Response resp = client.target(uri).request()
|
||||
.get();
|
||||
Assert.assertEquals(resp.getStatus(), 401);
|
||||
resp.close();
|
||||
|
||||
// Retrieve token
|
||||
OAuthClient.AccessTokenResponse accessTokenResp = retrieveAccessToken();
|
||||
String authHeader = "Bearer " + accessTokenResp.getAccessToken();
|
||||
|
||||
String resp2 = client.target(uri).request()
|
||||
.header(HttpHeaders.AUTHORIZATION, authHeader)
|
||||
.get(String.class);
|
||||
System.out.println(resp2);
|
||||
}
|
||||
|
||||
|
||||
private OAuthClient.AccessTokenResponse retrieveAccessToken() {
|
||||
OAuthClient oauth = new OAuthClient(driver);
|
||||
oauth.doLogin("test-user@localhost", "password");
|
||||
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||
OAuthClient.AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
|
||||
Assert.assertEquals(200, response.getStatusCode());
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package org.keycloak.testsuite.jaxrs;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.ws.rs.core.Application;
|
||||
import javax.ws.rs.core.Context;
|
||||
|
||||
import org.keycloak.jaxrs.JaxrsBearerTokenFilterImpl;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class JaxrsTestApplication extends Application {
|
||||
|
||||
protected Set<Class<?>> classes = new HashSet<Class<?>>();
|
||||
protected Set<Object> singletons = new HashSet<Object>();
|
||||
|
||||
public JaxrsTestApplication(@Context ServletContext context) throws Exception {
|
||||
singletons.add(new JaxrsTestResource());
|
||||
|
||||
String configFile = context.getInitParameter(JaxrsFilterTest.CONFIG_FILE_INIT_PARAM);
|
||||
JaxrsBearerTokenFilterImpl filter = new JaxrsBearerTokenFilterImpl();
|
||||
filter.setKeycloakConfigFile(configFile);
|
||||
singletons.add(filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Class<?>> getClasses() {
|
||||
return classes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Object> getSingletons() {
|
||||
return singletons;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
package org.keycloak.testsuite.jaxrs;
|
||||
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.SecurityContext;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@Path("res")
|
||||
public class JaxrsTestResource {
|
||||
|
||||
@Context
|
||||
protected SecurityContext securityContext;
|
||||
|
||||
@GET
|
||||
@Produces("application/json")
|
||||
public SimpleRepresentation get() {
|
||||
return new SimpleRepresentation("get", securityContext.getUserPrincipal().getName(), securityContext.isUserInRole("user"),
|
||||
securityContext.isUserInRole("admin"), securityContext.isUserInRole("jaxrs-app-user"));
|
||||
}
|
||||
|
||||
@POST
|
||||
@Produces("application/json")
|
||||
public SimpleRepresentation post() {
|
||||
return new SimpleRepresentation("post", securityContext.getUserPrincipal().getName(), securityContext.isUserInRole("user"),
|
||||
securityContext.isUserInRole("admin"), securityContext.isUserInRole("jaxrs-app-user"));
|
||||
}
|
||||
|
||||
public static class SimpleRepresentation {
|
||||
private String method;
|
||||
private String principal;
|
||||
private Boolean hasUserRole;
|
||||
private Boolean hasAdminRole;
|
||||
private Boolean hasJaxrsAppRole;
|
||||
|
||||
public SimpleRepresentation() {
|
||||
}
|
||||
|
||||
public SimpleRepresentation(String method, String principal, boolean hasUserRole, boolean hasAdminRole,
|
||||
boolean hasJaxrsAppRole) {
|
||||
this.method = method;
|
||||
this.principal = principal;
|
||||
this.hasUserRole = hasUserRole;
|
||||
this.hasAdminRole = hasAdminRole;
|
||||
this.hasJaxrsAppRole = hasJaxrsAppRole;
|
||||
}
|
||||
|
||||
public String getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public void setMethod(String method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public String getPrincipal() {
|
||||
return principal;
|
||||
}
|
||||
|
||||
public void setPrincipal(String principal) {
|
||||
this.principal = principal;
|
||||
}
|
||||
|
||||
public Boolean getHasUserRole() {
|
||||
return hasUserRole;
|
||||
}
|
||||
|
||||
public void setHasUserRole(Boolean hasUserRole) {
|
||||
this.hasUserRole = hasUserRole;
|
||||
}
|
||||
|
||||
public Boolean getHasAdminRole() {
|
||||
return hasAdminRole;
|
||||
}
|
||||
|
||||
public void setHasAdminRole(Boolean hasAdminRole) {
|
||||
this.hasAdminRole = hasAdminRole;
|
||||
}
|
||||
|
||||
public Boolean getHasJaxrsAppRole() {
|
||||
return hasJaxrsAppRole;
|
||||
}
|
||||
|
||||
public void setHasJaxrsAppRole(Boolean hasJaxrsAppRole) {
|
||||
this.hasJaxrsAppRole = hasJaxrsAppRole;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +1,14 @@
|
|||
package org.keycloak.testsuite.rule;
|
||||
|
||||
import io.undertow.servlet.Servlets;
|
||||
import io.undertow.servlet.api.DeploymentInfo;
|
||||
import io.undertow.servlet.api.FilterInfo;
|
||||
import io.undertow.servlet.api.LoginConfig;
|
||||
import io.undertow.servlet.api.SecurityConstraint;
|
||||
import io.undertow.servlet.api.ServletInfo;
|
||||
import io.undertow.servlet.api.WebResourceCollection;
|
||||
import org.jboss.resteasy.plugins.server.undertow.UndertowJaxrsServer;
|
||||
import org.jboss.resteasy.spi.ResteasyDeployment;
|
||||
import org.junit.rules.ExternalResource;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
@ -14,16 +18,23 @@ import org.keycloak.models.UserModel;
|
|||
import org.keycloak.models.utils.ModelToRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.services.filters.ClientConnectionFilter;
|
||||
import org.keycloak.services.filters.KeycloakSessionServletFilter;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.testsuite.Retry;
|
||||
import org.keycloak.testutils.KeycloakServer;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.Servlet;
|
||||
import javax.ws.rs.core.Application;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.Socket;
|
||||
import java.util.Map;
|
||||
|
||||
import org.keycloak.adapters.KeycloakConfigResolver;
|
||||
|
||||
/**
|
||||
|
@ -158,6 +169,22 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
|
|||
server.getServer().deploy(di);
|
||||
}
|
||||
|
||||
public void deployJaxrsApplication(String name, String contextPath, Class<? extends Application> applicationClass, Map<String,String> initParams) {
|
||||
ResteasyDeployment deployment = new ResteasyDeployment();
|
||||
deployment.setApplicationClass(applicationClass.getName());
|
||||
|
||||
DeploymentInfo di = server.getServer().undertowDeployment(deployment, "");
|
||||
di.setClassLoader(getClass().getClassLoader());
|
||||
di.setContextPath(contextPath);
|
||||
di.setDeploymentName(name);
|
||||
|
||||
for (Map.Entry<String,String> param : initParams.entrySet()) {
|
||||
di.addInitParameter(param.getKey(), param.getValue());
|
||||
}
|
||||
|
||||
server.getServer().deploy(di);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void after() {
|
||||
removeTestRealms();
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"realm": "test",
|
||||
"resource": "jaxrs-app",
|
||||
"auth-server-url": "/auth",
|
||||
"ssl-required" : "external",
|
||||
"bearer-only": true
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"realm": "test",
|
||||
"resource": "jaxrs-app",
|
||||
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
|
||||
"auth-server-url": "http://localhost:8081/auth",
|
||||
"ssl-required" : "external",
|
||||
"bearer-only": true,
|
||||
"principal-attribute": "preferred_username",
|
||||
"use-resource-role-mappings": true
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"realm": "test",
|
||||
"resource": "jaxrs-app",
|
||||
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
|
||||
"auth-server-url": "http://localhost:8081/auth",
|
||||
"ssl-required" : "all",
|
||||
"bearer-only": true
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"realm": "test",
|
||||
"resource": "jaxrs-app",
|
||||
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
|
||||
"auth-server-url": "http://localhost:8081/auth",
|
||||
"ssl-required" : "external",
|
||||
"bearer-only": true
|
||||
}
|
Loading…
Reference in a new issue