/*
 * Copyright 2002-2018 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
 *
 *      https://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.config.annotation.web.configurers;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;

import javax.servlet.http.HttpSession;

import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.CookieClearingLogoutHandler;
import org.springframework.security.web.authentication.logout.DelegatingLogoutSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;

/**
 * Adds logout support. Other {@link SecurityConfigurer} instances may invoke
 * {@link #addLogoutHandler(LogoutHandler)} in the {@link #init(HttpSecurityBuilder)} phase.
 *
 *
Security Filters

 *
 * The following Filters are populated
 *
 *

 *

 * {@link LogoutFilter}

 *

 *
 *
Shared Objects Created

 *
 * No shared Objects are created
 *
 *
Shared Objects Used

 *
 * No shared objects are used.
 *
 * @author Rob Winch
 * @since 3.2
 * @see RememberMeConfigurer
 */
public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>> extends
		AbstractHttpConfigurer<LogoutConfigurer<H>, H> {
	private List<LogoutHandler> logoutHandlers = new ArrayList<>();
	private SecurityContextLogoutHandler contextLogoutHandler = new SecurityContextLogoutHandler();
	private String logoutSuccessUrl = "/login?logout";
	private LogoutSuccessHandler logoutSuccessHandler;
	private String logoutUrl = "/logout";
	private RequestMatcher logoutRequestMatcher;
	private boolean permitAll;
	private boolean customLogoutSuccess;

	private LinkedHashMap<RequestMatcher, LogoutSuccessHandler> defaultLogoutSuccessHandlerMappings =
			new LinkedHashMap<>();

	/**
	 * Creates a new instance
	 * @see HttpSecurity#logout()
	 */
	public LogoutConfigurer() {
	}

	/**
	 * Adds a {@link LogoutHandler}. The {@link SecurityContextLogoutHandler} is added as
	 * the last {@link LogoutHandler} by default.
	 *
	 * @param logoutHandler the {@link LogoutHandler} to add
	 * @return the {@link LogoutConfigurer} for further customization
	 */
	public LogoutConfigurer<H> addLogoutHandler(LogoutHandler logoutHandler) {
		Assert.notNull(logoutHandler, "logoutHandler cannot be null");
		this.logoutHandlers.add(logoutHandler);
		return this;
	}

	/**
	 * Specifies if {@link SecurityContextLogoutHandler} should clear the {@link Authentication} at the time of logout.
	 * @param clearAuthentication true {@link SecurityContextLogoutHandler} should clear the {@link Authentication} (default), or false otherwise.
	 * @return the {@link LogoutConfigurer} for further customization
	 */
	public LogoutConfigurer<H> clearAuthentication(boolean clearAuthentication) {
		contextLogoutHandler.setClearAuthentication(clearAuthentication);
		return this;
	}

	/**
	 * Configures {@link SecurityContextLogoutHandler} to invalidate the
	 * {@link HttpSession} at the time of logout.
	 * @param invalidateHttpSession true if the {@link HttpSession} should be invalidated
	 * (default), or false otherwise.
	 * @return the {@link LogoutConfigurer} for further customization
	 */
	public LogoutConfigurer<H> invalidateHttpSession(boolean invalidateHttpSession) {
		contextLogoutHandler.setInvalidateHttpSession(invalidateHttpSession);
		return this;
	}

	/**
	 * The URL that triggers log out to occur (default is "/logout"). If CSRF protection
	 * is enabled (default), then the request must also be a POST. This means that by
	 * default POST "/logout" is required to trigger a log out. If CSRF protection is
	 * disabled, then any HTTP method is allowed.
	 *
	 *

	 * It is considered best practice to use an HTTP POST on any action that changes state
	 * (i.e. log out) to protect against CSRF attacks. If
	 * you really want to use an HTTP GET, you can use
	 * logoutRequestMatcher(new AntPathRequestMatcher(logoutUrl, "GET"));
	 *

	 *
	 * @see #logoutRequestMatcher(RequestMatcher)
	 * @see HttpSecurity#csrf()
	 *
	 * @param logoutUrl the URL that will invoke logout.
	 * @return the {@link LogoutConfigurer} for further customization
	 */
	public LogoutConfigurer<H> logoutUrl(String logoutUrl) {
		this.logoutRequestMatcher = null;
		this.logoutUrl = logoutUrl;
		return this;
	}

	/**
	 * The RequestMatcher that triggers log out to occur. In most circumstances users will
	 * use {@link #logoutUrl(String)} which helps enforce good practices.
	 *
	 * @see #logoutUrl(String)
	 *
	 * @param logoutRequestMatcher the RequestMatcher used to determine if logout should
	 * occur.
	 * @return the {@link LogoutConfigurer} for further customization
	 */
	public LogoutConfigurer<H> logoutRequestMatcher(RequestMatcher logoutRequestMatcher) {
		this.logoutRequestMatcher = logoutRequestMatcher;
		return this;
	}

	/**
	 * The URL to redirect to after logout has occurred. The default is "/login?logout".
	 * This is a shortcut for invoking {@link #logoutSuccessHandler(LogoutSuccessHandler)}
	 * with a {@link SimpleUrlLogoutSuccessHandler}.
	 *
	 * @param logoutSuccessUrl the URL to redirect to after logout occurred
	 * @return the {@link LogoutConfigurer} for further customization
	 */
	public LogoutConfigurer<H> logoutSuccessUrl(String logoutSuccessUrl) {
		this.customLogoutSuccess = true;
		this.logoutSuccessUrl = logoutSuccessUrl;
		return this;
	}

	/**
	 * A shortcut for {@link #permitAll(boolean)} with true as an argument.
	 * @return the {@link LogoutConfigurer} for further customizations
	 */
	public LogoutConfigurer<H> permitAll() {
		return permitAll(true);
	}

	/**
	 * Allows specifying the names of cookies to be removed on logout success. This is a
	 * shortcut to easily invoke {@link #addLogoutHandler(LogoutHandler)} with a
	 * {@link CookieClearingLogoutHandler}.
	 *
	 * @param cookieNamesToClear the names of cookies to be removed on logout success.
	 * @return the {@link LogoutConfigurer} for further customization
	 */
	public LogoutConfigurer<H> deleteCookies(String... cookieNamesToClear) {
		return addLogoutHandler(new CookieClearingLogoutHandler(cookieNamesToClear));
	}

	/**
	 * Sets the {@link LogoutSuccessHandler} to use. If this is specified,
	 * {@link #logoutSuccessUrl(String)} is ignored.
	 *
	 * @param logoutSuccessHandler the {@link LogoutSuccessHandler} to use after a user
	 * has been logged out.
	 * @return the {@link LogoutConfigurer} for further customizations
	 */
	public LogoutConfigurer<H> logoutSuccessHandler(
			LogoutSuccessHandler logoutSuccessHandler) {
		this.logoutSuccessUrl = null;
		this.customLogoutSuccess = true;
		this.logoutSuccessHandler = logoutSuccessHandler;
		return this;
	}

	/**
	 * Sets a default {@link LogoutSuccessHandler} to be used which prefers being invoked
	 * for the provided {@link RequestMatcher}. If no {@link LogoutSuccessHandler} is
	 * specified a {@link SimpleUrlLogoutSuccessHandler} will be used.
	 * If any default {@link LogoutSuccessHandler} instances are configured, then a
	 * {@link DelegatingLogoutSuccessHandler} will be used that defaults to a
	 * {@link SimpleUrlLogoutSuccessHandler}.
	 *
	 * @param handler the {@link LogoutSuccessHandler} to use
	 * @param preferredMatcher the {@link RequestMatcher} for this default
	 * {@link LogoutSuccessHandler}
	 * @return the {@link LogoutConfigurer} for further customizations
	 */
	public LogoutConfigurer<H> defaultLogoutSuccessHandlerFor(
			LogoutSuccessHandler handler, RequestMatcher preferredMatcher) {
		Assert.notNull(handler, "handler cannot be null");
		Assert.notNull(preferredMatcher, "preferredMatcher cannot be null");
		this.defaultLogoutSuccessHandlerMappings.put(preferredMatcher, handler);
		return this;
	}

	/**
	 * Grants access to the {@link #logoutSuccessUrl(String)} and the
	 * {@link #logoutUrl(String)} for every user.
	 *
	 * @param permitAll if true grants access, else nothing is done
	 * @return the {@link LogoutConfigurer} for further customization.
	 */
	public LogoutConfigurer<H> permitAll(boolean permitAll) {
		this.permitAll = permitAll;
		return this;
	}

	/**
	 * Gets the {@link LogoutSuccessHandler} if not null, otherwise creates a new
	 * {@link SimpleUrlLogoutSuccessHandler} using the {@link #logoutSuccessUrl(String)}.
	 *
	 * @return the {@link LogoutSuccessHandler} to use
	 */
	private LogoutSuccessHandler getLogoutSuccessHandler() {
		LogoutSuccessHandler handler = this.logoutSuccessHandler;
		if (handler == null) {
			handler = createDefaultSuccessHandler();
		}
		return handler;
	}

	private LogoutSuccessHandler createDefaultSuccessHandler() {
		SimpleUrlLogoutSuccessHandler urlLogoutHandler = new SimpleUrlLogoutSuccessHandler();
		urlLogoutHandler.setDefaultTargetUrl(logoutSuccessUrl);
		if (defaultLogoutSuccessHandlerMappings.isEmpty()) {
			return urlLogoutHandler;
		}
		DelegatingLogoutSuccessHandler successHandler = new DelegatingLogoutSuccessHandler(defaultLogoutSuccessHandlerMappings);
		successHandler.setDefaultLogoutSuccessHandler(urlLogoutHandler);
		return successHandler;
	}

	@Override
	public void init(H http) throws Exception {
		if (permitAll) {
			PermitAllSupport.permitAll(http, this.logoutSuccessUrl);
			PermitAllSupport.permitAll(http, this.getLogoutRequestMatcher(http));
		}

		DefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http
				.getSharedObject(DefaultLoginPageGeneratingFilter.class);
		if (loginPageGeneratingFilter != null && !isCustomLogoutSuccess()) {
			loginPageGeneratingFilter.setLogoutSuccessUrl(getLogoutSuccessUrl());
		}
	}

	@Override
	public void configure(H http) throws Exception {
		LogoutFilter logoutFilter = createLogoutFilter(http);
		http.addFilter(logoutFilter);
	}

	/**
	 * Returns true if the logout success has been customized via
	 * {@link #logoutSuccessUrl(String)} or
	 * {@link #logoutSuccessHandler(LogoutSuccessHandler)}.
	 *
	 * @return true if logout success handling has been customized, else false
	 */
	boolean isCustomLogoutSuccess() {
		return customLogoutSuccess;
	}

	/**
	 * Gets the logoutSuccesUrl or null if a
	 * {@link #logoutSuccessHandler(LogoutSuccessHandler)} was configured.
	 *
	 * @return the logoutSuccessUrl
	 */
	private String getLogoutSuccessUrl() {
		return logoutSuccessUrl;
	}

	/**
	 * Gets the {@link LogoutHandler} instances that will be used.
	 * @return the {@link LogoutHandler} instances. Cannot be null.
	 */
	List<LogoutHandler> getLogoutHandlers() {
		return logoutHandlers;
	}

	/**
	 * Creates the {@link LogoutFilter} using the {@link LogoutHandler} instances, the
	 * {@link #logoutSuccessHandler(LogoutSuccessHandler)} and the
	 * {@link #logoutUrl(String)}.
	 *
	 * @param http the builder to use
	 * @return the {@link LogoutFilter} to use.
	 * @throws Exception
	 */
	private LogoutFilter createLogoutFilter(H http) throws Exception {
		logoutHandlers.add(contextLogoutHandler);
		LogoutHandler[] handlers = logoutHandlers
				.toArray(new LogoutHandler[logoutHandlers.size()]);
		LogoutFilter result = new LogoutFilter(getLogoutSuccessHandler(), handlers);
		result.setLogoutRequestMatcher(getLogoutRequestMatcher(http));
		result = postProcess(result);
		return result;
	}

	@SuppressWarnings("unchecked")
	private RequestMatcher getLogoutRequestMatcher(H http) {
		if (logoutRequestMatcher != null) {
			return logoutRequestMatcher;
		}
		if (http.getConfigurer(CsrfConfigurer.class) != null) {
			this.logoutRequestMatcher = new AntPathRequestMatcher(this.logoutUrl, "POST");
		}
		else {
			this.logoutRequestMatcher = new OrRequestMatcher(
				new AntPathRequestMatcher(this.logoutUrl, "GET"),
				new AntPathRequestMatcher(this.logoutUrl, "POST"),
				new AntPathRequestMatcher(this.logoutUrl, "PUT"),
				new AntPathRequestMatcher(this.logoutUrl, "DELETE")
			);
		}
		return this.logoutRequestMatcher;
	}
}


