1 package pk.lucidxpo.ynami.spring.security;
2
3 import org.springframework.beans.factory.annotation.Autowired;
4 import org.springframework.beans.factory.annotation.Value;
5 import org.springframework.context.annotation.Bean;
6 import org.springframework.context.annotation.Configuration;
7 import org.springframework.security.authentication.AuthenticationManager;
8 import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
9 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
10 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
11 import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
12 import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;
13 import org.springframework.security.config.annotation.web.configurers.LogoutConfigurer;
14 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
15 import org.springframework.security.web.DefaultSecurityFilterChain;
16 import org.springframework.security.web.SecurityFilterChain;
17 import org.springframework.security.web.header.writers.DelegatingRequestMatcherHeaderWriter;
18 import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter;
19 import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
20 import org.springframework.security.web.util.matcher.RequestMatcher;
21 import pk.lucidxpo.ynami.spring.aspect.FeatureAssociation;
22 import pk.lucidxpo.ynami.spring.features.FeatureManagerWrappable;
23 import pk.lucidxpo.ynami.utils.ProfileManager;
24
25 import static org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter.XFrameOptionsMode.SAMEORIGIN;
26 import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher;
27 import static pk.lucidxpo.ynami.spring.features.FeatureToggles.WEB_SECURITY;
28
29 @Configuration
30 @EnableWebSecurity
31 public class SecurityConfig {
32 public static final String LOGIN_PAGE_URL = "/login.html";
33 static final String LOGIN_PROCESSING_URL = "/perform_login";
34 public static final String LOGIN_FAILURE_URL = LOGIN_PAGE_URL + "?error=true";
35
36 static final String LOGOUT_URL = "/perform_logout";
37 static final String LOGOUT_SUCCESS_URL = LOGIN_PAGE_URL + "?logout";
38
39 private static final AntPathRequestMatcher[] ENDPOINTS_WHITELIST = {
40 antMatcher("/css/**"),
41 antMatcher("/js/**"),
42 antMatcher("/img/**"),
43 antMatcher("/webjars/**"),
44 antMatcher("/favicon.ico")
45 };
46
47
48
49
50
51
52
53 private static final String CONTENT_POLICY_DIRECTIVES = "default-src 'self'"
54 + "; form-action 'self'"
55 + "; object-src 'none'"
56 + "; frame-ancestors 'none'"
57 + "; upgrade-insecure-requests"
58 + "; block-all-mixed-content"
59 + "; report-uri /report"
60 + "; report-to csp-violation-report";
61
62 private final String h2ConsolePattern;
63 private final ProfileManager profileManager;
64 private final FeatureManagerWrappable featureManager;
65
66 @Autowired
67 public SecurityConfig(@Value("${spring.h2.console.path:/h2-console}") final String h2ConsolePath,
68 final ProfileManager profileManager,
69 final FeatureManagerWrappable featureManager) {
70 this.profileManager = profileManager;
71 this.featureManager = featureManager;
72 this.h2ConsolePattern = h2ConsolePath.endsWith("/") ? h2ConsolePath + "**" : h2ConsolePath + "/**";
73 }
74
75 @Bean
76 public BCryptPasswordEncoder passwordEncoder() {
77 return new BCryptPasswordEncoder();
78 }
79
80 @Bean
81
82 public SecurityFilterChain filterChain(final HttpSecurity http) throws Exception {
83
84
85
86
87
88
89
90
91
92
93 if (profileManager.isH2Active()) {
94 setupH2ConsoleSecurity(http);
95 }
96
97 if (!featureManager.isActive(WEB_SECURITY)) {
98 return configureInsecureAccess(http);
99 }
100
101 return http
102 .authorizeHttpRequests((auth) -> auth.requestMatchers(ENDPOINTS_WHITELIST).permitAll())
103
104 .authorizeHttpRequests((auth) -> auth.anyRequest().authenticated())
105
106
107
108
109 .formLogin(this::configureFormLogin)
110
111 .logout(this::configureFormLogout)
112
113 .build();
114 }
115
116 @Bean
117 @FeatureAssociation(value = WEB_SECURITY)
118 public AuthenticationManager authenticationManager(
119 final AuthenticationConfiguration authenticationConfiguration) throws Exception {
120 return authenticationConfiguration.getAuthenticationManager();
121 }
122
123
124 public void setupH2ConsoleSecurity(final HttpSecurity http) throws Exception {
125 final RequestMatcher h2PathMatcher = new AntPathRequestMatcher(h2ConsolePattern);
126 final XFrameOptionsHeaderWriter sameOriginXFrameHeaderWriter = new XFrameOptionsHeaderWriter(SAMEORIGIN);
127
128
129
130 final DelegatingRequestMatcherHeaderWriter headerWriter =
131 new DelegatingRequestMatcherHeaderWriter(h2PathMatcher, sameOriginXFrameHeaderWriter);
132 http
133 .authorizeHttpRequests(auth -> auth.requestMatchers(h2PathMatcher).permitAll())
134 .headers(headers -> headers.addHeaderWriter(headerWriter))
135 ;
136 }
137
138 private DefaultSecurityFilterChain configureInsecureAccess(final HttpSecurity http) throws Exception {
139 return http
140
141
142
143
144
145 .csrf(AbstractHttpConfigurer::disable)
146 .authorizeHttpRequests((auth) -> auth.anyRequest().permitAll())
147 .build();
148 }
149
150 private void configureFormLogin(final FormLoginConfigurer<HttpSecurity> formLoginConfigurer) {
151 formLoginConfigurer
152 .loginPage(LOGIN_PAGE_URL)
153
154
155 .loginProcessingUrl(LOGIN_PROCESSING_URL)
156 .permitAll()
157 .failureUrl(LOGIN_FAILURE_URL);
158 }
159
160 private void configureFormLogout(LogoutConfigurer<HttpSecurity> logoutConfigurer) {
161 logoutConfigurer
162 .logoutUrl(LOGOUT_URL)
163 .logoutSuccessUrl(LOGOUT_SUCCESS_URL);
164 }
165
166
167
168
169
170
171
172
173
174
175
176 }