/*
 * Copyright 2002-2019 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 org.junit.Rule;
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.test.SpringTestRule;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

/**
 * Tests to verify that all the functionality of &lt;intercept-url&gt; attributes is
 * present
 *
 * @author Rob Winch
 * @author Josh Cummings
 *
 */
public class NamespaceHttpInterceptUrlTests {

	@Rule
	public final SpringTestRule spring = new SpringTestRule();

	@Autowired
	MockMvc mvc;

	@Test
	public void unauthenticatedRequestWhenUrlRequiresAuthenticationThenBehaviorMatchesNamespace() throws Exception {
		this.spring.register(HttpInterceptUrlConfig.class).autowire();
		this.mvc.perform(get("/users")).andExpect(status().isForbidden());
	}

	@Test
	public void authenticatedRequestWhenUrlRequiresElevatedPrivilegesThenBehaviorMatchesNamespace() throws Exception {
		this.spring.register(HttpInterceptUrlConfig.class).autowire();
		MockHttpServletRequestBuilder requestWithUser = get("/users").with(authentication(user("ROLE_USER")));
		this.mvc.perform(requestWithUser).andExpect(status().isForbidden());
	}

	@Test
	public void authenticatedRequestWhenAuthorizedThenBehaviorMatchesNamespace() throws Exception {
		this.spring.register(HttpInterceptUrlConfig.class, BaseController.class).autowire();
		MockHttpServletRequestBuilder requestWithAdmin = get("/users").with(authentication(user("ROLE_ADMIN")));
		this.mvc.perform(requestWithAdmin).andExpect(status().isOk()).andReturn();
	}

	@Test
	public void requestWhenMappedByPostInterceptUrlThenBehaviorMatchesNamespace() throws Exception {
		this.spring.register(HttpInterceptUrlConfig.class, BaseController.class).autowire();
		MockHttpServletRequestBuilder getWithUser = get("/admin/post").with(authentication(user("ROLE_USER")));
		this.mvc.perform(getWithUser).andExpect(status().isOk());
		MockHttpServletRequestBuilder postWithUser = post("/admin/post").with(authentication(user("ROLE_USER")));
		this.mvc.perform(postWithUser).andExpect(status().isForbidden());
		MockHttpServletRequestBuilder requestWithAdmin = post("/admin/post").with(csrf())
				.with(authentication(user("ROLE_ADMIN")));
		this.mvc.perform(requestWithAdmin).andExpect(status().isOk());
	}

	@Test
	public void requestWhenRequiresChannelThenBehaviorMatchesNamespace() throws Exception {
		this.spring.register(HttpInterceptUrlConfig.class).autowire();
		this.mvc.perform(get("/login")).andExpect(redirectedUrl("https://localhost/login"));
		this.mvc.perform(get("/secured/a")).andExpect(redirectedUrl("https://localhost/secured/a"));
		this.mvc.perform(get("https://localhost/user")).andExpect(redirectedUrl("http://localhost/user"));
	}

	private static Authentication user(String role) {
		return new UsernamePasswordAuthenticationToken("user", null, AuthorityUtils.createAuthorityList(role));
	}

	@EnableWebSecurity
	static class HttpInterceptUrlConfig extends WebSecurityConfigurerAdapter {

		@Override
		protected void configure(HttpSecurity http) throws Exception {
			// @formatter:off
			http
				.authorizeRequests().antMatchers(
					// the line below is similar to intercept-url@pattern:
					//    <intercept-url pattern="/users**" access="hasRole('ROLE_ADMIN')"/>
					//" access="hasRole('ROLE_ADMIN')"/>
"/users**", "/sessions/**").hasRole("ADMIN").antMatchers(
					// the line below is similar to intercept-url@method:
					//    <intercept-url pattern="/admin/post" access="hasRole('ROLE_ADMIN')" method="POST"/>
					//" access="hasRole('ROLE_ADMIN')" method="POST"/>
HttpMethod.POST, "/admin/post", "/admin/another-post/**").hasRole("ADMIN")
					.antMatchers("/signup").permitAll()
					.anyRequest().hasRole("USER")
					.and()
				.requiresChannel().antMatchers(
					// NOTE: channel security is configured separately of authorization (i.e. intercept-url@access
					// the line below is similar to intercept-url@requires-channel="https":
					//    <intercept-url pattern="/login" requires-channel="https"/>
					//" requires-channel="https"/>
"/login", "/secured/**").requiresSecure().anyRequest()..requiresInsecure();
			// @formatter:on
		}

		@Override
		protected void configure(AuthenticationManagerBuilder auth) throws Exception {
			// @formatter:off
			auth
				.inMemoryAuthentication()
					.withUser("user").password("password").roles("USER").and()
					.withUser("admin").password("password").roles("USER", "ADMIN");
			// @formatter:on
		}

	}

	@RestController
	static class BaseController {

		@GetMapping("/users")
		String users() {
			return "ok";
		}

		@GetMapping("/sessions")
		String sessions() {
			return "sessions";
		}

		@RequestMapping("/admin/post")
		String adminPost() {
			return "adminPost";
		}

		@GetMapping("/admin/another-post")
		String adminAnotherPost() {
			return "adminAnotherPost";
		}

		@GetMapping("/signup")
		String signup() {
			return "signup";
		}

	}

}

@@ -5952,32 +5952,56 @@
 l().antMatchers(
+%22/login%22, %22/secured/**%22)
 %0A%09%09%09%09%09// NOTE: c
@@ -6272,32 +6272,12 @@
 %22/%3E%0A
-%22/login%22, %22/secured/**%22)
+%09%09%09%09
 .req
@@ -6303,17 +6303,16 @@
 quest().
-.
 requires

Fix NamespaceHttpInterceptUrlTests