/* * 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.core; import static java.util.Collections.emptyMap; import static java.util.Collections.unmodifiableMap; import static javax.security.auth.message.AuthStatus.SEND_FAILURE; import static javax.security.auth.message.AuthStatus.SUCCESS; import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND; import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED; import java.io.IOException; import java.util.List; import java.util.Map; import javax.security.auth.Subject; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.message.AuthStatus; import javax.security.auth.message.MessageInfo; import javax.security.auth.message.config.ServerAuthContext; import javax.security.auth.message.module.ServerAuthModule; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * A convenience context that provides access to JASPIC Servlet Profile specific types * and functionality. * * @author Arjan Tijms * */ public class HttpMsgContext { private CallbackHandler handler; private Map<String, String> moduleOptions; private MessageInfo messageInfo; private Subject clientSubject; private AuthParameters authParameters; public HttpMsgContext(CallbackHandler handler, Map<String, String> moduleOptions, MessageInfo messageInfo, Subject clientSubject) { this.handler = handler; if (moduleOptions != null) { this.moduleOptions = unmodifiableMap(moduleOptions); } else { this.moduleOptions = emptyMap(); } this.messageInfo = messageInfo; this.clientSubject = clientSubject; if (messageInfo != null) { this.authParameters = Jaspic.getAuthParameters(getRequest()); } } /** * Checks if the current request is to a protected resource or not. A protected resource * is a resource (e.g. a Servlet, JSF page, JSP page etc) for which a constraint has been defined * in e.g. <code>web.xml<code>. * * @return true if a protected resource was requested, false if a public resource was requested. */ public boolean isProtected() { return Jaspic.isProtectedResource(messageInfo); } public boolean isAnyExplicitAuthCall() { return Jaspic.isExplicitAuthCall(getRequest()); } public boolean isAuthenticationRequest() { return Jaspic.isAuthenticationRequest(getRequest()); } /** * Asks the container to register the given username and roles in order to make * them available to the application for use with {@link HttpServletRequest#isUserInRole(String)} etc. * <p> * This will also ask the runtime to register an authentication session that will live as long as the * HTTP session is valid. * <p> * Note that after this call returned, the authenticated identity will not be immediately active. This * will only take place (should not errors occur) after the {@link ServerAuthContext} or {@link ServerAuthModule} * in which this call takes place return control back to the runtime. * * @param username the user name that will become the caller principal * @param roles the roles associated with the caller principal */ public void registerWithContainer(String username, List<String> roles) { registerWithContainer(username, roles, true); } /** * Asks the container to register the given username and roles in order to make * them available to the application for use with {@link HttpServletRequest#isUserInRole(String)} etc. * <p> * This will optionally (on the basis of the registerSession parameter) ask the runtime to register an * authentication session that will live as long as the HTTP session is valid. * <p> * Note that after this call returned, the authenticated identity will not be immediately active. This * will only take place (should not errors occur) after the {@link ServerAuthContext} or {@link ServerAuthModule} * in which this call takes place return control back to the runtime. * * @param username the user name that will become the caller principal * @param roles the roles associated with the caller principal * @param registerSession if true asks the container to register an authentication setting, if false does not ask this. */ public void registerWithContainer(String username, List<String> roles, boolean registerSession) { // Basic registration of the username and roles with the container notifyContainerAboutLogin(username, roles); // Explicitly set a flag that we did authentication, so code can check that this happened Jaspic.setDidAuthentication((HttpServletRequest) messageInfo.getRequestMessage()); if (registerSession) { Jaspic.setRegisterSession(messageInfo, username, roles); } } /** * Checks if during the current request code has asked the runtime to register an authentication session. * * @return true if code has asked to register an authentication session, false otherwise. */ public boolean isRegisterSession() { return Jaspic.isRegisterSession(messageInfo); } /** * Asks the runtime to register an authentication session. This will automatically remember the logged-in status * as long as the current HTTP session remains valid. Without this being asked, a SAM has to manually re-authenticate * with the runtime at the start of each request. * <p> * Note that the user name and roles being asked is an implementation detail; there is no portable way to have * an auth context read back the user name and roles that were processed by the {@link CallbackHandler}. * * @param username the user name for which authentication should be be remembered * @param roles the roles for which authentication should be remembered. */ public void setRegisterSession(String username, List<String> roles) { Jaspic.setRegisterSession(messageInfo, username, roles); } public void cleanClientSubject() { Jaspic.cleanSubject(clientSubject); } /** * Returns the parameters that were provided with the {@link Jaspic#authenticate(AuthParameters)} call. * * @return the parameters that were provided with the {@link Jaspic#authenticate(AuthParameters)} call, or a default instance. Never null. */ public AuthParameters getAuthParameters() { return authParameters; } /** * Returns the handler that the runtime provided to auth context. * * @return the handler that the runtime provided to auth context. */ public CallbackHandler getHandler() { return handler; } /** * Returns the module options that were set on the auth module to which this context belongs. * * @return the module options that were set on the auth module to which this context belongs. */ public Map<String, String> getModuleOptions() { return moduleOptions; } /** * Returns the named module option that was set on the auth module to which this context belongs. * * @return the named module option that was set on the auth module to which this context belongs, or null if no option with that name was set. */ public String getModuleOption(String key) { return moduleOptions.get(key); } /** * Returns the message info instance for the current request. * * @return the message info instance for the current request. */ public MessageInfo getMessageInfo() { return messageInfo; } /** * Returns the subject for which authentication is to take place. * * @return the subject for which authentication is to take place. */ public Subject getClientSubject() { return clientSubject; } /** * Returns the request object associated with the current request. * * @return the request object associated with the current request. */ public HttpServletRequest getRequest() { return (HttpServletRequest) messageInfo.getRequestMessage(); } /** * Returns the response object associated with the current request. * * @return the response object associated with the current request. */ public HttpServletResponse getResponse() { return (HttpServletResponse) messageInfo.getResponseMessage(); } /** * Sets the response status to 401 (not found). * <p> * As a convenience this method returns SEND_FAILURE, so this method can be used in * one fluent return statement from an auth module. * * @return {@link AuthStatus#SEND_FAILURE} */ public AuthStatus responseUnAuthorized() { try { getResponse().sendError(SC_UNAUTHORIZED); } catch (IOException e) { throw new IllegalStateException(e); } return SEND_FAILURE; } /** * Sets the response status to 404 (not found). * <p> * As a convenience this method returns SEND_FAILURE, so this method can be used in * one fluent return statement from an auth module. * * @return {@link AuthStatus#SEND_FAILURE} */ public AuthStatus responseNotFound() { try { getResponse().sendError(SC_NOT_FOUND); } catch (IOException e) { throw new IllegalStateException(e); } return SEND_FAILURE; } /** * Asks the container to register the given username and roles in order to make * them available to the application for use with {@link HttpServletRequest#isUserInRole(String)} etc. * * <p> * Note that after this call returned, the authenticated identity will not be immediately active. This * will only take place (should not errors occur) after the {@link ServerAuthContext} or {@link ServerAuthModule} * in which this call takes place return control back to the runtime. * * <p> * As a convenience this method returns SUCCESS, so this method can be used in * one fluent return statement from an auth module. * * @param username the user name that will become the caller principal * @param roles the roles associated with the caller principal * @return {@link AuthStatus#SUCCESS} * */ public AuthStatus notifyContainerAboutLogin(String username, List<String> roles) { Jaspic.notifyContainerAboutLogin(clientSubject, handler, username, roles); return SUCCESS; } /** * Instructs the container to "do nothing". * * <p> * This is a somewhat peculiar requirement of JASPIC, which incidentally almost no containers actually require * or enforce. * * <p> * When intending to do nothing, most JASPIC auth modules simply return "SUCCESS", but according to * the JASPIC spec the handler MUST have been used when returning that status. Because of this JASPIC * implicitly defines a "protocol" that must be followed in this case; * invoking the CallerPrincipalCallback handler with a null as the username. * * <p> * As a convenience this method returns SUCCESS, so this method can be used in * one fluent return statement from an auth module. * * @return {@link AuthStatus#SUCCESS} */ public AuthStatus doNothing() { Jaspic.notifyContainerAboutLogin(clientSubject, handler, null, null); return SUCCESS; } }