0

I need to switch my authentication/authorization code from Spring Security to Java ee. Actually I have this class:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, proxyTargetClass = true)
@PropertySource(value = { "classpath:application.properties" })
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier("myAuthPopulator")
    LdapAuthoritiesPopulator myAuthPopulator;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{
        LdapContextSource contextSource = new LdapContextSource();
        contextSource.setUrl("ldaps://vdap.fg.loal:636/");
        //contextSource.setBase("DC=fg,DC=local");
        contextSource.setReferral("follow"); 
        contextSource.setUserDn("CN=A0XXX32,CN=Administration,CN=fdam,DC=fg,DC=local");
        contextSource.setPassword("password!");
        contextSource.afterPropertiesSet();

        LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder> ldapAuthenticationProviderConfigurer = auth.ldapAuthentication();

        ldapAuthenticationProviderConfigurer
        .contextSource(contextSource)
        .userSearchBase("CN=ProxyUsers,CN=fdam,DC=fg,DC=local")
        .userSearchFilter("CN={0}")
        .ldapAuthoritiesPopulator(myAuthPopulator);     
    }

    @Configuration
    @Order(1)
    public static class ApiWebSecurityConfig extends WebSecurityConfigurerAdapter{
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
            .csrf().disable()
            .antMatcher("/client/**")
            .authorizeRequests()
            //Exclude send file from authentication because it doesn't work with spring authentication
            .antMatchers(HttpMethod.POST, "/client/file").permitAll()
            .anyRequest().authenticated()
            .and()
            .httpBasic();
        }
    }

    @Configuration
    @Order(2)
    public static class FormWebSecurityConfig extends WebSecurityConfigurerAdapter{

        @Autowired
        RoleServices roleServices;

        @Override
        public void configure(WebSecurity web) throws Exception {
            web
            //Spring Security ignores request to static resources such as CSS or JS files.
            .ignoring()
            .antMatchers("/static/**");
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {         
            List<Role> roles=roleServices.getRoles();
            //Retrieve array of roles(only string field without id)
            String[] rolesArray = new String[roles.size()];
            int i=0;
            for (Role role:roles){
                rolesArray[i++] = role.getRole();
            }

            http
            .authorizeRequests() //Authorize Request Configuration
            .anyRequest().hasAnyRole(rolesArray)//.authenticated()
            .and() //Login Form configuration for all others
            .formLogin()
            .loginPage("/login")
            //important because otherwise it goes in a loop because login page require authentication and authentication require login page
            .permitAll()
            .and()
            //redirect on page 403 if user doens't exist in DART database
            .exceptionHandling().accessDeniedPage("/403")
            .and()
            .logout()
            .logoutSuccessUrl("/login?logout")
            .permitAll();

        }
    }
}

I'm using remote Ldap to authenticate user and database to load roles, but Spring has several problem retrieving user in CN=ProxUsers so I'd like to use simple java code instead configureGlobal method. I already have java code to check user in Ldap:

@Override
    public void isAuthenticated(String username, String password) throws LdapException{
        if (databaseMatlabClientServices.getByUsersEnabled(username)== null)
            throw new LdapException("User doesn't exist into DART database. Please contact the administrator!");
        String dn="";;
        //First query to retriev DN
        Hashtable<String, Object> ldapEnv = new Hashtable<String, Object>();
        ldapEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        ldapEnv.put(Context.PROVIDER_URL, env.getRequiredProperty(PROPERTY_NAME_LDAP_URL));
//Without authentication        ldapEnv.put(Context.SECURITY_AUTHENTICATION, "none");
        //With authentication to access to LDAP server
        ldapEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
        ldapEnv.put(Context.SECURITY_PRINCIPAL, env.getRequiredProperty(PROPERTY_NAME_LDAP_NAME));
        ldapEnv.put(Context.SECURITY_CREDENTIALS, env.getRequiredProperty(PROPERTY_NAME_LDAP_PASSWORD));
        String[] returnAttribute = {"dn"};
        DirContext ctx = null;
        NamingEnumeration<SearchResult> results = null;
        try {
            ctx = new InitialDirContext(ldapEnv);
            SearchControls controls = new SearchControls();
            controls.setReturningAttributes(returnAttribute);
            controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
// without authentication on local server String filter = "uid=" + username ;
            String filter = "CN=" + username ;
            results = ctx.search(env.getRequiredProperty(PROPERTY_NAME_LDAP_USERSEARCHBASE), filter, controls);
            if (results.hasMore())
                dn = results.nextElement().getNameInNamespace();
            else 
                throw new LdapException("Wrong username. Please retry!");
        } catch (NamingException e) {
            throw new LdapException(e);
        } finally {
            try{
                if (results != null)
                    results.close();             
                if (ctx != null) 
                    ctx.close();
            }catch(Exception e){
                throw new LdapException(e);
            }
        }

        //Second query to try to access with obtained Dn and given password
        Hashtable<String, Object> authEnv = new Hashtable<String, Object>();
        authEnv.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
        authEnv.put(Context.PROVIDER_URL, env.getRequiredProperty(PROPERTY_NAME_LDAP_URL));
        authEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
        authEnv.put(Context.SECURITY_PRINCIPAL, dn);
        authEnv.put(Context.SECURITY_CREDENTIALS, password);
        DirContext ctx2 = null;
        try {
            ctx2 = new InitialDirContext(authEnv);
        } catch (AuthenticationException authEx) {
            throw new LdapException("Authentication error. Password was wrong");
        } catch(Exception e){
            throw new LdapException(e);
        }finally {
            try{         
                if (ctx2 != null) 
                    ctx2.close();
            }catch(Exception e){
                throw new LdapException(e);
            }
        }
    }

In Spring I get roles with this class

@Service("myAuthPopulator")
public class MyAuthoritiesPopulator implements LdapAuthoritiesPopulator {

    @Autowired
    private UserServices userServices;
    static final Logger LOG = LoggerFactory.getLogger(MyAuthoritiesPopulator.class);

    @Transactional(readOnly=true)
    @Override
    public Collection<? extends GrantedAuthority> getGrantedAuthorities(DirContextOperations userData, String username) {
        Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
        try{
            com.domain.User user = userServices.getByUsersEnabled(username);
            if (user==null){
                LOG.error("Threw exception in MyAuthoritiesPopulator::getGrantedAuthorities : User doesn't exist into DART database" );
            }
            else{
                //Use this if a user can have different roles
//              for(Role role : user.getRole()) {
//                  authorities.add(new SimpleGrantedAuthority(role.getRole()));
//              }
                authorities.add(new SimpleGrantedAuthority(user.getRole().getRole()));
                return authorities;
            }
        }catch(Exception e){
            LOG.error("Threw exception in MyAuthoritiesPopulator::getGrantedAuthorities : " + ErrorExceptionBuilder.buildErrorResponse(e)); }
        return authorities;
    }
}

This is my login page:

<body class="hold-transition login-page">
    <div class="login-box">
        <div class="login-logo">
            <b>DART</b> Sign in
        </div>
        <!-- /.login-logo -->
        <div class="login-box-body">
            <p class="login-box-msg">Sign in to start your session</p>
            <form th:action="@{/login}" method="post">
                <div th:if="${param.error}">
                    <script>
                        notifyMessage("Invalid username and password!", 'error');
                    </script>
                </div>
<!--                <div th:if="${param.logout}">
                    <script>
                        notifyMessage("You have been logged out!", 'error');
                    </script>
                </div> -->
                <div class="form-group has-feedback">
                    <input id="username" name="username" type="text"
                        class="form-control" placeholder="Username"> <span
                        class="glyphicon glyphicon-user form-control-feedback"></span>
                </div>
                <div class="form-group has-feedback">
                    <input id="password" name="password" type="password"
                        class="form-control" placeholder="Password"> <span
                        class="glyphicon glyphicon-lock form-control-feedback"></span>
                </div>
                <div class="row">
                    <div class="col-xs-8">
                        <div class="checkbox icheck">
                            <label> <input type="checkbox"> Remember Me
                            </label>
                        </div>
                    </div>
                    <!-- /.col -->
                    <div class="col-xs-4">
                        <button id="signinButton" type="submit"
                            class="btn btn-primary btn-block btn-flat">Sign In</button>
                    </div>
                    <!-- /.col -->
                </div>
            </form>

            <div >
                <img th:src="@{/static/assets/dist/img/fca login.jpg}" class="img-responsive" alt="FCA">
            </div>

        </div>
        <!-- /.login-box-body -->
    </div>
    <!-- /.login-box -->
