Newer
Older
package eu.hbp.mip.configuration;
import eu.hbp.mip.utils.CustomLoginUrlAuthenticationEntryPoint;
import eu.hbp.mip.utils.HTTPUtil;
import eu.hbp.mip.utils.UserActionLogging;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.security.oauth2.resource.AuthoritiesExtractor;
import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties;
import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoTokenServices;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.client.OAuth2ClientContext;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter;
import org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter;
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client;
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.csrf.CsrfFilter;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.security.web.csrf.CsrfTokenRepository;
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.WebUtils;
import org.springframework.security.oauth2.client.resource.BaseOAuth2ProtectedResourceDetails;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
//newlyadded for logout
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import java.net.URI;
// See https://spring.io/guides/tutorials/spring-boot-oauth2/ for reference about configuring OAuth2 login
// also http://cscarioni.blogspot.ch/2013/04/pro-spring-security-and-oauth-2.html
@Configuration
@EnableOAuth2Client
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(SecurityConfiguration.class);
@Autowired
private OAuth2ClientContext oauth2ClientContext;
/**
* Enable HBP collab authentication (1) or disable it (0). Default is 1
*/
@Value("#{'${hbp.authentication.enabled:1}'}")
private boolean authentication;
/**
* Absolute URL to redirect to when login is required
*/
@Value("#{'${frontend.loginUrl:/login/hbp}'}")
private String loginUrl;
/**
* Absolute URL to redirect to when logout is required
*/
@Value("#{'${hbp.client.logoutUri:http://88.197.53.10:8095/auth/realms/Demo/protocol/openid-connect/logout}'}")
private String logoutUri;
/**
* Absolute URL to redirect to after successful login
*/
@Value("#{'${frontend.redirectAfterLoginUrl:http://frontend/home}'}")
private String frontendRedirectAfterLogin;
/**
* Absolute URL to redirect to after logout has occurred
*/
@Value("#{'${frontend.redirectAfterLogoutUrl:/login/hbp}'}")
private String redirectAfterLogoutUrl;
/**
* URL to revoke auth token
*/
@Value("#{'${hbp.resource.revokeTokenUri:https://services.humanbrainproject.eu/oidc/revoke}'}")
private String revokeTokenURI;
// private HttpServletRequest request;
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http.addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class);
if (authentication) {
http.antMatcher("/**")
.authorizeRequests()
.antMatchers(
"/", "/login/**", "/health/**", "/info/**", "/metrics/**", "/trace/**", "/frontend/**", "/webjars/**", "/v2/api-docs", "/swagger-ui.html", "/swagger-resources/**"
)
.permitAll()
.antMatchers("/galaxy*","/galaxy/*").hasRole("Data Manager")
//.anyRequest().authenticated()
.anyRequest().hasRole("Researcher")
.and().exceptionHandling().authenticationEntryPoint(new CustomLoginUrlAuthenticationEntryPoint(loginUrl))
.and().logout().addLogoutHandler(authLogoutHandler()).logoutSuccessUrl(redirectAfterLogoutUrl)
.and().logout().permitAll()
.and().csrf().ignoringAntMatchers("/logout").csrfTokenRepository(csrfTokenRepository())
.and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class)
.addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);
}
else {
//keycloak
//KeycloakConfiguration.getKeycloakSecurityContext();
// .antMatchers("/**").permitAll().and().csrf().disable();
}
}
private Filter ssoFilter() {
OAuth2ClientAuthenticationProcessingFilter hbpFilter = new OAuth2ClientAuthenticationProcessingFilter("/login/hbp");
OAuth2RestTemplate hbpTemplate = new OAuth2RestTemplate(hbp(), oauth2ClientContext);
hbpFilter.setAuthenticationSuccessHandler(new SimpleUrlAuthenticationSuccessHandler(frontendRedirectAfterLogin));
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
hbpFilter.setRestTemplate(hbpTemplate);
hbpFilter.setTokenServices(new UserInfoTokenServices(hbpResource().getUserInfoUri(), hbp().getClientId()));
return hbpFilter;
}
@Bean
public FilterRegistrationBean oauth2ClientFilterRegistration(
OAuth2ClientContextFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(filter);
registration.setOrder(-100);
return registration;
}
@Bean(name="hbp")
@ConfigurationProperties("hbp.client")
public BaseOAuth2ProtectedResourceDetails hbp() {
return new AuthorizationCodeResourceDetails();
}
@Bean(name="hbpResource")
@ConfigurationProperties("hbp.resource")
public ResourceServerProperties hbpResource() {
return new ResourceServerProperties();
}
public boolean isAuthentication() {
return authentication;
}
public String getFrontendRedirectAfterLogin() {
return frontendRedirectAfterLogin;
}
private Filter csrfHeaderFilter() {
return new OncePerRequestFilter() {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
if (csrf != null) {
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
String token = csrf.getToken();
if (cookie == null || token != null && !token.equals(cookie.getValue())) {
cookie = new Cookie("XSRF-TOKEN", token);
cookie.setPath("/");
response.addCookie(cookie);
}
}
filterChain.doFilter(request, response);
}
};
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
private class CustomLogoutHandler implements LogoutHandler {
@Override
public void logout(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) {
// Hackish way of accessing to this information...
final UserInfo userInfo = (UserInfo) httpServletRequest.getSession().getAttribute("userInfo");
if (userInfo != null) {
userInfo.setFakeAuth(false);
}
if (oauth2ClientContext == null || oauth2ClientContext.getAccessToken() == null)
{
return;
}
String idToken = oauth2ClientContext.getAccessToken().getAdditionalInformation().get("id_token").toString();
StringBuilder query = new StringBuilder();
query.append("{");
query.append("\"token\":");
query.append("\"").append(idToken).append("\"");
query.append("}");
try {
int responseCode = HTTPUtil.sendPost(revokeTokenURI, query.toString(), new StringBuilder());
if (responseCode != 200)
{
LOGGER.warn("Cannot send request to OIDC server for revocation ! ");
}
else{
LOGGER.info("Should be logged out");
}
} catch (IOException e) {
LOGGER.warn("Cannot notify logout to OIDC server !");
LOGGER.trace("Cannot notify logout", e);
}
}
}
@Bean
public AuthoritiesExtractor keycloakAuthoritiesExtractor() {
return new KeycloakAuthoritiesExtractor();
}
public class KeycloakAuthoritiesExtractor
implements AuthoritiesExtractor {
@Override
public List<GrantedAuthority> extractAuthorities
(Map<String, Object> map) {
return AuthorityUtils
.commaSeparatedStringToAuthorityList(asAuthorities(map));
}
private String asAuthorities(Map<String, Object> map) {
List<String> authorities = new ArrayList<>();
// authorities.add("BAELDUNG_USER");
List<LinkedHashMap<String, String>> authz;
authz = (List<LinkedHashMap<String, String>>) map.get("authorities");
for (LinkedHashMap<String, String> entry : authz) {
authorities.add(entry.get("authority"));
}
return String.join(",", authorities);
}
}
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
private LogoutHandler authLogoutHandler() {
return (request, response, authentication) -> {
logout();
};
}
public void logout() {
// POSTするリクエストパラメーターを作成
UserActionLogging.LogAction("refresh token ", this.oauth2ClientContext.getAccessToken().getRefreshToken().getValue());
RestTemplate restTemplate = new RestTemplate();
MultiValueMap<String, String> formParams = new LinkedMultiValueMap<>();
formParams.add("client_id", hbp().getClientId());
// formParams.add("client_secret", registration.getClientSecret());
formParams.add("refresh_token", this.oauth2ClientContext.getAccessToken().getRefreshToken().getValue());
// リクエストヘッダーを作成
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
// リクエストを作成
RequestEntity<MultiValueMap<String, String>> requestEntity =
new RequestEntity<>(formParams, httpHeaders, HttpMethod.POST,
//URI.create("http://88.197.53.10:8095/auth/realms/Demo/protocol/openid-connect/logout")); //todo make this parameter
URI.create(logoutUri)); //todo make this parameter
// POSTリクエスト送信(ログアウト実行)
ResponseEntity<String> responseEntity = restTemplate.exchange(requestEntity, String.class);
}