/*
 * 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
 *
 *      https://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.access.method;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.security.access.ConfigAttribute;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

/**
 * Automatically tries a series of method definition sources, relying on the first source
 * of metadata that provides a non-null/non-empty response. Provides automatic caching of
 * the retrieved metadata.
 *
 * @author Ben Alex
 * @author Luke Taylor
 */
public final class DelegatingMethodSecurityMetadataSource extends
		AbstractMethodSecurityMetadataSource {
	private final static List<ConfigAttribute> NULL_CONFIG_ATTRIBUTE = Collections
			.emptyList();

	private final List<MethodSecurityMetadataSource> methodSecurityMetadataSources;
	private final Map<DefaultCacheKey, Collection<ConfigAttribute>> attributeCache = new HashMap<DefaultCacheKey, Collection<ConfigAttribute>>();

	// ~ Constructor
	// ====================================================================================================

	public DelegatingMethodSecurityMetadataSource(
			List<MethodSecurityMetadataSource> methodSecurityMetadataSources) {
		Assert.notNull(methodSecurityMetadataSources,
				"MethodSecurityMetadataSources cannot be null");
		this.methodSecurityMetadataSources = methodSecurityMetadataSources;
	}

	// ~ Methods
	// ========================================================================================================

	public Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {
		DefaultCacheKey cacheKey = new DefaultCacheKey(method, targetClass);
		synchronized (attributeCache) {
			Collection<ConfigAttribute> cached = attributeCache.get(cacheKey);
			// Check for canonical value indicating there is no config attribute,

			if (cached != null) {
				return cached;
			}

			// No cached value, so query the sources to find a result
			Collection<ConfigAttribute> attributes = null;
			for (MethodSecurityMetadataSource s : methodSecurityMetadataSources) {
				attributes = s.getAttributes(method, targetClass);
				if (attributes != null && !attributes.isEmpty()) {
					break;
				}
			}

			// Put it in the cache.
			if (attributes == null || attributes.isEmpty()) {
				this.attributeCache.put(cacheKey, NULL_CONFIG_ATTRIBUTE);
				return NULL_CONFIG_ATTRIBUTE;
			}

			if (logger.isDebugEnabled()) {
				logger.debug("Caching method [" + cacheKey + "] with attributes "
						+ attributes);
			}

			this.attributeCache.put(cacheKey, attributes);

			return attributes;
		}
	}

	@Override
	public Collection<ConfigAttribute> getAllConfigAttributes() {
		Set<ConfigAttribute> set = new HashSet<>();
		for (MethodSecurityMetadataSource s : methodSecurityMetadataSources) {
			Collection<ConfigAttribute> attrs = s.getAllConfigAttributes();
			if (attrs != null) {
				set.addAll(attrs);
			}
		}
		return set;
	}

	public List<MethodSecurityMetadataSource> getMethodSecurityMetadataSources() {
		return methodSecurityMetadataSources;
	}

	// ~ Inner Classes
	// ==================================================================================================

	private static class DefaultCacheKey {
		private final Method method;
		private final Class<?> targetClass;

		public DefaultCacheKey(Method method, Class<?> targetClass) {
			this.method = method;
			this.targetClass = targetClass;
		}

		@Override
		public boolean equals(Object other) {
			DefaultCacheKey otherKey = (DefaultCacheKey) other;
			return (this.method.equals(otherKey.method) && ObjectUtils.nullSafeEquals(
					this.targetClass, otherKey.targetClass));
		}

		@Override
		public int hashCode() {
			return this.method.hashCode() * 21
					+ (this.targetClass != null ? this.targetClass.hashCode() : 0);
		}

		@Override
		public String toString() {
			return "CacheKey[" + (targetClass == null ? "-" : targetClass.getName())
					+ "; " + method + "]";
		}
	}
}


/*
 * Copyright 2002-2018 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
 *
 *      https://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.crypto.password;

import org.springframework.security.crypto.codec.Utf8;
import org.springframework.security.crypto.keygen.BytesKeyGenerator;
import org.springframework.security.crypto.keygen.KeyGenerators;

import java.security.MessageDigest;
import java.util.Base64;

/**
 * This {@link PasswordEncoder} is provided for legacy purposes only and is not considered
 * secure.
 *
 * A version of {@link PasswordEncoder} which supports Ldap SHA and SSHA (salted-SHA)
 * encodings. The values are base-64 encoded and have the label "{SHA}" (or "{SSHA}")
 * prepended to the encoded hash. These can be made lower-case in the encoded password, if
 * required, by setting the <tt>forceLowerCasePrefix</tt> property to true.
 *
 * Also supports plain text passwords, so can safely be used in cases when both encoded
 * and non-encoded passwords are in use or when a null implementation is required.
 *
 * @author Luke Taylor
 * @deprecated Digest based password encoding is not considered secure. Instead use an
 * adaptive one way function like BCryptPasswordEncoder, Pbkdf2PasswordEncoder, or
 * SCryptPasswordEncoder. Even better use {@link DelegatingPasswordEncoder} which supports
 * password upgrades. There are no plans to remove this support. It is deprecated to indicate
 * that this is a legacy implementation and using it is considered insecure.
 */
@Deprecated
public class LdapShaPasswordEncoder implements PasswordEncoder {
	// ~ Static fields/initializers
	// =====================================================================================

	/** The number of bytes in a SHA hash */
	private static final int SHA_LENGTH = 20;
	private static final String SSHA_PREFIX = "{SSHA}";
	private static final String SSHA_PREFIX_LC = SSHA_PREFIX.toLowerCase();
	private static final String SHA_PREFIX = "{SHA}";
	private static final String SHA_PREFIX_LC = SHA_PREFIX.toLowerCase();

	// ~ Instance fields
	// ================================================================================================
	private BytesKeyGenerator saltGenerator;

	private boolean forceLowerCasePrefix;

	// ~ Constructors
	// ===================================================================================================

	public LdapShaPasswordEncoder() {
		this(KeyGenerators.secureRandom());
	}

	public LdapShaPasswordEncoder(BytesKeyGenerator saltGenerator) {
		if (saltGenerator == null) {
			throw new IllegalArgumentException("saltGenerator cannot be null");
		}
		this.saltGenerator = saltGenerator;
	}

	// ~ Methods
	// ========================================================================================================

	private byte[] combineHashAndSalt(byte[] hash, byte[] salt) {
		if (salt == null) {
			return hash;
		}

		byte[] hashAndSalt = new byte[hash.length + salt.length];
		System.arraycopy(hash, 0, hashAndSalt, 0, hash.length);
		System.arraycopy(salt, 0, hashAndSalt, hash.length, salt.length);

		return hashAndSalt;
	}

	/**
	 * Calculates the hash of password (and salt bytes, if supplied) and returns a base64
	 * encoded concatenation of the hash and salt, prefixed with {SHA} (or {SSHA} if salt
	 * was used).
	 *
	 * @param rawPass the password to be encoded.
	 *
	 * @return the encoded password in the specified format
	 *
	 */
	public String encode(CharSequence rawPass) {
		byte[] salt = this.saltGenerator.generateKey();
		return encode(rawPass, salt);
	}

	private String encode(CharSequence rawPassword, byte[] salt) {
		MessageDigest sha;

		try {
			sha = MessageDigest.getInstance("SHA");
			sha.update(Utf8.encode(rawPassword));
		}
		catch (java.security.NoSuchAlgorithmException e) {
			throw new IllegalStateException("No SHA implementation available!");
		}

		if (salt != null) {
			sha.update(salt);
		}

		byte[] hash = combineHashAndSalt(sha.digest(), (byte[]) salt);

		String prefix;

		if (salt == null || salt.length == 0) {
			prefix = forceLowerCasePrefix ? SHA_PREFIX_LC : SHA_PREFIX;
		}
		else {
			prefix = forceLowerCasePrefix ? SSHA_PREFIX_LC : SSHA_PREFIX;
		}

		return prefix + Utf8.decode(Base64.getEncoder().encode(hash));
	}

	private byte[] extractSalt(String encPass) {
		String encPassNoLabel = encPass.substring(6);

		byte[] hashAndSalt = Base64.getDecoder().decode(encPassNoLabel.getBytes());
		int saltLength = hashAndSalt.length - SHA_LENGTH;
		byte[] salt = new byte[saltLength];
		System.arraycopy(hashAndSalt, SHA_LENGTH, salt, 0, saltLength);

		return salt;
	}

