/**
* Copyright 2005-2016 hdiv.org
*
* 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.hdiv.filter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hdiv.context.RequestContextHolder;
import org.hdiv.session.ISession;
import org.hdiv.util.Constants;
/**
* A wrapper for HTTP servlet response.
*
* @author Gorka Vicente
* @see javax.servlet.http.HttpServletResponseWrapper
* @since HDIV 1.1
*/
public class ResponseWrapper extends HttpServletResponseWrapper {
/**
* Commons Logging instance.
*/
private static final Log log = LogFactory.getLog(ResponseWrapper.class);
/**
* HTTP header to sent cookies
*/
protected static final String SET_COOKIE = "Set-Cookie";
/**
* The set of Cookies associated with this Response.
*/
protected Map<String, SavedCookie> cookies = new HashMap<String, SavedCookie>();
/**
* Confidentiality indicator to know if information is accessible only for those who are authorized.
*/
protected boolean confidentiality;
/**
* Indicates if cookie confidentiality is applied or not. If the value is <code>true</code> cookie values must not be replaced by
* relative values. If it is <code>false</code> they must be replaced by relative values to provide confidentiality.
*/
protected boolean avoidCookiesConfidentiality;
/**
* Request context data.
*/
protected RequestContextHolder requestContext;
/**
* Session object wrapper.
*/
protected ISession session;
/**
* Constructs a response object wrapping the given response.
*
* @param context request context
*/
public ResponseWrapper(final RequestContextHolder context) {
this(context, context.getResponse());
}
/**
* Constructs a response object wrapping the given response.
*
* @param context request context
* @param response response instance
*/
protected ResponseWrapper(final RequestContextHolder context, final HttpServletResponse response) {
super(response);
requestContext = context;
if (log.isDebugEnabled()) {
log.debug("New ResponseWrapper instance.");
}
}
/**
* The default behavior of this method is to return setHeader(String name, String value) on the wrapped response object.
*
* @param name the name of the header
* @param value the header value
* @see javax.servlet.http.HttpServletResponseWrapper#setHeader(java.lang.String, java.lang.String)
*/
@Override
public void setHeader(final String name, final String value) {
String confidentialValue = value;
if (name.equalsIgnoreCase(SET_COOKIE)) {
cookies.clear();
removeCookiesFromSession();
List<String> parseValues = parseCookieString(value);
if (confidentiality && !avoidCookiesConfidentiality) {
confidentialValue = replaceOriginalValues(parseValues, value);
}
}
super.setHeader(name, confidentialValue);
}
/**
* The default behavior of this method is to return addHeader(String name, String value) on the wrapped response object.
*
* @param name the name of the header
* @param value the header value
* @see javax.servlet.http.HttpServletResponseWrapper#addHeader(java.lang.String, java.lang.String)
*/
@Override
public void addHeader(final String name, final String value) {
String confidentialValue = value;
if (name.equalsIgnoreCase(SET_COOKIE)) {
List<String> parseValues = parseCookieString(value);
if (confidentiality && !avoidCookiesConfidentiality) {
confidentialValue = replaceOriginalValues(parseValues, value);
}
}
super.addHeader(name, confidentialValue);
}
/**
* Replaces cookies' original values by relative values in order to provide confidentiality.
*
* @param values List of the original values to be replaced
* @param value Original value of the cookie to be added
* @return Confidential values for the cookies
*/
protected String replaceOriginalValues(final List<String> values, String value) {
for (String currentValue : values) {
value = value.replaceFirst("=" + currentValue, "=0");
}
return value;
}
/**
* Resets the response.
*/
@Override
public void reset() {
super.reset();
cookies.clear();
removeCookiesFromSession();
}
/**
* Parses an http cookie request header and append a keyword/value pair to <code>cookies</code> map.
*
* @param cookieString value assigned to Set-Cookie attribute
* @return Cookie list
*/
protected List<String> parseCookieString(String cookieString) {
final List<String> values = new ArrayList<String>();
cookieString = cookieString.trim();
// Cookie fields are separated by ';'
StringTokenizer tokens = new StringTokenizer(cookieString, ";");
while (tokens.hasMoreTokens()) {
// field name is separated from value by '='
StringTokenizer t = new StringTokenizer(tokens.nextToken(), "=");
String name = t.nextToken().trim();
if (t.hasMoreTokens()) {
String value = t.nextToken().trim();
cookies.put(name, new SavedCookie(name, value));
values.add(value);
}
}
updateSessionCookies();
return values;
}
/**
* Adds the specified cookie to the response. It can be called multiple times to set more than one cookie.
*
* @param cookie The <code>Cookie</code> to return to the client
* @see javax.servlet.http.HttpServletResponse#addCookie
*/
@Override
public void addCookie(final Cookie cookie) {
cookies.put(cookie.getName(), new SavedCookie(cookie));
updateSessionCookies();
if (confidentiality && !avoidCookiesConfidentiality) {
cookie.setValue("0");
}
super.addCookie(cookie);
}
/**
* It updates cookies stored in the user's session with the wrapper's cookies.
*/
@SuppressWarnings("unchecked")
protected void updateSessionCookies() {
Map<String, SavedCookie> sessionOriginalCookies = session.getAttribute(requestContext, Constants.HDIV_COOKIES_KEY, Map.class);
if (sessionOriginalCookies != null && sessionOriginalCookies.size() > 0) {
sessionOriginalCookies.putAll(cookies);
session.setAttribute(requestContext, Constants.HDIV_COOKIES_KEY, sessionOriginalCookies);
}
else {
session.setAttribute(requestContext, Constants.HDIV_COOKIES_KEY, cookies);
}
}
/**
* Removes from user's session the cookies added by the application.
*/
protected void removeCookiesFromSession() {
session.removeAttribute(requestContext, Constants.HDIV_COOKIES_KEY);
}
/**
* Obtains all the cookies added by the application.
*
* @return cookies added by the application
*/
public Map<String, SavedCookie> getCookies() {
return cookies;
}
/**
* @param confidentiality the confidentiality to set
*/
public void setConfidentiality(final boolean confidentiality) {
this.confidentiality = confidentiality;
}
/**
* @param avoidCookiesConfidentiality the avoidCookiesConfidentiality to set
*/
public void setAvoidCookiesConfidentiality(final boolean avoidCookiesConfidentiality) {
this.avoidCookiesConfidentiality = avoidCookiesConfidentiality;
}
/**
* @param session the session to set
*/
public void setSession(final ISession session) {
this.session = session;
}
}