0

Hi I am using spring boot 1.4.0+spring-security-saml2-core 1.0.2 with the help of following link Spring-Boot-Saml-example it is working great. Now i would like to create an in memory user with the same granted authorities that the SAML user is issued.Kindly find the below code of SAMLUserDetailsServiceImpl for reference.Your help should be appreciable.

@Service
public class SAMLUserDetailsServiceImpl implements SAMLUserDetailsService {

// Logger
private static final Logger LOG = LoggerFactory.getLogger(SAMLUserDetailsServiceImpl.class);

@Value("${emailSamlAttrName}")
private String emailSamlAttrName;

public Object loadUserBySAML(SAMLCredential credential)
        throws UsernameNotFoundException {

    // The method is supposed to identify local account of user referenced by
    // data in the SAML assertion and return UserDetails object describing the user.

    String userID = credential.getNameID().getValue();
    Attribute emailAttribute = credential.getAttribute(emailSamlAttrName);
    String userEmail = getAttributeValue(credential.getAttribute(emailSamlAttrName).getAttributeValues().get(0));
    LOG.info(userID + " is logged in");
    List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
    GrantedAuthority authority = new SimpleGrantedAuthority("ROLE_USER");
    authorities.add(authority);

    // In a real scenario, this implementation has to locate user in a arbitrary
    // dataStore based on information present in the SAMLCredential and
    // returns such a date in a form of application specific UserDetails object.
    return new User(userID, "<abc123>", true, true, true, true, authorities);
}

As per the @blur0224 comment i put the following code in:

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
      auth.inMemoryAuthentication().withUser("sample").password("sample123").roles("NONE");
      auth.inMemoryAuthentication().withUser("dummy").password("dummy123").roles("USER");
      auth.inMemoryAuthentication().withUser("proxy").password("proxy123").roles("USER");
}

But it throws the following error....

11:09:41.345 [http-nio-8040-exec-1] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor [transactionId: ]- Secure object:   FilterInvocation: URL: /api/v1/users/authenticated; Attributes:    [hasRole('ROLE_NONE') or hasRole('ROLE_USER')]
11:09:41.345 [http-nio-8040-exec-1] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor [transactionId: ]- Previously Authenticated:   org.springframework.security.authentication.AnonymousAuthenticationToken@9055c2b    c: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true;   Details:   org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
11:09:41.351 [http-nio-8040-exec-1] DEBUG o.s.s.access.vote.AffirmativeBased [transactionId: ]- Voter: org.springframework.security.web.access.expression.WebExpressionVoter@2809e0b9, returned: -1
11:09:41.352 [http-nio-8040-exec-1] DEBUG o.s.b.f.s.DefaultListableBeanFactory [transactionId: ]- Returning cached instance of singleton bean 'delegatingApplicationListener'
11:09:41.352 [http-nio-8040-exec-1] DEBUG o.s.b.f.s.DefaultListableBeanFactory [transactionId: ]- Returning cached instance of singleton bean 'authorizationAuditListener'
11:09:41.354 [http-nio-8040-exec-1] DEBUG o.s.b.f.s.DefaultListableBeanFactory [transactionId: ]- Returning cached instance of singleton bean 'delegatingApplicationListener'
11:09:41.354 [http-nio-8040-exec-1] DEBUG o.s.b.f.s.DefaultListableBeanFactory [transactionId: ]- Returning cached instance of singleton bean 'auditListener'
11:09:41.354 [http-nio-8040-exec-1] DEBUG o.s.b.a.audit.listener.AuditListener [transactionId: ]- AuditEvent [timestamp=Wed Jul 27 11:09:41 CEST 2016, principal=anonymousUser, type=AUTHORIZATION_FAILURE, data={type=org.springframework.security.access.AccessDeniedException, message=Access is denied}]
11:09:41.359 [http-nio-8040-exec-1] DEBUG o.s.s.w.a.ExceptionTranslationFilter [transactionId: ]- Access is denied (user is anonymous); redirecting to authentication entry point
org.springframework.security.access.AccessDeniedException: Access is denied
at  org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:83)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:232)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:123)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:90)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:114)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:122)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:169)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:48)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:207)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilterInternal(BasicAuthenticationFilter.java:158)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:120)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.saml.metadata.MetadataGeneratorFilter.doFilter(MetadataGeneratorFilter.java:87)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:87)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:121)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:103)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:676)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:522)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1095)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:672)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1500)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1456)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)

