LDAP configuration in XNAT 1.7.0

570 views
Skip to first unread message

G Lenz

unread,
Oct 3, 2016, 1:49:41 PM10/3/16
to xnat_discussion
Hello,
I recently installed version 1.7.0 and I'm trying to enable LDAP functionality. How is that possible?
Adding 'provider.ldap1' properties to xnat-conf.properties in /data/xnat/home/config doesn't do anything. Is that bit supposed to be integrated into the app itself?

I'm using Tomcat 7, OpenJDK 8 and Postgres 9.3

Thanks so much

G Lenz

unread,
Oct 7, 2016, 9:52:57 AM10/7/16
to xnat_discussion
Can someone confirm the setup is the same for 1.7 than it is for 1.6?

McKay, Mike

unread,
Oct 7, 2016, 11:08:54 AM10/7/16
to xnat_di...@googlegroups.com

Yes, we have made changes to how LDAP providers are specified. What you will want to do is create a plugin with the properties for your ldap provider (or throw a file into one of your existing plugins). To have a single LDAP provider be the means of authentication to XNAT, simply create a jar with a file in it located at META-INF/xnat/auth/ldap1-provider.properties relative to the top level of the jar (and put the jar in your XNAT plugins directory). The contents of this file should be something like:

name=LDAP

id=ldap1

type=ldap

address=ldap://ldapurl:389/dc=my,dc=domain

userdn=cn=MyServiceAccount,ou=MyGroup,dc=my,dc=domain

password=MyPassword

search.base=ou=people

search.filter=(uid={0})

 

If no provider is specified, it assumes you want to have a single db provider. If you want multiple ways of authenticating to XNAT, you will want to have multiple properties files. For example, localdb-provider.properties would look like:

name=Database

id=localdb

type=db

 

So if you wanted two ldap providers, you could create ldap1-provider.properties and ldap2-provider.properties, and if you want a single ldap provider and the local database provider, you could create ldap1-provider.properties and localdb-provider.properties. If you want to control the order of the providers (so your most used provider shows up first in the dropdown on the login page), you can add order=1 to the one you want to show up first and order=2 to the one you want to show up second.

 