	/**
	 * Checks the validity of an unencoded password against an encoded one in the form
	 * "{SSHA}sQuQF8vj8Eg2Y1hPdh3bkQhCKQBgjhQI".
	 *
	 * @param rawPassword unencoded password to be verified.
	 * @param encodedPassword the actual SSHA or SHA encoded password
	 *
	 * @return true if they match (independent of the case of the prefix).
	 */
	public boolean matches(CharSequence rawPassword, String encodedPassword) {
		return matches(rawPassword == null ? null : rawPassword.toString(), encodedPassword);
	}

	private boolean matches(String rawPassword, String encodedPassword) {
		String prefix = extractPrefix(encodedPassword);

		if (prefix == null) {
			return PasswordEncoderUtils.equals(encodedPassword, rawPassword);
		}

		byte[] salt;
		if (prefix.equals(SSHA_PREFIX) || prefix.equals(SSHA_PREFIX_LC)) {
			salt = extractSalt(encodedPassword);
		}
		else if (!prefix.equals(SHA_PREFIX) && !prefix.equals(SHA_PREFIX_LC)) {
			throw new IllegalArgumentException("Unsupported password prefix '" + prefix
					+ "'");
		}
		else {
			// Standard SHA
			salt = null;
		}

		int startOfHash = prefix.length();

		String encodedRawPass = encode(rawPassword, salt).substring(startOfHash);

		return PasswordEncoderUtils
				.equals(encodedRawPass, encodedPassword.substring(startOfHash));
	}

	/**
	 * Returns the hash prefix or null if there isn't one.
	 */
	private String extractPrefix(String encPass) {
		if (!encPass.startsWith("{")) {
			return null;
		}

		int secondBrace = encPass.lastIndexOf('}');

		if (secondBrace < 0) {
			throw new IllegalArgumentException(
					"Couldn't find closing brace for SHA prefix");
		}

		return encPass.substring(0, secondBrace + 1);
	}

	public void setForceLowerCasePrefix(boolean forceLowerCasePrefix) {
		this.forceLowerCasePrefix = forceLowerCasePrefix;
	}
}


/*
 * 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
 *
 *      https://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.samples.servletapi.mvc;

import java.io.IOException;
import java.security.Principal;

import javax.naming.AuthenticationException;
import javax.servlet.AsyncContext;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

/**
 * A Spring MVC Controller that demonstrates Spring Security's integration with the
 * standard Servlet API's. Specifically it demonstrates the following:
 *

 *
{@link #authenticate(HttpServletRequest, HttpServletResponse)} - Integration with
 * {@link HttpServletRequest#authenticate(HttpServletResponse)}

 *
{@link #login(HttpServletRequest, HttpServletResponse, LoginForm, BindingResult)} -
 * Integration with {@link HttpServletRequest#login(String, String)}

 *
{@link #logout(HttpServletRequest, HttpServletResponse, RedirectAttributes)} - Integration with
 * {@link HttpServletRequest#logout()}

 *
{@link #remoteUser(HttpServletRequest)} - Integration with
 * {@link HttpServletRequest#getRemoteUser()}

 *
{@link #userPrincipal(HttpServletRequest)} - Integration with
 * {@link HttpServletRequest#getUserPrincipal()}

 *
{@link #authentication(Authentication)} - Spring MVC's ability to resolve the
 * {@link Authentication} since it is found on
 * {@link HttpServletRequest#getUserPrincipal()}

 *

 *
 * @author Rob Winch
 *
 */
@Controller
public class ServletApiController {
	/**
	 * Demonstrates that {@link HttpServletRequest#authenticate(HttpServletResponse)} will
	 * send the user to the log in page configured within Spring Security if the user is
	 * not already authenticated.
	 *
	 * @param request
	 * @param response
	 * @return
	 * @throws ServletException
	 * @throws IOException
	 */
	@RequestMapping("/authenticate")
	public String authenticate(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		boolean authenticate = request.authenticate(response);
		return authenticate ? "index" : null;
	}

	/**
	 * Demonstrates that you can authenticate with Spring Security using
	 * {@link HttpServletRequest#login(String, String)}.
	 *
	 *

	 * If we fail to authenticate, a {@link ServletException} is thrown that wraps the
	 * original {@link AuthenticationException} from Spring Security. This means we can
	 * catch the {@link ServletException} to display the error message. Alternatively, we
	 * could allow the {@link ServletException} to propegate and Spring Security's
	 * {@link ExceptionTranslationFilter} would catch it and process it appropriately.
	 *

	 *

	 * In this method we choose to use Spring MVC's {@link ModelAttribute} to make things
	 * easier for our form. However, this is not necessary. We could have just as easily
	 * obtained the request parameters from the {@link HttpServletRequest} object.
	 * Remember all of these examples would work in a standard {@link Servlet} or anything
	 * with access to the {@link HttpServletRequest} and {@link HttpServletResponse}.
	 *

	 *
	 * @param request
	 * @param response
	 * @param loginForm
	 * @param result
	 * @return
	 * @throws ServletException
	 */
	@RequestMapping(value = "/login", method = RequestMethod.POST)
	public String login(HttpServletRequest request, HttpServletResponse response,
			@ModelAttribute LoginForm loginForm, BindingResult result)
			throws ServletException {
		try {
			request.login(loginForm.getUsername(), loginForm.getPassword());
		}
		catch (ServletException authenticationFailed) {
			result.rejectValue(null, "authentication.failed",
					authenticationFailed.getMessage());
			return "login";
		}
		return "redirect:/";
	}

	/**
	 * Demonstrates that invoking {@link HttpServletRequest#logout()} will log the user
	 * out. Note that the response does not get processed, so you need to write something
	 * to the response.
	 * @param request
	 * @param response
	 * @param redirect
	 * @return
	 * @throws ServletException
	 */
	@RequestMapping("/logout")
	public String logout(HttpServletRequest request, HttpServletResponse response,
			RedirectAttributes redirect) throws ServletException {
		request.logout();
		return "redirect:/";
	}

	/**
	 * Demonstrates Spring Security with {@link AsyncContext#start(Runnable)}. Spring
	 * Security will automatically transfer the {@link SecurityContext} from the thread
	 * that {@link AsyncContext#start(Runnable)} is invoked to the new Thread that invokes
	 * the {@link Runnable}.
	 * @param request
	 * @param response
	 */
	@RequestMapping("/async")
	public void asynch(HttpServletRequest request, HttpServletResponse response) {
		final AsyncContext async = request.startAsync();
		async.start(new Runnable() {
			public void run() {
				Authentication authentication = SecurityContextHolder.getContext()
						.getAuthentication();
				try {
					final HttpServletResponse asyncResponse = (HttpServletResponse) async
							.getResponse();
					asyncResponse.setStatus(HttpServletResponse.SC_OK);
					asyncResponse.getWriter().write(String.valueOf(authentication));
					async.complete();
				}
				catch (Exception e) {
					throw new RuntimeException(e);
				}
			}
		});
	}

	/**
	 * Demonstrates that Spring Security automatically populates
	 * {@link HttpServletRequest#getRemoteUser()} with the current username.
	 * @param request
	 * @return
	 */
	@ModelAttribute("remoteUser")
	public String remoteUser(HttpServletRequest request) {
		return request.getRemoteUser();
	}

	/**
	 * Demonstrates that Spring Security automatically populates
	 * {@link HttpServletRequest#getUserPrincipal()} with the {@link Authentication} that
	 * is present on {@link SecurityContextHolder#getContext()}
	 * @param request
	 * @return
	 */
	@ModelAttribute("userPrincipal")
	public Principal userPrincipal(HttpServletRequest request) {
		return request.getUserPrincipal();
	}

	/**
	 * Spring MVC will automatically resolve any object that implements {@link Principal}
	 * using {@link HttpServletRequest#getUserPrincipal()}. This means you can easily
	 * resolve the {@link Authentication} just by adding it as an argument to your MVC
	 * controller. Alternatively, you could also have an argument of type
	 * {@link Principal} which would not couple your controller to Spring Security.
	 * @param authentication
	 * @return
	 */
	@ModelAttribute
	public Authentication authentication(Authentication authentication) {
		return authentication;
	}

	@RequestMapping("/")
	public String welcome() {
		return "index";
	}

	@RequestMapping(value = "/login", method = RequestMethod.GET)
	public String login(@ModelAttribute LoginForm loginForm) {
		return "login";
	}
}


/*
 * 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
 *
 *      https://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.authentication;

import java.io.IOException;

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

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;

/**
 *

 * In the pre-authenticated authentication case (unlike CAS, for example) the user will
 * already have been identified through some external mechanism and a secure context
 * established by the time the security-enforcement filter is invoked.
 *

 * Therefore this class isn't actually responsible for the commencement of authentication,
 * as it is in the case of other providers. It will be called if the user is rejected by
 * the AbstractPreAuthenticatedProcessingFilter, resulting in a null authentication.
 *

 * The commence method will always return an
 * HttpServletResponse.SC_FORBIDDEN (403 error).
 *
 * @see org.springframework.security.web.access.ExceptionTranslationFilter
 *
 * @author Luke Taylor
 * @author Ruud Senden
 * @since 2.0
 */
public class Http403ForbiddenEntryPoint implements AuthenticationEntryPoint {
	private static final Log logger = LogFactory.getLog(Http403ForbiddenEntryPoint.class);

	/**
	 * Always returns a 403 error code to the client.
	 */
	public void commence(HttpServletRequest request, HttpServletResponse response,
			AuthenticationException arg2) throws IOException, ServletException {
		if (logger.isDebugEnabled()) {
			logger.debug("Pre-authenticated entry point called. Rejecting access");
		}
		response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied");
	}
}


