Using Spring Security SAML Siteminder - spring-security

I'm trying to integrate siteminder with Spring Security.
Users already connected to Windows (Domain Authentification) can access to the WebApplication ( Tomcat - RHEL ) with the current Windows username, roles are managed by the WebApp it self.
it is possible ? it there any example that might help me to kick off this project
i used to read about Spring Security but i can't figure out how to implement this configuration.
https://docs.spring.io/spring-security-saml/docs/1.0.0.RELEASE/reference/html/chapter-quick-start.html

We are in the process of rewriting Spring Security SAML for a better user experience.
In the meantime, you can explore our milestone release 2.0.0.M24 being the latest.
Gradle URL
repositories {
...
maven { url "https://repo.spring.io/milestone" }
...
}
Import the dependency
compile "org.springframework.security.extensions:spring-security-saml2-core:2.0.0.M24")
There is a sample project available[1] if you want to try it out. It's part of the core repository.
A github contributor has also created a separate sample and guide
This should get you started.
The future will look something like this (not yet released):
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
//application security
.authorizeRequests()
.anyRequest().authenticated()
.and()
//saml security
.apply(
SamlServiceProviderConfigurer.saml2Login()
)
;
// #formatter:on
}
#Bean
protected ServiceProviderConfigurationResolver configurationResolver() {
return fromConfiguration(
config -> config
.keys(
KeyData.builder()
.id("sp-signing-key")
.privateKey(privateKey)
.certificate(certificate)
.passphrase("sppassword")
.build()
)
.providers(
ExternalIdentityProviderConfiguration.builder()
.alias("simplesamlphp")
.metadata("http://simplesaml-for-spring-saml.cfapps.io/saml2/idp/metadata.php")
.build()
)
);
}
private String privateKey = "-----BEGIN RSA PRIVATE KEY-----\n" +
"Proc-Type: 4,ENCRYPTED\n" +
"DEK-Info: DES-EDE3-CBC,7C8510E4CED17A9F\n" +
"\n" +
"...==\n" +
"-----END RSA PRIVATE KEY-----";
private String certificate = "-----BEGIN CERTIFICATE-----\n" +
"...\n" +
"RZ/nbTJ7VTeZOSyRoVn5XHhpuJ0B\n" +
"-----END CERTIFICATE-----";
}
References:
[1] https://github.com/spring-projects/spring-security-saml/blob/ca29a44d4777425e9553d8e97ef960ef1b95d935/samples/boot/simple-service-provider/src/main/resources/application.yml#L23-L168
[2] https://github.com/Endeios/samlv2app

Related

How to properly configure spring-security with vaadin14 to handle 2 entry points - keyclaok and DB