You can either put these properties files in the same plugin, or create a separate plugin for each of them. If you create these as XNAT plugins (with the bit of additional overhead described here: https://wiki.xnat.org/display/XW2/Step+1+of+10+Creating+an+XNAT+plugin+project), these plugins will show up in Administer->Site Administration->Manage Plugins and you can manage them from there.

 

One of the things we want to add very soon is a section in Site Administration specifically to deal with authentication providers. You would still be able to specify them as I described here, but you would also be able to add, remove, and reorder providers from the UI. We unfortunately ran out of time to get this into XNAT 1.7.0, but I expect this to be part of 1.7.1. For now, you can use basically the same configuration as before for your LDAP providers; you’ll just have to put it in a different place. And if you do set them up as separate XNAT plugins (as described in the link I included), you should be able to have some control from the UI over what means of authentication users can use to login to the site.

 

-Mike

--
You received this message because you are subscribed to the Google Groups "xnat_discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to xnat_discussi...@googlegroups.com.
To post to this group, send email to xnat_di...@googlegroups.com.
Visit this group at https://groups.google.com/group/xnat_discussion.
For more options, visit https://groups.google.com/d/optout.

 


The materials in this message are private and may contain Protected Healthcare Information or other information of a sensitive nature. If you are not the intended recipient, be advised that any unauthorized use, disclosure, copying or the taking of any action in reliance on the contents of this information is strictly prohibited. If you have received this email in error, please immediately notify the sender via telephone or return mail.

Tom Close

unread,
Oct 11, 2016, 12:18:42 AM10/11/16
to xnat_discussion
Hi Mike,

Thanks a lot for the update on configuring LDAP for 1.7. Despite being a newbie with both XNAT and LDAP I have had some success setting it up but have got a bit stuck and was hoping you could give me some pointers.

I have setup the LDAP plugin with the following configuration

name=My University
id=ldap1
type=ldap
address=ldaps://lds.myuni.edu:636/dc=myuni,dc=edu
userdn=uid=myserviceaccount,ou=users,dc= myuni,dc=edu
password=myserviceaccountpassword
search.base=ou=users
search.filter=(uid={0})

which seems to be able to connect to my uni's LDAP server fine but it throws the following exception (found in $HOME/logs/security.log) when I try to login with my uni username/password

2016-10-11 14:20:41,578 [http-bio-8080-exec-10] ERROR org.nrg.xnat.security.XnatAuthenticationFilter - An internal error occurred while trying to authenticate the user.
org.springframework.security.authentication.InternalAuthenticationServiceException: [LDAP: error code 32 - 0000208D: NameErr: DSID-0315270B, problem 2001 (NO_OBJECT), data 0, best match of:
        'DC=myuni,DC=edu'
^@]; nested exception is javax.naming.NameNotFoundException: [LDAP: error code 32 - 0000208D: NameErr: DSID-0315270B, problem 2001 (NO_OBJECT), data 0, best match of:
        'DC=myuni,DC=edu'
^@]; remaining name 'uid=myusername,ou=users'
        at org.springframework.security.ldap.authentication.LdapAuthenticationProvider.doAuthentication(LdapAuthenticationProvider.java:208)
etc...

So I assume it is a problem with the search.base setting. However, your initial suggestion of search.base=ou=people doesn't work either (the remaining name is then just 'ou=people') and I am clean out of ideas.

When I log into the LDAP service using JXplorer (from the VM running our XNAT instance with the above credentials) I see the following tree structure

World
  | - edu
       | - myuni
             | - groups
             | - users
             | - etc...

and a search of uid=myusername,ou=users,dc=myuni,dc=edu returns my record without problem so I am not sure what the difference between that and the search string that XNAT is using is. Do you have any ideas?

Many thanks,

Tom

Herrick, Rick

unread,
Oct 11, 2016, 7:44:27 PM10/11/16
to xnat_di...@googlegroups.com
Try removing the ou=users from your userdn setting. I think that's getting added twice, once from the userdn and again from the search.base setting.


_____________________________
From: Tom Close <tom.g...@gmail.com>
Sent: Monday, October 10, 2016 11:18 PM
Subject: Re: [XNAT Discussion] Re: LDAP configuration in XNAT 1.7.0
To: xnat_discussion <xnat_di...@googlegroups.com>

Tom Close

unread,
Oct 11, 2016, 8:14:08 PM10/11/16
to xnat_discussion
Hi Rick,

Thanks for the suggestion although if I remove users from the userdn i.e.

name=My University
id=ldap1
type=ldap
address=ldaps://lds.myuni.edu:636/dc=myuni,dc=edu
userdn=uid=myserviceaccount,dc=myuni,dc=edu
password=myserviceaccountpassword
search.base=ou=users
search.filter=(uid={0})

XNAT can't login to the LDAP server with the service account and gives the following error

ERROR org.nrg.xnat.security.XnatAuthenticationFilter - An internal error occurred while trying to authenticate the user.
org.springframework.security.authentication.InternalAuthenticationServiceException: [LDAP: error code 49 - 8009030C: LdapErr: DSID-0C0903D9, comment: AcceptSecurityContext error, data 2030, v2580^@]; nested exception is javax.naming.AuthenticationException: [LDAP: error code 49 - 8009030C: LdapErr: DSID-0C0903D9, comment: AcceptSecurityContext error, data 2030, v2580^@]

Strangely, making the search.base empty

name=My University
id=ldap1
type=ldap
address=ldaps://lds.myuni.edu:636/dc=myuni,dc=edu
userdn=uid=myserviceaccount,ou=users,dc=myuni,dc=edu
password=myserviceaccountpassword
search.base=
search.filter=(uid={0})

results in exactly the same error message as before, i.e.

2016-10-11 14:20:41,578 [http-bio-8080-exec-10] ERROR org.nrg.xnat.security.XnatAuthenticationFilter - An internal error occurred while trying to authenticate the user.
org.springframework.security.authentication.InternalAuthenticationServiceException: [LDAP: error code 32 - 0000208D: NameErr: DSID-0315270B, problem 2001 (NO_OBJECT), data 0, best match of:
        'DC=myuni,DC=edu'
^@]; nested exception is javax.naming.NameNotFoundException: [LDAP: error code 32 - 0000208D: NameErr: DSID-0315270B, problem 2001 (NO_OBJECT), data 0, best match of:
        'DC=myuni,DC=edu'
^@]; remaining name 'uid=myusername,ou=users'
        at org.springframework.security.ldap.authentication.LdapAuthenticationProvider.doAuthentication(LdapAuthenticationProvider.java:208)
etc...

Setting search.base='' results in a lexical error and removing search.base altogether causes a "searchBase must not be null, (an empty string is acceptable)" error. 

Thanks,

Tom

Tom Close

unread,
Oct 11, 2016, 8:22:43 PM10/11/16
to xnat_discussion
Could it be somehow related to using ldaps instead of the ldap protocol? Has anyone else had success using ldaps?

G Lenz

unread,
Oct 12, 2016, 5:06:16 AM10/12/16
to xnat_discussion
Thanks Mike for the advice with the plugin, I got it to work on our system.

Tom:
Most of the time I had received an error code 32: No Such Object

What helped me figuring out the right configuration was this:
First of all look at what error code it gives you: https://www.ldap.com/ldap-result-code-reference

Then I had a look what the logs of the LDAP container on my Docker machine would say if I logged in via PhpLDAPAdmin or whatever tool you use to manage your LDAP server.
There I would see what base and userdn is used and then I replicated that in the plugin configuration. For me the solution was to omit the search.base string, although this depends on your setup of course.

Hope this helps,
Gregor

McKay, Mike

unread,
Oct 12, 2016, 10:20:34 AM10/12/16
to xnat_di...@googlegroups.com

Tom,

 

I do think you want to avoid repetition between the userdn and search.base. Regarding the error code 49, I’m not sure exactly what the problem is, but another XNAT user encountered that error and was able to get it working. Maybe something similar would work for you: https://groups.google.com/forum/#!topic/xnat_discussion/YrfddbtQLz4

 

-Mike

Tom Close

unread,
Oct 14, 2016, 12:43:12 AM10/14/16
to xnat_discussion
Hi Guys,

Thanks again for helping me with this. I'm sorry to keep this post going but this is the last critical issue preventing me from rolling out XNAT on our site and it has got me stumped!

I would just like to check my understanding of the process involved in the LDAP authentication. This is my naive conceptualisation of what happens when XNAT tries to do authenticate a user with LDAP
  1. Establish a connection to the LDAP server using service account credentials stored in the userdn/password settings in the properties file
  2. Using this connection, attempt to perform a simple bind with the username prepended to the search.base and address using the password from the login screen. i.e. uid=users_username,<search-base>,dc=myuni,dc=edu
Regarding the error code 49 ('Invalid Credentials'), I had assumed that this was because the DN for my service account was no longer correct when I removed the ou=users from the userdn setting, which meant that XNAT couldn't even do step 1. For example, if I change myserviceaccount to myserviceaccount1 I get error code 49. Whereas with ou=users in the userdn setting XNAT I think I was at least getting to step 2 before running into error code 32 ('No Such Object') when trying to do the simple bind (this is what I think Simon Doran was getting at in https://groups.google.com/forum/#!topic/xnat_discussion/YrfddbtQLz4). Am I right in thinking this?

With the following configuration

name=My University
id=ldap1
type=ldap
address=ldaps://lds.myuni.edu:636/dc=myuni,dc=edu
userdn=uid=myserviceaccount,ou=users,dc=myuni,dc=edu
password=myserviceaccountpassword
search.base=
search.filter=(uid={0})

I would have thought that the DN used for the simple bind (step 2) would be

uid=users_username,dc=myuni,dc=edu

However, the error message in the logs still contains the ou=users part.

2016-10-11 14:20:41,578 [http-bio-8080-exec-10] ERROR org.nrg.xnat.security.XnatAuthenticationFilter - An internal error occurred while trying to authenticate the user.
org.springframework.security.authentication.InternalAuthenticationServiceException: [LDAP: error code 32 - 0000208D: NameErr: DSID-0315270B, problem 2001 (NO_OBJECT), data 0, best match of:
        'DC=myuni,DC=edu'
^@]; nested exception is javax.naming.NameNotFoundException: [LDAP: error code 32 - 0000208D: NameErr: DSID-0315270B, problem 2001 (NO_OBJECT), data 0, best match of:
        'DC=myuni,DC=edu'
^@]; remaining name 'uid=users_username,ou=users'
        at org.springframework.security.ldap.authentication.LdapAuthenticationProvider.doAuthentication(LdapAuthenticationProvider.java:208)

What am I missing, how does the 'ou=users' get there? In any case, the way I read this error message is that XNAT tried to bind with 

uid=users_username,ou=users,dc=myuni,dc=edu

which should work as this is what I have confirmed works with JXplorer using the same credentials/VM. 

Is there any possibility that this lookup issue could be caused by using LDAPS instead of LDAP? Are the calls that XNAT makes to the LDAP server SSL compatible? Where would I find the relevant lines in the XNAT source code?

Many thanks,

Tom

Herrick, Rick

unread,
Oct 14, 2016, 1:39:03 PM10/14/16
to xnat_discussion

Try this:

name=My University
id=ldap1
type=ldap
address=ldaps://lds.myuni.edu:636/
userdn=uid=myserviceaccount,ou=users,dc=myuni,dc=edu
password=myserviceaccountpassword
search.base=ou=users,dc=myuni,dc=edu
search.filter=(uid={0})

Unfortunately, my Macbook is currently undergoing surgery, but I have a nice working LDAP configuration on there that I could test this out with. Note that the main thing I did was move the dcs off of the URL and into the search.base setting. Let me know if this works for you. I'm building a new OpenLDAP instance to test against, but that'll take a while to get working properly.


Rick Herrick

Sr. Programmer/Analyst

Neuroinformatics Research Group

Washington University School of Medicine

(314) 827-4250


From: xnat_di...@googlegroups.com <xnat_di...@googlegroups.com> on behalf of Tom Close <tom.g...@gmail.com>
Sent: Thursday, October 13, 2016 11:43:11 PM
To: xnat_discussion

Tom Close

unread,
Oct 16, 2016, 7:57:43 PM10/16/16
to xnat_discussion
Hi Rick,

With that configuration I get

2016-10-17 10:48:13,455 [http-bio-8080-exec-3] ERROR org.nrg.xnat.security.XnatAuthenticationFilter - An internal error occurred while trying to authenticate the user.
org.springframework.security.authentication.InternalAuthenticationServiceException: [LDAP: error code 32 - 0000208D: NameErr: DSID-0315270B, problem 2001 (NO_OBJECT), data 0, best match of:
        'DC=myuni,DC=edu'
^@]; nested exception is javax.naming.NameNotFoundException: [LDAP: error code 32 - 0000208D: NameErr: DSID-0315270B, problem 2001 (NO_OBJECT), data 0, best match of:
        'DC=myuni,DC=edu'
^@]; remaining name 'uid=user_username,ou=users,dc=myuni,dc=edu'

Cheers,

Tom

Tom Close

unread,
Oct 17, 2016, 2:54:00 AM10/17/16
to xnat_discussion
Looking at the source code for BindAuthenticator.java in the org.springframework.security.dap.authentication package

       private DirContextOperations bindWithDn(String userDnStr, String username,

String password) {

BaseLdapPathContextSource ctxSource = (BaseLdapPathContextSource) getContextSource();

DistinguishedName userDn = new DistinguishedName(userDnStr);

DistinguishedName fullDn = new DistinguishedName(userDn);

fullDn.prepend(ctxSource.getBaseLdapPath());


logger.debug("Attempting to bind as " + fullDn);


DirContext ctx = null;

try {

ctx = getContextSource().getContext(fullDn.toString(), password);

// Check for password policy control

PasswordPolicyControl ppolicy = PasswordPolicyControlExtractor

.extractControl(ctx);


logger.debug("Retrieving attributes...");


Attributes attrs = ctx.getAttributes(userDn, getUserAttributes());


DirContextAdapter result = new DirContextAdapter(attrs, userDn,

ctxSource.getBaseLdapPath());


if (ppolicy != null) {

result.setAttributeValue(ppolicy.getID(), ppolicy);

}


return result;

}

catch (NamingException e) {

// This will be thrown if an invalid user name is used and the method may

// be called multiple times to try different names, so we trap the exception

// unless a subclass wishes to implement more specialized behaviour.

if ((e instanceof org.springframework.ldap.AuthenticationException)

|| (e instanceof org.springframework.ldap.OperationNotSupportedException)) {

handleBindException(userDnStr, username, e);

}

else {

throw e;

}

}

catch (javax.naming.NamingException e) {

throw LdapUtils.convertLdapException(e);

}

finally {

LdapUtils.closeContext(ctx);

}


return null;

}


it seems as though the debug logging would be useful in determining what string is being used to bind with the ldap server. So I changed the following lines in /var/lib/tomcat7/webapps/ROOT/WEB-INF/classes/log4j.properties 

log4j.category.org.springframework.security=DEBUG, security
log4j.additivity.org.springframework.security=false
log4j.category.org.springframework.ldap=DEBUG, security
log4j.additivity.org.springframework.ldap=false
log4j.category.org.nrg.xnat.security=DEBUG, security
log4j.additivity.org.nrg.xnat.security=false

However, this prints out a lot of information but not the one I was after, i.e. "Attempting to bind as " +... Do you know what I need to set in order to see all the debugging options? Also the warning, error and info lines didn't show up using DEBUG, which would be handy to use to navigate the log is there a way to show them all together.

Tom Close

unread,
Oct 17, 2016, 2:54:42 AM10/17/16
to xnat_discussion

Here is the full stack trace of the error in case it helps

        at org.springframework.security.ldap.authentication.LdapAuthenticationProvider.doAuthentication(LdapAuthenticationProvider.java:208)
        at org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider.authenticate(AbstractLdapAuthenticationProvider.java:82)
        at org.nrg.xnat.security.provider.XnatLdapAuthenticationProvider.authenticate(XnatLdapAuthenticationProvider.java:47)
        at org.nrg.xnat.security.XnatProviderManager.authenticate(XnatProviderManager.java:108)
        at org.nrg.xnat.security.XnatAuthenticationFilter.attemptAuthentication(XnatAuthenticationFilter.java:122)
        at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217)
        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.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.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:133)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
        at org.nrg.xnat.security.XnatExpiredPasswordFilter.doFilter(XnatExpiredPasswordFilter.java:147)
        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.access.channel.ChannelProcessingFilter.doFilter(ChannelProcessingFilter.java:152)
        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:241)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:221)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
        at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:683)
        at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:956)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:436)
        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1078)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:625)
        at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)
        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)
