blob: 4ef706fbda98eee342d3728006a9fae7919a40b4 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.cxf.rs.security.oidc.rp;
import java.io.ByteArrayInputStream;
import java.net.URI;
import java.nio.charset.StandardCharsets;
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.container.PreMatching;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import org.apache.cxf.common.util.StringUtils;
import org.apache.cxf.jaxrs.ext.MessageContext;
import org.apache.cxf.jaxrs.ext.MessageContextImpl;
import org.apache.cxf.jaxrs.impl.MetadataMap;
import org.apache.cxf.jaxrs.utils.FormUtils;
import org.apache.cxf.jaxrs.utils.JAXRSUtils;
import org.apache.cxf.rs.security.jose.jwt.JwtException;
import org.apache.cxf.rs.security.jose.jwt.JwtUtils;
import org.apache.cxf.rs.security.oauth2.client.ClientTokenContext;
import org.apache.cxf.rs.security.oauth2.client.ClientTokenContextManager;
import org.apache.cxf.rs.security.oidc.common.IdToken;
@PreMatching
@Priority(Priorities.AUTHENTICATION + 2)
public class OidcRpAuthenticationFilter implements ContainerRequestFilter {
@Context
private MessageContext mc;
private ClientTokenContextManager stateManager;
private String redirectUri;
private String roleClaim;
public void filter(ContainerRequestContext rc) {
if (checkSecurityContext(rc)) {
return;
} else if (redirectUri != null) {
URI redirectAddress = null;
if (redirectUri.startsWith("/")) {
String basePath = (String)mc.get("http.base.path");
redirectAddress = UriBuilder.fromUri(basePath).path(redirectUri).build();
} else if (redirectUri.startsWith("http")) {
redirectAddress = URI.create(redirectUri);
} else {
UriBuilder ub = rc.getUriInfo().getBaseUriBuilder().path(redirectUri);
redirectAddress = ub.build();
}
rc.abortWith(Response.seeOther(redirectAddress)
.header(HttpHeaders.CACHE_CONTROL, "no-cache, no-store")
.header("Pragma", "no-cache")
.build());
} else {
rc.abortWith(Response.status(401).build());
}
}
protected boolean checkSecurityContext(ContainerRequestContext rc) {
OidcClientTokenContext tokenContext = (OidcClientTokenContext)stateManager.getClientTokenContext(mc);
if (tokenContext == null) {
return false;
}
IdToken idToken = tokenContext.getIdToken();
try {
// If ID token has expired then the context is no longer valid
JwtUtils.validateJwtExpiry(idToken, 0, idToken.getExpiryTime() != null);
} catch (JwtException ex) {
stateManager.removeClientTokenContext(new MessageContextImpl(JAXRSUtils.getCurrentMessage()));
return false;
}
OidcClientTokenContextImpl newTokenContext = new OidcClientTokenContextImpl();
newTokenContext.setToken(tokenContext.getToken());
newTokenContext.setIdToken(idToken);
newTokenContext.setUserInfo(tokenContext.getUserInfo());
newTokenContext.setState(toRequestState(rc));
JAXRSUtils.getCurrentMessage().setContent(ClientTokenContext.class, newTokenContext);
OidcSecurityContext oidcSecCtx = new OidcSecurityContext(newTokenContext);
oidcSecCtx.setRoleClaim(roleClaim);
rc.setSecurityContext(oidcSecCtx);
return true;
}
private MultivaluedMap<String, String> toRequestState(ContainerRequestContext rc) {
MultivaluedMap<String, String> requestState = new MetadataMap<String, String>();
requestState.putAll(rc.getUriInfo().getQueryParameters(true));
if (MediaType.APPLICATION_FORM_URLENCODED_TYPE.isCompatible(rc.getMediaType())) {
String body = FormUtils.readBody(rc.getEntityStream(), StandardCharsets.UTF_8.name());
FormUtils.populateMapFromString(requestState, JAXRSUtils.getCurrentMessage(), body,
StandardCharsets.UTF_8.name(), true);
rc.setEntityStream(new ByteArrayInputStream(StringUtils.toBytesUTF8(body)));
}
return requestState;
}
public void setRedirectUri(String redirectUri) {
this.redirectUri = redirectUri;
}
public void setClientTokenContextManager(ClientTokenContextManager manager) {
this.stateManager = manager;
}
public void setRoleClaim(String roleClaim) {
this.roleClaim = roleClaim;
}
}