/*
 * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
 *
 * 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
 *
 *      https://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.acls.afterinvocation;

import java.util.Collection;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.AuthorizationServiceException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.acls.model.AclService;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.core.Authentication;

/**
 *

 * Given a Collection of domain object instances returned from a secure
 * object invocation, remove any Collection elements the principal does not
 * have appropriate permission to access as defined by the {@link AclService}.
 *

 * The AclService is used to retrieve the access control list (ACL)
 * permissions associated with each Collection domain object instance element
 * for the current Authentication object.
 *

 * This after invocation provider will fire if any {@link ConfigAttribute#getAttribute()}
 * matches the {@link #processConfigAttribute}. The provider will then lookup the ACLs
 * from the AclService and ensure the principal is
 * {@link org.springframework.security.acls.model.Acl#isGranted(List, List, boolean)
 * Acl.isGranted()} when presenting the {@link #requirePermission} array to that method.
 *

 * If the principal does not have permission, that element will not be included in the
 * returned Collection.
 *

 * Often users will setup a BasicAclEntryAfterInvocationProvider with a
 * {@link #processConfigAttribute} of AFTER_ACL_COLLECTION_READ and a
 * {@link #requirePermission} of BasePermission.READ. These are also the
 * defaults.
 *

 * If the provided returnObject is null, a null
 * Collection will be returned. If the provided returnObject is
 * not a Collection, an {@link AuthorizationServiceException} will be thrown.
 *

 * All comparisons and prefixes are case sensitive.
 *
 * @author Ben Alex
 * @author Paulo Neves
 */
public class AclEntryAfterInvocationCollectionFilteringProvider extends
		AbstractAclProvider {
	// ~ Static fields/initializers
	// =====================================================================================

	protected static final Log logger = LogFactory
			.getLog(AclEntryAfterInvocationCollectionFilteringProvider.class);

	// ~ Constructors
	// ===================================================================================================

	public AclEntryAfterInvocationCollectionFilteringProvider(AclService aclService,
			List<Permission> requirePermission) {
		super(aclService, "AFTER_ACL_COLLECTION_READ", requirePermission);
	}

	// ~ Methods
	// ========================================================================================================

	@SuppressWarnings("unchecked")
	public Object decide(Authentication authentication, Object object,
			Collection<ConfigAttribute> config, Object returnedObject)
			throws AccessDeniedException {

		if (returnedObject == null) {
			logger.debug("Return object is null, skipping");

			return null;
		}

		for (ConfigAttribute attr : config) {
			if (!this.supports(attr)) {
				continue;
			}

			// Need to process the Collection for this invocation
			Filterer filterer;

			if (returnedObject instanceof Collection) {
				filterer = new CollectionFilterer((Collection) returnedObject);
			}
			else if (returnedObject.getClass().isArray()) {
				filterer = new ArrayFilterer((Object[]) returnedObject);
			}
			else {
				throw new AuthorizationServiceException(
						"A Collection or an array (or null) was required as the "
								+ "returnedObject, but the returnedObject was: "
								+ returnedObject);
			}

			// Locate unauthorised Collection elements
			for (Object domainObject : filterer) {
				// Ignore nulls or entries which aren't instances of the configured domain
				// object class
				if (domainObject == null
						|| !getProcessDomainObjectClass().isAssignableFrom(
								domainObject.getClass())) {
					continue;
				}

				if (!hasPermission(authentication, domainObject)) {
					filterer.remove(domainObject);

					if (logger.isDebugEnabled()) {
						logger.debug("Principal is NOT authorised for element: "
								+ domainObject);
					}
				}
			}

			return filterer.getFilteredObject();
		}

		return returnedObject;
	}
}


/*
 * 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
 *
 *      https://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.firewall;

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

/**
 *

 * User's should consider using {@link StrictHttpFirewall} because rather than trying to
 * sanitize a malicious URL it rejects the malicious URL providing better security
 * guarantees.
 *

 * Default implementation which wraps requests in order to provide consistent
 * values of the {@code servletPath} and {@code pathInfo}, which do not contain
 * path parameters (as defined in
 * RFC 2396). Different
 * servlet containers interpret the servlet spec differently as to how path
 * parameters are treated and it is possible they might be added in order to
 * bypass particular security constraints. When using this implementation, they
 * will be removed for all requests as the request passes through the security
 * filter chain. Note that this means that any segments in the decoded path
 * which contain a semi-colon, will have the part following the semi-colon
 * removed for request matching. Your application should not contain any valid
 * paths which contain semi-colons.
 *

 * If any un-normalized paths are found (containing directory-traversal
 * character sequences), the request will be rejected immediately. Most
 * containers normalize the paths before performing the servlet-mapping, but
 * again this is not guaranteed by the servlet spec.
 *
 * @author Luke Taylor
 * @see StrictHttpFirewall
 */
public class DefaultHttpFirewall implements HttpFirewall {
	private boolean allowUrlEncodedSlash;

	@Override
	public FirewalledRequest getFirewalledRequest(HttpServletRequest request) throws RequestRejectedException {
		FirewalledRequest fwr = new RequestWrapper(request);

		if (!isNormalized(fwr.getServletPath()) || !isNormalized(fwr.getPathInfo())) {
			throw new RequestRejectedException("Un-normalized paths are not supported: " + fwr.getServletPath()
					+ (fwr.getPathInfo() != null ? fwr.getPathInfo() : ""));
		}

		String requestURI = fwr.getRequestURI();
		if (containsInvalidUrlEncodedSlash(requestURI)) {
			throw new RequestRejectedException("The requestURI cannot contain encoded slash. Got " + requestURI);
		}

		return fwr;
	}

	@Override
	public HttpServletResponse getFirewalledResponse(HttpServletResponse response) {
		return new FirewalledResponse(response);
	}

	/**
	 *

	 * Sets if the application should allow a URL encoded slash character.
	 *

	 *

	 * If true (default is false), a URL encoded slash will be allowed in the
	 * URL. Allowing encoded slashes can cause security vulnerabilities in some
	 * situations depending on how the container constructs the
	 * HttpServletRequest.
	 *

	 *
	 * @param allowUrlEncodedSlash
	 *            the new value (default false)
	 */
	public void setAllowUrlEncodedSlash(boolean allowUrlEncodedSlash) {
		this.allowUrlEncodedSlash = allowUrlEncodedSlash;
	}

	private boolean containsInvalidUrlEncodedSlash(String uri) {
		if (this.allowUrlEncodedSlash || uri == null) {
			return false;
		}

		if (uri.contains("%2f") || uri.contains("%2F")) {
			return true;
		}

		return false;
	}

	/**
	 * Checks whether a path is normalized (doesn't contain path traversal
	 * sequences like "./", "/../" or "/.")
	 *
	 * @param path
	 *            the path to test
	 * @return true if the path doesn't contain any path-traversal character
	 *         sequences.
	 */
	private boolean isNormalized(String path) {
		if (path == null) {
			return true;
		}

		for (int j = path.length(); j > 0;) {
			int i = path.lastIndexOf('/', j - 1);
			int gap = j - i;

			if (gap == 2 && path.charAt(i + 1) == '.') {
				// ".", "/./" or "/."
				return false;
			} else if (gap == 3 && path.charAt(i + 1) == '.' && path.charAt(i + 2) == '.') {
				return false;
			}

			j = i;
		}

		return true;
	}

}


/*
 * 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
 *
 *      https://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.config.http;

import java.util.*;

import javax.servlet.Filter;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
import org.springframework.security.web.session.SessionManagementFilter;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.security.web.util.matcher.AnyRequestMatcher;

public class DefaultFilterChainValidator implements FilterChainProxy.FilterChainValidator {
	private final Log logger = LogFactory.getLog(getClass());

	public void validate(FilterChainProxy fcp) {
		for (SecurityFilterChain filterChain : fcp.getFilterChains()) {
			checkLoginPageIsntProtected(fcp, filterChain.getFilters());
			checkFilterStack(filterChain.getFilters());
		}

		checkPathOrder(new ArrayList<>(fcp.getFilterChains()));
		checkForDuplicateMatchers(new ArrayList<>(
				fcp.getFilterChains()));
	}

	private void checkPathOrder(List<SecurityFilterChain> filterChains) {
		// Check that the universal pattern is listed at the end, if at all
		Iterator<SecurityFilterChain> chains = filterChains.iterator();

		while (chains.hasNext()) {
			RequestMatcher matcher = ((DefaultSecurityFilterChain) chains.next())
					.getRequestMatcher();
			if (AnyRequestMatcher.INSTANCE.equals(matcher) && chains.hasNext()) {
				throw new IllegalArgumentException(
						"A universal match pattern ('/**') is defined "
								+ " before other patterns in the filter chain, causing them to be ignored. Please check the "
								+ "ordering in your <security:http> namespace or FilterChainProxy bean configuration");
			}
		}
	}

	private void checkForDuplicateMatchers(List<SecurityFilterChain> chains) {

		while (chains.size() > 1) {
			DefaultSecurityFilterChain chain = (DefaultSecurityFilterChain) chains
					.remove(0);

			for (SecurityFilterChain test : chains) {
				if (chain.getRequestMatcher().equals(
						((DefaultSecurityFilterChain) test).getRequestMatcher())) {
					throw new IllegalArgumentException(
							"The FilterChainProxy contains two filter chains using the"
									+ " matcher "
									+ chain.getRequestMatcher()
									+ ". If you are using multiple <http> namespace "
									+ "elements, you must use a 'pattern' attribute to define the request patterns to which they apply.");
				}
			}
		}
	}

	@SuppressWarnings({ "unchecked" })
	private <F extends Filter> F getFilter(Class<F> type, List<Filter> filters) {
		for (Filter f : filters) {
			if (type.isAssignableFrom(f.getClass())) {
				return (F) f;
			}
		}

		return null;
	}

	/**
	 * Checks the filter list for possible errors and logs them
	 */
	private void checkFilterStack(List<Filter> filters) {
		checkForDuplicates(SecurityContextPersistenceFilter.class, filters);
		checkForDuplicates(UsernamePasswordAuthenticationFilter.class, filters);
		checkForDuplicates(SessionManagementFilter.class, filters);
		checkForDuplicates(BasicAuthenticationFilter.class, filters);
		checkForDuplicates(SecurityContextHolderAwareRequestFilter.class, filters);
		checkForDuplicates(JaasApiIntegrationFilter.class, filters);
		checkForDuplicates(ExceptionTranslationFilter.class, filters);
		checkForDuplicates(FilterSecurityInterceptor.class, filters);
	}

	private void checkForDuplicates(Class<? extends Filter> clazz, List<Filter> filters) {
		for (int i = 0; i < filters.size(); i++) {
			Filter f1 = filters.get(i);
			if (clazz.isAssignableFrom(f1.getClass())) {
				// Found the first one, check remaining for another
				for (int j = i + 1; j < filters.size(); j++) {
					Filter f2 = filters.get(j);
					if (clazz.isAssignableFrom(f2.getClass())) {
						logger.warn("Possible error: Filters at position " + i + " and "
								+ j + " are both " + "instances of " + clazz.getName());
						return;
					}
				}
			}
		}
	}

	/*
	 * Checks for the common error of having a login page URL protected by the security
	 * interceptor
	 */
	private void checkLoginPageIsntProtected(FilterChainProxy fcp,
			List<Filter> filterStack) {
		ExceptionTranslationFilter etf = getFilter(ExceptionTranslationFilter.class,
				filterStack);

		if (etf == null
				|| !(etf.getAuthenticationEntryPoint() instanceof LoginUrlAuthenticationEntryPoint)) {
			return;
		}

		String loginPage = ((LoginUrlAuthenticationEntryPoint) etf
				.getAuthenticationEntryPoint()).getLoginFormUrl();
		logger.info("Checking whether login URL '" + loginPage
				+ "' is accessible with your configuration");
		FilterInvocation loginRequest = new FilterInvocation(loginPage, "POST");
		List<Filter> filters = null;

		try {
			filters = fcp.getFilters(loginPage);
		}
		catch (Exception e) {
			// May happen legitimately if a filter-chain request matcher requires more
			// request data than that provided
			// by the dummy request used when creating the filter invocation.
			logger.info("Failed to obtain filter chain information for the login page. Unable to complete check.");
		}

		if (filters == null || filters.isEmpty()) {
			logger.debug("Filter chain is empty for the login page");
			return;
		}

		if (getFilter(DefaultLoginPageGeneratingFilter.class, filters) != null) {
			logger.debug("Default generated login page is in use");
			return;
		}

		FilterSecurityInterceptor fsi = getFilter(FilterSecurityInterceptor.class,
				filters);
		FilterInvocationSecurityMetadataSource fids = fsi.getSecurityMetadataSource();

		Collection<ConfigAttribute> attributes = fids.getAttributes(loginRequest);

		if (attributes == null) {
			logger.debug("No access attributes defined for login page URL");
			if (fsi.isRejectPublicInvocations()) {
				logger.warn("FilterSecurityInterceptor is configured to reject public invocations."
						+ " Your login page may not be accessible.");
			}
			return;
		}

		AnonymousAuthenticationFilter anonPF = getFilter(
				AnonymousAuthenticationFilter.class, filters);
		if (anonPF == null) {
			logger.warn("The login page is being protected by the filter chain, but you don't appear to have"
					+ " anonymous authentication enabled. This is almost certainly an error.");
			return;
		}

		// Simulate an anonymous access with the supplied attributes.
		AnonymousAuthenticationToken token = new AnonymousAuthenticationToken("key",
				anonPF.getPrincipal(), anonPF.getAuthorities());
		try {
			fsi.getAccessDecisionManager().decide(token, loginRequest, attributes);
		}
		catch (AccessDeniedException e) {
			logger.warn("Anonymous access to the login page doesn't appear to be enabled. This is almost certainly "
					+ "an error. Please check your configuration allows unauthenticated access to the configured "
					+ "login page. (Simulated access was rejected: " + e + ")");
		}
		catch (Exception e) {
			// May happen legitimately if a filter-chain request matcher requires more
			// request data than that provided
			// by the dummy request used when creating the filter invocation. See SEC-1878
			logger.info(
					"Unable to check access to the login page to determine if anonymous access is allowed. This might be an error, but can happen under normal circumstances.",
					e);
		}
	}

}


