diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 92cb1456b70b838b1aca9996c6e07e50f2294148..ecf98a13c6466e75440427b8d63769dd1daae44e 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -137,7 +137,6 @@ services: SERVER_NAME: dbrepo.ossdip.at SPRING_PROFILES_ACTIVE: sandbox KEY_STORE_PASSWORD: ${KEY_STORE_PASSWORD} - SAML_SIGN_KEY: ${SAML_SIGN_KEY} ports: - "9097:9097" depends_on: diff --git a/fda-authentication-service/README.md b/fda-authentication-service/README.md index 94b71d2f46c71cb10dce42c1265895949889fe74..41b35d9b8f46e6d188f9460f52c9ddf0f864017a 100644 --- a/fda-authentication-service/README.md +++ b/fda-authentication-service/README.md @@ -20,7 +20,7 @@ set KEY_STORE_PASSWORD "..." The key store is a secure container that contains the SSL/TLS certificate: -1. Let's Encrypt private key for `dbrepo.ossdip.at` with alias `1` +1. Let's Encrypt private key for `dbrepo.ossdip.at` with alias `ssl` ## Development diff --git a/fda-authentication-service/rest-service/src/main/java/at/tuwien/config/SamlConfig.java b/fda-authentication-service/rest-service/src/main/java/at/tuwien/config/SamlConfig.java index 06910b2d0a4c50bb2a7f386180d2c06222e49295..d4a86e3efe06b774393477e1c743fca821e4dc4a 100644 --- a/fda-authentication-service/rest-service/src/main/java/at/tuwien/config/SamlConfig.java +++ b/fda-authentication-service/rest-service/src/main/java/at/tuwien/config/SamlConfig.java @@ -1,5 +1,6 @@ package at.tuwien.config; +import lombok.extern.log4j.Log4j2; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; import org.apache.velocity.app.VelocityEngine; @@ -35,12 +36,17 @@ import org.springframework.security.saml.util.VelocityFactory; import org.springframework.security.saml.websso.*; import org.springframework.security.web.*; import org.springframework.security.web.access.channel.ChannelProcessingFilter; +import org.springframework.security.web.authentication.logout.LogoutHandler; +import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; +import org.springframework.security.web.csrf.CsrfFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import java.io.IOException; import java.util.*; +@Log4j2 @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(securedEnabled = true) @@ -155,7 +161,7 @@ public class SamlConfig extends WebSecurityConfigurerAdapter { extendedMetadata.setIdpDiscoveryEnabled(true); extendedMetadata.setSignMetadata(true); extendedMetadata.setSigningKey(samlSignKey); -// extendedMetadata.setEncryptionKey(samlSignKey); + extendedMetadata.setEncryptionKey(samlSignKey); return extendedMetadata; } @@ -177,7 +183,9 @@ public class SamlConfig extends WebSecurityConfigurerAdapter { public CachingMetadataManager metadata(ExtendedMetadataDelegate extendedMetadataDelegate) throws MetadataProviderException { final List<MetadataProvider> providers = new ArrayList<>(); providers.add(extendedMetadataDelegate); - return new CachingMetadataManager(providers); + CachingMetadataManager metadataManager = new CachingMetadataManager(providers); + metadataManager.setDefaultIDP(idpProviderMetadata); + return metadataManager; } @Bean @@ -227,17 +235,39 @@ public class SamlConfig extends WebSecurityConfigurerAdapter { return new SAMLProcessorImpl(bindings); } + @Bean + public SecurityContextLogoutHandler logoutHandler() { + SecurityContextLogoutHandler logoutHandler = new SecurityContextLogoutHandler(); + logoutHandler.setInvalidateHttpSession(true); + logoutHandler.setClearAuthentication(true); + return logoutHandler; + } + + @Bean + public SAMLLogoutFilter samlLogoutFilter() { + return new SAMLLogoutFilter(successLogoutHandler(), + new LogoutHandler[]{logoutHandler()}, + new LogoutHandler[]{logoutHandler()}); + } + + @Bean + public SAMLLogoutProcessingFilter samlLogoutProcessingFilter() { + return new SAMLLogoutProcessingFilter(successLogoutHandler(), logoutHandler()); + } + @Bean public FilterChainProxy samlFilter() throws Exception { - final List<SecurityFilterChain> chains = new ArrayList<>(); - chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/login/**"), - samlEntryPoint())); - chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/metadata/**"), - metadataDisplayFilter())); + List<SecurityFilterChain> chains = new ArrayList<>(); chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SSO/**"), samlWebSSOProcessingFilter())); chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/discovery/**"), samlIDPDiscovery())); + chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/login/**"), + samlEntryPoint())); + chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/logout/**"), + samlLogoutFilter())); + chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SingleLogout/**"), + samlLogoutProcessingFilter())); return new FilterChainProxy(chains); } @@ -256,13 +286,23 @@ public class SamlConfig extends WebSecurityConfigurerAdapter { http.csrf() .disable(); http.addFilterBefore(metadataGeneratorFilter(), ChannelProcessingFilter.class) - .addFilterAfter(samlFilter(), BasicAuthenticationFilter.class); - /* allow metadata and saml stuff */ + .addFilterAfter(samlFilter(), BasicAuthenticationFilter.class) + .addFilterBefore(samlFilter(), CsrfFilter.class); http.authorizeRequests() - .antMatchers("/saml/**").permitAll() - .antMatchers("/health").permitAll() - .antMatchers("/error").permitAll() - .anyRequest().authenticated(); + .antMatchers("/").permitAll() +// .antMatchers("/saml/**").permitAll() +// .antMatchers("/health").permitAll() +// .antMatchers("/error").permitAll() + .anyRequest() + .authenticated(); + http.logout() + .addLogoutHandler((request, response, authentication) -> { + try { + response.sendRedirect("/saml/logout"); + } catch (IOException e) { + log.error("Failed to logout: {}", e.getMessage()); + } + }); } @Bean diff --git a/fda-authentication-service/rest-service/src/main/java/at/tuwien/config/SecurityConfig.java b/fda-authentication-service/rest-service/src/main/java/at/tuwien/config/SecurityConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..aaa72f6a2fcc534e235a60447dac43d988f479ee --- /dev/null +++ b/fda-authentication-service/rest-service/src/main/java/at/tuwien/config/SecurityConfig.java @@ -0,0 +1,16 @@ +package at.tuwien.config; + +import at.tuwien.service.AuthenticationService; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.saml.SAMLAuthenticationProvider; + +@Configuration +public class SecurityConfig { + + @Bean + public SAMLAuthenticationProvider samlAuthenticationProvider() { + return new AuthenticationService(); + } + +} diff --git a/fda-authentication-service/rest-service/src/main/java/at/tuwien/endpoints/TestEndpoint.java b/fda-authentication-service/rest-service/src/main/java/at/tuwien/endpoints/TestEndpoint.java new file mode 100644 index 0000000000000000000000000000000000000000..7753941f5bb815178641c8988eb79f37cc5e88de --- /dev/null +++ b/fda-authentication-service/rest-service/src/main/java/at/tuwien/endpoints/TestEndpoint.java @@ -0,0 +1,24 @@ +package at.tuwien.endpoints; + +import lombok.extern.log4j.Log4j2; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@Log4j2 +@RestController("/api/test") +@CrossOrigin(origins = "*") +public class TestEndpoint { + + @GetMapping("/") + public String index() { + final Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + log.debug("auth {}", auth); + log.debug("auth principal {}", auth.getPrincipal()); + return "hello"; + } + +} \ No newline at end of file diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/service/AuthenticationService.java b/fda-authentication-service/services/src/main/java/at/tuwien/service/AuthenticationService.java new file mode 100644 index 0000000000000000000000000000000000000000..5fe5755f346a014ed8a2c76682fc92cb1abf2740 --- /dev/null +++ b/fda-authentication-service/services/src/main/java/at/tuwien/service/AuthenticationService.java @@ -0,0 +1,25 @@ +package at.tuwien.service; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.providers.ExpiringUsernameAuthenticationToken; +import org.springframework.security.saml.SAMLAuthenticationProvider; +import org.springframework.security.saml.SAMLCredential; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; + +@Service +public class AuthenticationService extends SAMLAuthenticationProvider { + + @Override + public Collection<? extends GrantedAuthority> getEntitlements(SAMLCredential credential, Object userDetail) { + if (userDetail instanceof ExpiringUsernameAuthenticationToken) { + return new ArrayList<>(((ExpiringUsernameAuthenticationToken) userDetail) + .getAuthorities()); + } else { + return Collections.emptyList(); + } + } +} \ No newline at end of file diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/service/SamlUserDetailsService.java b/fda-authentication-service/services/src/main/java/at/tuwien/service/SamlUserDetailsService.java deleted file mode 100644 index 874fe51d64dced70e8dbf00759f70e3c3d2cefa3..0000000000000000000000000000000000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/service/SamlUserDetailsService.java +++ /dev/null @@ -1,23 +0,0 @@ -package at.tuwien.service; - -import lombok.extern.log4j.Log4j2; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.security.saml.SAMLCredential; -import org.springframework.security.saml.userdetails.SAMLUserDetailsService; -import org.springframework.stereotype.Service; - -import java.util.List; - -@Log4j2 -@Service -public class SamlUserDetailsService implements SAMLUserDetailsService { - - @Override - public Object loadUserBySAML(SAMLCredential credential) throws UsernameNotFoundException { - String id = credential.getNameID().getValue(); - /* right now we only support users */ - return new User(id, "empty", true, true, true, true, List.of(new SimpleGrantedAuthority("ROLE_USER"))); - } -}