I have a vaadin14 application that I want to enable different types of authentication mechanisms on different url paths. One is a test url, where authentication should use DB, and the other is the production url that uses keycloak.
I was able to get each authentication mechanism to work separately, but once I try to put both, I get unexpected results.
In both cases, I get login page, but the authentication doesn't work correctly. Here's my security configuration, what am I doing wrong?
#Configuration
#EnableWebSecurity
public class ApplicationSecurityConfiguration {
#Configuration
#Order(2)
public static class DBAuthConfigurationAdapter extends WebSecurityConfigurerAdapter {
private static final String LOGIN_PROCESSING_URL = "/login";
private static final String LOGIN_FAILURE_URL = "/login?error";
private static final String LOGIN_URL = "/login";
private static final String LOGOUT_SUCCESS_URL = "/login";
/**
* Require login to access internal pages and configure login form.
*/
#Override
protected void configure(HttpSecurity http) throws Exception {
// Not using Spring CSRF here to be able to use plain HTML for the login page
http.csrf().disable()
// Register our CustomRequestCache, that saves unauthorized access attempts, so
// the user is redirected after login.
.requestCache().requestCache(new CustomRequestCache())
// Restrict access to our application.
.and().antMatcher("/test**").authorizeRequests()
// Allow all flow internal requests.
.requestMatchers(SecurityUtils::isFrameworkInternalRequest).permitAll()
// Allow all requests by logged in users.
.anyRequest().hasRole("USER")
// Configure the login page.
.and().formLogin().loginPage(LOGIN_URL).permitAll().loginProcessingUrl(LOGIN_PROCESSING_URL)
.failureUrl(LOGIN_FAILURE_URL)
// Configure logout
.and().logout().logoutSuccessUrl(LOGOUT_SUCCESS_URL);
}
#Bean
#Override
public UserDetailsService userDetailsService() {
Properties users = null;
try {
users = PropertiesLoaderUtils.loadAllProperties("users.properties");
return new InMemoryUserDetailsManager(users);
} catch (IOException e) {
e.printStackTrace();
}
UserDetails user =
User.withUsername("user")
.password("{noop}password")
.roles("ACTOR")
.build();
return new InMemoryUserDetailsManager(user);
}
/**
* Allows access to static resources, bypassing Spring security.
*/
#Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers(
// Vaadin Flow static resources
"/VAADIN/**",
// the standard favicon URI
"/favicon.ico",
// the robots exclusion standard
"/robots.txt",
// web application manifest
"/manifest.webmanifest",
"/sw.js",
"/offline-page.html",
// icons and images
"/icons/**",
"/images/**",
// (development mode) static resources
"/frontend/**",
// (development mode) webjars
"/webjars/**",
// (development mode) H2 debugging console
"/h2-console/**",
// (production mode) static resources
"/frontend-es5/**", "/frontend-es6/**",
"/resources/**");
}
}
#Order(1)
#Configuration
#ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
public static class AppKeycloakSecurity extends KeycloakWebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(
AuthenticationManagerBuilder auth) throws Exception {
KeycloakAuthenticationProvider keycloakAuthenticationProvider
= keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(
new SimpleAuthorityMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
#Bean
public KeycloakSpringBootConfigResolver KeycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
#Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(
new SessionRegistryImpl());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.httpBasic().disable();
http.formLogin().disable();
http.anonymous().disable();
http.csrf().disable();
http.headers().frameOptions().disable();
http
.antMatcher("/prod**")
.authorizeRequests()
.antMatchers("/vaadinServlet/UIDL/**").permitAll()
.antMatchers("/vaadinServlet/HEARTBEAT/**").permitAll()
.requestMatchers(SecurityUtils::isFrameworkInternalRequest).permitAll()
.anyRequest().hasRole("actor");
http
.logout()
.addLogoutHandler(keycloakLogoutHandler())
.logoutUrl("/logout").permitAll()
.logoutSuccessUrl("/");
http
.addFilterBefore(keycloakPreAuthActionsFilter(), LogoutFilter.class);
http
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint());
http
.sessionManagement()
.sessionAuthenticationStrategy(sessionAuthenticationStrategy());
}
}
}
Navigating within a Vaadin UI will change the URL in your browser, but it will not necessarily create a browser request to that exact URL, effectively bypassing the access control defined by Spring security for that URL. As such, Vaadin is really not suited for the request URL-based security approach that Spring provides. For this issue alone you could take a look at my add-on Spring Boot Security for Vaadin which I specifically created to close the gap between Spring security and Vaadin.
But while creating two distinct Spring security contexts based on the URL is fairly easy, this - for the same reason - will not work well or at all with Vaadin. And that's something even my add-on couldn't help with.
Update: As combining both security contexts is an option for you, I can offer the following solution (using my add-on):
Starting from the Keycloak example, you would have to do the following:
Change WebSecurityConfig to also add your DB-based AuthenticationProvider. Adding your UserDetailsService should still be enough. Make sure to give every user a suitable role.
You have to remove this line from application.properties: codecamp.vaadin.security.standard-auth.enabled = false
This will re-enable the standard login without Keycloak via a Vaadin view.
Adapt the KeycloakRouteAccessDeniedHandler to ignore all test views that shouldn't be protected by Keycloak.
I already prepared all this in Gitlab repo and removed everything not important for the main point of this solution. See the individual commits and their diffs to also help focus in on the important bits.

AuthenticationManger in Spring security webflux

I am trying to build a custom authentication manager for my spring-webflux app. However I find that my manager is never called. My code below:
#Bean
SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) {
return http
.authorizeExchange().pathMatchers("/**").authenticated().and().httpBasic().disable()
.securityContextRepository(webSessionServerSecurityContextRepository())
.addFilterAfter(new AuthenticationWebFilter(bearerTokenAuthenticationManager()),
SecurityWebFiltersOrder.REACTOR_CONTEXT)
.build();
}
What am I doing wrong?
Assuming you put this bean in a class annotated with #Configuration and #EnableWebFluxSecurity your problem seems that you didn't disabled csrf that is configured by default by Spring Security.
You can do that with the following:
#Bean
SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) {
return http
.authorizeExchange().pathMatchers("/**").authenticated()
.and()
.httpBasic().disable()
.csrf().disable() // Disable csrf
.securityContextRepository(webSessionServerSecurityContextRepository())
.addFilterAfter(new AuthenticationWebFilter(bearerTokenAuthenticationManager()),
SecurityWebFiltersOrder.REACTOR_CONTEXT)
.build();
}
Furthermore, you have to configure correctly the AuthenticationWebFilter.
An AuthenticationWebFilter has the following dependencies:
...most of them are provided by default as HttpBasic deps (copy and pasted from Spring Security source code):
private final ReactiveAuthenticationManagerResolver<ServerWebExchange> authenticationManagerResolver;
private ServerAuthenticationSuccessHandler authenticationSuccessHandler = new WebFilterChainServerAuthenticationSuccessHandler();
private ServerAuthenticationConverter authenticationConverter = new ServerHttpBasicAuthenticationConverter();
private ServerAuthenticationFailureHandler authenticationFailureHandler = new ServerAuthenticationEntryPointFailureHandler(new HttpBasicServerAuthenticationEntryPoint());
private ServerSecurityContextRepository securityContextRepository = NoOpServerSecurityContextRepository.getInstance(); // Stateless session
private ServerWebExchangeMatcher requiresAuthenticationMatcher = ServerWebExchangeMatchers.anyExchange();
You could set whatever you want with the setters method of AuthenticationWebFilter. An AuthenticationWebFilter has the following logic:
So depending of the case you have to configure one dependency or another. You could see a complete example of how Authentication and Authorization works in my repo: https://github.com/soasada/kotlin-coroutines-webflux-security (is in kotlin but for the case is the same)