/*
 * 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
 *
 *      https://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.util;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import org.springframework.util.Assert;

/**
 * Handler for analyzing {@link Throwable} instances.
 *
 * Can be subclassed to customize its behavior.
 *
 * @author Andreas Senft
 * @since 2.0
 */
public class ThrowableAnalyzer {

	/**
	 * Default extractor for {@link Throwable} instances.
	 *
	 * @see Throwable#getCause()
	 */
	public static final ThrowableCauseExtractor DEFAULT_EXTRACTOR = new ThrowableCauseExtractor() {
		public Throwable extractCause(Throwable throwable) {
			return throwable.getCause();
		}
	};

	/**
	 * Default extractor for {@link InvocationTargetException} instances.
	 *
	 * @see InvocationTargetException#getTargetException()
	 */
	public static final ThrowableCauseExtractor INVOCATIONTARGET_EXTRACTOR = new ThrowableCauseExtractor() {
		public Throwable extractCause(Throwable throwable) {
			verifyThrowableHierarchy(throwable, InvocationTargetException.class);
			return ((InvocationTargetException) throwable).getTargetException();
		}
	};

	/**
	 * Comparator to order classes ascending according to their hierarchy relation. If two
	 * classes have a hierarchical relation, the "higher" class is considered to be
	 * greater by this comparator.

	 * For hierarchically unrelated classes their fully qualified name will be compared.
	 */
	private static final Comparator<Class<? extends Throwable>> CLASS_HIERARCHY_COMPARATOR = new Comparator<Class<? extends Throwable>>() {

		public int compare(Class<? extends Throwable> class1,
				Class<? extends Throwable> class2) {
			if (class1.isAssignableFrom(class2)) {
				return 1;
			}
			else if (class2.isAssignableFrom(class1)) {
				return -1;
			}
			else {
				return class1.getName().compareTo(class2.getName());
			}
		}

	};

	/**
	 * Map of registered cause extractors. key: Class<Throwable>; value:
	 * ThrowableCauseExctractor
	 */
	private final Map<Class<? extends Throwable>, ThrowableCauseExtractor> extractorMap;

	/**
	 * Creates a new ThrowableAnalyzer instance.
	 */
	public ThrowableAnalyzer() {
		this.extractorMap = new TreeMap<Class<? extends Throwable>, ThrowableCauseExtractor>(
				CLASS_HIERARCHY_COMPARATOR);

		initExtractorMap();
	}

	/**
	 * Registers a ThrowableCauseExtractor for the specified type. Can be
	 * used in subclasses overriding {@link #initExtractorMap()}.
	 *
	 * @param throwableType the type (has to be a subclass of Throwable)
	 * @param extractor the associated ThrowableCauseExtractor (not
	 * null)
	 *
	 * @throws IllegalArgumentException if one of the arguments is invalid
	 */
	protected final void registerExtractor(Class<? extends Throwable> throwableType,
			ThrowableCauseExtractor extractor) {
		Assert.notNull(extractor, "Invalid extractor: null");

		this.extractorMap.put(throwableType, extractor);
	}

	/**
	 * Initializes associations between Throwables and
	 * ThrowableCauseExtractors. The default implementation performs the
	 * following registrations:
	 *

	 *
{@link #DEFAULT_EXTRACTOR} for {@link Throwable}

	 *
{@link #INVOCATIONTARGET_EXTRACTOR} for {@link InvocationTargetException}

	 *

	 * Subclasses overriding this method are encouraged to invoke the super method to
	 * perform the default registrations. They can register additional extractors as
	 * required.
	 *

	 * Note: An extractor registered for a specific type is applicable for that type
	 * and all subtypes thereof. However, extractors registered to more specific
	 * types are guaranteed to be resolved first. So in the default case
	 * InvocationTargetExceptions will be handled by {@link #INVOCATIONTARGET_EXTRACTOR}
	 * while all other throwables are handled by {@link #DEFAULT_EXTRACTOR}.
	 *
	 * @see #registerExtractor(Class, ThrowableCauseExtractor)
	 */
	protected void initExtractorMap() {
		registerExtractor(InvocationTargetException.class, INVOCATIONTARGET_EXTRACTOR);
		registerExtractor(Throwable.class, DEFAULT_EXTRACTOR);
	}

	/**
	 * Returns an array containing the classes for which extractors are registered. The
	 * order of the classes is the order in which comparisons will occur for resolving a
	 * matching extractor.
	 *
	 * @return the types for which extractors are registered
	 */
	@SuppressWarnings("unchecked")
	final Class<? extends Throwable>[] getRegisteredTypes() {
		Set<Class<? extends Throwable>> typeList = this.extractorMap.keySet();
		return typeList.toArray(new Class[typeList.size()]);
	}

	/**
	 * Determines the cause chain of the provided Throwable. The returned
	 * array contains all throwables extracted from the stacktrace, using the registered
	 * {@link ThrowableCauseExtractor extractors}. The elements of the array are ordered:
	 * The first element is the passed in throwable itself. The following elements appear
	 * in their order downward the stacktrace.
	 *

	 * Note: If no {@link ThrowableCauseExtractor} is registered for this instance then
	 * the returned array will always only contain the passed in throwable.
	 *
	 * @param throwable the Throwable to analyze
	 * @return an array of all determined throwables from the stacktrace
	 *
	 * @throws IllegalArgumentException if the throwable is null
	 *
	 * @see #initExtractorMap()
	 */
	public final Throwable[] determineCauseChain(Throwable throwable) {
		if (throwable == null) {
			throw new IllegalArgumentException("Invalid throwable: null");
		}

		List<Throwable> chain = new ArrayList<>();
		Throwable currentThrowable = throwable;

		while (currentThrowable != null) {
			chain.add(currentThrowable);
			currentThrowable = extractCause(currentThrowable);
		}

		return chain.toArray(new Throwable[chain.size()]);
	}

	/**
	 * Extracts the cause of the given throwable using an appropriate extractor.
	 *
	 * @param throwable the Throwable (not null
	 * @return the cause, may be null if none could be resolved
	 */
	private Throwable extractCause(Throwable throwable) {
		for (Map.Entry<Class<? extends Throwable>, ThrowableCauseExtractor> entry : extractorMap
				.entrySet()) {
			Class<? extends Throwable> throwableType = entry.getKey();
			if (throwableType.isInstance(throwable)) {
				ThrowableCauseExtractor extractor = entry.getValue();
				return extractor.extractCause(throwable);
			}
		}

		return null;
	}

	/**
	 * Returns the first throwable from the passed in array that is assignable to the
	 * provided type. A returned instance is safe to be cast to the specified type.
	 *

	 * If the passed in array is null or empty this method returns null.
	 *
	 * @param throwableType the type to look for
	 * @param chain the array (will be processed in element order)
	 * @return the found Throwable, null if not found
	 *
	 * @throws IllegalArgumentException if the provided type is null or no
	 * subclass of Throwable
	 */
	public final Throwable getFirstThrowableOfType(
			Class<? extends Throwable> throwableType, Throwable[] chain) {
		if (chain != null) {
			for (Throwable t : chain) {
				if ((t != null) && throwableType.isInstance(t)) {
					return t;
				}
			}
		}

		return null;
	}

