/**
* 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.config;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hdiv.context.RequestContextHolder;
import org.hdiv.regex.PatternMatcher;
import org.hdiv.regex.PatternMatcherFactory;
import org.hdiv.state.IPage;
import org.hdiv.state.scope.StateScopeType;
import org.hdiv.util.Method;
import org.hdiv.validator.EditableDataValidationProvider;
/**
* Class containing HDIV configuration initialized from Spring Factory.
*
* @author Roberto Velasco
* @author Gorka Vicente
* @author Gotzon Illarramendi
*/
public class HDIVConfig implements Serializable {
private static final long serialVersionUID = 1L;
private static final Log log = LogFactory.getLog(HDIVConfig.class);
private static final String DEFAULT_STATE_PARAMETER_NAME = "_HDIV_STATE_";
private static final String DEFAULT_MODIFY_STATE_PARAMETER_NAME = "_MODIFY_HDIV_STATE_";
/**
* Regular expression executor factory.
*
* @since 2.1.6
*/
protected transient PatternMatcherFactory patternMatcherFactory;
/**
* List with the pages that will not be Treated by the HDIV filter. The init pages are initialized by the Spring factory.
*/
protected StartPage[] startPages = new StartPage[0];
/**
* List with the parameters that will not be validated by the HDIV filter. The init parameters are initialized by the Spring factory.
*/
protected List<PatternMatcher> startParameters = new ArrayList<PatternMatcher>();
/**
* Url of the error page to which HDIV will redirect the request if it doesn't pass the HDIV validation.
*/
protected String errorPage;
/**
* Url of the error page to which HDIV will redirect the request if it doesn't pass the HDIV validation caused by session expiration and
* the user is not logged in the application.
*/
protected String sessionExpiredLoginPage;
/**
* Url of the error page to which HDIV will redirect the request if it doesn't pass the HDIV validation caused by session expiration and
* the user is logged in the application.
*/
protected String sessionExpiredHomePage;
/**
* Confidentiality indicator to know if information is accessible only for those who are authorized.
*/
protected boolean confidentiality = true;
/**
* Parameters which HDIV validation will not be applied to.
*/
protected Map<PatternMatcher, List<PatternMatcher>> paramsWithoutValidation;
/**
* Validation provider for editable fields (text/textarea).
*/
protected EditableDataValidationProvider editableDataValidationProvider;
/**
* If <code>avoidCookiesIntegrity</code> is true, cookie integrity will not be applied.
*/
protected boolean avoidCookiesIntegrity = false;
/**
* If <code>avoidCookiesConfidentiality</code> is true, cookie confidentiality will not be applied.
*/
protected boolean avoidCookiesConfidentiality = false;
/**
* if <code>avoidValidationInUrlsWithoutParams</code> is true, HDIV validation will not be applied in urls without parameters.
*
* @since HDIV 2.1.0
*/
protected boolean avoidValidationInUrlsWithoutParams = false;
/**
* Extensions that we have to protect with HDIV's state.
*
* @since HDIV 2.0
*/
protected List<PatternMatcher> protectedURLPatterns;
/**
* Extensions that we have not to protect with HDIV's state.
*
* @since HDIV 2.1.0
*/
protected List<String> excludedURLExtensions;
/**
* HDIV adds an extra parameter to all links and forms. By default this parameter is _HDIV_STATE_. If <code>randomName</code> is true a
* random name is generated instead of default name (_HDIV_STATE_)
*
* @since HDIV 2.1.0
*/
protected boolean randomName = false;
/**
* Name of the parameter that contains state identification. Default value is '_HDIV_STATE_'.
*/
protected String stateParameterName = DEFAULT_STATE_PARAMETER_NAME;
/**
* Name of the parameter that contains state identification to modify. Used for ajax requests.
*/
protected String modifyStateParameterName = DEFAULT_MODIFY_STATE_PARAMETER_NAME;
/**
* If debug mode is enabled, the attacks are logged but the requests are not stopped.
*
* @since HDIV 2.1.1
*/
protected boolean debugMode = false;
/**
* Show error page on request with editable validation errors.
*
* @since 2.1.4
*/
protected boolean showErrorPageOnEditableValidation = false;
/**
* Reuse previous {@link IPage} when an AJAX request is received and don't create a new one.
*
* @since 2.1.7
*/
protected boolean reuseExistingPageInAjaxRequest = false;
/**
* Pages whose link and forms never expire.
*
* @since 2.1.7
*/
protected Map<PatternMatcher, String> longLivingPages = new HashMap<PatternMatcher, String>();
/**
* True if URLs should be obfuscated
*/
private boolean urlObfuscation = false;
@Deprecated
public void setStrategy(final Strategy strategy) {
}
/**
* Checks if <code>parameter</code> is an init parameter, in which case it will not be treated by HDIV.
*
* @param parameter Parameter name
* @return True if <code>parameter</code> is an init parameter. False otherwise.
*/
public boolean isStartParameter(final String parameter) {
for (PatternMatcher matcher : startParameters) {
if (matcher.matches(parameter)) {
return true;
}
}
return false;
}
private void addStartPage(final StartPage startPage) {
if (log.isDebugEnabled()) {
log.debug("Added a StartPage: " + startPage);
}
List<StartPage> pages = new ArrayList<StartPage>(Arrays.asList(startPages));
pages.add(startPage);
startPages = pages.toArray(new StartPage[pages.size()]);
}
/**
* Checks if <code>target</code> is an init action, in which case it will not be treated by HDIV.
*
* @param target target name
* @param method request method (get,post...)
* @return True if <code>target</code> is an init action. False otherwise.
*/
public boolean isStartPage(final String target, final Method method) {
for (int i = 0; i < startPages.length; i++) {
StartPage startPage = startPages[i];
PatternMatcher m = startPage.compiledPattern;
if (m.matches(target) && (startPage.isAnyMethod() || startPage.method == method)) {
return true;
}
}
return false;
}
public boolean hasExtensionToExclude(String path) {
if (excludedURLExtensions == null) {
return false;
}
int pos = path.indexOf('?');
if (pos > 0) {
path = path.substring(0, pos);
}
if (path.length() == 0) {
return false;
}
if (path.charAt(path.length() - 1) == '/') {
return false;
}
int pound = path.indexOf('#');
if (pound >= 0) {
path = path.substring(0, pound);
}
int size = excludedURLExtensions.size();
for (int i = 0; i < size; i++) {
if (path.endsWith(excludedURLExtensions.get(i))) {
return true;
}
}
return false;
}
/**
* Check if the parameter needs confidentiality.
*
* @param context request context
* @param paramName parameter name to check
* @return boolean
*/
public boolean isParameterWithoutConfidentiality(final RequestContextHolder context, final String paramName) {
String modifyHdivStateParameterName = context.getHdivModifyParameterName();
if (modifyHdivStateParameterName != null && modifyHdivStateParameterName.equals(paramName)) {
return true;
}
return false;
}
/**
* Checks if the parameter <code>parameter</code> is defined by the user as a no required validation parameter for the action
* <code>action</code>.
*
* @param action action name
* @param parameter parameter name
* @return True if it is parameter that needs no validation. False otherwise.
*/
public boolean isParameterWithoutValidation(final String action, final String parameter) {
if (action == null) {
return false;
}
if (paramsWithoutValidation == null) {
return false;
}
for (Entry<PatternMatcher, List<PatternMatcher>> entry : paramsWithoutValidation.entrySet()) {
if (entry.getKey().matches(action)) {
for (PatternMatcher paramMatcher : entry.getValue()) {
if (paramMatcher.matches(parameter)) {
return true;
}
}
}
}
return false;
}
/**
* Calculates if the provided url path is configured as a long-living pages.
*
* @param url url path
* @return Scope name or null if it is not a long-living page
*/
public StateScopeType isLongLivingPages(final String url) {
for (Map.Entry<PatternMatcher, String> page : longLivingPages.entrySet()) {
PatternMatcher m = page.getKey();
if (m.matches(url)) {
return StateScopeType.byName(page.getValue());
}
}
return null;
}
/**
* Checks if the HDIV validation must be applied to the parameter <code>parameter</code>
*
* @param parameter Parameter name
* @param hdivParameter Name of the parameter that HDIV will include in the requests or/and forms which contains the state identifier
* parameter
* @return True if <code>parameter</code> doesn't need HDIV validation.
*/
public boolean needValidation(final String parameter, final String hdivParameter) {
if (parameter.equals(hdivParameter) || isStartParameter(parameter)) {
return false;
}
return true;
}
public String getErrorPage() {
return errorPage;
}
public void setErrorPage(String errorPage) {
if (errorPage != null && !errorPage.startsWith("/")) {
errorPage = "/" + errorPage;
}
this.errorPage = errorPage;
if (errorPage != null) {
PatternMatcher matcher = patternMatcherFactory.getPatternMatcher(errorPage);
addStartPage(new StartPage((Method) null, matcher));
}
}
public String getSessionExpiredLoginPage() {
return sessionExpiredLoginPage;
}
public void setSessionExpiredLoginPage(String sessionExpiredLoginPage) {
if (sessionExpiredLoginPage != null && !sessionExpiredLoginPage.startsWith("/")) {
sessionExpiredLoginPage = "/" + sessionExpiredLoginPage;
}
this.sessionExpiredLoginPage = sessionExpiredLoginPage;
if (sessionExpiredLoginPage != null) {
PatternMatcher matcher = patternMatcherFactory.getPatternMatcher(sessionExpiredLoginPage);
addStartPage(new StartPage((Method) null, matcher));
}
}
public String getSessionExpiredHomePage() {
return sessionExpiredHomePage;
}
public void setSessionExpiredHomePage(String sessionExpiredHomePage) {
if (sessionExpiredHomePage != null && !sessionExpiredHomePage.startsWith("/")) {
sessionExpiredHomePage = "/" + sessionExpiredHomePage;
}
this.sessionExpiredHomePage = sessionExpiredHomePage;
if (sessionExpiredHomePage != null) {
PatternMatcher matcher = patternMatcherFactory.getPatternMatcher(sessionExpiredHomePage);
addStartPage(new StartPage((Method) null, matcher));
}
}
public boolean getConfidentiality() {
return confidentiality;
}
public void setConfidentiality(final boolean confidentiality) {
this.confidentiality = confidentiality;
}
public void setParamsWithoutValidation(final Map<String, List<String>> paramsWithoutValidation) {
this.paramsWithoutValidation = new HashMap<PatternMatcher, List<PatternMatcher>>();
for (Entry<String, List<String>> entry : paramsWithoutValidation.entrySet()) {
PatternMatcher matcher = patternMatcherFactory.getPatternMatcher(entry.getKey());
List<PatternMatcher> paramMatchers = new ArrayList<PatternMatcher>();
for (String param : entry.getValue()) {
PatternMatcher paramMatcher = patternMatcherFactory.getPatternMatcher(param);
paramMatchers.add(paramMatcher);
}
this.paramsWithoutValidation.put(matcher, paramMatchers);
}
}
/**
* It creates a map from the list of start pages defined by the user.
*
* @param userStartPages list of start pages defined by the user
*/
public void setUserStartPages(final List<StartPage> userStartPages) {
for (StartPage startPage : userStartPages) {
PatternMatcher matcher = patternMatcherFactory.getPatternMatcher(startPage.getPattern());
startPage.setCompiledPattern(matcher);
addStartPage(startPage);
}
}
/**
* It creates a map from the list of init parameters defined by the user.
*
* @param userStartParameters list of init parameters defined by the user
*/
public void setUserStartParameters(final List<String> userStartParameters) {
for (String useStartParameter : userStartParameters) {
startParameters.add(patternMatcherFactory.getPatternMatcher(useStartParameter));
}
}
/**
* @return Returns true if cookies' confidentiality is activated.
*/
public boolean isCookiesConfidentialityActivated() {
return !avoidCookiesConfidentiality;
}
/**
* @param avoidCookiesConfidentiality the avoidCookiesConfidentiality to set
*/
public void setAvoidCookiesConfidentiality(final boolean avoidCookiesConfidentiality) {
this.avoidCookiesConfidentiality = avoidCookiesConfidentiality;
}
/**
* @return Returns true if cookies' integrity is activated.
*/
public boolean isCookiesIntegrityActivated() {
return !avoidCookiesIntegrity;
}
/**
* @param avoidCookiesIntegrity the avoidCookiesIntegrity to set
*/
public void setAvoidCookiesIntegrity(final boolean avoidCookiesIntegrity) {
this.avoidCookiesIntegrity = avoidCookiesIntegrity;
}
/**
* @return Returns true if validation in urls without parameters is activated.
*/
public boolean isValidationInUrlsWithoutParamsActivated() {
return !avoidValidationInUrlsWithoutParams;
}
/**
* @param avoidValidationInUrlsWithoutParams The avoidValidationInUrlsWithoutParams to set.
*/
public void setAvoidValidationInUrlsWithoutParams(final Boolean avoidValidationInUrlsWithoutParams) {
this.avoidValidationInUrlsWithoutParams = avoidValidationInUrlsWithoutParams.booleanValue();
}
/**
* @param protectedExtensions The protected extensions to set.
* @since HDIV 2.0
*/
public void setProtectedExtensions(final List<String> protectedExtensions) {
protectedURLPatterns = new ArrayList<PatternMatcher>();
for (String protectedExtension : protectedExtensions) {
protectedURLPatterns.add(patternMatcherFactory.getPatternMatcher(protectedExtension));
}
}
public void setExcludedExtensions(final List<String> excludedExtensions) {
if (excludedURLExtensions == null) {
excludedURLExtensions = new ArrayList<String>();
}
excludedURLExtensions.addAll(excludedExtensions);
}
/**
* @return Returns the protected extensions.
* @since HDIV 2.0
*/
public List<PatternMatcher> getProtectedURLPatterns() {
return protectedURLPatterns;
}
/**
* @return Returns the excluded extensions.
* @since HDIV 2.1.0
*/
public List<String> getExcludedURLExtensions() {
return excludedURLExtensions;
}
/**
* @return the randomName
*/
public boolean isRandomName() {
return randomName;
}
/**
* @param randomName the randomName to set
*/
public void setRandomName(final boolean randomName) {
this.randomName = randomName;
}
@Deprecated
public Strategy getStrategy() {
return Strategy.MEMORY;
}
/**
* @return the debugMode
*/
public boolean isDebugMode() {
return debugMode;
}
/**
* @param debugMode the debugMode to set
*/
public void setDebugMode(final boolean debugMode) {
this.debugMode = debugMode;
}
/**
* @return the showErrorPageOnEditableValidation
*/
public boolean isShowErrorPageOnEditableValidation() {
return showErrorPageOnEditableValidation;
}
/**
* @param showErrorPageOnEditableValidation the showErrorPageOnEditableValidation to set
*/
public void setShowErrorPageOnEditableValidation(final boolean showErrorPageOnEditableValidation) {
this.showErrorPageOnEditableValidation = showErrorPageOnEditableValidation;
}
/**
* @return the reuseExistingPageInAjaxRequest
*/
public boolean isReuseExistingPageInAjaxRequest() {
return reuseExistingPageInAjaxRequest;
}
/**
* @param reuseExistingPageInAjaxRequest the reuseExistingPageInAjaxRequest to set
*/
public void setReuseExistingPageInAjaxRequest(final boolean reuseExistingPageInAjaxRequest) {
this.reuseExistingPageInAjaxRequest = reuseExistingPageInAjaxRequest;
}
/**
* @param patternMatcherFactory the patternMatcherFactory to set
*/
public void setPatternMatcherFactory(final PatternMatcherFactory patternMatcherFactory) {
this.patternMatcherFactory = patternMatcherFactory;
}
/**
* @return the stateParameterName
*/
public String getStateParameterName() {
return stateParameterName;
}
/**
* @param stateParameterName the stateParameterName to set
*/
public void setStateParameterName(final String stateParameterName) {
this.stateParameterName = stateParameterName;
}
/**
* @return the modifyStateParameterName
*/
public String getModifyStateParameterName() {
return modifyStateParameterName;
}
/**
* @param modifyStateParameterName the modifyStateParameterName to set
*/
public void setModifyStateParameterName(final String modifyStateParameterName) {
this.modifyStateParameterName = modifyStateParameterName;
}
/**
* @param longLivingPages the longLivingPages to set
*/
public void setLongLivingPages(final Map<String, String> longLivingPages) {
for (Map.Entry<String, String> page : longLivingPages.entrySet()) {
PatternMatcher pattern = patternMatcherFactory.getPatternMatcher(page.getKey());
String scope = page.getValue();
this.longLivingPages.put(pattern, scope);
}
}
/**
* @param editableDataValidationProvider the editableDataValidationProvider to set
*/
public void setEditableDataValidationProvider(final EditableDataValidationProvider editableDataValidationProvider) {
this.editableDataValidationProvider = editableDataValidationProvider;
}
/**
* @return the editableDataValidationProvider
*/
public EditableDataValidationProvider getEditableDataValidationProvider() {
return editableDataValidationProvider;
}
public boolean isUrlObfuscation() {
return urlObfuscation;
}
public void setUrlObfuscation(final boolean urlObfuscation) {
this.urlObfuscation = urlObfuscation;
}
@Override
public String toString() {
StringBuilder result = new StringBuilder().append("");
result = result.append(" Confidentiality=").append(getConfidentiality());
result.append(" avoidCookiesIntegrity=").append(avoidCookiesIntegrity);
result.append(" avoidCookiesConfidentiality=").append(avoidCookiesConfidentiality);
result.append(" avoidValidationInUrlsWithoutParams=").append(avoidValidationInUrlsWithoutParams);
result.append(" strategy=").append(getStrategy());
result.append(" randomName=").append(isRandomName());
result.append(" errorPage=").append(getErrorPage());
result.append(" sessionExpiredLoginPage=").append(sessionExpiredLoginPage);
result.append(" sessionExpiredHomePage=").append(sessionExpiredHomePage);
result.append(" excludedExtensions=").append(excludedURLExtensions);
result.append(" protectedExtensions=").append(getProtectedURLPatterns());
result.append(" startPages=").append(startPages);
result.append(" startParameters=").append(startParameters);
result.append(" paramsWithoutValidation=").append(paramsWithoutValidation);
result.append(" longLivingPages=").append(longLivingPages);
result.append(" debugMode=").append(debugMode);
result.append(" showErrorPageOnEditableValidation=").append(showErrorPageOnEditableValidation);
return result.toString();
}
}