|
18 | 18 | */ |
19 | 19 | package de.rwth.idsg.steve.config; |
20 | 20 |
|
21 | | -import com.fasterxml.jackson.databind.ObjectMapper; |
22 | | -import com.google.common.base.Strings; |
23 | | -import de.rwth.idsg.steve.web.api.ApiControllerAdvice; |
24 | 21 | import lombok.RequiredArgsConstructor; |
25 | 22 | import lombok.extern.slf4j.Slf4j; |
26 | 23 | import org.springframework.context.annotation.Bean; |
27 | 24 | import org.springframework.context.annotation.Configuration; |
28 | 25 | import org.springframework.core.annotation.Order; |
29 | | -import org.springframework.http.HttpStatus; |
30 | | -import org.springframework.http.MediaType; |
31 | | -import org.springframework.security.authentication.AuthenticationManager; |
32 | | -import org.springframework.security.authentication.DisabledException; |
33 | 26 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; |
34 | 27 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; |
35 | 28 | import org.springframework.security.config.http.SessionCreationPolicy; |
36 | | -import org.springframework.security.core.Authentication; |
37 | | -import org.springframework.security.core.AuthenticationException; |
38 | 29 | import org.springframework.security.crypto.factory.PasswordEncoderFactories; |
39 | 30 | import org.springframework.security.crypto.password.DelegatingPasswordEncoder; |
40 | 31 | import org.springframework.security.crypto.password.PasswordEncoder; |
41 | | -import org.springframework.security.web.AuthenticationEntryPoint; |
42 | 32 | import org.springframework.security.web.SecurityFilterChain; |
43 | | -import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter; |
44 | | - |
45 | | -import jakarta.servlet.ServletException; |
46 | | -import jakarta.servlet.http.HttpServletRequest; |
47 | | -import jakarta.servlet.http.HttpServletResponse; |
48 | | - |
49 | | -import java.io.IOException; |
| 33 | +import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; |
50 | 34 |
|
51 | 35 | import static de.rwth.idsg.steve.SteveConfiguration.CONFIG; |
52 | 36 |
|
@@ -105,88 +89,12 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti |
105 | 89 |
|
106 | 90 | @Bean |
107 | 91 | @Order(1) |
108 | | - public SecurityFilterChain apiKeyFilterChain(HttpSecurity http, ObjectMapper jacksonObjectMapper) throws Exception { |
| 92 | + public SecurityFilterChain apiKeyFilterChain(HttpSecurity http, ApiAuthenticationManager apiAuthenticationManager) throws Exception { |
109 | 93 | return http.securityMatcher(CONFIG.getApiMapping() + "/**") |
110 | 94 | .csrf(k -> k.disable()) |
111 | 95 | .sessionManagement(k -> k.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) |
112 | | - .addFilter(new ApiKeyFilter()) |
| 96 | + .addFilter(new BasicAuthenticationFilter(apiAuthenticationManager, apiAuthenticationManager)) |
113 | 97 | .authorizeHttpRequests(k -> k.anyRequest().authenticated()) |
114 | | - .exceptionHandling(k -> k.authenticationEntryPoint(new ApiKeyAuthenticationEntryPoint(jacksonObjectMapper))) |
115 | 98 | .build(); |
116 | 99 | } |
117 | | - |
118 | | - /** |
119 | | - * Enable Web APIs only if both properties for API key are set. This has two consequences: |
120 | | - * 1) Backwards compatibility: Existing installations with older properties file, that does not include these two |
121 | | - * new keys, will not expose the APIs. Every call will be blocked by default. |
122 | | - * 2) If you want to expose your APIs, you MUST set these properties. This action activates authentication (i.e. |
123 | | - * APIs without authentication are not possible, and this is a good thing). |
124 | | - */ |
125 | | - public static class ApiKeyFilter extends AbstractPreAuthenticatedProcessingFilter implements AuthenticationManager { |
126 | | - |
127 | | - private final String headerKey; |
128 | | - private final String headerValue; |
129 | | - private final boolean isApiEnabled; |
130 | | - |
131 | | - public ApiKeyFilter() { |
132 | | - setAuthenticationManager(this); |
133 | | - |
134 | | - headerKey = CONFIG.getWebApi().getHeaderKey(); |
135 | | - headerValue = CONFIG.getWebApi().getHeaderValue(); |
136 | | - isApiEnabled = !Strings.isNullOrEmpty(headerKey) && !Strings.isNullOrEmpty(headerValue); |
137 | | - |
138 | | - if (!isApiEnabled) { |
139 | | - log.warn("Web APIs will not be exposed. Reason: 'webapi.key' and 'webapi.value' are not set in config file"); |
140 | | - } |
141 | | - } |
142 | | - |
143 | | - @Override |
144 | | - protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) { |
145 | | - if (!isApiEnabled) { |
146 | | - throw new DisabledException("Web APIs are not exposed"); |
147 | | - } |
148 | | - return request.getHeader(headerKey); |
149 | | - } |
150 | | - |
151 | | - @Override |
152 | | - protected Object getPreAuthenticatedCredentials(HttpServletRequest request) { |
153 | | - return null; |
154 | | - } |
155 | | - |
156 | | - @Override |
157 | | - public Authentication authenticate(Authentication authentication) throws AuthenticationException { |
158 | | - if (!isApiEnabled) { |
159 | | - throw new DisabledException("Web APIs are not exposed"); |
160 | | - } |
161 | | - |
162 | | - String principal = (String) authentication.getPrincipal(); |
163 | | - authentication.setAuthenticated(headerValue.equals(principal)); |
164 | | - return authentication; |
165 | | - } |
166 | | - } |
167 | | - |
168 | | - public static class ApiKeyAuthenticationEntryPoint implements AuthenticationEntryPoint { |
169 | | - |
170 | | - private final ObjectMapper mapper; |
171 | | - |
172 | | - private ApiKeyAuthenticationEntryPoint(ObjectMapper mapper) { |
173 | | - this.mapper = mapper; |
174 | | - } |
175 | | - |
176 | | - @Override |
177 | | - public void commence(HttpServletRequest request, HttpServletResponse response, |
178 | | - AuthenticationException authException) throws IOException, ServletException { |
179 | | - HttpStatus status = HttpStatus.UNAUTHORIZED; |
180 | | - |
181 | | - var apiResponse = ApiControllerAdvice.createResponse( |
182 | | - request.getRequestURL().toString(), |
183 | | - status, |
184 | | - "Full authentication is required to access this resource" |
185 | | - ); |
186 | | - |
187 | | - response.setStatus(status.value()); |
188 | | - response.setContentType(MediaType.APPLICATION_JSON_VALUE); |
189 | | - response.getWriter().print(mapper.writeValueAsString(apiResponse)); |
190 | | - } |
191 | | - } |
192 | 100 | } |
0 commit comments