blob: fc346b448891e70b2ecd622ad5a9fc33ae9838fd [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.geronimo.microprofile.impl.jwtauth.servlet;
import static java.util.Collections.emptySet;
import static java.util.stream.Collectors.toList;
import java.security.Principal;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.function.Supplier;
import java.util.stream.Stream;
import javax.security.auth.Subject;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import org.apache.geronimo.microprofile.impl.jwtauth.JwtException;
import org.apache.geronimo.microprofile.impl.jwtauth.jaxrs.JAXRSRequestForwarder;
import org.apache.geronimo.microprofile.impl.jwtauth.jwt.JwtParser;
import org.eclipse.microprofile.jwt.JsonWebToken;
public class JwtRequest extends HttpServletRequestWrapper implements TokenAccessor {
private final Supplier<JsonWebToken> tokenExtractor;
private final String headerName;
private volatile JsonWebToken token; // cache for perf reasons
public JwtRequest(final JwtParser service, final String header, final String cookie,
final String prefix, final HttpServletRequest request) {
super(request);
this.headerName = header;
this.tokenExtractor = () -> {
if (token != null) {
return token;
}
synchronized (this) {
if (token != null) {
return token;
}
boolean fromHeader = true;
String auth = String.class.cast(
getAttribute("org.apache.geronimo.microprofile.impl.jwtauth.jaxrs.JAXRSRequestForwarder.header"));
if (auth == null) {
auth = getHeader(header);
}
if (auth == null) {
final Cookie[] cookies = getCookies();
if (cookies != null) {
fromHeader = false;
auth = Stream.of(cookies)
.filter(it -> cookie.equalsIgnoreCase(it.getName()))
.findFirst()
.map(Cookie::getValue)
.orElse(null);
}
}
if (auth == null || auth.isEmpty()) {
throw new JwtException("No " + header + " header", HttpServletResponse.SC_UNAUTHORIZED);
}
if (fromHeader) {
if (!auth.toLowerCase(Locale.ROOT).startsWith(prefix)) {
throw new JwtException("No prefix " + prefix + " in header " + header, HttpServletResponse.SC_UNAUTHORIZED);
}
token = service.parse(auth.substring(prefix.length()));
} else {
token = service.parse(auth.startsWith(prefix) ? auth.substring(prefix.length()) : auth);
}
setAttribute(JsonWebToken.class.getName(), token);
return token;
}
};
// integration hook if needed
setAttribute(JwtRequest.class.getName(), this);
setAttribute(JsonWebToken.class.getName() + ".supplier", tokenExtractor);
setAttribute(Principal.class.getName() + ".supplier", tokenExtractor);
// not portable but used by some servers like tomee
setAttribute("javax.security.auth.subject.callable", (Callable<Subject>) () -> {
final Set<Principal> principals = new LinkedHashSet<>();
final JsonWebToken namePrincipal = tokenExtractor.get();
principals.add(namePrincipal);
principals.addAll(namePrincipal.getGroups().stream().map(role -> (Principal) () -> role).collect(toList()));
return new Subject(true, principals, emptySet(), emptySet());
});
}
public String getHeaderName() {
return headerName;
}
public TokenAccessor asTokenAccessor() {
return this;
}
@Override
public JsonWebToken getToken() {
return tokenExtractor.get();
}
@Override
public Principal getUserPrincipal() {
return tokenExtractor.get();
}
@Override
public boolean isUserInRole(final String role) {
return tokenExtractor.get().getGroups().contains(role);
}
@Override
public String getAuthType() {
return "MP-JWT";
}
}