security.xml to java config, how to implement a custom securityMetataSource

So, in my migration towards a javaconfig style spring security, I'm running into an issue with regards to a custom securityMetadataSource. It doesn't seem to get picked up automatically, and I'm not sure how to include it in the http security wizard. Because we have multiple site configurations that can have various security configurations, we keep the security in a separate site specific config class
#Bean
DefaultFilterInvocationSecurityMetadataSource securityMetadataSource(){
SecurityExpressionHandler<FilterInvocation> securityExpressionHandler = new DefaultWebSecurityExpressionHandler();
LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> map = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();
processSecurityPath(map,"/signin/**","permitAll");
processSecurityPath(map,"/redirect/**","permitAll");
processSecurityPath(map,"/*/*/account/g_product/add*/**" ,"hasAnyRole('ROLE_ADMIN','ROLE_EDITOR')");
processSecurityPath(map,"/**" ,"permitAll");
ExpressionBasedFilterInvocationSecurityMetadataSource ms = new ExpressionBasedFilterInvocationSecurityMetadataSource(map, securityExpressionHandler);
return ms;
}
private void processSecurityPath(LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> map, String path, String securityRule){
map.put(new AntPathRequestMatcher(path), Arrays.<ConfigAttribute>asList(new SecurityConfig(securityRule)));
}
And this is the main security config
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilter(myFilterSecurityInterceptor)
.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.exceptionHandling().accessDeniedHandler(new MyAccessDeniedHandler())
.and()
.authorizeRequests()
// .anyRequest().authenticated() // this should be handled by the DefaultFilterInvocationSecurityMetadataSource that is part of the project specific config
[...]

Spring OAuth and Boot Integration Test