Caused by: org.springframework.ldap.NameNotFoundException: [LDAP: error code 32 - 0000208D: NameErr: DSID-0315270B, problem 2001 (NO_OBJECT), data 0, best match of:
        'DC=monash,DC=edu'
^@]; nested exception is javax.naming.NameNotFoundException: [LDAP: error code 32 - 0000208D: NameErr: DSID-0315270B, problem 2001 (NO_OBJECT), data 0, best match of:
        'DC=monash,DC=edu'
^@]; remaining name 'uid=tclose,ou=users,dc=monash,dc=edu'
        at org.springframework.ldap.support.LdapUtils.convertLdapException(LdapUtils.java:183)
        at org.springframework.security.ldap.authentication.BindAuthenticator.bindWithDn(BindAuthenticator.java:148)
        at org.springframework.security.ldap.authentication.BindAuthenticator.authenticate(BindAuthenticator.java:95)
        at org.springframework.security.ldap.authentication.LdapAuthenticationProvider.doAuthentication(LdapAuthenticationProvider.java:189)
        ... 44 more
Caused by: javax.naming.NameNotFoundException: [LDAP: error code 32 - 0000208D: NameErr: DSID-0315270B, problem 2001 (NO_OBJECT), data 0, best match of:
        'DC=monash,DC=edu'
^@]; remaining name 'uid=tclose,ou=users,dc=monash,dc=edu'
        at com.sun.jndi.ldap.LdapCtx.mapErrorCode(LdapCtx.java:3160)
        at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:3081)
        at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:2888)
        at com.sun.jndi.ldap.LdapCtx.c_getAttributes(LdapCtx.java:1329)
        at com.sun.jndi.toolkit.ctx.ComponentDirContext.p_getAttributes(ComponentDirContext.java:235)
        at com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.getAttributes(PartialCompositeDirContext.java:141)
        at javax.naming.directory.InitialDirContext.getAttributes(InitialDirContext.java:152)
        at org.springframework.security.ldap.authentication.BindAuthenticator.bindWithDn(BindAuthenticator.java:124)

