/*
 * Copyright 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
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.codenergic.theskeleton.core.security;

import org.codenergic.theskeleton.user.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;
import java.util.Collections;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@ConditionalOnWebApplication
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
	@Autowired
	private UserService userService;
	@Autowired
	private PasswordEncoder passwordEncoder;

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.authorizeRequests()
				.antMatchers("/oauth/authorize").authenticated()
				.and()
			.formLogin().permitAll()
				.loginPage("/login")
				.loginProcessingUrl("/auth/login")
				.failureUrl("/login?error")
				.and()
			.rememberMe()
				.rememberMeParameter("remember-me")
				.and()
			.logout()
				.logoutUrl("/auth/logout")
				.invalidateHttpSession(true)
				.permitAll()
				.and()
			.headers()
				.frameOptions().sameOrigin()
				.and()
			.sessionManagement()
				.sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
				.and()
			.csrf()
				.requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize"))
				.disable()
			.cors().configurationSource(corsConfigurationSource());
	}

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(userService)
				.passwordEncoder(passwordEncoder);
	}

	@Bean
	@Override
	public AuthenticationManager authenticationManagerBean() throws Exception {
		return super.authenticationManagerBean();
	}

	/**
	 * <a href="https://github.com/spring-projects/spring-boot/issues/5834#issuecomment-296370088">See explanation here</a>
	 * @return
	 */
	@Bean
	public CorsConfigurationSource corsConfigurationSource() {
		final CorsConfiguration configuration = new CorsConfiguration();
		configuration.setAllowedOrigins(Collections.singletonList("*"));
		configuration.setAllowedMethods(Arrays.asList("HEAD", "GET", "POST", "PUT", "DELETE", "PATCH"));
		// setAllowCredentials(true) is important, otherwise:
		// The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
		configuration.setAllowCredentials(true);
		// setAllowedHeaders is important! Without it, OPTIONS preflight request
		// will fail with 403 Invalid CORS request
		configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type"));
		final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
		source.registerCorsConfiguration("/**", configuration);
		return source;
	}
}

@@ -856,24 +856,92 @@
 pplication;%0A
+import org.springframework.boot.web.servlet.FilterRegistrationBean;%0A
 import org.s
@@ -1033,32 +1033,73 @@
 .Configuration;%0A
+import org.springframework.core.Ordered;%0A
 import org.sprin
@@ -1916,69 +1916,8 @@
 on;%0A
-import org.springframework.web.cors.CorsConfigurationSource;%0A
 impo
@@ -1980,16 +1980,66 @@
 nSource;
+%0Aimport org.springframework.web.filter.CorsFilter;
 %0A%0Aimport
@@ -3118,55 +3118,8 @@
 rs()
-.configurationSource(corsConfigurationSource())
 ;%0A%09%7D
@@ -3566,20 +3566,8 @@
 /a%3E%0A
-%09 * @return%0A
 %09 */
@@ -3586,55 +3586,45 @@
 lic 
-CorsConfigurationSource corsConfigurationSource
+FilterRegistrationBean corsFilterBean
 () %7B
@@ -4482,21 +4482,166 @@
 ;%0A%09%09
-return source
+FilterRegistrationBean corsFilter = new FilterRegistrationBean(new CorsFilter(source));%0A%09%09corsFilter.setOrder(Ordered.HIGHEST_PRECEDENCE);%0A%09%09return corsFilter
 ;%0A%09%7D

Fix #43: CORS filter does not work when oauth2 token are invalid or expired