</body>

I've read a lot of documents but I don't understand how I can use java ee to my purpose, could you help me?

Update I have created a class

@Configuration 
public class LdapConfiguration { 

    @Bean 
    public LdapContextSource contextSource () { 
        LdapContextSource contextSource= new LdapContextSource(); 
        contextSource.setUrl("ldaps://vdap.fg.loal:636/"); 
        contextSource.setBase("DC=fg,DC=local"); 
        contextSource.setReferral("follow"); 
        contextSource.setUserDn("CN=A0XXX32,CN=Administration,CN=fdam,DC=fg,DC=local"); 
        contextSource.setPassword("password!"); 

        contextSource.afterPropertiesSet(); 

        return contextSource; 
    } 

    @Bean(name = "ldapTemplate") 
    public LdapTemplate ldapTemplate() { 
        return new LdapTemplate(contextSource()); 
    } 
}

and a test web service:

@RequestMapping(value = { "/test/{commonName}/{password}"}, method = RequestMethod.GET)
    public @ResponseBody Response appBufferSearch(ModelMap model, @PathVariable String commonName, @PathVariable String password){ 
        AndFilter filter = new AndFilter(); 
        filter.and(new EqualsFilter("CN", commonName)); 
        boolean isAuthenticated = ldapTemplate.authenticate("CN=ProxyUsers,CN=fdam", filter.encode(), password);
        return new Response(true, true, isAuthenticated, null);
    }

and it find my user into LDAP server, so the problem is in my configureGlobal, do you know why?

luca
  • 3,248
  • 10
  • 66
  • 145
  • 2
    If you can hack together a manual solution then also spring security will work. Instead of working around it, work with the framework and fix the issues. – M. Deinum Apr 26 '16 at 07:40
  • You'd be much better off patching the framework, if there is indeed an issue. Spring is extremely extensible. Hacking together your own solution opens you up to massive risk, as you as using untested and unaudited code for one of the most important parts of your application. – Boris the Spider Apr 26 '16 at 07:43
  • I spent whole days fixing this error in Spring without any result. (here I even have one open question http://stackoverflow.com/questions/36305360/configure-spring-security-for-ldap-connection). I don't have any other solutions to try in Spring, obviously I'd prefer to fix it in Spring. It can't bind user in ProxyUsers – luca Apr 26 '16 at 07:46
  • 1
    @luca well then write a custom [`UserDetailsService`](http://docs.spring.io/autorepo/docs/spring-security/3.2.2.RELEASE/apidocs/org/springframework/security/core/userdetails/UserDetailsService.html) with your custom LDAP connection code. You might well be able to reuse the vast majority of the existing Spring LDAP servce. I don't know what your issue is, but throwing the baby out with the bathwater is certainly not the best idea... – Boris the Spider Apr 26 '16 at 07:50
  • I have to read about UserDetailService because I'm not an expert of Spring, I even read about two authentication strategies supplied with Spring Security, i'd try password comparison instead of user binding but I haven't found any example. – luca Apr 26 '16 at 08:01
  • Can you post/add the values of the constants you are using in your code, without it it is quite hard to determine if you are doing the same as you have configured Spring Security with. Also Do you really need a `userSearch`? If the users are in a single location you could easier use a `userDn` instead. – M. Deinum Apr 26 '16 at 08:56
  • the constants are the same because I use properties file and I think that the user are all in the same location. However I test Spring security web service and it find user, I've updated my question – luca Apr 26 '16 at 09:12
  • Sorry, why are you _manually_ authenticating a user in the `appBufferSearch` request mapping? Is that just to demonstrate that your LDAP config works? – Boris the Spider Apr 26 '16 at 09:18
  • P.S. don't manually call `afterPropertiesSet`, that's Spring's job. – Boris the Spider Apr 26 '16 at 09:21
  • I used this web services to check if Spring finds user in LDAP. With manually authenticating I get true or false depends on if user exists or less. Ok I remove afterPropertiesSet, thanks. Now I know where is the error, I have to find out how to solve it. Any idea? – luca Apr 26 '16 at 09:41
  • I switch the question here : http://stackoverflow.com/questions/36866228/spring-security-binding-doenst-work-for-user-different-from-manager – luca Apr 26 '16 at 14:26

0 Answers0