	/**
	 * Verifies that the provided throwable is a valid subclass of the provided type (or
	 * of the type itself). If expectdBaseType is null, no check
	 * will be performed.
	 *

	 * Can be used for verification purposes in implementations of
	 * {@link ThrowableCauseExtractor extractors}.
	 *
	 * @param throwable the Throwable to check
	 * @param expectedBaseType the type to check against
	 *
	 * @throws IllegalArgumentException if throwable is either
	 * null or its type is not assignable to expectedBaseType
	 */
	public static void verifyThrowableHierarchy(Throwable throwable,
			Class<? extends Throwable> expectedBaseType) {
		if (expectedBaseType == null) {
			return;
		}

		if (throwable == null) {
			throw new IllegalArgumentException("Invalid throwable: null");
		}
		Class<? extends Throwable> throwableType = throwable.getClass();

		if (!expectedBaseType.isAssignableFrom(throwableType)) {
			throw new IllegalArgumentException("Invalid type: '"
					+ throwableType.getName() + "'. Has to be a subclass of '"
					+ expectedBaseType.getName() + "'");
		}
	}
}


/*
 * Copyright 2002-2017 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
 *
 *      https://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.config.annotation.method.configuration;

import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.AdviceModeImportSelector;
import org.springframework.context.annotation.AutoProxyRegistrar;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Rob Winch
 * @since 5.0
 */
class ReactiveMethodSecuritySelector extends
	AdviceModeImportSelector<EnableReactiveMethodSecurity> {

	@Override
	protected String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return getProxyImports();
			default:
				throw new IllegalStateException("AdviceMode " + adviceMode + " is not supported");
		}
	}

	/**
	 * Return the imports to use if the {@link AdviceMode} is set to {@link AdviceMode#PROXY}.
	 * <p>Take care of adding the necessary JSR-107 import if it is available.
	 */
	private String[] getProxyImports() {
		List<String> result = new ArrayList<>();
		result.add(AutoProxyRegistrar.class.getName());
		result.add(ReactiveMethodSecurityConfiguration.class.getName());
		return result.toArray(new String[result.size()]);
	}
}


/*
 * Copyright 2002-2018 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
 *
 *      https://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.config.method;

import static org.springframework.security.config.Elements.*;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.config.AopNamespaceUtils;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.aop.target.LazyInitTargetSource;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanReference;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.access.annotation.Jsr250MethodSecurityMetadataSource;
import org.springframework.security.access.annotation.Jsr250Voter;
import org.springframework.security.access.annotation.SecuredAnnotationSecurityMetadataSource;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.ExpressionBasedAnnotationAttributeFactory;
import org.springframework.security.access.expression.method.ExpressionBasedPostInvocationAdvice;
import org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.intercept.AfterInvocationProviderManager;
import org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor;
import org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor;
import org.springframework.security.access.intercept.aspectj.AspectJMethodSecurityInterceptor;
import org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource;
import org.springframework.security.access.method.MapBasedMethodSecurityMetadataSource;
import org.springframework.security.access.prepost.PostInvocationAdviceProvider;
import org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter;
import org.springframework.security.access.prepost.PrePostAnnotationSecurityMetadataSource;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.access.vote.AuthenticatedVoter;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.Elements;
import org.springframework.security.config.authentication.AuthenticationManagerFactoryBean;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;

/**
 * Processes the top-level "global-method-security" element.
 *
 * @author Ben Alex
 * @author Luke Taylor
 * @author Rob Winch
 * @since 2.0
 */
public class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionParser {

	private final Log logger = LogFactory.getLog(getClass());

	private static final String ATT_AUTHENTICATION_MANAGER_REF = "authentication-manager-ref";
	private static final String ATT_ACCESS = "access";
	private static final String ATT_EXPRESSION = "expression";
	private static final String ATT_ACCESS_MGR = "access-decision-manager-ref";
	private static final String ATT_RUN_AS_MGR = "run-as-manager-ref";
	private static final String ATT_USE_JSR250 = "jsr250-annotations";
	private static final String ATT_USE_SECURED = "secured-annotations";
	private static final String ATT_USE_PREPOST = "pre-post-annotations";
	private static final String ATT_REF = "ref";
	private static final String ATT_MODE = "mode";
	private static final String ATT_ADVICE_ORDER = "order";
	private static final String ATT_META_DATA_SOURCE_REF = "metadata-source-ref";

	public BeanDefinition parse(Element element, ParserContext pc) {
		CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(
				element.getTagName(), pc.extractSource(element));
		pc.pushContainingComponent(compositeDef);

		Object source = pc.extractSource(element);
		// The list of method metadata delegates
		ManagedList<BeanMetadataElement> delegates = new ManagedList<>();

		boolean jsr250Enabled = "enabled".equals(element.getAttribute(ATT_USE_JSR250));
		boolean useSecured = "enabled".equals(element.getAttribute(ATT_USE_SECURED));
		boolean prePostAnnotationsEnabled = "enabled".equals(element
				.getAttribute(ATT_USE_PREPOST));
		boolean useAspectJ = "aspectj".equals(element.getAttribute(ATT_MODE));

		BeanDefinition preInvocationVoter = null;
		ManagedList<BeanMetadataElement> afterInvocationProviders = new ManagedList<>();

		// Check for an external SecurityMetadataSource, which takes priority over other
		// sources
		String metaDataSourceId = element.getAttribute(ATT_META_DATA_SOURCE_REF);

		if (StringUtils.hasText(metaDataSourceId)) {
			delegates.add(new RuntimeBeanReference(metaDataSourceId));
		}

		if (prePostAnnotationsEnabled) {
			Element prePostElt = DomUtils.getChildElementByTagName(element,
					INVOCATION_HANDLING);
			Element expressionHandlerElt = DomUtils.getChildElementByTagName(element,
					EXPRESSION_HANDLER);

			if (prePostElt != null && expressionHandlerElt != null) {
				pc.getReaderContext().error(
						INVOCATION_HANDLING + " and " + EXPRESSION_HANDLER
								+ " cannot be used together ", source);
			}

			BeanDefinitionBuilder preInvocationVoterBldr = BeanDefinitionBuilder
					.rootBeanDefinition(PreInvocationAuthorizationAdviceVoter.class);
			// After-invocation provider to handle post-invocation filtering and
			// authorization expression annotations.
			BeanDefinitionBuilder afterInvocationBldr = BeanDefinitionBuilder
					.rootBeanDefinition(PostInvocationAdviceProvider.class);
			// The metadata source for the security interceptor
			BeanDefinitionBuilder mds = BeanDefinitionBuilder
					.rootBeanDefinition(PrePostAnnotationSecurityMetadataSource.class);

			if (prePostElt != null) {
				// Customized override of expression handling system
				String attributeFactoryRef = DomUtils.getChildElementByTagName(
						prePostElt, INVOCATION_ATTRIBUTE_FACTORY).getAttribute("ref");
				String preAdviceRef = DomUtils.getChildElementByTagName(prePostElt,
						PRE_INVOCATION_ADVICE).getAttribute("ref");
				String postAdviceRef = DomUtils.getChildElementByTagName(prePostElt,
						POST_INVOCATION_ADVICE).getAttribute("ref");

				mds.addConstructorArgReference(attributeFactoryRef);
				preInvocationVoterBldr.addConstructorArgReference(preAdviceRef);
				afterInvocationBldr.addConstructorArgReference(postAdviceRef);
			}
			else {
				// The default expression-based system
				String expressionHandlerRef = expressionHandlerElt == null ? null
						: expressionHandlerElt.getAttribute("ref");

				if (StringUtils.hasText(expressionHandlerRef)) {
					logger.info("Using bean '" + expressionHandlerRef
							+ "' as method ExpressionHandler implementation");
					RootBeanDefinition lazyInitPP = new RootBeanDefinition(
							LazyInitBeanDefinitionRegistryPostProcessor.class);
					lazyInitPP.getConstructorArgumentValues().addGenericArgumentValue(
							expressionHandlerRef);
					pc.getReaderContext().registerWithGeneratedName(lazyInitPP);

					BeanDefinitionBuilder lazyMethodSecurityExpressionHandlerBldr = BeanDefinitionBuilder
							.rootBeanDefinition(LazyInitTargetSource.class);
					lazyMethodSecurityExpressionHandlerBldr.addPropertyValue(
							"targetBeanName", expressionHandlerRef);

					BeanDefinitionBuilder expressionHandlerProxyBldr = BeanDefinitionBuilder
							.rootBeanDefinition(ProxyFactoryBean.class);
					expressionHandlerProxyBldr.addPropertyValue("targetSource",
							lazyMethodSecurityExpressionHandlerBldr.getBeanDefinition());
					expressionHandlerProxyBldr.addPropertyValue("proxyInterfaces",
							MethodSecurityExpressionHandler.class);

					expressionHandlerRef = pc.getReaderContext().generateBeanName(
							expressionHandlerProxyBldr.getBeanDefinition());

					pc.registerBeanComponent(new BeanComponentDefinition(
							expressionHandlerProxyBldr.getBeanDefinition(),
							expressionHandlerRef));
				}
				else {
					RootBeanDefinition expressionHandler = registerWithDefaultRolePrefix(pc, DefaultMethodSecurityExpressionHandlerBeanFactory.class);

					expressionHandlerRef = pc.getReaderContext().generateBeanName(
							expressionHandler);
					pc.registerBeanComponent(new BeanComponentDefinition(
							expressionHandler, expressionHandlerRef));
					logger.info("Expressions were enabled for method security but no SecurityExpressionHandler was configured. "
							+ "All hasPermision() expressions will evaluate to false.");
				}

				BeanDefinitionBuilder expressionPreAdviceBldr = BeanDefinitionBuilder
						.rootBeanDefinition(ExpressionBasedPreInvocationAdvice.class);
				expressionPreAdviceBldr.addPropertyReference("expressionHandler",
						expressionHandlerRef);
				preInvocationVoterBldr.addConstructorArgValue(expressionPreAdviceBldr
						.getBeanDefinition());

				BeanDefinitionBuilder expressionPostAdviceBldr = BeanDefinitionBuilder
						.rootBeanDefinition(ExpressionBasedPostInvocationAdvice.class);
				expressionPostAdviceBldr.addConstructorArgReference(expressionHandlerRef);
				afterInvocationBldr.addConstructorArgValue(expressionPostAdviceBldr
						.getBeanDefinition());

				BeanDefinitionBuilder annotationInvocationFactory = BeanDefinitionBuilder
						.rootBeanDefinition(ExpressionBasedAnnotationAttributeFactory.class);
				annotationInvocationFactory
						.addConstructorArgReference(expressionHandlerRef);
				mds.addConstructorArgValue(annotationInvocationFactory
						.getBeanDefinition());
			}

			preInvocationVoter = preInvocationVoterBldr.getBeanDefinition();
			afterInvocationProviders.add(afterInvocationBldr.getBeanDefinition());
			delegates.add(mds.getBeanDefinition());
		}

		if (useSecured) {
			delegates.add(BeanDefinitionBuilder.rootBeanDefinition(
					SecuredAnnotationSecurityMetadataSource.class).getBeanDefinition());
		}

		if (jsr250Enabled) {
			RootBeanDefinition jsrMetadataSource = registerWithDefaultRolePrefix(pc, Jsr250MethodSecurityMetadataSourceBeanFactory.class);
			delegates.add(jsrMetadataSource);
		}

		// Now create a Map<String, ConfigAttribute> for each <protect-pointcut>
		// sub-element
		Map<String, List<ConfigAttribute>> pointcutMap = parseProtectPointcuts(pc,
				DomUtils.getChildElementsByTagName(element, PROTECT_POINTCUT));

		if (pointcutMap.size() > 0) {
			if (useAspectJ) {
				pc.getReaderContext().error(
						"You can't use AspectJ mode with protect-pointcut definitions",
						source);
			}
			// Only add it if there are actually any pointcuts defined.
			BeanDefinition mapBasedMetadataSource = new RootBeanDefinition(
					MapBasedMethodSecurityMetadataSource.class);
			BeanReference ref = new RuntimeBeanReference(pc.getReaderContext()
					.generateBeanName(mapBasedMetadataSource));

			delegates.add(ref);
			pc.registerBeanComponent(new BeanComponentDefinition(mapBasedMetadataSource,
					ref.getBeanName()));
			registerProtectPointcutPostProcessor(pc, pointcutMap, ref, source);
		}

		BeanReference metadataSource = registerDelegatingMethodSecurityMetadataSource(pc,
				delegates, source);

		// Check for additional after-invocation-providers..
		List<Element> afterInvocationElts = DomUtils.getChildElementsByTagName(element,
				Elements.AFTER_INVOCATION_PROVIDER);

		for (Element elt : afterInvocationElts) {
			afterInvocationProviders.add(new RuntimeBeanReference(elt
					.getAttribute(ATT_REF)));
		}

		String accessManagerId = element.getAttribute(ATT_ACCESS_MGR);

		if (!StringUtils.hasText(accessManagerId)) {
			accessManagerId = registerAccessManager(pc, jsr250Enabled, preInvocationVoter);
		}

		String authMgrRef = element.getAttribute(ATT_AUTHENTICATION_MANAGER_REF);

		String runAsManagerId = element.getAttribute(ATT_RUN_AS_MGR);
		BeanReference interceptor = registerMethodSecurityInterceptor(pc, authMgrRef,
				accessManagerId, runAsManagerId, metadataSource,
				afterInvocationProviders, source, useAspectJ);

		if (useAspectJ) {
			BeanDefinitionBuilder aspect = BeanDefinitionBuilder
					.rootBeanDefinition("org.springframework.security.access.intercept.aspectj.aspect.AnnotationSecurityAspect");
			aspect.setFactoryMethod("aspectOf");
			aspect.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
			aspect.addPropertyValue("securityInterceptor", interceptor);
			String id = pc.getReaderContext().registerWithGeneratedName(
					aspect.getBeanDefinition());
			pc.registerBeanComponent(new BeanComponentDefinition(aspect
					.getBeanDefinition(), id));
		}
		else {
			registerAdvisor(pc, interceptor, metadataSource, source,
					element.getAttribute(ATT_ADVICE_ORDER));
			AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(pc, element);
		}

		pc.popAndRegisterContainingComponent();

		return null;
	}