<span style="font-family:&q

Herrick, Rick

unread,
Oct 17, 2016, 10:09:13 AM10/17/16
to xnat_di...@googlegroups.com
Is there any chance you could export the schema (just the schema, not the data) from your LDAP server? To be clear, this is most likely really an LDAP issue, not an XNAT one, so it's just figuring out the particular mystical incantation to get the server responding properly.

Sent from Outlook on my iPhone: please excuse typos or terse responses.

--
You received this message because you are subscribed to the Google Groups "xnat_discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to xnat_discussi...@googlegroups.com.
To post to this group, send email to xnat_di...@googlegroups.com.
Visit this group at https://groups.google.com/group/xnat_discussion.
For more options, visit https://groups.google.com/d/optout.

Tom Close

unread,
Oct 18, 2016, 3:07:28 AM10/18/16
to xnat_discussion
I can ask, although I am a couple of steps away from the guys who configure it so it could take a little while. Any thoughts on how I get the logger to spit out the fullDn that XNAT is using?

McKay, Mike

unread,
Oct 18, 2016, 12:38:39 PM10/18/16
to xnat_di...@googlegroups.com

I don’t believe that there is currently a way to do this without adding logging statements to the code. The getLdapContextSource method in src/main/java/org/nrg/xnat/security/config/LdapAuthenticationProviderConfigurator.java is where the LDAP server object gets created from a list of properties.  You could add a logging statement after afterPropertiesSet() and see what the values are at that point. Or you could leave the code as is and attach a debugger and see what the values are at that point. This code should get executed on Tomcat restarts if you have specified an LDAP provider.