What is the best way to run Spring Boot integration tests agains a OAuth Resource server configured web application.
I can think of two theoretical approaches:
Mock the security context in the resource server without acutally calling the Authorization server.
Embed the Authorization server as part of the test and redirect the authentication to it.
I was wondering how others have approach this problem.
This answer is very similar to the one provided by Ondrej, but is quite a bit simpler.
Spring Security 4 provides Test support. To use it ensure you have spring-security-test-4.0.2.RELEASE.jar (or newer version on your classpath). You will also want to ensure you are working with spring-test-4.1.0.RELEASE (or newer).
Next you can use MockMvc as the other answer indicates. However, if you setup MockMvc with the following:
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
#WebAppConfiguration
public class OAuthTests {
#Autowired
private WebApplicationContext context;
private MockMvc mvc;
#Before
public void setup() {
mvc = MockMvcBuilders
.webAppContextSetup(context)
// ADD THIS!!
.apply(springSecurity())
.build();
}
This makes it so
You no longer need to worry about running in stateless mode or not
It also means you do not need to use apply(springSecurity()) as indicated in the other answer.
In short, you should be able to do something like this:
#Test
#WithSecurityContext('user')
public void performOAuth() throws Exception {
...
// No need for apply(security())!!
restParcelMockMvc.perform(get("/api/some-resource"))
.andExpect(...);
}
I'd encourage you to read through the rest of the Spring Security Testing section of the reference as it provides lots of additional details including how to use custom authentication.
I use spring security 4.x #WithSecurityContext('user') annotation to create mock SecurityContext with 'user' logged in. Then when calling my REST API using MockMvc I retrieve SecurityContext and attach it to the call.
Like this:
#Test
#Transactional
#WithSecurityContext('user')
public void getAllParcels() throws Exception {
// Initialize the database
long size = parcelRepository.count();
parcelRepository.saveAndFlush(parcel);
// Get all the parcels
restParcelMockMvc.perform(get("/api/parcels").with(security()))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.[" + size + "].id").value(parcel.getId()))
.andExpect(jsonPath("$.[" + size + "].lot").value(DEFAULT_LOT))
.andExpect(jsonPath("$.[" + size + "].localName").value(DEFAULT_LOCAL_NAME));
}
where security() is static method:
public static RequestPostProcessor security() {
return SecurityMockMvcRequestPostProcessors.securityContext(SecurityContextHolder.getContext());
}
So using #WithSecurityContext('user') mock SecurityContext with authenticated user with login 'user' is created for my test method. Then in that method I retrieve this mock SecurityContext and attach it to the REST API call to make my oAuth think user is allready authenticated. It's basically the first approach you suggested in your question.
For this to work you must switch your OAuth to be statefull for the tests. Otherwise it won't work.
ie like this:
#Configuration
public class OAuth2ServerConfiguration {
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Autowired(required = false)
#Qualifier("oauth2StatelessSecurityContext")
private Boolean stateless = Boolean.TRUE; // STATEFUL switching for tests!
#Inject
private Http401UnauthorizedEntryPoint authenticationEntryPoint;
#Inject
private AjaxLogoutSuccessHandler ajaxLogoutSuccessHandler;
#Override
public void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.logout()
.logoutUrl("/api/logout")
.logoutSuccessHandler(ajaxLogoutSuccessHandler)
.and()
.csrf()
.requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize"))
.disable()
.headers()
.frameOptions().disable().and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/authenticate").permitAll()
.antMatchers("/api/register").permitAll()
.antMatchers("/api/logs/**").hasAnyAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/api/**").authenticated()
.antMatchers("/metrics/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/health/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/trace/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/dump/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/shutdown/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/beans/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/configprops/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/info/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/autoconfig/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/env/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/trace/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/api-docs/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/protected/**").authenticated();
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.stateless(stateless);
super.configure(resources);
}
}
...
You see my stateless property which gets injected only in tests. In normal run it uses it's default value true (so it's stateless). For tests I declare oauth2StatelessSecurityContext Bean with value false so it turns statefull for tests.
I define this configuration for tests:
#Configuration
public class OAuth2Statefull {
#Bean
#Primary
public Boolean oauth2StatelessSecurityContext() {
return Boolean.FALSE;
}
}
That's how I did it. I hope my explanation is understandable.

Spring Security 3.2 setting authenticationEntryPoint in HttpSecurity has no effect (Java-Config)

I want to convert spring security configuration from xml to java config.
Is't nearly done, the last issue is the AuthenticationEntryPoint. The settings of it in HttpSecurity will be ignored.
I use Spring security 3.2.0.M2
Snipped of SecurityConfig.class
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterAfter(httpPayloadFilter(), ChannelProcessingFilter.class)
.addFilterAfter(httpRestLoginFilter(), SecurityContextPersistenceFilter.class)
.authorizeUrls()
.antMatchers("/**").hasRole("USER")
.antMatchers("/secure/clientident/**").hasRole("REQUESTVALID")
.and()
.httpBasic().authenticationEntryPoint(delegatingAuthenticationEntryPoint());
}
#Bean
public DelegatingAuthenticationEntryPoint delegatingAuthenticationEntryPoint() {
ELRequestMatcher matcher = new ELRequestMatcher("hasHeader('user-agent', 'Mozilla') or " +
"hasHeader('user-agent', 'Chromium') or " +
"hasHeader('user-agent', 'Chrome') or " +
"hasHeader('user-agent', 'Safari')");
LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> map =
new LinkedHashMap<RequestMatcher, AuthenticationEntryPoint>();
map.put(matcher, new BasicAuthenticationEntryPoint());
DelegatingAuthenticationEntryPoint delegatingAuthenticationEntryPoint = new DelegatingAuthenticationEntryPoint(map);
delegatingAuthenticationEntryPoint.setDefaultEntryPoint(new Http403ForbiddenEntryPoint());
return delegatingAuthenticationEntryPoint;
}
I always get the "HTTP 403" (guess Http403ForbiddenEntryPoint) on client side.
I also have tried an easier config without delegatingAuthenticationEntryPoint like.
.httpBasic().authenticationEntryPoint(new BasicAuthenticationEntryPoint())
this will not work too.
Has anyone an idea what I'm doing wrong?
Add:
Should have locked better. Found another post about this issue.
need spring security java config example showing basic auth only
A Ticket SEC-2198 has also been placed.
current workaround.
.exceptionHandling()
.authenticationEntryPoint(delegatingAuthenticationEntryPoint())
.and()
.httpBasic();

Resources