	/**
	 * Register the default AccessDecisionManager. Adds the special JSR 250 voter jsr-250
	 * is enabled and an expression voter if expression-based access control is enabled.
	 * @return
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private String registerAccessManager(ParserContext pc, boolean jsr250Enabled,
			BeanDefinition expressionVoter) {

		BeanDefinitionBuilder accessMgrBuilder = BeanDefinitionBuilder
				.rootBeanDefinition(AffirmativeBased.class);
		ManagedList voters = new ManagedList(4);

		if (expressionVoter != null) {
			voters.add(expressionVoter);
		}
		voters.add(new RootBeanDefinition(RoleVoter.class));
		voters.add(new RootBeanDefinition(AuthenticatedVoter.class));

		if (jsr250Enabled) {
			voters.add(new RootBeanDefinition(Jsr250Voter.class));
		}

		accessMgrBuilder.addConstructorArgValue(voters);

		BeanDefinition accessManager = accessMgrBuilder.getBeanDefinition();
		String id = pc.getReaderContext().generateBeanName(accessManager);
		pc.registerBeanComponent(new BeanComponentDefinition(accessManager, id));

		return id;
	}

	@SuppressWarnings("rawtypes")
	private BeanReference registerDelegatingMethodSecurityMetadataSource(
			ParserContext pc, ManagedList delegates, Object source) {
		RootBeanDefinition delegatingMethodSecurityMetadataSource = new RootBeanDefinition(
				DelegatingMethodSecurityMetadataSource.class);
		delegatingMethodSecurityMetadataSource.setSource(source);
		delegatingMethodSecurityMetadataSource.getConstructorArgumentValues()
				.addGenericArgumentValue(delegates);

		String id = pc.getReaderContext().generateBeanName(
				delegatingMethodSecurityMetadataSource);
		pc.registerBeanComponent(new BeanComponentDefinition(
				delegatingMethodSecurityMetadataSource, id));

		return new RuntimeBeanReference(id);
	}

	private void registerProtectPointcutPostProcessor(ParserContext parserContext,
			Map<String, List<ConfigAttribute>> pointcutMap,
			BeanReference mapBasedMethodSecurityMetadataSource, Object source) {
		RootBeanDefinition ppbp = new RootBeanDefinition(
				ProtectPointcutPostProcessor.class);
		ppbp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		ppbp.setSource(source);
		ppbp.getConstructorArgumentValues().addGenericArgumentValue(
				mapBasedMethodSecurityMetadataSource);
		ppbp.getPropertyValues().addPropertyValue("pointcutMap", pointcutMap);
		parserContext.getReaderContext().registerWithGeneratedName(ppbp);
	}

	private Map<String, List<ConfigAttribute>> parseProtectPointcuts(
			ParserContext parserContext, List<Element> protectPointcutElts) {
		Map<String, List<ConfigAttribute>> pointcutMap = new LinkedHashMap<String, List<ConfigAttribute>>();

		for (Element childElt : protectPointcutElts) {
			String accessConfig = childElt.getAttribute(ATT_ACCESS);
			String expression = childElt.getAttribute(ATT_EXPRESSION);

			if (!StringUtils.hasText(accessConfig)) {
				parserContext.getReaderContext().error("Access configuration required",
						parserContext.extractSource(childElt));
			}

			if (!StringUtils.hasText(expression)) {
				parserContext.getReaderContext().error("Pointcut expression required",
						parserContext.extractSource(childElt));
			}

			String[] attributeTokens = StringUtils
					.commaDelimitedListToStringArray(accessConfig);
			List<ConfigAttribute> attributes = new ArrayList<>(
					attributeTokens.length);

			for (String token : attributeTokens) {
				attributes.add(new SecurityConfig(token));
			}

			pointcutMap.put(expression, attributes);
		}

		return pointcutMap;
	}

	private BeanReference registerMethodSecurityInterceptor(ParserContext pc,
			String authMgrRef, String accessManagerId, String runAsManagerId,
			BeanReference metadataSource,
			List<BeanMetadataElement> afterInvocationProviders, Object source,
			boolean useAspectJ) {
		BeanDefinitionBuilder bldr = BeanDefinitionBuilder
				.rootBeanDefinition(useAspectJ ? AspectJMethodSecurityInterceptor.class
						: MethodSecurityInterceptor.class);
		bldr.getRawBeanDefinition().setSource(source);
		bldr.addPropertyReference("accessDecisionManager", accessManagerId);
		RootBeanDefinition authMgr = new RootBeanDefinition(
				AuthenticationManagerDelegator.class);
		authMgr.getConstructorArgumentValues().addGenericArgumentValue(authMgrRef);
		bldr.addPropertyValue("authenticationManager", authMgr);
		bldr.addPropertyValue("securityMetadataSource", metadataSource);

		if (StringUtils.hasText(runAsManagerId)) {
			bldr.addPropertyReference("runAsManager", runAsManagerId);
		}

		if (!afterInvocationProviders.isEmpty()) {
			BeanDefinition afterInvocationManager;
			afterInvocationManager = new RootBeanDefinition(
					AfterInvocationProviderManager.class);
			afterInvocationManager.getPropertyValues().addPropertyValue("providers",
					afterInvocationProviders);
			bldr.addPropertyValue("afterInvocationManager", afterInvocationManager);
		}

		BeanDefinition bean = bldr.getBeanDefinition();
		String id = pc.getReaderContext().generateBeanName(bean);
		pc.registerBeanComponent(new BeanComponentDefinition(bean, id));

		return new RuntimeBeanReference(id);
	}

	private void registerAdvisor(ParserContext parserContext, BeanReference interceptor,
			BeanReference metadataSource, Object source, String adviceOrder) {
		if (parserContext.getRegistry().containsBeanDefinition(
				BeanIds.METHOD_SECURITY_METADATA_SOURCE_ADVISOR)) {
			parserContext.getReaderContext().error(
					"Duplicate <global-method-security> detected.", source);
		}
		RootBeanDefinition advisor = new RootBeanDefinition(
				MethodSecurityMetadataSourceAdvisor.class);

		if (StringUtils.hasText(adviceOrder)) {
			advisor.getPropertyValues().addPropertyValue("order", adviceOrder);
		}

		// advisor must be an infrastructure bean as Spring's
		// InfrastructureAdvisorAutoProxyCreator will ignore it
		// otherwise
		advisor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		advisor.setSource(source);
		advisor.getConstructorArgumentValues().addGenericArgumentValue(
				interceptor.getBeanName());
		advisor.getConstructorArgumentValues().addGenericArgumentValue(metadataSource);
		advisor.getConstructorArgumentValues().addGenericArgumentValue(
				metadataSource.getBeanName());

		parserContext.getRegistry().registerBeanDefinition(
				BeanIds.METHOD_SECURITY_METADATA_SOURCE_ADVISOR, advisor);
	}

	private RootBeanDefinition registerWithDefaultRolePrefix(ParserContext pc, Class<? extends AbstractGrantedAuthorityDefaultsBeanFactory> beanFactoryClass) {
		RootBeanDefinition beanFactoryDefinition = new RootBeanDefinition(beanFactoryClass);
		String beanFactoryRef = pc.getReaderContext().generateBeanName(beanFactoryDefinition);
		pc.getRegistry().registerBeanDefinition(beanFactoryRef, beanFactoryDefinition);

		RootBeanDefinition bean = new RootBeanDefinition();
		bean.setFactoryBeanName(beanFactoryRef);
		bean.setFactoryMethodName("getBean");
		return bean;
	}

	/**
	 * Delays the lookup of the AuthenticationManager within MethodSecurityInterceptor, to
	 * prevent issues like SEC-933.
	 *
	 * @author Luke Taylor
	 * @since 3.0
	 */
	static final class AuthenticationManagerDelegator implements AuthenticationManager,
			BeanFactoryAware {
		private AuthenticationManager delegate;
		private final Object delegateMonitor = new Object();
		private BeanFactory beanFactory;
		private final String authMgrBean;

		AuthenticationManagerDelegator(String authMgrBean) {
			this.authMgrBean = StringUtils.hasText(authMgrBean) ? authMgrBean
					: BeanIds.AUTHENTICATION_MANAGER;
		}

		public Authentication authenticate(Authentication authentication)
				throws AuthenticationException {
			synchronized (delegateMonitor) {
				if (delegate == null) {
					Assert.state(beanFactory != null,
							() -> "BeanFactory must be set to resolve " + authMgrBean);
					try {
						delegate = beanFactory.getBean(authMgrBean,
								AuthenticationManager.class);
					}
					catch (NoSuchBeanDefinitionException e) {
						if (BeanIds.AUTHENTICATION_MANAGER.equals(e.getBeanName())) {
							throw new NoSuchBeanDefinitionException(
									BeanIds.AUTHENTICATION_MANAGER,
									AuthenticationManagerFactoryBean.MISSING_BEAN_ERROR_MESSAGE);
						}
						throw e;
					}
				}
			}

			return delegate.authenticate(authentication);
		}

		public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
			this.beanFactory = beanFactory;
		}
	}

	static class Jsr250MethodSecurityMetadataSourceBeanFactory extends AbstractGrantedAuthorityDefaultsBeanFactory {
		private Jsr250MethodSecurityMetadataSource source = new Jsr250MethodSecurityMetadataSource();

		public Jsr250MethodSecurityMetadataSource getBean() {
			source.setDefaultRolePrefix(this.rolePrefix);
			return source;
		}
	}

	static class DefaultMethodSecurityExpressionHandlerBeanFactory extends AbstractGrantedAuthorityDefaultsBeanFactory {
		private DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();

		public DefaultMethodSecurityExpressionHandler getBean() {
			handler.setDefaultRolePrefix(this.rolePrefix);
			return handler;
		}
	}

	static abstract class AbstractGrantedAuthorityDefaultsBeanFactory implements ApplicationContextAware {
		protected String rolePrefix = "ROLE_";

		@Override
		public final void setApplicationContext(ApplicationContext applicationContext)
				throws BeansException {
			String[] grantedAuthorityDefaultsBeanNames = applicationContext.getBeanNamesForType(GrantedAuthorityDefaults.class);
			if (grantedAuthorityDefaultsBeanNames.length == 1) {
				GrantedAuthorityDefaults grantedAuthorityDefaults = applicationContext.getBean(grantedAuthorityDefaultsBeanNames[0], GrantedAuthorityDefaults.class);
				this.rolePrefix = grantedAuthorityDefaults.getRolePrefix();
			}
		}
	}

	/**
	 * Delays setting a bean of a given name to be lazyily initialized until after all the
	 * beans are registered.
	 *
	 * @author Rob Winch
	 * @since 3.2
	 */
	private static final class LazyInitBeanDefinitionRegistryPostProcessor implements
			BeanDefinitionRegistryPostProcessor {
		private final String beanName;

		private LazyInitBeanDefinitionRegistryPostProcessor(String beanName) {
			this.beanName = beanName;
		}

		public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
				throws BeansException {
			if (!registry.containsBeanDefinition(beanName)) {
				return;
			}
			BeanDefinition beanDefinition = registry.getBeanDefinition(beanName);
			beanDefinition.setLazyInit(true);
		}

		public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
				throws BeansException {
		}
	}
}