Herrick, Rick

unread,
Oct 18, 2016, 1:35:02 PM10/18/16
to xnat_di...@googlegroups.com

Well, you can also try setting the logging for the Spring Security packages to DEBUG. Look in your deployed web app in the folder WEB-INF/conf for the file log4j.properties. That should have a configuration something like this:

 

log4j.category.org.springframework.security = WARN, security

log4j.additivity.org.springframework.security = false

log4j.category.org.nrg.xnat.security = WARN, security

log4j.additivity.org.nrg.xnat.security = false

 

If it doesn’t have a configuration like that, then go ahead and add it. You’ll also need to add this in that case:

 

log4j.appender.security = org.apache.log4j.DailyRollingFileAppender

log4j.appender.security.DatePattern='.'yyy-MM-dd

log4j.appender.security.file = ${applicationRoot}/logs/security.log

log4j.appender.security.layout = org.apache.log4j.PatternLayout

log4j.appender.security.layout.conversionPattern = %d [%t] %-5p %c - %m%n

log4j.appender.security.append = true

 

If that file DOES contain that configuration, then all you need to do is change those “WARN” settings to “DEBUG”. Restart Tomcat and you should get a lot more information in the security.log file (note: you’ll get a LOT MORE, so you want to make sure to turn that back to WARN when you’re done).

 

-- 

Rick Herrick

Sr. Programmer/Analyst

Neuroinformatics Research Group

Washington University School of Medicine

 

Tom Close

unread,
Oct 19, 2016, 12:58:02 AM10/19/16
to xnat_discussion
Thanks Rick, I ended up adding the following to my log4j properties as setting log4j.category.org.springframework.security=DEBUG was a bit overwhelming ;)

log4j.category.org.springframework.security.providers.ldap.LdapAuthenticationProvider=debug, security
log4j.category.org.springframework.security.ldap.populator.DefaultLdapAuthoritiesPopulator=debug, security
log4j.category.org.springframework.security.userdetails.ldap.LdapUserDetailsMapper=debug, security
log4j.category.org.springframework.security.providers.ldap.authenticator.BindAuthenticator=debug, security
log4j.category.org.springframework.security.ldap.search.FilterBasedLdapUserSearch=debug, security
log4j.category.org.springframework.security.ldap.SpringSecurityLdapTemplate=debug, security

