I'm trying to configure the baseDN on my Spring ldap context source, but it keeps throwing an exception:
the config is as follows:
<beans:bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<beans:constructor-arg index="0" value="${ldap_server}"/>
<beans:constructor-arg index="1" value="${ldap_searchbase}"/>
</beans:bean>
my ldap_searchbase has a space in it, and I've looked at the Spring code: that causes the issue:
public DefaultSpringSecurityContextSource(String providerUrl) {
Assert.hasLength(providerUrl, "An LDAP connection URL must be supplied.");
StringTokenizer st = new StringTokenizer(providerUrl);
ArrayList<String> urls = new ArrayList<String>();
// Work out rootDn from the first URL and check that the other URLs (if any) match
while (st.hasMoreTokens()) {
String url = st.nextToken();
String urlRootDn = LdapUtils.parseRootDnFromUrl(url);
urls.add(url.substring(0, url.lastIndexOf(urlRootDn)));
logger.info(" URL '" + url + "', root DN is '" + urlRootDn + "'");
if (rootDn == null) {
rootDn = urlRootDn;
} else if (!rootDn.equals(urlRootDn)) {
throw new IllegalArgumentException("Root DNs must be the same when using multiple URLs");
}
}
I get the "Root DNs must be the same when using multiple URLs" error, and I notice that the String tokenizer tokens by whitespace, so it's chomping my baseDN and making it into a seperate LDAP server URL. What gives? What am I doing wrong here?
I get the same issue (obviously) if I configure as such:
<beans:bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<beans:constructor-arg index="0" value="${ldap_server}/${ldap_searchbase}"/>
</beans:bean>
DefaultSpringSecurityContextSource was written in a way that it cannot handle spaces as it self.
This was first reported as a bug in ISSUE-2264.
But as per the comments of the bug, it seems this was a known issue and solution suggest to use an escape character(i.e. %20)
More examples of LDAP URL can be found in : Section-3
Therefore when you define your LDAP DNs do as below:
Real URL:
ldap://ldap.itd.umich.edu/o=University of Michigan,c=US
Corrected URL to be used for DefaultSpringSecurityContextSource Parameter:
ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US
Alright - so I haven't figured out how I can provide a search base to the security context source, but by doing this:
<beans:bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<beans:constructor-arg index="0" value="${ldap_server}"/>
</beans:bean>
and:
<beans:bean id="ldapUserSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<beans:constructor-arg index="0" value="${ldap_searchbase}"/>
<beans:constructor-arg index="1" value="${ldap_auth_search_filter}"/>
<beans:constructor-arg index="2" ref="contextSource" />
</beans:bean>
it works.
Related
I have setup a security context meant for REST. The configuration is as
<!-- authentication manager and password hashing -->
<authentication-manager alias="authenticationManager">
<authentication-provider ref="daoAuthenticationProvider" />
</authentication-manager>
<beans:bean id="daoAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="userDetailsService" />
<beans:property name="passwordEncoder" ref="passwordEncoder" />
</beans:bean>
<beans:bean id="userDetailsService" name="userAuthenticationProvider"
class="com.myapp.auth.AuthenticationUserDetailsGetter" />
<beans:bean id="passwordEncoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
</beans:bean>
<global-method-security pre-post-annotations="enabled" />
<!-- web services -->
<http use-expressions="true" pattern="/rest/**"
disable-url-rewriting="true" entry-point-ref="restAuthenticationEntryPoint">
<custom-filter ref="restProcessingFilter" position="FORM_LOGIN_FILTER" />
<intercept-url pattern="/rest/login" access="permitAll"/>
<intercept-url pattern="/rest/**" access="isAuthenticated()" />
<logout delete-cookies="JSESSIONID" />
</http>
<beans:bean id="restProcessingFilter" class="com.myapp.auth.RestUsernamePasswordAuthenticationFilter">
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="filterProcessesUrl" value="/rest/login" />
</beans:bean>
And I overrided the UsernamePasswordAuthenticationFilter as
#Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) {
Authentication authentication = null;
String username = request.getParameter("j_username");
String password = request.getParameter("j_password");
boolean valid = authService.authenticate(username, password);
if (valid) {
User user = updateLocalUserInfo(username);
authentication = new UsernamePasswordAuthenticationToken(user,
null, AuthorityUtils.createAuthorityList("USER"));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
return authentication;
}
The above authentication is working fine when I tried it with
RestClient restClient = new RestClient();
String result = restClient.login("hq", "a1234567"); // RestTemplate.postForObject
The only thing left is the result from the authentication post (atm, result is null). How can I configure my security configuration in order to retrieve some result ? A flag or session ID will suffice.
I think best bet here would be AuthenticationSuccessHandler.
As this will only kick in if the authentication was successful.
You can generate some sort of UUID and set that in your response directly. I have used very similar approach for ReST Auth and have not hit any problems yet.
For detailed implementation guide please refer : https://stackoverflow.com/a/23930186/876142
Update for comment #1 :
You can get response just like any normal ReST request.
This is how I am sending back my Token as JSON
String tokenJsonResponse = new ObjectMapper().writeValueAsString(authResponse);
httpResponse.addHeader("Content-Type", "application/json");
httpResponse.getWriter().print(tokenJsonResponse);
Assuming you know how to use RestTemplate, rest is trivial.
To secure my REST API's I am using #Secured({"ROLE_ADMIN", "ROLE_SUPERADMIN", ...some more} etc.
But I have to repeat this on every API manually. I found https://burtbeckwith.com/blog/?p=1398 for Groovy but couldn't find about how to do it in JAVA.
From my understanding I feel that I have to write a custom annotation (e.g. #MySecured("OnlyAdmins") ) which will work as a sort of Pre-Processor and will get replaced with the above #Secured annotation.
However I wanted to know if there is any better way to achieve the same? Also it would be really helpful if somebody could point me to some ready made custom annotation source code for achieving this.
Thanks in advance
To avoid this, create a parent role ALL_ADMINS, and setup spring security hierarchical roles, see the documentation for further details.
creating a role voter with the configured role hierarchy:
<bean id="roleVoter" class="org.springframework.security.access.vote.RoleHierarchyVoter">
<constructor-arg ref="roleHierarchy" />
</bean>
<bean id="roleHierarchy"
class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
<property name="hierarchy">
<value>
ROLE_ALL_ADMINS > ROLE_ADMIN
ROLE_ALL_ADMINS > ROLE_SUPERADMIN
...
</value>
</property>
</bean>
Then applying it to a custom access decision manager:
<beans:bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
<beans:property name="decisionVoters">
<beans:list>
<beans:bean ref="roleVoter" />
<beans:bean class="org.springframework.security.access.vote.AuthenticatedVoter"/>
</beans:list>
</beans:property>
</beans:bean>
and last configuring the custom access decision manager to be used by #Secured:
<global-method-security access-decision-manager-ref="accessDecisionManager">
...
</global-method-security>
I am using Spring security to validate user login.
User credentials are stored in database.
Here is the related section from my "appContext-security.xml" file.
This code works - but my problem is that I am using raw SQL query for "user-by-username-query" and "authorities-by-username-query' tags.
Thus if I have to support multiple databases and if the Sql syntax varies, then I have a problem.
So can I put those queries in some form of a Java class? so that I can change the SQL syntax in that java class easily and make these SQLs DB dependent?
<authentication-manager alias="authManager">
<authentication-provider>
<password-encoder hash="md5"/>
<jdbc-user-service data-source-ref="jndiDataSource"
users-by-username-query="select name, password, enabled from USER where user_status<>0 and name=?"
authorities-by-username-query="select m.name,p.name from USER m, ROLE p where m.name=? and m.application_role=p.id"/>
</authentication-provider>
</authentication-manager>
<beans:bean id="jndiDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<beans:property name="jndiName" value="java:/appManaged"/>
</beans:bean>
Any help will be much appreciated.
You can declare JdbcDaoImpl as a bean manually instead of using <jdbc-user-service>:
<authentication-manager alias="authManager">
<authentication-provider user-service-ref = "jdbcUserService">
<password-encoder hash="md5"/>
</authentication-provider>
</authentication-manager>
<beans:bean id = "jdbcUserService"
class = "org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<beans:property name = "dataSource" ref = "jndiDataSource" />
<beans:property name = "usersByUsernameQuery"
value = "select name, password, enabled from USER where user_status<>0 and name=?"
" />
...
</beans:bean>
Then you can do whatever you want, for example, declare it to be obtained from a factory that sets appropriate queries, or something like that.
In Spring Security we use the intercept-url tag to define the access for URLs as below:
<intercept-url pattern="/**" access="ROLE_ADMIN" />
<intercept-url pattern="/student" access="ROLE_STUDENT" />
This is hard coded in applicationContext-security.xml. I want to read the access values from a database table instead. I have defined my own UserDetailsService and I read the roles for the logged in user from the database. How do I assign these roles to the URL patterns during runtime?
The FilterInvocationSecurityMetadataSourceParser class in Spring-security (try Ctrl/Cmd+Shift+T in STS with the source code) parses the intercept-url tags and creates instances of ExpressionBasedFilterInvocationSecurityMetadataSource, that extends DefaultFilterInvocationSecurityMetadataSource that implements FilterInvocationSecurityMetadataSource that extends SecurityMetadataSource.
What I did is to create a custom class that implements FilterInvocationSecurityMetadataSource, OptionsFromDataBaseFilterInvocationSecurityMetadataSource. I used DefaultFilterInvocationSecurityMetadataSource as base to use urlMatcher, to implement the support() method and something like that.
Then you must to implement these methods:
Collection getAttributes(Object object), where you can access to database, searching for the 'object' being secured (normally the URL to access) to obtain the allowed ConfigAttribute's (normally the ROLE's)
boolean supports(Class clazz)
Collection getAllConfigAttributes()
Be careful with the later, because it's called at startup and maybe is not well configured at this time (I mean, with the datasources or persistence context autowired, depending on what are you using). The solution in a web environment is to configure the contextConfigLocation in the web.xml to load the applicationContext.xml before the applicationContext-security.xml
The final step is to customize the applicationContext-security.xml to load this bean.
For doing that, I used regular beans in this file instead of the security namespace:
<beans:bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
<filter-chain-map path-type="ant">
<filter-chain pattern="/images/*" filters="none" />
<filter-chain pattern="/resources/**" filters="none" />
<filter-chain pattern="/**" filters="
securityContextPersistenceFilter,
logoutFilter,
basicAuthenticationFilter,
exceptionTranslationFilter,
filterSecurityInterceptor"
/>
</filter-chain-map>
</beans:bean>
You have to define all the related beans. For instance:
<beans:bean id="filterSecurityInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<beans:property name="authenticationManager" ref="authenticationManager"></beans:property>
<beans:property name="accessDecisionManager" ref="affirmativeBased"></beans:property>
<beans:property name="securityMetadataSource" ref="optionsFromDataBaseFilterInvocationSecurityMetadataSource"></beans:property>
<beans:property name="validateConfigAttributes" value="true"/></beans:bean>
I know that is not a well explained answer, but it's not as difficult as it seems.
Just use the spring source as base and you will obtain what you want.
Debugging with the data in your database, will help you a lot.
Actually, spring security 3.2 do not encourage to do this according to http://docs.spring.io/spring-security/site/docs/3.2.x/reference/htmlsingle/faq.html#faq-dynamic-url-metadata
but, it is possible (but not elegant) using http element in namespace with a custom accessDecisionManager..
The config should be:
<http pattern="/login.action" security="none"/>
<http pattern="/media/**" security="none"/>
<http access-decision-manager-ref="accessDecisionManager" >
<intercept-url pattern="/**" access="ROLE_USER"/>
<form-login login-page="/login.action"
authentication-failure-url="/login?error=1"
default-target-url="/console.action"/>
<logout invalidate-session="true" delete-cookies="JSESIONID"/>
<session-management session-fixation-protection="migrateSession">
<concurrency-control max-sessions="1" error-if-maximum-exceeded="true" expired-url="/login.action"/>
</session-management>
<!-- NO ESTA FUNCIONANDO, los tokens no se ponen en el request!
<csrf />
-->
</http>
<authentication-manager>
<authentication-provider>
<user-service>
<user name="test" password="test" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
<beans:bean id="accessDecisionManager" class="openjsoft.core.services.security.auth.CustomAccessDecisionManager">
<beans:property name="allowIfAllAbstainDecisions" value="false"/>
<beans:property name="decisionVoters">
<beans:list>
<beans:bean class="org.springframework.security.access.vote.RoleVoter"/>
</beans:list>
</beans:property>
</beans:bean>
The CustomAccessDecisionManager should be...
public class CustomAccessDecisionManager extends AbstractAccessDecisionManager {
...
public void decide(Authentication authentication, Object filter,
Collection<ConfigAttribute> configAttributes)
throws AccessDeniedException, InsufficientAuthenticationException {
if ((filter == null) || !this.supports(filter.getClass())) {
throw new IllegalArgumentException("Object must be a FilterInvocation");
}
String url = ((FilterInvocation) filter).getRequestUrl();
String contexto = ((FilterInvocation) filter).getRequest().getContextPath();
Collection<ConfigAttribute> roles = service.getConfigAttributesFromSecuredUris(contexto, url);
int deny = 0;
for (AccessDecisionVoter voter : getDecisionVoters()) {
int result = voter.vote(authentication, filter, roles);
if (logger.isDebugEnabled()) {
logger.debug("Voter: " + voter + ", returned: " + result);
}
switch (result) {
case AccessDecisionVoter.ACCESS_GRANTED:
return;
case AccessDecisionVoter.ACCESS_DENIED:
deny++;
break;
default:
break;
}
}
if (deny > 0) {
throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied",
"Access is denied"));
}
// To get this far, every AccessDecisionVoter abstained
checkAllowIfAllAbstainDecisions();
}
...
}
Where getConfigAttributesFromSecuredUris retrieve form DB de roles for the specific URL
I have kind of the same problem, basically I'd like to keep separate the list of intercept-url from the other springsecurity configuration section, the first to belong to the application configuration the latter to the product (core, plugin) configuration.
There is a proposal in the JIRA of spring, concerning this problem.
I don't want to give up to use the springsecurity namespace, so I was thinking to some possible solutions in order to deal with this.
In order to have the list of intercept-url dynamically created you have to inject the securitymetadatasource object in the FilterSecurityInterceptor.
Using springsecurity schema the instance of FilterSecurityInterceptor is created by the HttpBuilder class and there is no way to pass the securitymetadatasource as property defined in the schema configuration file, as less as using kind of workaround, which could be:
Define a custom filter, to be executed before FilterSecurityInterceptor, in this filter retrieving the instance FilterSecurityInterceptor (assuming a unique http section is defined) by the spring context and inject there the securitymetadatasource instance;
The same as above but in a HandlerInterceptor.
What do you think?
This the solution I've applied in order to split the list of intercept-url entries from the other spring security configuration.
<security:custom-filter ref="parancoeFilterSecurityInterceptor"
before="FILTER_SECURITY_INTERCEPTOR" />
........
<bean id="parancoeFilterSecurityInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor" >
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="securityMetadataSource" ref="securityMetadataSource"/>
</bean>
The bean securityMetadataSource can be put either in the same configuration file or in another configuration file.
<security:filter-security-metadata-source
id="securityMetadataSource" use-expressions="true">
<security:intercept-url pattern="/admin/**"
access="hasRole('ROLE_ADMIN')" />
</security:filter-security-metadata-source>
Of course you can decide to implement your own securityMetadataSource bean by implementing the interface FilterInvocationSecurityMetadataSource.
Something like this:
<bean id="securityMetadataSource" class="mypackage.MyImplementationOfFilterInvocationSecurityMetadataSource" />
Hope this helps.
This is how it can be done in Spring Security 3.2:
#Configuration
#EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public SecurityConfigDao securityConfigDao() {
SecurityConfigDaoImpl impl = new SecurityConfigDaoImpl() ;
return impl ;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
/* get a map of patterns and authorities */
Map<String,String> viewPermissions = securityConfigDao().viewPermissions() ;
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry interceptUrlRegistry = http
.authorizeRequests().antMatchers("/publicAccess/**")
.permitAll();
for (Map.Entry<String, String> entry: viewPermissions.entrySet()) {
interceptUrlRegistry.antMatchers(entry.getKey()).hasAuthority(entry.getValue());
}
interceptUrlRegistry.anyRequest().authenticated()
.and()
...
/* rest of the configuration */
}
}
A simple solution that works for me.
<intercept-url pattern="/**/**" access="#{#customAuthenticationProvider.returnStringMethod}" />
<intercept-url pattern="/**" access="#{#customAuthenticationProvider.returnStringMethod}" />
customAuthenticationProvider is a bean
<beans:bean id="customAuthenticationProvider"
class="package.security.CustomAuthenticationProvider" />
in CustomAuthenticationProvider class create method:
public synchronized String getReturnStringMethod()
{
//get data from database (call your method)
if(condition){
return "IS_AUTHENTICATED_ANONYMOUSLY";
}
return "ROLE_ADMIN,ROLE_USER";
}
I'm trying to authenticate and then query our corporate LDAP using Spring LDAP and Spring security. I managed to make authentication work but when I attempt to run search I always get the following exception
In order to perform this operation a successful bind must be completed on the connection
After much research I have a theory that after I authenticate and before I can query I need to bind to connection. I just don't know what and how?
Just to mention - I can successfully browse and search our LDAP using JXplorer so my parameters are correct.
Here's section of my securityContext.xml
<security:http auto-config='true'>
<security:intercept-url pattern="/reports/goodbye.html"
access="ROLE_LOGOUT" />
<security:intercept-url pattern="/reports/**" access="ROLE_USER" />
<security:http-basic />
<security:logout logout-url="/reports/logout"
logout-success-url="/reports/goodbye.html" />
</security:http>
<security:ldap-server url="ldap://s140.foo.com:1389/dc=td,dc=foo,dc=com" />
<security:authentication-manager>
<security:authentication-provider ref="ldapAuthProvider">
</security:authentication-provider>
</security:authentication-manager>
<!-- Security beans -->
<bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<constructor-arg value="ldap://s140.foo.com:1389/dc=td,dc=foo,dc=com" />
</bean>
<bean id="ldapAuthProvider"
class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
<constructor-arg>
<bean class="foo.bar.reporting.server.security.ldap.LdapAuthenticatorImpl">
<property name="contextFactory" ref="contextSource" />
<property name="principalPrefix" value="TD\" />
<property name="employee" ref="employee"></property>
</bean>
</constructor-arg>
<constructor-arg>
<bean class="foo.bar.reporting.server.security.ldap.LdapAuthoritiesPopulator" />
</constructor-arg>
</bean>
<!-- DAOs -->
<bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
<constructor-arg ref="contextSource" />
Here's code snippet from LdapAuthenticatorImpl that performs authentication. No problem here:
#Override
public DirContextOperations authenticate(final Authentication authentication) {
// Grab the username and password out of the authentication object.
final String name = authentication.getName();
final String principal = this.principalPrefix + name;
String password = "";
if (authentication.getCredentials() != null) {
password = authentication.getCredentials().toString();
}
if (!("".equals(principal.trim())) && !("".equals(password.trim()))) {
final InitialLdapContext ldapContext = (InitialLdapContext)
this.contextFactory.getContext(principal, password);
// We need to pass the context back out, so that the auth provider
// can add it to the Authentication object.
final DirContextOperations authAdapter = new DirContextAdapter();
authAdapter.addAttributeValue("ldapContext", ldapContext);
this.employee.setqId(name);
return authAdapter;
} else {
throw new BadCredentialsException("Blank username and/or password!");
}
}
And here's another code snippet from EmployeeDao with my futile attempt to query:
public List<Employee> queryEmployeesByName(String query)
throws BARServerException {
AndFilter filter = new AndFilter();
filter.and(new EqualsFilter("objectclass", "person"));
filter.and(new WhitespaceWildcardsFilter("cn", query));
try {
// the following line throws bind exception
List result = ldapTemplate.search(BASE, filter.encode(),
new AttributesMapper() {
#Override
public Employee mapFromAttributes(Attributes attrs)
throws NamingException {
Employee emp = new Employee((String) attrs.get("cn").get(),
(String) attrs.get("cn").get(),
(String) attrs.get("cn").get());
return emp;
}
});
return result;
} catch (Exception e) {
throw new BarServerException("Failed to query LDAP", e);
}
}
And lastly - the exception I'm getting
org.springframework.ldap.UncategorizedLdapException:
Uncategorized exception occured during LDAP processing; nested exception is
javax.naming.NamingException: [LDAP: error code 1 - 00000000: LdapErr:
DSID-0C090627, comment: In order to perform this operation a successful bind
must be completed on the connection., data 0, vece]; remaining name
'DC=TD,DC=FOO,DC=COM'
It looks like your LDAP is configured to not allow a search without binding to it (no anonymous bind). Also you have implemented PasswordComparisonAuthenticator and not BindAuthenticator to authenticate to LDAP.
You could try modifying your queryEmployeesByName() method to bind and then search, looking at some examples in the doc.
I'm going to accept #Raghuram answer mainly because it got me thinking in the right direction.
Why my code was failing? Turned out - the way I wired it I was trying to perform anonymous search which is prohibited by the system - hence the error.
How to rewire example above to work? First thing (and ugly thing at that) you need to provide user name and password of user that will be used to access the system. Very counterintuitive even when you login and authenticated, even if you are using BindAuthenticator system will not attempt to reuse your credentials. Bummer. So you need to stick 2 parameters into contextSource definition like so:
<bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<constructor-arg value="ldap://foo.com:389/dc=td,dc=foo,dc=com" />
<!-- TODO - need to hide this or encrypt a password -->
<property name="userDn" value="CN=admin,OU=Application,DC=TD,DC=FOO,DC=COM" />
<property name="password" value="blah" />
</bean>
Doing that allowed me to replace custom implementation of authenticator with generic BindAuthenticator and then my Java search started working
I got the same error, couldn't find a solution.
Finally I changed the application pool identity to network service and everything worked like a charm.
(I have windows authentication and anonymous enabled on my site)