/*
 * Copyright 2002-2016 the original author or authors.
 *
 * 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.springframework.security.web.header.writers;

import org.springframework.security.web.header.HeaderWriter;
import org.springframework.util.Assert;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * <p>
 * Provides support for <a href="https://www.w3.org/TR/CSP2/">Content Security Policy (CSP) Level 2</a>.
 * </p>
 *
 * <p>
 * CSP provides a mechanism for web applications to mitigate content injection vulnerabilities,
 * such as cross-site scripting (XSS). CSP is a declarative policy that allows web application authors to inform
 * the client (user-agent) about the sources from which the application expects to load resources.
 * </p>
 *
 * <p>
 * For example, a web application can declare that it only expects to load script from specific, trusted sources.
 * This declaration allows the client to detect and block malicious scripts injected into the application by an attacker.
 * </p>
 *
 * <p>
 * A declaration of a security policy contains a set of security policy directives (for example, script-src and object-src),
 * each responsible for declaring the restrictions for a particular resource type.
 * The list of directives defined can be found at
 * <a href="https://www.w3.org/TR/CSP2/#directives">Directives</a>.
 * </p>
 *
 * <p>
 * Each directive has a name and value. For detailed syntax on writing security policies, see
 * <a href="https://www.w3.org/TR/CSP2/#syntax-and-algorithms">Syntax and Algorithms</a>.
 * </p>
 *
 * <p>
 * This implementation of {@link HeaderWriter} writes one of the following headers:
 * </p>
 * <ul>
 * 	<li>Content-Security-Policy</li>
 * 	<li>Content-Security-Policy-Report-Only</li>
 * </ul>
 *
 * <p>
 * By default, the Content-Security-Policy header is included in the response.
 * However, calling {@link #setReportOnly(boolean)} with {@code true} will include the
 * Content-Security-Policy-Report-Only header in the response.
 * <strong>NOTE:</strong> The supplied security policy directive(s) will be used for whichever header is enabled (included).
 * </p>
 *
 * <p>
 * <strong>
 * CSP is not intended as a first line of defense against content injection vulnerabilities.
 * Instead, CSP is used to reduce the harm caused by content injection attacks.
 * As a first line of defense against content injection, web application authors should validate their input and encode their output.
 * </strong>
 * </p>
 *
 * @author Joe Grandja
 * @since 4.1
 */
public final class ContentSecurityPolicyHeaderWriter implements HeaderWriter {
	private static final String CONTENT_SECURITY_POLICY_HEADER = "Content-Security-Policy";

	private static final String CONTENT_SECURITY_POLICY_REPORT_ONLY_HEADER = "Content-Security-Policy-Report-Only";

	private String policyDirectives;

	private boolean reportOnly;

	/**
	 * Creates a new instance
	 *
	 * @param policyDirectives maps to {@link #setPolicyDirectives(String)}
	 * @throws IllegalArgumentException if policyDirectives is null or empty
	 */
	public ContentSecurityPolicyHeaderWriter(String policyDirectives) {
		setPolicyDirectives(policyDirectives);
		this.reportOnly = false;
	}

	/**
	 * @see org.springframework.security.web.header.HeaderWriter#writeHeaders(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
	 */
	@Override
	public void writeHeaders(HttpServletRequest request, HttpServletResponse response) {
		response.setHeader((!reportOnly ? CONTENT_SECURITY_POLICY_HEADER : CONTENT_SECURITY_POLICY_REPORT_ONLY_HEADER), policyDirectives);
	}

	/**
	 * Sets the security policy directive(s) to be used in the response header.
	 *
	 * @param policyDirectives the security policy directive(s)
	 * @throws IllegalArgumentException if policyDirectives is null or empty
	 */
	public void setPolicyDirectives(String policyDirectives) {
		Assert.hasLength(policyDirectives, "policyDirectives cannot be null or empty");
		this.policyDirectives = policyDirectives;
	}

	/**
	 * If true, includes the Content-Security-Policy-Report-Only header in the response,
	 * otherwise, defaults to the Content-Security-Policy header.

	 * @param reportOnly set to true for reporting policy violations only
	 */
	public void setReportOnly(boolean reportOnly) {
		this.reportOnly = reportOnly;
	}

	@Override
	public String toString() {
		return getClass().getName() + " [policyDirectives=" + policyDirectives + "; reportOnly=" + reportOnly + "]";
	}

}