which seems to add some relevant logs:

2016-10-19 11:13:23,943 [http-bio-8080-exec-9] DEBUG org.springframework.security.ldap.search.FilterBasedLdapUserSearch - Searching for user 'users_username', with user search [ searchFilter: '(uid={0})', searchBase: 'ou=users', scope: subtree, searchTimeLimit: 0, derefLinkFlag: false ]
2016-10-19 11:13:23,943 [http-bio-8080-exec-9] DEBUG org.springframework.security.ldap.search.FilterBasedLdapUserSearch - Searching for user 'users_username', with user search [ searchFilter: '(uid={0})', searchBase: 'ou=users', scope: subtree, searchTimeLimit: 0, derefLinkFlag: false ]
2016-10-19 11:13:25,705 [http-bio-8080-exec-9] DEBUG org.springframework.security.ldap.SpringSecurityLdapTemplate - Searching for entry under DN 'dc=monash,dc=edu', base = 'ou=users', filter = '(uid={0})'
2016-10-19 11:13:25,705 [http-bio-8080-exec-9] DEBUG org.springframework.security.ldap.SpringSecurityLdapTemplate - Searching for entry under DN 'dc=monash,dc=edu', base = 'ou=users', filter = '(uid={0})'
2016-10-19 11:13:25,721 [http-bio-8080-exec-9] DEBUG org.springframework.security.ldap.SpringSecurityLdapTemplate - Found DN: uid=users_username,OU=users
2016-10-19 11:13:25,721 [http-bio-8080-exec-9] DEBUG org.springframework.security.ldap.SpringSecurityLdapTemplate - Found DN: uid=users_username,OU=users
2016-10-19 11:13:25,960 [http-bio-8080-exec-9] ERROR org.nrg.xnat.security.XnatAuthenticationFilter - An internal error occurred while trying to authenticate the user.
org.springframework.security.authentication.InternalAuthenticationServiceException: [LDAP: error code 32 - 0000208D: NameErr: DSID-0315270B, problem 2001 (NO_OBJECT), data 0, best match of:
'DC=monash,DC=edu'
]; nested exception is javax.naming.NameNotFoundException: [LDAP: error code 32 - 0000208D: NameErr: DSID-0315270B, problem 2001 (NO_OBJECT), data 0, best match of:
'DC=monash,DC=edu'
]; remaining name 'uid=users_username,ou=users'
at org.springframework.security.ldap.authentication.LdapAuthenticationProvider.doAuthentication(LdapAuthenticationProvider.java:208)
]; nested exception is javax.naming.NameNotFoundException: [LDAP: error code 32 - 0000208D: NameErr: DSID-0315270B, problem 2001 (NO_OBJECT), data 0, best match of:
'DC=monash,DC=edu'
]; remaining name 'uid=users_username,ou=users'
at org.springframework.ldap.support.LdapUtils.convertLdapException(LdapUtils.java:183)
at org.springframework.security.ldap.authentication.BindAuthenticator.bindWithDn(BindAuthenticator.java:148)
at org.springframework.security.ldap.authentication.BindAuthenticator.authenticate(BindAuthenticator.java:95)
at org.springframework.security.ldap.authentication.LdapAuthenticationProvider.doAuthentication(LdapAuthenticationProvider.java:189)
... 44 more
Caused by: javax.naming.NameNotFoundException: [LDAP: error code 32 - 0000208D: NameErr: DSID-0315270B, problem 2001 (NO_OBJECT), data 0, best match of:
'DC=monash,DC=edu'
]; remaining name 'uid=users_username,ou=users'
at com.sun.jndi.ldap.LdapCtx.mapErrorCode(LdapCtx.java:3160)
at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:3081)
at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:2888)
at com.sun.jndi.ldap.LdapCtx.c_getAttributes(LdapCtx.java:1329)
at com.sun.jndi.toolkit.ctx.ComponentDirContext.p_getAttributes(ComponentDirContext.java:235)
at com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.getAttributes(PartialCompositeDirContext.java:141)
at javax.naming.directory.InitialDirContext.getAttributes(InitialDirContext.java:152)
at org.springframework.security.ldap.authentication.BindAuthenticator.bindWithDn(BindAuthenticator.java:124)
... 46 more

Looking at these logs suggests that the LDAP server initially finds the DN uid=users_username,OU=users,DC=myuni,DC=edu (see 2016-10-19 11:13:25,721) before the internal error occurs trying to authenticate the user. For example, when I use the rubbish username 'wwwwwwwwwwww' it doesn't report finding the DN (as you would hope) or throw an exception:
 