/*
 * Copyright 2002-2018 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
 *
 *      https://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.server.header;

import reactor.core.publisher.Mono;

import org.springframework.util.Assert;
import org.springframework.web.server.ServerWebExchange;

/**
 * Writes the {@code Contet-Security-Policy} response header with configured policy
 * directives.
 *
 * @author Vedran Pavic
 * @since 5.1
 */
public final class ContentSecurityPolicyServerHttpHeadersWriter
		implements ServerHttpHeadersWriter {

	public static final String CONTENT_SECURITY_POLICY = "Content-Security-Policy";

	public static final String CONTENT_SECURITY_POLICY_REPORT_ONLY = "Content-Security-Policy-Report-Only";

	private String policyDirectives;

	private boolean reportOnly;

	private ServerHttpHeadersWriter delegate;

	@Override
	public Mono<Void> writeHttpHeaders(ServerWebExchange exchange) {
		return (this.delegate != null) ? this.delegate.writeHttpHeaders(exchange)
				: Mono.empty();
	}

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

	/**
	 * Set whether to include the {@code Content-Security-Policy-Report-Only} header in
	 * the response. Otherwise, defaults to the {@code Content-Security-Policy} header.
	 * @param reportOnly whether to only report policy violations
	 */
	public void setReportOnly(boolean reportOnly) {
		this.reportOnly = reportOnly;
		this.delegate = createDelegate();
	}

	private ServerHttpHeadersWriter createDelegate() {
		if (this.policyDirectives != null) {
			// @formatter:off
		return StaticServerHttpHeadersWriter.builder()
				.header(resolveHeader(this.reportOnly), this.policyDirectives)
				.build();
		// @formatter:on
		}
		else {
			return null;
		}
	}

	private static String resolveHeader(boolean reportOnly) {
		return reportOnly ? CONTENT_SECURITY_POLICY_REPORT_ONLY : CONTENT_SECURITY_POLICY;
	}

}


/*
 * Copyright 2002-2018 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
 *
 *      https://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.oauth2.client.endpoint;

import org.springframework.core.convert.converter.Converter;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2AuthorizationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestOperations;
import org.springframework.web.client.RestTemplate;

import java.util.Arrays;

/**
 * The default implementation of an {@link OAuth2AccessTokenResponseClient}
 * for the {@link AuthorizationGrantType#AUTHORIZATION_CODE authorization_code} grant.
 * This implementation uses a {@link RestOperations} when requesting
 * an access token credential at the Authorization Server's Token Endpoint.
 *
 * @author Joe Grandja
 * @since 5.1
 * @see OAuth2AccessTokenResponseClient
 * @see OAuth2AuthorizationCodeGrantRequest
 * @see OAuth2AccessTokenResponse
 * @see Section 4.1.3 Access Token Request (Authorization Code Grant)
 * @see Section 4.1.4 Access Token Response (Authorization Code Grant)
 */
public final class DefaultAuthorizationCodeTokenResponseClient implements OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> {
	private static final String INVALID_TOKEN_RESPONSE_ERROR_CODE = "invalid_token_response";

	private Converter<OAuth2AuthorizationCodeGrantRequest, RequestEntity<?>> requestEntityConverter =
			new OAuth2AuthorizationCodeGrantRequestEntityConverter();

	private RestOperations restOperations;

	public DefaultAuthorizationCodeTokenResponseClient() {
		RestTemplate restTemplate = new RestTemplate(Arrays.asList(
				new FormHttpMessageConverter(), new OAuth2AccessTokenResponseHttpMessageConverter()));
		restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
		this.restOperations = restTemplate;
	}

	@Override
	public OAuth2AccessTokenResponse getTokenResponse(OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest) {
		Assert.notNull(authorizationCodeGrantRequest, "authorizationCodeGrantRequest cannot be null");

		RequestEntity<?> request = this.requestEntityConverter.convert(authorizationCodeGrantRequest);

		ResponseEntity<OAuth2AccessTokenResponse> response;
		try {
			response = this.restOperations.exchange(request, OAuth2AccessTokenResponse.class);
		} catch (RestClientException ex) {
			OAuth2Error oauth2Error = new OAuth2Error(INVALID_TOKEN_RESPONSE_ERROR_CODE,
					"An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response: " + ex.getMessage(), null);
			throw new OAuth2AuthorizationException(oauth2Error, ex);
		}

		OAuth2AccessTokenResponse tokenResponse = response.getBody();

		if (CollectionUtils.isEmpty(tokenResponse.getAccessToken().getScopes())) {
			// As per spec, in Section 5.1 Successful Access Token Response
			// https://tools.ietf.org/html/rfc6749#section-5.1
			// If AccessTokenResponse.scope is empty, then default to the scope
			// originally requested by the client in the Token Request
			tokenResponse = OAuth2AccessTokenResponse.withResponse(tokenResponse)
					.scopes(authorizationCodeGrantRequest.getClientRegistration().getScopes())
					.build();
		}

		return tokenResponse;
	}

	/**
	 * Sets the {@link Converter} used for converting the {@link OAuth2AuthorizationCodeGrantRequest}
	 * to a {@link RequestEntity} representation of the OAuth 2.0 Access Token Request.
	 *
	 * @param requestEntityConverter the {@link Converter} used for converting to a {@link RequestEntity} representation of the Access Token Request
	 */
	public void setRequestEntityConverter(Converter<OAuth2AuthorizationCodeGrantRequest, RequestEntity<?>> requestEntityConverter) {
		Assert.notNull(requestEntityConverter, "requestEntityConverter cannot be null");
		this.requestEntityConverter = requestEntityConverter;
	}

	/**
	 * Sets the {@link RestOperations} used when requesting the OAuth 2.0 Access Token Response.
	 *
	 *

	 * NOTE: At a minimum, the supplied {@code restOperations} must be configured with the following:
	 *

	 *
{@link HttpMessageConverter}'s - {@link FormHttpMessageConverter} and {@link OAuth2AccessTokenResponseHttpMessageConverter}

	 *
{@link ResponseErrorHandler} - {@link OAuth2ErrorResponseErrorHandler}

	 *

	 *
	 * @param restOperations the {@link RestOperations} used when requesting the Access Token Response
	 */
	public void setRestOperations(RestOperations restOperations) {
		Assert.notNull(restOperations, "restOperations cannot be null");
		this.restOperations = restOperations;
	}
}