WebSecurityConfig.java

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .httpBasic()
            .authenticationEntryPoint(samlEntryPoint());
    http
        .csrf()
            .disable();
    http
        .addFilterBefore(metadataGeneratorFilter(), ChannelProcessingFilter.class)
        .addFilterAfter(samlFilter(), BasicAuthenticationFilter.class);
    http
        .authorizeRequests()
        .antMatchers("/error").permitAll()
        .antMatchers("/saml/**").permitAll()
        .antMatchers("/api/*/users/authenticated").access(String.format("hasRole('%s') or hasRole('%s')", ROLE_NONE, ROLE_USER))
        .antMatchers("/api/**").access(String.format("hasRole('%s')", ROLE_USER))
        .anyRequest().access(String.format("hasRole('%s')", ROLE_USER));
    http
        .logout()
            .logoutSuccessUrl("/");
}
VelNaga
  • 3,593
  • 6
  • 48
  • 82
  • Seems to be ok. But why can't just map the user in local data store and build the user object using data retrieved from data store and assertion? Also, what happens when user is not available in local data store? – Zeigeist Jul 26 '16 at 14:24
  • @Zeigeist This question is the continuation of the following question http://stackoverflow.com/questions/38528502/spring-boot-disable-saml-security-in-local-enviroment/38532157?noredirect=1#comment64568477_38532157 so you will get an idea of my requirement – VelNaga Jul 26 '16 at 14:25
  • @Zeigeist i do not want to create any local data store.I want simple solution or minimal modifications to run the app in my local.It is working fine in all the higher environments but we don't have a keystore or metadata xml in our local so we cannot login the application in our local. – VelNaga Jul 26 '16 at 14:30
  • Just a thought, can't you use Transient as NameID for local environment ONLY and ask your IdP to send it in Assertion? That way you avoid these changes and you touch the configurations and not the code. BTW, Transient NameID definition: Indicates that the content of the element is an identifier with transient semantics and SHOULD be treated as an opaque and temporary value by the Service Provider (SP) and do not need to map user locally at SP. – Zeigeist Jul 26 '16 at 15:37
  • @Zeigeist Sorry i am new to SAML i am not getting you making the variable transient will resolve the issue?How it will create dummy or proxy user?Kindly explain me if i am wrong – VelNaga Jul 26 '16 at 15:46
  • Check this. http://stackoverflow.com/questions/11693297/what-are-the-different-nameid-format-used-for. Importantly refer Transient NameID and check if it fits your use case. BTW, NameID is one of configuration we do between SP and IdP which uniquely identify an user and SP just trust the assertion and user present in it. – Zeigeist Jul 26 '16 at 15:55
  • @Zeigeist I cannot touch the configuration details but i am ok to change the code. – VelNaga Jul 27 '16 at 09:20

2 Answers2

1

In your SAMLUserDetailsServiceImpl you are currently not looking up the user in a DB to get any specific information about them. This being the case, you can setup your local security configuration to create in memory users with the same granted authority they have when they go through SAML. ROLE_USER You can use the example below as a guide for setting up a user that you can log in as for your local development.

Example of In Memory Users

Are you missing this?

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/info/**").hasAnyRole("ADMIN","USER").
        and().formLogin();
    }

You need two separate security configurations: WebSecurityConfig and LocalSecurityConfig When running locally you want the quickest, easiest way to replicate what your implementation of saml provides you with, which is a user with granted authorities. Note that you will need to have @Profile("dev","test","prod") on your SAML Configuration files WebSecurityConfig

@Configuration
@ComponentScan("com.whatever.etc")
@EnableWebSecurity
@Profile("local")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/**").hasAnyRole("ADMIN","USER").
        and().httpBasic();
    }
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
          auth.inMemoryAuthentication().withUser("ram").password("ram123").roles("ADMIN");
          auth.inMemoryAuthentication().withUser("ravan").password("ravan123").roles("USER");
          auth.inMemoryAuthentication().withUser("kans").password("kans123").roles("USER");
    }
}   

You can't simply add users to the SAML configuration because the it has a different entry point. By creating a separate configuration, you have granular control of how you want the application to behave running locally only. Also, it is possible but not necessary to combine the two configurations into a single file. I would recommend getting them working separately first.

blur0224
  • 972
  • 8
  • 26
  • I updated my question. i followed the link you shared.But getting exception while hitting the URL.Am i did anything wrong? – VelNaga Jul 27 '16 at 09:30
  • Could you please post your whole security config file for your local configuration? – blur0224 Jul 27 '16 at 12:22
  • It's confidential i cannot share those files.I have samlKeystore.jks & a idp-metadata.xml.For further information you can find the following link [link](https://github.com/vdenotaris/spring-boot-security-saml-sample ) – VelNaga Jul 27 '16 at 12:56
  • I mean, just the portion related to your local security that are active with your local spring profile. Your post is missing something, see my edited answer. Also, when running using the local profile, you should not be using any SAML configuration and vice versa. – blur0224 Jul 27 '16 at 12:58
  • thank you for your continuous reply.I have the entries you have mentioned please see my updated post.Do we really need to hide the SAML configuration? Now i didn't disabled it. – VelNaga Jul 27 '16 at 14:01
  • Could you please specify exactly which file do you need for reference? But i exactly followed the following link https://github.com/vdenotaris/spring-boot-security-saml-sample So what ever files do you need to check you can check from that link – VelNaga Jul 27 '16 at 14:04
  • The configurations conflict. You will need separate configurations. See my edits. – blur0224 Jul 27 '16 at 14:14
  • @blur0224.Created separate configurations for local and disabled other environments configurations but keep on getting the same error.No Luck!! – VelNaga Jul 27 '16 at 16:21
  • Now i am able to run but while getting the UserDetails from "@AuthenticationPrincipal Principal principal" It returns null but i want one of the in-memory user to return.While trying to create a separate SAMLUserDetailsServiceImpl.java for local profile it asks so many beans to autowire.Is there any other way to achieve it? – VelNaga Jul 28 '16 at 09:16
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/118537/discussion-between-blur0224-and-velnaga). – blur0224 Jul 28 '16 at 11:59
1

You can use this library: spring-boot-security-saml

That besides highly simplifying the use of spring-security-saml and spring-boot it already has an in-memory SAMLUserDetails out of the box.

You could extend that class to return the granted authorities from whichever SAML attribute is returned by your IDP

Ulises
  • 9,115
  • 2
  • 30
  • 27
  • Sorry i am unable to get you.I am new to SAML. Hope you saw the link i shared.I exactly followed the link and it is working fine.For local setup how can we store user data in in-memory using the link you shared?Could you please provide a hint – VelNaga Jul 27 '16 at 09:19