2016-10-19 11:15:55,945 [http-bio-8080-exec-8] DEBUG org.springframework.security.ldap.search.FilterBasedLdapUserSearch - Searching for user 'wwwwwwwwwwww', with user search [ searchFilter: '(uid={0})', searchBase: 'ou=users', scope: subtree, searchTimeLimit: 0, derefLinkFlag: false ]
2016-10-19 11:15:55,945 [http-bio-8080-exec-8] DEBUG org.springframework.security.ldap.search.FilterBasedLdapUserSearch - Searching for user 'wwwwwwwwwwww', with user search [ searchFilter: '(uid={0})', searchBase: 'ou=users', scope: subtree, searchTimeLimit: 0, derefLinkFlag: false ]
2016-10-19 11:15:57,352 [http-bio-8080-exec-8] DEBUG org.springframework.security.ldap.SpringSecurityLdapTemplate - Searching for entry under DN 'dc=monash,dc=edu', base = 'ou=users', filter = '(uid={0})'
2016-10-19 11:15:57,352 [http-bio-8080-exec-8] DEBUG org.springframework.security.ldap.SpringSecurityLdapTemplate - Searching for entry under DN 'dc=monash,dc=edu', base = 'ou=users', filter = '(uid={0})'

Looking at the source code of BindAuthenticator.java in the Spring framework seems to support this

 68         public DirContextOperations authenticate(Authentication authentication) {
 69                 DirContextOperations user = null;
 70                 Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
 71                                 "Can only process UsernamePasswordAuthenticationToken objects");
 72
 73                 String username = authentication.getName();
 74                 String password = (String) authentication.getCredentials();
 75
 76                 if (!StringUtils.hasLength(password)) {
 77                         logger.debug("Rejecting empty password for user " + username);
 78                         throw new BadCredentialsException(messages.getMessage(
 79                                         "BindAuthenticator.emptyPassword", "Empty Password"));
 80                 }
 81
 82                 // If DN patterns are configured, try authenticating with them directly
 83                 for (String dn : getUserDns(username)) {
 84                         user = bindWithDn(dn, username, password);
 85
 86                         if (user != null) {
 87                                 break;
 88                         }
 89                 }
 90
 91                 // Otherwise use the configured search object to find the user and authenticate
 92                 // with the returned DN.
 93                 if (user == null && getUserSearch() != null) {
 94                         DirContextOperations userFromSearch = getUserSearch().searchForUser(username);
 95                         user = bindWithDn(userFromSearch.getDn().toString(), username, password);
 96                 }
 97
 98                 if (user == null) {
 99                         throw new BadCredentialsException(messages.getMessage(
100                                         "BindAuthenticator.badCredentials", "Bad credentials"));
101                 }
102
103                 return user;
104         }

as the debug message "Searching for user..." comes from within searchForUser(),  and "Found DN: ..." comes from within searchForSingleEntryInternal, which is called by searchForUser. So it looks as though searchForUser on line 94 in BindAuthenticator.java finds the user okay but then there is a problem the bindWithDn on line 95.

So it does seem like there might something strange with the LDAP server config. I will try to chase it up with LDAP server admins and see whether they can work it out or at least provide me with the schema to play with.


Tom Close

unread,
Oct 21, 2016, 2:25:54 AM10/21/16
to xnat_discussion
So it seems the problem I am having is due the fact our LDAP server doesn't allow the retrieval of attributes when bound as a regular user, only with the service account, and the org.springframework.security.ldap.authentication.BindAuthenticator that xnat uses assumes that they will be able to (see bolded text)

public DirContextOperations authenticate(Authentication authentication) {

DirContextOperations user = null;

Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,

"Can only process UsernamePasswordAuthenticationToken objects");


String username = authentication.getName();

String password = (String) authentication.getCredentials();


if (!StringUtils.hasLength(password)) {

logger.debug("Rejecting empty password for user " + username);

throw new BadCredentialsException(messages.getMessage(

"BindAuthenticator.emptyPassword", "Empty Password"));

}


// If DN patterns are configured, try authenticating with them directly

for (String dn : getUserDns(username)) {

user = bindWithDn(dn, username, password);


if (user != null) {

break;

}

}


// Otherwise use the configured search object to find the user and authenticate

// with the returned DN.

if (user == null && getUserSearch() != null) {

DirContextOperations userFromSearch = getUserSearch().searchForUser(username);

user = bindWithDn(userFromSearch.getDn().toString(), username, password);

}


if (user == null) {

throw new BadCredentialsException(messages.getMessage(

"BindAuthenticator.badCredentials", "Bad credentials"));

}


return user;


Now my understanding is that these attributes are ignored by XNAT anyway, is that right? If so, do you think would it be possible to write a Authenticator that just attempts to retrieve the context, i.e. ctx = getContextSource().getContext(fullDn.toString(), password); and then returns the user from the searchForUser(username) instead, which could be passed to XnatLdapAuthenticationProvider instead of BindAuthenticator?

McKay, Mike

unread,
Oct 21, 2016, 11:49:29 AM10/21/16
to xnat_di...@googlegroups.com

