mirror of https://github.com/apache/nifi.git
NIFI-9060 Refactored HTTP Cookie Path Handling
- Implemented ApplicationCookieService for adding and retrieving HTTP Cookies - Added getCookieResourceUri() leveraging allowed proxy headers to support optional Cookie Paths - Refactored Access Resources to use ApplicationCookieService for processing - Changed __Host- prefix to __Secure- prefix for Bearer Token cookie to support Cookie Path processing - Removed unnecessary jetty-http dependency from nifi-web-api - Corrected NiFi path references in JavaScript to support prefixed paths Signed-off-by: Nathan Gough <thenatog@gmail.com> This closes #5329.
This commit is contained in:
parent
5b7af511fc
commit
84dbf915a9
|
@ -32,8 +32,6 @@ import javax.ws.rs.core.UriBuilderException;
|
|||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
|
@ -43,8 +41,6 @@ public final class WebUtils {
|
|||
|
||||
private static final Logger logger = LoggerFactory.getLogger(WebUtils.class);
|
||||
|
||||
final static ReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
|
||||
public static final String PROXY_SCHEME_HTTP_HEADER = "X-ProxyScheme";
|
||||
public static final String PROXY_HOST_HTTP_HEADER = "X-ProxyHost";
|
||||
public static final String PROXY_PORT_HTTP_HEADER = "X-ProxyPort";
|
||||
|
@ -57,6 +53,8 @@ public final class WebUtils {
|
|||
public static final String FORWARDED_CONTEXT_HTTP_HEADER = "X-Forwarded-Context";
|
||||
public static final String FORWARDED_PREFIX_HTTP_HEADER = "X-Forwarded-Prefix";
|
||||
|
||||
private static final String EMPTY = "";
|
||||
|
||||
private WebUtils() {
|
||||
}
|
||||
|
||||
|
@ -207,7 +205,7 @@ public final class WebUtils {
|
|||
return contextPath;
|
||||
} catch (UriBuilderException e) {
|
||||
logger.error("Error determining context path on " + jspDisplayName + ": " + e.getMessage());
|
||||
return "";
|
||||
return EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -218,27 +216,18 @@ public final class WebUtils {
|
|||
* @param request the HTTP request
|
||||
* @return the provided context path or an empty string
|
||||
*/
|
||||
public static String determineContextPath(HttpServletRequest request) {
|
||||
String contextPath = request.getContextPath();
|
||||
String proxyContextPath = request.getHeader(PROXY_CONTEXT_PATH_HTTP_HEADER);
|
||||
String forwardedContext = request.getHeader(FORWARDED_CONTEXT_HTTP_HEADER);
|
||||
String prefix = request.getHeader(FORWARDED_PREFIX_HTTP_HEADER);
|
||||
public static String determineContextPath(final HttpServletRequest request) {
|
||||
final String proxyContextPath = request.getHeader(PROXY_CONTEXT_PATH_HTTP_HEADER);
|
||||
final String forwardedContext = request.getHeader(FORWARDED_CONTEXT_HTTP_HEADER);
|
||||
final String prefix = request.getHeader(FORWARDED_PREFIX_HTTP_HEADER);
|
||||
|
||||
logger.debug("Context path: " + contextPath);
|
||||
String determinedContextPath = "";
|
||||
|
||||
// If a context path header is set, log each
|
||||
String determinedContextPath = EMPTY;
|
||||
if (anyNotBlank(proxyContextPath, forwardedContext, prefix)) {
|
||||
logger.debug(String.format("On the request, the following context paths were parsed" +
|
||||
" from headers:\n\t X-ProxyContextPath: %s\n\tX-Forwarded-Context: %s\n\tX-Forwarded-Prefix: %s",
|
||||
proxyContextPath, forwardedContext, prefix));
|
||||
|
||||
// Implementing preferred order here: PCP, FC, FP
|
||||
determinedContextPath = Stream.of(proxyContextPath, forwardedContext, prefix)
|
||||
.filter(StringUtils::isNotBlank).findFirst().orElse("");
|
||||
.filter(StringUtils::isNotBlank).findFirst().orElse(EMPTY);
|
||||
}
|
||||
|
||||
logger.debug("Determined context path: " + determinedContextPath);
|
||||
return determinedContextPath;
|
||||
}
|
||||
|
||||
|
@ -366,7 +355,7 @@ public final class WebUtils {
|
|||
portFromHostHeader = null;
|
||||
}
|
||||
if (StringUtils.isNotBlank(portFromHostHeader) && StringUtils.isNotBlank(portHeaderValue)) {
|
||||
logger.warn(String.format("The proxied host header contained a port, but was overridden by the proxied port header"));
|
||||
logger.warn("Forwarded Host Port [{}] replaced with Forwarded Port [{}]", portFromHostHeader, portHeaderValue);
|
||||
}
|
||||
port = StringUtils.isNotBlank(portHeaderValue) ? portHeaderValue : (StringUtils.isNotBlank(portFromHostHeader) ? portFromHostHeader : null);
|
||||
return port;
|
||||
|
|
|
@ -432,11 +432,5 @@
|
|||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-http</artifactId>
|
||||
<version>${jetty.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
|
@ -34,6 +34,7 @@ import org.apache.nifi.authorization.user.NiFiUserDetails;
|
|||
import org.apache.nifi.authorization.user.NiFiUserUtils;
|
||||
import org.apache.nifi.authorization.util.IdentityMappingUtil;
|
||||
import org.apache.nifi.util.FormatUtils;
|
||||
import org.apache.nifi.web.api.cookie.ApplicationCookieName;
|
||||
import org.apache.nifi.web.api.dto.AccessConfigurationDTO;
|
||||
import org.apache.nifi.web.api.dto.AccessStatusDTO;
|
||||
import org.apache.nifi.web.api.entity.AccessConfigurationEntity;
|
||||
|
@ -42,7 +43,6 @@ import org.apache.nifi.web.security.InvalidAuthenticationException;
|
|||
import org.apache.nifi.web.security.LogoutException;
|
||||
import org.apache.nifi.web.security.ProxiedEntitiesUtils;
|
||||
import org.apache.nifi.web.security.UntrustedProxyException;
|
||||
import org.apache.nifi.web.security.http.SecurityCookieName;
|
||||
import org.apache.nifi.web.security.jwt.provider.BearerTokenProvider;
|
||||
import org.apache.nifi.web.security.jwt.revocation.JwtLogoutListener;
|
||||
import org.apache.nifi.web.security.kerberos.KerberosService;
|
||||
|
@ -62,9 +62,7 @@ import org.springframework.security.oauth2.server.resource.BearerTokenAuthentica
|
|||
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;
|
||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
|
||||
import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.ws.rs.Consumes;
|
||||
|
@ -81,6 +79,7 @@ import javax.ws.rs.core.Response;
|
|||
import javax.ws.rs.core.UriBuilder;
|
||||
import java.net.URI;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
@ -96,7 +95,6 @@ public class AccessResource extends ApplicationResource {
|
|||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AccessResource.class);
|
||||
protected static final String AUTHENTICATION_NOT_ENABLED_MSG = "User authentication/authorization is only supported when running over HTTPS.";
|
||||
static final String LOGOUT_REQUEST_IDENTIFIER = "nifi-logout-request-identifier";
|
||||
|
||||
private X509CertificateExtractor certificateExtractor;
|
||||
private X509AuthenticationProvider x509AuthenticationProvider;
|
||||
|
@ -259,7 +257,7 @@ public class AccessResource extends ApplicationResource {
|
|||
accessStatus.setStatus(AccessStatusDTO.Status.UNKNOWN.name());
|
||||
accessStatus.setMessage("Access Unknown: Authorization Header not found.");
|
||||
// Remove Session Cookie when Authorization Header not found
|
||||
removeCookie(httpServletResponse, SecurityCookieName.AUTHORIZATION_BEARER.getName());
|
||||
applicationCookieService.removeCookie(getCookieResourceUri(), httpServletResponse, ApplicationCookieName.AUTHORIZATION_BEARER);
|
||||
} else {
|
||||
try {
|
||||
// authenticate the token
|
||||
|
@ -275,10 +273,7 @@ public class AccessResource extends ApplicationResource {
|
|||
accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name());
|
||||
accessStatus.setMessage("You are already logged in.");
|
||||
} catch (final AuthenticationException iae) {
|
||||
if (WebUtils.getCookie(httpServletRequest, SecurityCookieName.AUTHORIZATION_BEARER.getName()) != null) {
|
||||
removeCookie(httpServletResponse, SecurityCookieName.AUTHORIZATION_BEARER.getName());
|
||||
}
|
||||
|
||||
applicationCookieService.removeCookie(getCookieResourceUri(), httpServletResponse, ApplicationCookieName.AUTHORIZATION_BEARER);
|
||||
throw iae;
|
||||
}
|
||||
}
|
||||
|
@ -343,7 +338,7 @@ public class AccessResource extends ApplicationResource {
|
|||
@ApiResponse(code = 500, message = "Unable to create access token because an unexpected error occurred.")
|
||||
}
|
||||
)
|
||||
public Response createAccessTokenFromTicket(@Context HttpServletRequest httpServletRequest) {
|
||||
public Response createAccessTokenFromTicket(@Context final HttpServletRequest httpServletRequest, @Context final HttpServletResponse httpServletResponse) {
|
||||
|
||||
// only support access tokens when communicating over HTTPS
|
||||
if (!httpServletRequest.isSecure()) {
|
||||
|
@ -379,7 +374,8 @@ public class AccessResource extends ApplicationResource {
|
|||
final LoginAuthenticationToken loginAuthenticationToken = new LoginAuthenticationToken(mappedIdentity, expiration, "KerberosService");
|
||||
final String token = bearerTokenProvider.getBearerToken(loginAuthenticationToken);
|
||||
final URI uri = URI.create(generateResourceUri("access", "kerberos"));
|
||||
return generateTokenResponse(generateCreatedResponse(uri, token), token);
|
||||
setBearerToken(httpServletResponse, token);
|
||||
return generateCreatedResponse(uri, token).build();
|
||||
} catch (final AuthenticationException e) {
|
||||
throw new AccessDeniedException(e.getMessage(), e);
|
||||
}
|
||||
|
@ -414,9 +410,10 @@ public class AccessResource extends ApplicationResource {
|
|||
}
|
||||
)
|
||||
public Response createAccessToken(
|
||||
@Context HttpServletRequest httpServletRequest,
|
||||
@FormParam("username") String username,
|
||||
@FormParam("password") String password) {
|
||||
@Context final HttpServletRequest httpServletRequest,
|
||||
@Context final HttpServletResponse httpServletResponse,
|
||||
@FormParam("username") final String username,
|
||||
@FormParam("password") final String password) {
|
||||
|
||||
// only support access tokens when communicating over HTTPS
|
||||
if (!httpServletRequest.isSecure()) {
|
||||
|
@ -450,9 +447,10 @@ public class AccessResource extends ApplicationResource {
|
|||
throw new AdministrationException(iae.getMessage(), iae);
|
||||
}
|
||||
|
||||
final String token = bearerTokenProvider.getBearerToken(loginAuthenticationToken);
|
||||
final String bearerToken = bearerTokenProvider.getBearerToken(loginAuthenticationToken);
|
||||
final URI uri = URI.create(generateResourceUri("access", "token"));
|
||||
return generateTokenResponse(generateCreatedResponse(uri, token), token);
|
||||
setBearerToken(httpServletResponse, bearerToken);
|
||||
return generateCreatedResponse(uri, bearerToken).build();
|
||||
}
|
||||
|
||||
@DELETE
|
||||
|
@ -484,7 +482,7 @@ public class AccessResource extends ApplicationResource {
|
|||
try {
|
||||
logger.info("Logout Started [{}]", mappedUserIdentity);
|
||||
logger.debug("Removing Authorization Cookie [{}]", mappedUserIdentity);
|
||||
removeCookie(httpServletResponse, SecurityCookieName.AUTHORIZATION_BEARER.getName());
|
||||
applicationCookieService.removeCookie(getCookieResourceUri(), httpServletResponse, ApplicationCookieName.AUTHORIZATION_BEARER);
|
||||
|
||||
final String bearerToken = bearerTokenResolver.resolve(httpServletRequest);
|
||||
jwtLogoutListener.logout(bearerToken);
|
||||
|
@ -493,14 +491,7 @@ public class AccessResource extends ApplicationResource {
|
|||
final LogoutRequest logoutRequest = new LogoutRequest(UUID.randomUUID().toString(), mappedUserIdentity);
|
||||
logoutRequestManager.start(logoutRequest);
|
||||
|
||||
// generate a cookie to store the logout request identifier
|
||||
final Cookie cookie = new Cookie(LOGOUT_REQUEST_IDENTIFIER, logoutRequest.getRequestIdentifier());
|
||||
cookie.setPath("/");
|
||||
cookie.setHttpOnly(true);
|
||||
cookie.setMaxAge(60);
|
||||
cookie.setSecure(true);
|
||||
httpServletResponse.addCookie(cookie);
|
||||
|
||||
applicationCookieService.addCookie(getCookieResourceUri(), httpServletResponse, ApplicationCookieName.LOGOUT_REQUEST_IDENTIFIER, logoutRequest.getRequestIdentifier());
|
||||
return generateOkResponse().build();
|
||||
} catch (final LogoutException e) {
|
||||
logger.error("Logout Failed Identity [{}]", mappedUserIdentity, e);
|
||||
|
@ -538,22 +529,16 @@ public class AccessResource extends ApplicationResource {
|
|||
LogoutRequest completeLogoutRequest(final HttpServletResponse httpServletResponse) {
|
||||
LogoutRequest logoutRequest = null;
|
||||
|
||||
// check if a logout request identifier is present and if so complete the request
|
||||
final Cookie cookie = WebUtils.getCookie(httpServletRequest, LOGOUT_REQUEST_IDENTIFIER);
|
||||
final String logoutRequestIdentifier = cookie == null ? null : cookie.getValue();
|
||||
if (logoutRequestIdentifier != null) {
|
||||
final Optional<String> cookieValue = getLogoutRequestIdentifier();
|
||||
if (cookieValue.isPresent()) {
|
||||
final String logoutRequestIdentifier = cookieValue.get();
|
||||
logoutRequest = logoutRequestManager.complete(logoutRequestIdentifier);
|
||||
}
|
||||
|
||||
if (logoutRequest == null) {
|
||||
logger.warn("Logout Request [{}] not found", logoutRequestIdentifier);
|
||||
} else {
|
||||
logger.info("Logout Request [{}] Completed [{}]", logoutRequestIdentifier, logoutRequest.getMappedUserIdentity());
|
||||
} else {
|
||||
logger.warn("Logout Request Cookie [{}] not found", ApplicationCookieName.LOGOUT_REQUEST_IDENTIFIER.getCookieName());
|
||||
}
|
||||
|
||||
// remove the cookie if it existed
|
||||
removeLogoutRequestCookie(httpServletResponse);
|
||||
|
||||
return logoutRequest;
|
||||
}
|
||||
|
||||
|
@ -578,8 +563,22 @@ public class AccessResource extends ApplicationResource {
|
|||
return getNiFiUri() + "logout-complete";
|
||||
}
|
||||
|
||||
void removeLogoutRequestCookie(final HttpServletResponse httpServletResponse) {
|
||||
removeCookie(httpServletResponse, LOGOUT_REQUEST_IDENTIFIER);
|
||||
/**
|
||||
* Send Set-Cookie header to remove Logout Request Identifier cookie from client
|
||||
*
|
||||
* @param httpServletResponse HTTP Servlet Response
|
||||
*/
|
||||
protected void removeLogoutRequestCookie(final HttpServletResponse httpServletResponse) {
|
||||
applicationCookieService.removeCookie(getCookieResourceUri(), httpServletResponse, ApplicationCookieName.LOGOUT_REQUEST_IDENTIFIER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Logout Request Identifier from current HTTP Request Cookie header
|
||||
*
|
||||
* @return Optional Logout Request Identifier
|
||||
*/
|
||||
protected Optional<String> getLogoutRequestIdentifier() {
|
||||
return applicationCookieService.getCookieValue(httpServletRequest, ApplicationCookieName.LOGOUT_REQUEST_IDENTIFIER);
|
||||
}
|
||||
|
||||
// setters
|
||||
|
|
|
@ -50,21 +50,20 @@ import org.apache.nifi.util.ComponentIdGenerator;
|
|||
import org.apache.nifi.util.NiFiProperties;
|
||||
import org.apache.nifi.web.NiFiServiceFacade;
|
||||
import org.apache.nifi.web.Revision;
|
||||
import org.apache.nifi.web.api.cookie.ApplicationCookieName;
|
||||
import org.apache.nifi.web.api.cookie.ApplicationCookieService;
|
||||
import org.apache.nifi.web.api.cookie.StandardApplicationCookieService;
|
||||
import org.apache.nifi.web.api.dto.RevisionDTO;
|
||||
import org.apache.nifi.web.api.entity.ComponentEntity;
|
||||
import org.apache.nifi.web.api.entity.Entity;
|
||||
import org.apache.nifi.web.api.entity.TransactionResultEntity;
|
||||
import org.apache.nifi.web.security.ProxiedEntitiesUtils;
|
||||
import org.apache.nifi.web.security.http.SecurityCookieName;
|
||||
import org.apache.nifi.web.security.util.CacheKey;
|
||||
import org.apache.nifi.web.util.WebUtils;
|
||||
import org.eclipse.jetty.http.HttpCookie;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.ws.rs.core.CacheControl;
|
||||
|
@ -114,12 +113,13 @@ public abstract class ApplicationResource {
|
|||
public static final String DISCONNECTED_NODE_ACKNOWLEDGED = "disconnectedNodeAcknowledged";
|
||||
static final String LOGIN_ERROR_TITLE = "Unable to continue login sequence";
|
||||
static final String LOGOUT_ERROR_TITLE = "Unable to continue logout sequence";
|
||||
private static final int VALID_FOR_SESSION_ONLY = -1;
|
||||
|
||||
protected static final String NON_GUARANTEED_ENDPOINT = "Note: This endpoint is subject to change as NiFi and it's REST API evolve.";
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ApplicationResource.class);
|
||||
|
||||
private static final String ROOT_PATH = "/";
|
||||
|
||||
public static final String NODEWISE = "false";
|
||||
|
||||
@Context
|
||||
|
@ -128,6 +128,7 @@ public abstract class ApplicationResource {
|
|||
@Context
|
||||
protected UriInfo uriInfo;
|
||||
|
||||
protected ApplicationCookieService applicationCookieService = new StandardApplicationCookieService();
|
||||
protected NiFiProperties properties;
|
||||
private RequestReplicator requestReplicator;
|
||||
private ClusterCoordinator clusterCoordinator;
|
||||
|
@ -164,14 +165,23 @@ public abstract class ApplicationResource {
|
|||
return uri.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Resource URI used for Cookie Domain and Path properties
|
||||
*
|
||||
* @return Cookie Resource URI
|
||||
*/
|
||||
protected URI getCookieResourceUri() {
|
||||
final UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
|
||||
return buildResourceUri(uriBuilder.replacePath(ROOT_PATH).build());
|
||||
}
|
||||
|
||||
private URI buildResourceUri(final String... path) {
|
||||
final UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
|
||||
uriBuilder.segment(path);
|
||||
URI uri = uriBuilder.build();
|
||||
return buildResourceUri(uriBuilder.segment(path).build());
|
||||
}
|
||||
|
||||
private URI buildResourceUri(final URI uri) {
|
||||
try {
|
||||
|
||||
// check for proxy settings
|
||||
|
||||
final String scheme = getFirstHeaderValue(PROXY_SCHEME_HTTP_HEADER, FORWARDED_PROTO_HTTP_HEADER);
|
||||
final String hostHeaderValue = getFirstHeaderValue(PROXY_HOST_HTTP_HEADER, FORWARDED_HOST_HTTP_HEADER);
|
||||
final String portHeaderValue = getFirstHeaderValue(PROXY_PORT_HTTP_HEADER, FORWARDED_PORT_HTTP_HEADER);
|
||||
|
@ -180,25 +190,20 @@ public abstract class ApplicationResource {
|
|||
final String port = WebUtils.determineProxiedPort(hostHeaderValue, portHeaderValue);
|
||||
|
||||
// Catch header poisoning
|
||||
String allowedContextPaths = properties.getAllowedContextPaths();
|
||||
String resourcePath = WebUtils.getResourcePath(uri, httpServletRequest, allowedContextPaths);
|
||||
final String allowedContextPaths = properties.getAllowedContextPaths();
|
||||
final String resourcePath = WebUtils.getResourcePath(uri, httpServletRequest, allowedContextPaths);
|
||||
|
||||
// determine the port uri
|
||||
int uriPort = uri.getPort();
|
||||
if (port != null) {
|
||||
if (StringUtils.isWhitespace(port)) {
|
||||
uriPort = -1;
|
||||
} else {
|
||||
try {
|
||||
uriPort = Integer.parseInt(port);
|
||||
} catch (final NumberFormatException nfe) {
|
||||
logger.warn(String.format("Unable to parse proxy port HTTP header '%s'. Using port from request URI '%s'.", port, uriPort));
|
||||
}
|
||||
if (StringUtils.isNumeric(port)) {
|
||||
try {
|
||||
uriPort = Integer.parseInt(port);
|
||||
} catch (final NumberFormatException nfe) {
|
||||
logger.warn("Parsing Proxy Port [{}] Failed: Using URI Port [{}]", port, uriPort);
|
||||
}
|
||||
}
|
||||
|
||||
// construct the URI
|
||||
uri = new URI(
|
||||
return new URI(
|
||||
(StringUtils.isBlank(scheme)) ? uri.getScheme() : scheme,
|
||||
uri.getUserInfo(),
|
||||
(StringUtils.isBlank(host)) ? uri.getHost() : host,
|
||||
|
@ -206,11 +211,9 @@ public abstract class ApplicationResource {
|
|||
resourcePath,
|
||||
uri.getQuery(),
|
||||
uri.getFragment());
|
||||
|
||||
} catch (final URISyntaxException use) {
|
||||
throw new UriBuilderException(use);
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -314,7 +317,7 @@ public abstract class ApplicationResource {
|
|||
}
|
||||
|
||||
protected MultivaluedMap<String, String> getRequestParameters() {
|
||||
final MultivaluedMap<String, String> entity = new MultivaluedHashMap();
|
||||
final MultivaluedMap<String, String> entity = new MultivaluedHashMap<>();
|
||||
|
||||
for (final Map.Entry<String, String[]> entry : httpServletRequest.getParameterMap().entrySet()) {
|
||||
if (entry.getValue() == null) {
|
||||
|
@ -330,7 +333,7 @@ public abstract class ApplicationResource {
|
|||
}
|
||||
|
||||
protected Map<String, String> getHeaders() {
|
||||
return getHeaders(new HashMap<String, String>());
|
||||
return getHeaders(new HashMap<>());
|
||||
}
|
||||
|
||||
protected Map<String, String> getHeaders(final Map<String, String> overriddenHeaders) {
|
||||
|
@ -818,7 +821,7 @@ public abstract class ApplicationResource {
|
|||
}
|
||||
}
|
||||
|
||||
private final class Request<T extends Entity> {
|
||||
private static final class Request<T extends Entity> {
|
||||
final String userChain;
|
||||
final String uri;
|
||||
final Revision revision;
|
||||
|
@ -1283,19 +1286,14 @@ public abstract class ApplicationResource {
|
|||
|
||||
}
|
||||
|
||||
protected Response generateTokenResponse(ResponseBuilder builder, String token) {
|
||||
// currently there is no way to use javax.servlet-api to set SameSite=Strict, so we do this using Jetty
|
||||
HttpCookie jwtCookie = new HttpCookie(SecurityCookieName.AUTHORIZATION_BEARER.getName(), token, null, "/", VALID_FOR_SESSION_ONLY, true, true, null, 0, HttpCookie.SameSite.STRICT);
|
||||
return builder.header(HttpHeader.SET_COOKIE.asString(), jwtCookie.getRFC6265SetCookie()).build();
|
||||
}
|
||||
|
||||
protected void removeCookie(final HttpServletResponse httpServletResponse, final String cookieName) {
|
||||
final Cookie cookie = new Cookie(cookieName, null);
|
||||
cookie.setPath("/");
|
||||
cookie.setHttpOnly(true);
|
||||
cookie.setMaxAge(0);
|
||||
cookie.setSecure(true);
|
||||
httpServletResponse.addCookie(cookie);
|
||||
/**
|
||||
* Set Bearer Token as HTTP Session Cookie using standard Cookie Name
|
||||
*
|
||||
* @param response HTTP Servlet Response
|
||||
* @param bearerToken JSON Web Token
|
||||
*/
|
||||
protected void setBearerToken(final HttpServletResponse response, final String bearerToken) {
|
||||
applicationCookieService.addSessionCookie(getCookieResourceUri(), response, ApplicationCookieName.AUTHORIZATION_BEARER, bearerToken);
|
||||
}
|
||||
|
||||
protected String getNiFiUri() {
|
||||
|
|
|
@ -39,17 +39,15 @@ import org.apache.http.message.BasicNameValuePair;
|
|||
import org.apache.nifi.authentication.exception.AuthenticationNotSupportedException;
|
||||
import org.apache.nifi.authorization.user.NiFiUserUtils;
|
||||
import org.apache.nifi.util.NiFiProperties;
|
||||
import org.apache.nifi.web.security.http.SecurityCookieName;
|
||||
import org.apache.nifi.web.api.cookie.ApplicationCookieName;
|
||||
import org.apache.nifi.web.security.jwt.provider.BearerTokenProvider;
|
||||
import org.apache.nifi.web.security.oidc.OIDCEndpoints;
|
||||
import org.apache.nifi.web.security.oidc.OidcService;
|
||||
import org.apache.nifi.web.security.token.LoginAuthenticationToken;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.ws.rs.Consumes;
|
||||
|
@ -65,6 +63,7 @@ import java.io.IOException;
|
|||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
@ -78,7 +77,6 @@ import java.util.regex.Pattern;
|
|||
public class OIDCAccessResource extends AccessResource {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(OIDCAccessResource.class);
|
||||
private static final String OIDC_REQUEST_IDENTIFIER = "oidc-request-identifier";
|
||||
private static final String OIDC_ID_TOKEN_AUTHN_ERROR = "Unable to exchange authorization for ID token: ";
|
||||
private static final String OPEN_ID_CONNECT_SUPPORT_IS_NOT_CONFIGURED_MSG = "OpenId Connect support is not configured";
|
||||
private static final String REVOKE_ACCESS_TOKEN_LOGOUT = "oidc_access_token_logout";
|
||||
|
@ -144,12 +142,12 @@ public class OIDCAccessResource extends AccessResource {
|
|||
)
|
||||
public void oidcCallback(@Context HttpServletRequest httpServletRequest, @Context HttpServletResponse httpServletResponse) throws Exception {
|
||||
final AuthenticationResponse oidcResponse = parseOidcResponse(httpServletRequest, httpServletResponse, LOGGING_IN);
|
||||
final Optional<String> requestIdentifier = getOidcRequestIdentifier();
|
||||
|
||||
final String oidcRequestIdentifier = WebUtils.getCookie(httpServletRequest, OIDC_REQUEST_IDENTIFIER).getValue();
|
||||
|
||||
if (oidcResponse != null && oidcResponse.indicatesSuccess()) {
|
||||
if (requestIdentifier.isPresent() && oidcResponse != null && oidcResponse.indicatesSuccess()) {
|
||||
final AuthenticationSuccessResponse successfulOidcResponse = (AuthenticationSuccessResponse) oidcResponse;
|
||||
|
||||
final String oidcRequestIdentifier = requestIdentifier.get();
|
||||
checkOidcState(httpServletResponse, oidcRequestIdentifier, successfulOidcResponse, LOGGING_IN);
|
||||
|
||||
try {
|
||||
|
@ -210,8 +208,8 @@ public class OIDCAccessResource extends AccessResource {
|
|||
return Response.status(Response.Status.CONFLICT).entity(OPEN_ID_CONNECT_SUPPORT_IS_NOT_CONFIGURED_MSG).build();
|
||||
}
|
||||
|
||||
final String oidcRequestIdentifier = WebUtils.getCookie(httpServletRequest, OIDC_REQUEST_IDENTIFIER).getValue();
|
||||
if (oidcRequestIdentifier == null) {
|
||||
final Optional<String> requestIdentifier = getOidcRequestIdentifier();
|
||||
if (!requestIdentifier.isPresent()) {
|
||||
final String message = "The login request identifier was not found in the request. Unable to continue.";
|
||||
logger.warn(message);
|
||||
return Response.status(Response.Status.BAD_REQUEST).entity(message).build();
|
||||
|
@ -221,12 +219,13 @@ public class OIDCAccessResource extends AccessResource {
|
|||
removeOidcRequestCookie(httpServletResponse);
|
||||
|
||||
// get the jwt
|
||||
final String jwt = oidcService.getJwt(oidcRequestIdentifier);
|
||||
final String jwt = oidcService.getJwt(requestIdentifier.get());
|
||||
if (jwt == null) {
|
||||
throw new IllegalArgumentException("A JWT for this login request identifier could not be found. Unable to continue.");
|
||||
}
|
||||
|
||||
return generateTokenResponse(generateOkResponse(jwt), jwt);
|
||||
setBearerToken(httpServletResponse, jwt);
|
||||
return generateOkResponse(jwt).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
|
@ -247,7 +246,7 @@ public class OIDCAccessResource extends AccessResource {
|
|||
}
|
||||
|
||||
final String mappedUserIdentity = NiFiUserUtils.getNiFiUserIdentity();
|
||||
removeCookie(httpServletResponse, SecurityCookieName.AUTHORIZATION_BEARER.getName());
|
||||
applicationCookieService.removeCookie(getCookieResourceUri(), httpServletResponse, ApplicationCookieName.AUTHORIZATION_BEARER);
|
||||
logger.debug("Invalidated JWT for user [{}]", mappedUserIdentity);
|
||||
|
||||
// Get the oidc discovery url
|
||||
|
@ -291,13 +290,13 @@ public class OIDCAccessResource extends AccessResource {
|
|||
)
|
||||
public void oidcLogoutCallback(@Context HttpServletRequest httpServletRequest, @Context HttpServletResponse httpServletResponse) throws Exception {
|
||||
final AuthenticationResponse oidcResponse = parseOidcResponse(httpServletRequest, httpServletResponse, !LOGGING_IN);
|
||||
final Optional<String> requestIdentifier = getOidcRequestIdentifier();
|
||||
|
||||
final String oidcRequestIdentifier = WebUtils.getCookie(httpServletRequest, OIDC_REQUEST_IDENTIFIER).getValue();
|
||||
|
||||
if (oidcResponse != null && oidcResponse.indicatesSuccess()) {
|
||||
if (requestIdentifier.isPresent() && oidcResponse != null && oidcResponse.indicatesSuccess()) {
|
||||
final AuthenticationSuccessResponse successfulOidcResponse = (AuthenticationSuccessResponse) oidcResponse;
|
||||
|
||||
// confirm state
|
||||
final String oidcRequestIdentifier = requestIdentifier.get();
|
||||
checkOidcState(httpServletResponse, oidcRequestIdentifier, successfulOidcResponse, false);
|
||||
|
||||
// Get the oidc discovery url
|
||||
|
@ -404,31 +403,16 @@ public class OIDCAccessResource extends AccessResource {
|
|||
* @return the authorization URI
|
||||
*/
|
||||
private URI oidcRequestAuthorizationCode(@Context HttpServletResponse httpServletResponse, String callback) {
|
||||
|
||||
final String oidcRequestIdentifier = UUID.randomUUID().toString();
|
||||
|
||||
// generate a cookie to associate this login sequence
|
||||
final Cookie cookie = new Cookie(OIDC_REQUEST_IDENTIFIER, oidcRequestIdentifier);
|
||||
cookie.setPath("/");
|
||||
cookie.setHttpOnly(true);
|
||||
cookie.setMaxAge(60);
|
||||
cookie.setSecure(true);
|
||||
httpServletResponse.addCookie(cookie);
|
||||
|
||||
// get the state for this request
|
||||
applicationCookieService.addCookie(getCookieResourceUri(), httpServletResponse, ApplicationCookieName.OIDC_REQUEST_IDENTIFIER, oidcRequestIdentifier);
|
||||
final State state = oidcService.createState(oidcRequestIdentifier);
|
||||
|
||||
// build the authorization uri
|
||||
final URI authorizationUri = UriBuilder.fromUri(oidcService.getAuthorizationEndpoint())
|
||||
return UriBuilder.fromUri(oidcService.getAuthorizationEndpoint())
|
||||
.queryParam("client_id", oidcService.getClientId())
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("scope", oidcService.getScope().toString())
|
||||
.queryParam("state", state.getValue())
|
||||
.queryParam("redirect_uri", callback)
|
||||
.build();
|
||||
|
||||
// return Authorization URI
|
||||
return authorizationUri;
|
||||
}
|
||||
|
||||
private String determineLogoutMethod(String oidcDiscoveryUrl) {
|
||||
|
@ -475,7 +459,7 @@ public class OIDCAccessResource extends AccessResource {
|
|||
}
|
||||
|
||||
@PreDestroy
|
||||
private final void closeClient() throws IOException {
|
||||
public void closeClient() throws IOException {
|
||||
httpClient.close();
|
||||
}
|
||||
|
||||
|
@ -494,8 +478,8 @@ public class OIDCAccessResource extends AccessResource {
|
|||
return null;
|
||||
}
|
||||
|
||||
final String oidcRequestIdentifier = WebUtils.getCookie(httpServletRequest, OIDC_REQUEST_IDENTIFIER).getValue();
|
||||
if (oidcRequestIdentifier == null) {
|
||||
final Optional<String> requestIdentifier = getOidcRequestIdentifier();
|
||||
if (!requestIdentifier.isPresent()) {
|
||||
forwardToMessagePage(httpServletRequest, httpServletResponse, pageTitle,"The request identifier was " +
|
||||
"not found in the request. Unable to continue.");
|
||||
return null;
|
||||
|
@ -522,16 +506,12 @@ public class OIDCAccessResource extends AccessResource {
|
|||
// confirm state
|
||||
final State state = successfulOidcResponse.getState();
|
||||
if (state == null || !oidcService.isStateValid(oidcRequestIdentifier, state)) {
|
||||
logger.error("The state value returned by the OpenId Connect Provider does not match the stored " +
|
||||
"state. Unable to continue login/logout process.");
|
||||
logger.error("OIDC Request [{}] State [{}] not valid", oidcRequestIdentifier, state);
|
||||
|
||||
// remove the oidc request cookie
|
||||
removeOidcRequestCookie(httpServletResponse);
|
||||
|
||||
// forward to the error page
|
||||
forwardToMessagePage(httpServletRequest, httpServletResponse, getForwardPageTitle(isLogin), "Purposed state does not match " +
|
||||
"the stored state. Unable to continue login/logout process.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -552,7 +532,11 @@ public class OIDCAccessResource extends AccessResource {
|
|||
}
|
||||
|
||||
private void removeOidcRequestCookie(final HttpServletResponse httpServletResponse) {
|
||||
removeCookie(httpServletResponse, OIDC_REQUEST_IDENTIFIER);
|
||||
applicationCookieService.removeCookie(getCookieResourceUri(), httpServletResponse, ApplicationCookieName.OIDC_REQUEST_IDENTIFIER);
|
||||
}
|
||||
|
||||
private Optional<String> getOidcRequestIdentifier() {
|
||||
return applicationCookieService.getCookieValue(httpServletRequest, ApplicationCookieName.OIDC_REQUEST_IDENTIFIER);
|
||||
}
|
||||
|
||||
public void setOidcService(OidcService oidcService) {
|
||||
|
|
|
@ -25,19 +25,17 @@ import org.apache.nifi.authorization.util.IdentityMapping;
|
|||
import org.apache.nifi.authorization.util.IdentityMappingUtil;
|
||||
import org.apache.nifi.idp.IdpType;
|
||||
import org.apache.nifi.util.NiFiProperties;
|
||||
import org.apache.nifi.web.api.cookie.ApplicationCookieName;
|
||||
import org.apache.nifi.web.security.logout.LogoutRequest;
|
||||
import org.apache.nifi.web.security.saml.SAMLCredentialStore;
|
||||
import org.apache.nifi.web.security.saml.SAMLEndpoints;
|
||||
import org.apache.nifi.web.security.saml.SAMLService;
|
||||
import org.apache.nifi.web.security.saml.SAMLStateManager;
|
||||
import org.apache.nifi.web.security.token.LoginAuthenticationToken;
|
||||
import org.opensaml.saml2.metadata.provider.MetadataProviderException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.saml.SAMLCredential;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.ws.rs.Consumes;
|
||||
|
@ -53,6 +51,7 @@ import javax.ws.rs.core.UriInfo;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -65,7 +64,6 @@ import java.util.stream.Collectors;
|
|||
public class SAMLAccessResource extends AccessResource {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SAMLAccessResource.class);
|
||||
private static final String SAML_REQUEST_IDENTIFIER = "saml-request-identifier";
|
||||
private static final String SAML_METADATA_MEDIA_TYPE = "application/samlmetadata+xml";
|
||||
private static final String LOGOUT_REQUEST_IDENTIFIER_NOT_FOUND = "The logout request identifier was not found in the request. Unable to continue.";
|
||||
private static final String LOGOUT_REQUEST_NOT_FOUND_FOR_GIVEN_IDENTIFIER = "No logout request was found for the given identifier. Unable to continue.";
|
||||
|
@ -84,7 +82,7 @@ public class SAMLAccessResource extends AccessResource {
|
|||
value = "Retrieves the service provider metadata.",
|
||||
notes = NON_GUARANTEED_ENDPOINT
|
||||
)
|
||||
public Response samlMetadata(@Context HttpServletRequest httpServletRequest, @Context HttpServletResponse httpServletResponse) throws Exception {
|
||||
public Response samlMetadata(@Context HttpServletRequest httpServletRequest, @Context HttpServletResponse httpServletResponse) {
|
||||
// only consider user specific access over https
|
||||
if (!httpServletRequest.isSecure()) {
|
||||
throw new AuthenticationNotSupportedException(AUTHENTICATION_NOT_ENABLED_MSG);
|
||||
|
@ -122,12 +120,7 @@ public class SAMLAccessResource extends AccessResource {
|
|||
final String samlRequestIdentifier = UUID.randomUUID().toString();
|
||||
|
||||
// generate a cookie to associate this login sequence
|
||||
final Cookie cookie = new Cookie(SAML_REQUEST_IDENTIFIER, samlRequestIdentifier);
|
||||
cookie.setPath("/");
|
||||
cookie.setHttpOnly(true);
|
||||
cookie.setMaxAge(60);
|
||||
cookie.setSecure(true);
|
||||
httpServletResponse.addCookie(cookie);
|
||||
applicationCookieService.addCookie(getCookieResourceUri(), httpServletResponse, ApplicationCookieName.SAML_REQUEST_IDENTIFIER, samlRequestIdentifier);
|
||||
|
||||
// get the state for this request
|
||||
final String relayState = samlStateManager.createState(samlRequestIdentifier);
|
||||
|
@ -137,7 +130,6 @@ public class SAMLAccessResource extends AccessResource {
|
|||
samlService.initiateLogin(httpServletRequest, httpServletResponse, relayState);
|
||||
} catch (Exception e) {
|
||||
forwardToLoginMessagePage(httpServletRequest, httpServletResponse, e.getMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -184,8 +176,8 @@ public class SAMLAccessResource extends AccessResource {
|
|||
initializeSamlServiceProvider();
|
||||
|
||||
// ensure the request has the cookie with the request id
|
||||
final String samlRequestIdentifier = WebUtils.getCookie(httpServletRequest, SAML_REQUEST_IDENTIFIER).getValue();
|
||||
if (samlRequestIdentifier == null) {
|
||||
final Optional<String> requestIdentifier = getSamlRequestIdentifier();
|
||||
if (!requestIdentifier.isPresent()) {
|
||||
forwardToLoginMessagePage(httpServletRequest, httpServletResponse, "The login request identifier was not found in the request. Unable to continue.");
|
||||
return;
|
||||
}
|
||||
|
@ -199,6 +191,7 @@ public class SAMLAccessResource extends AccessResource {
|
|||
}
|
||||
|
||||
// ensure the RelayState value in the request matches the store state
|
||||
final String samlRequestIdentifier = requestIdentifier.get();
|
||||
if (!samlStateManager.isStateValid(samlRequestIdentifier, requestState)) {
|
||||
logger.error("The RelayState value returned by the SAML IDP does not match the stored state. Unable to continue login process.");
|
||||
removeSamlRequestCookie(httpServletResponse);
|
||||
|
@ -258,7 +251,7 @@ public class SAMLAccessResource extends AccessResource {
|
|||
notes = NON_GUARANTEED_ENDPOINT
|
||||
)
|
||||
public Response samlLoginExchange(@Context HttpServletRequest httpServletRequest,
|
||||
@Context HttpServletResponse httpServletResponse) throws Exception {
|
||||
@Context HttpServletResponse httpServletResponse) {
|
||||
|
||||
// only consider user specific access over https
|
||||
if (!httpServletRequest.isSecure()) {
|
||||
|
@ -271,30 +264,26 @@ public class SAMLAccessResource extends AccessResource {
|
|||
return Response.status(Response.Status.CONFLICT).entity(SAMLService.SAML_SUPPORT_IS_NOT_CONFIGURED).build();
|
||||
}
|
||||
|
||||
logger.info("Attempting to exchange SAML login request for a NiFi JWT...");
|
||||
|
||||
// ensure saml service provider is initialized
|
||||
initializeSamlServiceProvider();
|
||||
|
||||
// ensure the request has the cookie with the request identifier
|
||||
final String samlRequestIdentifier = WebUtils.getCookie(httpServletRequest, SAML_REQUEST_IDENTIFIER).getValue();
|
||||
if (samlRequestIdentifier == null) {
|
||||
final Optional<String> requestIdentifier = getSamlRequestIdentifier();
|
||||
if (!requestIdentifier.isPresent()) {
|
||||
final String message = "The login request identifier was not found in the request. Unable to continue.";
|
||||
logger.warn(message);
|
||||
return Response.status(Response.Status.BAD_REQUEST).entity(message).build();
|
||||
}
|
||||
|
||||
// remove the saml request cookie
|
||||
removeSamlRequestCookie(httpServletResponse);
|
||||
|
||||
// get the jwt
|
||||
final String samlRequestIdentifier = requestIdentifier.get();
|
||||
final String jwt = samlStateManager.getJwt(samlRequestIdentifier);
|
||||
if (jwt == null) {
|
||||
throw new IllegalArgumentException("A JWT for this login request identifier could not be found. Unable to continue.");
|
||||
}
|
||||
|
||||
// generate the response
|
||||
logger.info("SAML login exchange complete");
|
||||
logger.info("SAML Login Request [{}] Completed", samlRequestIdentifier);
|
||||
setBearerToken(httpServletResponse, jwt);
|
||||
return generateOkResponse(jwt).build();
|
||||
}
|
||||
|
||||
|
@ -312,13 +301,14 @@ public class SAMLAccessResource extends AccessResource {
|
|||
assert(isSamlEnabled(httpServletRequest, httpServletResponse, !LOGGING_IN));
|
||||
|
||||
// ensure the logout request identifier is present
|
||||
final String logoutRequestIdentifier = WebUtils.getCookie(httpServletRequest, LOGOUT_REQUEST_IDENTIFIER).getValue();
|
||||
if (StringUtils.isBlank(logoutRequestIdentifier)) {
|
||||
final Optional<String> cookieValue = getLogoutRequestIdentifier();
|
||||
if (!cookieValue.isPresent()) {
|
||||
forwardToLogoutMessagePage(httpServletRequest, httpServletResponse, LOGOUT_REQUEST_IDENTIFIER_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
// ensure there is a logout request in progress for the given identifier
|
||||
final String logoutRequestIdentifier = cookieValue.get();
|
||||
final LogoutRequest logoutRequest = logoutRequestManager.get(logoutRequestIdentifier);
|
||||
if (logoutRequest == null) {
|
||||
forwardToLogoutMessagePage(httpServletRequest, httpServletResponse, LOGOUT_REQUEST_NOT_FOUND_FOR_GIVEN_IDENTIFIER);
|
||||
|
@ -341,9 +331,8 @@ public class SAMLAccessResource extends AccessResource {
|
|||
try {
|
||||
logger.info("Initiating SAML Single Logout with IDP...");
|
||||
samlService.initiateLogout(httpServletRequest, httpServletResponse, samlCredential);
|
||||
} catch (Exception e) {
|
||||
} catch (final Exception e) {
|
||||
forwardToLogoutMessagePage(httpServletRequest, httpServletResponse, e.getMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -400,13 +389,14 @@ public class SAMLAccessResource extends AccessResource {
|
|||
initializeSamlServiceProvider();
|
||||
|
||||
// ensure the logout request identifier is present
|
||||
final String logoutRequestIdentifier = WebUtils.getCookie(httpServletRequest, LOGOUT_REQUEST_IDENTIFIER).getValue();
|
||||
if (StringUtils.isBlank(logoutRequestIdentifier)) {
|
||||
final Optional<String> requestIdentifier = getLogoutRequestIdentifier();
|
||||
if (!requestIdentifier.isPresent()) {
|
||||
forwardToLogoutMessagePage(httpServletRequest, httpServletResponse, LOGOUT_REQUEST_IDENTIFIER_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
// ensure there is a logout request in progress for the given identifier
|
||||
final String logoutRequestIdentifier = requestIdentifier.get();
|
||||
final LogoutRequest logoutRequest = logoutRequestManager.get(logoutRequestIdentifier);
|
||||
if (logoutRequest == null) {
|
||||
forwardToLogoutMessagePage(httpServletRequest, httpServletResponse, LOGOUT_REQUEST_NOT_FOUND_FOR_GIVEN_IDENTIFIER);
|
||||
|
@ -473,7 +463,7 @@ public class SAMLAccessResource extends AccessResource {
|
|||
httpServletResponse.sendRedirect(getNiFiLogoutCompleteUri());
|
||||
}
|
||||
|
||||
private void initializeSamlServiceProvider() throws MetadataProviderException {
|
||||
private void initializeSamlServiceProvider() {
|
||||
if (!samlService.isServiceProviderInitialized()) {
|
||||
final String samlMetadataUri = generateResourceUri("saml", "metadata");
|
||||
final String baseUri = samlMetadataUri.replace("/saml/metadata", "");
|
||||
|
@ -490,7 +480,7 @@ public class SAMLAccessResource extends AccessResource {
|
|||
}
|
||||
|
||||
private void removeSamlRequestCookie(final HttpServletResponse httpServletResponse) {
|
||||
removeCookie(httpServletResponse, SAML_REQUEST_IDENTIFIER);
|
||||
applicationCookieService.removeCookie(getCookieResourceUri(), httpServletResponse, ApplicationCookieName.SAML_REQUEST_IDENTIFIER);
|
||||
}
|
||||
|
||||
private boolean isSamlEnabled(final HttpServletRequest httpServletRequest, final HttpServletResponse httpServletResponse, boolean isLogin) throws Exception {
|
||||
|
@ -514,6 +504,10 @@ public class SAMLAccessResource extends AccessResource {
|
|||
return isLogin ? ApplicationResource.LOGIN_ERROR_TITLE : ApplicationResource.LOGOUT_ERROR_TITLE;
|
||||
}
|
||||
|
||||
private Optional<String> getSamlRequestIdentifier() {
|
||||
return applicationCookieService.getCookieValue(httpServletRequest, ApplicationCookieName.SAML_REQUEST_IDENTIFIER);
|
||||
}
|
||||
|
||||
public void setSamlService(SAMLService samlService) {
|
||||
this.samlService = samlService;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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.nifi.web.api.cookie;
|
||||
|
||||
import org.apache.nifi.web.security.http.SecurityCookieName;
|
||||
|
||||
/**
|
||||
* Application Cookie Names
|
||||
*/
|
||||
public enum ApplicationCookieName {
|
||||
AUTHORIZATION_BEARER(SecurityCookieName.AUTHORIZATION_BEARER.getName()),
|
||||
|
||||
LOGOUT_REQUEST_IDENTIFIER("nifi-logout-request-identifier"),
|
||||
|
||||
OIDC_REQUEST_IDENTIFIER("nifi-oidc-request-identifier"),
|
||||
|
||||
SAML_REQUEST_IDENTIFIER("nifi-saml-request-identifier");
|
||||
|
||||
private final String cookieName;
|
||||
|
||||
ApplicationCookieName(final String cookieName) {
|
||||
this.cookieName = cookieName;
|
||||
}
|
||||
|
||||
public String getCookieName() {
|
||||
return cookieName;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* 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.nifi.web.api.cookie;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.net.URI;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Application Cookie Service capable of generating and retrieving HTTP Cookies using standard properties
|
||||
*/
|
||||
public interface ApplicationCookieService {
|
||||
/**
|
||||
* Generate cookie with specified value
|
||||
*
|
||||
* @param resourceUri Resource URI containing path and domain
|
||||
* @param response HTTP Servlet Response
|
||||
* @param applicationCookieName Application Cookie Name to be added
|
||||
* @param value Cookie value to be added
|
||||
*/
|
||||
void addCookie(URI resourceUri, HttpServletResponse response, ApplicationCookieName applicationCookieName, String value);
|
||||
|
||||
/**
|
||||
* Generate cookie with session-based expiration and specified value
|
||||
*
|
||||
* @param resourceUri Resource URI containing path and domain
|
||||
* @param response HTTP Servlet Response
|
||||
* @param applicationCookieName Application Cookie Name
|
||||
* @param value Cookie value to be added
|
||||
*/
|
||||
void addSessionCookie(URI resourceUri, HttpServletResponse response, ApplicationCookieName applicationCookieName, String value);
|
||||
|
||||
/**
|
||||
* Get cookie value using specified name
|
||||
*
|
||||
* @param request HTTP Servlet Response
|
||||
* @param applicationCookieName Application Cookie Name to be retrieved
|
||||
* @return Optional Cookie Value
|
||||
*/
|
||||
Optional<String> getCookieValue(HttpServletRequest request, ApplicationCookieName applicationCookieName);
|
||||
|
||||
/**
|
||||
* Generate cookie with an empty value instructing the client to remove the cookie
|
||||
*
|
||||
* @param resourceUri Resource URI containing path and domain
|
||||
* @param response HTTP Servlet Response
|
||||
* @param applicationCookieName Application Cookie Name to be removed
|
||||
*/
|
||||
void removeCookie(URI resourceUri, HttpServletResponse response, ApplicationCookieName applicationCookieName);
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* 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.nifi.web.api.cookie;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import java.net.URI;
|
||||
import java.time.Duration;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Standard implementation of Application Cookie Service using Spring Framework utilities
|
||||
*/
|
||||
public class StandardApplicationCookieService implements ApplicationCookieService {
|
||||
private static final Duration MAX_AGE_SESSION = Duration.ofSeconds(-1);
|
||||
|
||||
private static final Duration MAX_AGE_REMOVE = Duration.ZERO;
|
||||
|
||||
private static final Duration MAX_AGE_STANDARD = Duration.ofSeconds(60);
|
||||
|
||||
private static final String DEFAULT_PATH = "/";
|
||||
|
||||
private static final String SAME_SITE_STRICT = "Strict";
|
||||
|
||||
private static final boolean SECURE_ENABLED = true;
|
||||
|
||||
private static final boolean HTTP_ONLY_ENABLED = true;
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(StandardApplicationCookieService.class);
|
||||
|
||||
/**
|
||||
* Generate cookie with specified value
|
||||
*
|
||||
* @param resourceUri Resource URI containing path and domain
|
||||
* @param response HTTP Servlet Response
|
||||
* @param applicationCookieName Application Cookie Name to be added
|
||||
* @param value Cookie value to be added
|
||||
*/
|
||||
@Override
|
||||
public void addCookie(final URI resourceUri, final HttpServletResponse response, final ApplicationCookieName applicationCookieName, final String value) {
|
||||
final ResponseCookie.ResponseCookieBuilder responseCookieBuilder = getCookieBuilder(resourceUri, applicationCookieName, value, MAX_AGE_STANDARD);
|
||||
setResponseCookie(response, responseCookieBuilder.build());
|
||||
logger.debug("Added Cookie [{}] URI [{}]", applicationCookieName.getCookieName(), resourceUri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate cookie with session-based expiration and specified value as well as SameSite Strict property
|
||||
*
|
||||
* @param resourceUri Resource URI containing path and domain
|
||||
* @param response HTTP Servlet Response
|
||||
* @param applicationCookieName Application Cookie Name
|
||||
* @param value Cookie value to be added
|
||||
*/
|
||||
@Override
|
||||
public void addSessionCookie(final URI resourceUri, final HttpServletResponse response, final ApplicationCookieName applicationCookieName, final String value) {
|
||||
final ResponseCookie.ResponseCookieBuilder responseCookieBuilder = getCookieBuilder(resourceUri, applicationCookieName, value, MAX_AGE_SESSION);
|
||||
responseCookieBuilder.sameSite(SAME_SITE_STRICT);
|
||||
setResponseCookie(response, responseCookieBuilder.build());
|
||||
logger.debug("Added Session Cookie [{}] URI [{}]", applicationCookieName.getCookieName(), resourceUri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cookie value using specified name
|
||||
*
|
||||
* @param request HTTP Servlet Response
|
||||
* @param applicationCookieName Application Cookie Name to be retrieved
|
||||
* @return Optional Cookie Value
|
||||
*/
|
||||
@Override
|
||||
public Optional<String> getCookieValue(final HttpServletRequest request, final ApplicationCookieName applicationCookieName) {
|
||||
final Cookie cookie = WebUtils.getCookie(request, applicationCookieName.getCookieName());
|
||||
return cookie == null ? Optional.empty() : Optional.of(cookie.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate cookie with an empty value instructing the client to remove the cookie with a maximum age of 60 seconds
|
||||
*
|
||||
* @param resourceUri Resource URI containing path and domain
|
||||
* @param response HTTP Servlet Response
|
||||
* @param applicationCookieName Application Cookie Name to be removed
|
||||
*/
|
||||
@Override
|
||||
public void removeCookie(final URI resourceUri, final HttpServletResponse response, final ApplicationCookieName applicationCookieName) {
|
||||
Objects.requireNonNull(response, "Response required");
|
||||
final ResponseCookie.ResponseCookieBuilder responseCookieBuilder = getCookieBuilder(resourceUri, applicationCookieName, StringUtils.EMPTY, MAX_AGE_REMOVE);
|
||||
setResponseCookie(response, responseCookieBuilder.build());
|
||||
logger.debug("Removed Cookie [{}] URI [{}]", applicationCookieName.getCookieName(), resourceUri);
|
||||
}
|
||||
|
||||
private ResponseCookie.ResponseCookieBuilder getCookieBuilder(final URI resourceUri,
|
||||
final ApplicationCookieName applicationCookieName,
|
||||
final String value,
|
||||
final Duration maxAge) {
|
||||
Objects.requireNonNull(resourceUri, "Resource URI required");
|
||||
Objects.requireNonNull(applicationCookieName, "Response Cookie Name required");
|
||||
return ResponseCookie.from(applicationCookieName.getCookieName(), value)
|
||||
.path(getCookiePath(resourceUri))
|
||||
.domain(resourceUri.getHost())
|
||||
.secure(SECURE_ENABLED)
|
||||
.httpOnly(HTTP_ONLY_ENABLED)
|
||||
.maxAge(maxAge);
|
||||
}
|
||||
|
||||
private void setResponseCookie(final HttpServletResponse response, final ResponseCookie responseCookie) {
|
||||
response.addHeader(HttpHeaders.SET_COOKIE, responseCookie.toString());
|
||||
}
|
||||
|
||||
private String getCookiePath(final URI resourceUri) {
|
||||
return StringUtils.defaultIfBlank(resourceUri.getPath(), DEFAULT_PATH);
|
||||
}
|
||||
}
|
|
@ -147,6 +147,7 @@ public class TestDataTransferResource {
|
|||
final URI locationUri = new URI(locationUriStr);
|
||||
doReturn(uriBuilder).when(uriInfo).getBaseUriBuilder();
|
||||
doReturn(uriBuilder).when(uriBuilder).path(any(String.class));
|
||||
doReturn(uriBuilder).when(uriBuilder).segment(any(String.class));
|
||||
doReturn(locationUri).when(uriBuilder).build();
|
||||
return uriInfo;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* 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.nifi.web.api.cookie;
|
||||
|
||||
import org.apache.nifi.util.StringUtils;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.mock.web.MockCookie;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import java.net.URI;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class StandardApplicationCookieServiceTest {
|
||||
private static final String DOMAIN = "localhost.localdomain";
|
||||
|
||||
private static final String RESOURCE_URI = String.format("https://%s", DOMAIN);
|
||||
|
||||
private static final String ROOT_PATH = "/";
|
||||
|
||||
private static final String CONTEXT_PATH = "/context";
|
||||
|
||||
private static final String CONTEXT_RESOURCE_URI = String.format("https://%s%s", DOMAIN, CONTEXT_PATH);
|
||||
|
||||
private static final int EXPECTED_MAX_AGE = 60;
|
||||
|
||||
private static final int SESSION_MAX_AGE = -1;
|
||||
|
||||
private static final int REMOVE_MAX_AGE = 0;
|
||||
|
||||
private static final String SAME_SITE_STRICT = "SameSite=Strict";
|
||||
|
||||
private static final String COOKIE_VALUE = UUID.randomUUID().toString();
|
||||
|
||||
private static final ApplicationCookieName COOKIE_NAME = ApplicationCookieName.LOGOUT_REQUEST_IDENTIFIER;
|
||||
|
||||
private URI resourceUri;
|
||||
|
||||
private URI contextResourceUri;
|
||||
|
||||
private StandardApplicationCookieService service;
|
||||
|
||||
@Mock
|
||||
private HttpServletRequest request;
|
||||
|
||||
@Mock
|
||||
private HttpServletResponse response;
|
||||
|
||||
@Captor
|
||||
private ArgumentCaptor<String> cookieArgumentCaptor;
|
||||
|
||||
@Before
|
||||
public void setService() {
|
||||
service = new StandardApplicationCookieService();
|
||||
resourceUri = URI.create(RESOURCE_URI);
|
||||
contextResourceUri = URI.create(CONTEXT_RESOURCE_URI);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddCookie() {
|
||||
service.addCookie(resourceUri, response, COOKIE_NAME, COOKIE_VALUE);
|
||||
|
||||
verify(response).addHeader(eq(HttpHeaders.SET_COOKIE), cookieArgumentCaptor.capture());
|
||||
final String setCookieHeader = cookieArgumentCaptor.getValue();
|
||||
assertAddCookieMatches(setCookieHeader, ROOT_PATH, EXPECTED_MAX_AGE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddCookieContextPath() {
|
||||
service.addCookie(contextResourceUri, response, COOKIE_NAME, COOKIE_VALUE);
|
||||
|
||||
verify(response).addHeader(eq(HttpHeaders.SET_COOKIE), cookieArgumentCaptor.capture());
|
||||
final String setCookieHeader = cookieArgumentCaptor.getValue();
|
||||
assertAddCookieMatches(setCookieHeader, CONTEXT_PATH, EXPECTED_MAX_AGE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddSessionCookie() {
|
||||
service.addSessionCookie(resourceUri, response, COOKIE_NAME, COOKIE_VALUE);
|
||||
|
||||
verify(response).addHeader(eq(HttpHeaders.SET_COOKIE), cookieArgumentCaptor.capture());
|
||||
|
||||
final String setCookieHeader = cookieArgumentCaptor.getValue();
|
||||
assertAddCookieMatches(setCookieHeader, ROOT_PATH, SESSION_MAX_AGE);
|
||||
assertTrue("SameSite not found", setCookieHeader.endsWith(SAME_SITE_STRICT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddSessionCookieContextPath() {
|
||||
service.addSessionCookie(contextResourceUri, response, COOKIE_NAME, COOKIE_VALUE);
|
||||
|
||||
verify(response).addHeader(eq(HttpHeaders.SET_COOKIE), cookieArgumentCaptor.capture());
|
||||
|
||||
final String setCookieHeader = cookieArgumentCaptor.getValue();
|
||||
assertAddCookieMatches(setCookieHeader, CONTEXT_PATH, SESSION_MAX_AGE);
|
||||
assertTrue("SameSite not found", setCookieHeader.endsWith(SAME_SITE_STRICT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCookieValue() {
|
||||
final Cookie cookie = new Cookie(COOKIE_NAME.getCookieName(), COOKIE_VALUE);
|
||||
when(request.getCookies()).thenReturn(new Cookie[]{cookie});
|
||||
final Optional<String> cookieValue = service.getCookieValue(request, COOKIE_NAME);
|
||||
assertTrue(cookieValue.isPresent());
|
||||
assertEquals(COOKIE_VALUE, cookieValue.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCookieValueEmpty() {
|
||||
final Optional<String> cookieValue = service.getCookieValue(request, COOKIE_NAME);
|
||||
assertFalse(cookieValue.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveCookie() {
|
||||
service.removeCookie(resourceUri, response, COOKIE_NAME);
|
||||
|
||||
verify(response).addHeader(eq(HttpHeaders.SET_COOKIE), cookieArgumentCaptor.capture());
|
||||
final String setCookieHeader = cookieArgumentCaptor.getValue();
|
||||
assertRemoveCookieMatches(setCookieHeader, ROOT_PATH);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveCookieContextPath() {
|
||||
service.removeCookie(contextResourceUri, response, COOKIE_NAME);
|
||||
|
||||
verify(response).addHeader(eq(HttpHeaders.SET_COOKIE), cookieArgumentCaptor.capture());
|
||||
final String setCookieHeader = cookieArgumentCaptor.getValue();
|
||||
assertRemoveCookieMatches(setCookieHeader, CONTEXT_PATH);
|
||||
}
|
||||
|
||||
private void assertAddCookieMatches(final String setCookieHeader, final String path, final long maxAge) {
|
||||
final Cookie cookie = MockCookie.parse(setCookieHeader);
|
||||
assertCookieMatches(setCookieHeader, cookie, path);
|
||||
assertEquals(COOKIE_VALUE, cookie.getValue());
|
||||
assertEquals(maxAge, cookie.getMaxAge());
|
||||
}
|
||||
|
||||
private void assertRemoveCookieMatches(final String setCookieHeader, final String path) {
|
||||
final Cookie cookie = MockCookie.parse(setCookieHeader);
|
||||
assertCookieMatches(setCookieHeader, cookie, path);
|
||||
assertEquals(StringUtils.EMPTY, cookie.getValue());
|
||||
assertEquals(REMOVE_MAX_AGE, cookie.getMaxAge());
|
||||
}
|
||||
|
||||
private void assertCookieMatches(final String setCookieHeader, final Cookie cookie, final String path) {
|
||||
assertEquals("Cookie Name not matched", COOKIE_NAME.getCookieName(), cookie.getName());
|
||||
assertEquals("Path not matched", path, cookie.getPath());
|
||||
assertEquals("Domain not matched", DOMAIN, cookie.getDomain());
|
||||
assertTrue("HTTP Only not matched", cookie.isHttpOnly());
|
||||
assertTrue("Secure not matched", cookie.getSecure());
|
||||
}
|
||||
}
|
|
@ -20,7 +20,8 @@ package org.apache.nifi.web.security.http;
|
|||
* Enumeration of HTTP Cookie Names for Security
|
||||
*/
|
||||
public enum SecurityCookieName {
|
||||
AUTHORIZATION_BEARER("__Host-Authorization-Bearer");
|
||||
/** See IETF Cookie Prefixes Draft Section 3.1 related to Secure prefix handling */
|
||||
AUTHORIZATION_BEARER("__Secure-Authorization-Bearer");
|
||||
|
||||
private String name;
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
}).on('mouseleave', function () {
|
||||
$(this).removeClass('link-over');
|
||||
}).on('click', function () {
|
||||
window.location = '<%= contextPath %>/nifi';
|
||||
window.location = '<%= contextPath %>/nifi/';
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -116,9 +116,9 @@
|
|||
if (accessStatus.status === 'ACTIVE') {
|
||||
// reload as appropriate - no need to schedule token refresh as the page is reloading
|
||||
if (top !== window) {
|
||||
parent.window.location = '/nifi';
|
||||
parent.window.location = '../nifi/';
|
||||
} else {
|
||||
window.location = '/nifi';
|
||||
window.location = '../nifi/';
|
||||
}
|
||||
} else {
|
||||
$('#login-message-title').text('Unable to log in');
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
}).on('mouseleave', function () {
|
||||
$(this).removeClass('link-over');
|
||||
}).on('click', function () {
|
||||
window.location = '../nifi';
|
||||
window.location = '../nifi/';
|
||||
});
|
||||
});
|
||||
}));
|
|
@ -110,9 +110,9 @@
|
|||
// handle home
|
||||
$('#user-home').on('click', function () {
|
||||
if (top !== window) {
|
||||
parent.window.location = '../nifi';
|
||||
parent.window.location = '../nifi/';
|
||||
} else {
|
||||
window.location = '../nifi';
|
||||
window.location = '../nifi/';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
headerText: 'Session Expired',
|
||||
dialogContent: 'Your session has expired. Please press Ok to log in again.',
|
||||
okHandler: function () {
|
||||
window.location = '/nifi';
|
||||
window.location = '../nifi/';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue