/*
* Copyright 2013 OmniFaces.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.omnifaces.security.jaspic.filters;
import static javax.security.auth.message.AuthStatus.SUCCESS;
import static org.omnifaces.security.jaspic.Utils.isOneOf;
import static org.omnifaces.security.jaspic.Utils.redirect;
import static org.omnifaces.security.jaspic.core.Jaspic.authenticateFromFilter;
import static org.omnifaces.security.jaspic.core.Jaspic.getLastStatus;
import static org.omnifaces.security.jaspic.core.Jaspic.isDidAuthenticationAndSucceeded;
import java.io.IOException;
import javax.security.auth.message.module.ServerAuthModule;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.omnifaces.security.jaspic.request.HttpServletRequestDelegator;
import org.omnifaces.security.jaspic.request.RequestData;
import org.omnifaces.security.jaspic.request.RequestDataDAO;
/**
* This filter explicitly makes a call to {@link HttpServletRequest#authenticate(HttpServletResponse)} (via a helper method so it can be
* recognized as an explicit call) at the start of each request.
* <p>
*
* The reason for this Filter is that in here CDI, EJB, etc are available, while in a
* {@link ServerAuthModule#validateRequest(javax.security.auth.message.MessageInfo, javax.security.auth.Subject, javax.security.auth.Subject)}
* this is for most servers not the case.
* <p>
* Additionally, in this Filter we can wrap the request if needed. This should be possible in <code>validateHttpRequest</code> as well, but
* historically JASPIC implementations never supported this. As of 2014, WildFly 8 and GlassFish 4 support it.
*
*/
public class OmniServerAuthFilter extends HttpFilter {
private final RequestDataDAO requestDAO = new RequestDataDAO();
@Override
public void doFilter(HttpServletRequest request, HttpServletResponse response, HttpSession session, FilterChain chain) throws ServletException, IOException {
// Trigger an explicit call to the SAMs, so they will execute within a context where CDI, EJB etc is available.
authenticateFromFilter(request, response);
if (isOneOf(getLastStatus(request), SUCCESS, null)) {
HttpServletRequest newRequest = request;
RequestData requestData = requestDAO.get(request);
if (requestData != null) {
boolean matchesRequest = requestData.matchesRequest(request);
// Note that we check for "isDidAuthentication()" below to see if authentication actually happened during the call above
// to "authenticateFromFilter()". The return status "SUCCESS" is too general, and could mean we have a remembered authentication
// or an unauthenticated identity. Here we need to react on the initial authentication only.
if (!matchesRequest && SUCCESS.equals(getLastStatus(request)) && isDidAuthenticationAndSucceeded(request)) {
// If we just authenticated but are on another URL than where we should take the user, we redirect the user to this original URL.
// It's less ideal for the SAM to do this, since there's no status code for doing authentication AND
// redirecting (after which JASPIC should not invoke the resource).
redirect(response, requestData.getFullRequestURL());
// Don't continue the chain (this is what a SAM cannot instruct JASPIC to do).
return;
}
// If there was a saved request and it matches with the current request, it means the user was originally redirected from
// a protected resource to authenticate and has now been redirected back to it. Since we reach this filter, it means
// the user is now authenticated to access this protected resource.
if (matchesRequest) {
if (requestData.isRestoreRequest()) {
// We restore the original request here by providing a wrapped request. This will ensure the
// original request parameters (GET + POST) as well as the original cookies etc are available again.
newRequest = new HttpServletRequestDelegator(request, requestData);
}
requestDAO.remove(request);
}
}
chain.doFilter(newRequest, response);
}
}
}