These attributes are not ignored by XNAT. XNAT uses them to get the user’s email address from LDAP. It then creates a new XNAT user account using this information. It would be possible in principle to change the code so that instead of creating new users, it searched for an existing XNAT user that seemed likely to be the same person as the one who just logged in via LDAP and add LDAP as an additional means of authentication for that user. But it isn’t a trivial change, and if you’re able to change your LDAP’s attribute permissions, that would be easier.

 

-Mike

 

From: xnat_di...@googlegroups.com [mailto:xnat_di...@googlegroups.com] On Behalf Of Tom Close
Sent: Friday, October 21, 2016 1:26 AM
To: xnat_discussion <xnat_di...@googlegroups.com>
Subject: Re: [XNAT Discussion] Re: LDAP configuration in XNAT 1.7.0

 

So it seems the problem I am having is due the fact our LDAP server doesn't allow the retrieval of attributes when bound as a regular user, only with the service account, and the org.springframework.security.ldap.authentication.BindAuthenticator that xnat uses assumes that they will be able to (see bolded text)

--

You received this message because you are subscribed to the Google Groups "xnat_discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to xnat_discussi...@googlegroups.com.
To post to this group, send email to xnat_di...@googlegroups.com.
Visit this group at https://groups.google.com/group/xnat_discussion.
For more options, visit https://groups.google.com/d/optout.

Tom Close

unread,
Oct 21, 2016, 5:25:08 PM10/21/16
to xnat_discussion
That does sound like a lot of work, as well as a higher ongoing administration load. I also don't like my chances of getting the uni to change its security policy for my installation.

How about if the authenticator attempts to get the attributes using the user account but if that fails it attempts to get the attributes using the service account instead (I have this confirmed this works using some toy code)?

McKay, Mike

unread,
Oct 21, 2016, 6:42:17 PM10/21/16
to xnat_di...@googlegroups.com

That seems like a reasonable approach and less prone to error than a larger overhaul of the code. Let us know if that doesn’t work and we can try to come up with an alternative. And actually if it does work that would be good for us to know too in case someone else is dealing with the same issues.

Tom Close

unread,
Oct 23, 2016, 6:51:01 PM10/23/16
to xnat_discussion
I just had a look at the Spring-Security GitHub repo as I was going to create an issue about this and it appears that someone has beat me to it (https://github.com/cloudfoundry/uaa/issues/342) and this bug has been fixed in the latest version, 4.1, (https://github.com/spring-projects/spring-security/commit/6b436ff4099694f8bb344ca4f8c6a6782c6ed31b). 

So I was planning to check out whether Spring-Security 4.1.3 works well with XNAT 1.7. However, I am not familiar with building enterprise java applications, what is the best way to upgrade this dependency and redeploy XNAT?

McKay, Mike

unread,
Oct 23, 2016, 10:55:36 PM10/23/16
to xnat_di...@googlegroups.com

The first thing I would try is to simply change def vSpringSecurity = '4.0.4.RELEASE' to def vSpringSecurity = '4.1.3.RELEASE' in build.gradle and rebuild (see https://bitbucket.org/xnatdev/xnat-web for more information about rebuilding).

Herrick, Rick

unread,
Oct 24, 2016, 10:30:38 AM10/24/16
to xnat_di...@googlegroups.com

Something to be aware of if you change the version of Spring Security: Spring Security 4.1.x has a number of dependencies on Spring Framework 4.3.x. Because of how Gradle resolves transitive dependencies, that means that any of those libraries that are referenced by XNAT at 4.2.x will be upgraded to 4.3.x. Specifically, these dependencies get upgraded:

 

·         spring-aop

·         spring-beans

·         spring-context

·         spring-core

·         spring-expression

·         spring-jdbc

·         spring-tx

·         spring-web

 

This will leave a number of other dependencies at a 4.2.x version:

 

·         spring-context-support

·         spring-jms

·         spring-messaging

·         spring-orm

·         spring-oxm

·         spring-test

·         spring-webmvc

 

This is not likely to end well.

 

My main point is, if you want to try upgrading Spring Security, you should also upgrade Spring Framework itself, so change these lines:

 

def vSpring = '4.2.7.RELEASE'

def vSpringSecurity = '4.0.4.RELEASE'

 

To this:

 

def vSpring = '4.3.3.RELEASE'

def vSpringSecurity = '4.1.3.RELEASE'

 

Now, once you’ve done this, you’re on your own, since we haven’t tested and validated against these dependencies. It might work. It might not. I don’t have a machine I can test on right at the moment to really make a guess. If you do try this, please let us know if it works, if any complications arise, etc.

 

Tom Close

unread,
Oct 24, 2016, 8:40:44 PM10/24/16
to xnat_discussion
Looking good!

I upgraded the dependencies as suggested and I am able to log in using LDAP! I have only had a quick check of the site so there may be a few little gotchas hiding away but it has all been smooth so far. Will keep you posted if anything pops up that might be related.

Thanks for your help debugging this :)

<span style="font-size:8.5p

Reply all
Reply to author
Forward
0 new messages