1 package com.atlassian.user.impl.ldap.security.authentication;
2
3 import com.atlassian.user.EntityException;
4 import com.atlassian.user.impl.ldap.properties.LdapConnectionProperties;
5 import com.atlassian.user.impl.ldap.properties.LdapSearchProperties;
6 import com.atlassian.user.impl.ldap.repository.LdapContextFactory;
7 import com.atlassian.user.impl.ldap.search.DefaultLDAPUserAdaptor;
8 import com.atlassian.user.impl.ldap.search.LDAPUserAdaptor;
9 import com.atlassian.user.impl.ldap.search.LdapFilterFactory;
10 import com.atlassian.user.repository.RepositoryIdentifier;
11 import com.atlassian.user.security.authentication.Authenticator;
12 import com.atlassian.util.profiling.UtilTimerStack;
13 import net.sf.ldaptemplate.support.filter.AndFilter;
14 import net.sf.ldaptemplate.support.filter.EqualsFilter;
15 import org.apache.commons.lang.StringUtils;
16 import org.apache.log4j.Logger;
17
18 import javax.naming.NamingException;
19 import javax.naming.AuthenticationException;
20 import javax.naming.directory.DirContext;
21 import javax.naming.directory.InitialDirContext;
22 import javax.naming.directory.SearchControls;
23 import java.util.Hashtable;
24
25 public class DefaultLDAPAuthenticator implements Authenticator
26 {
27 private static final Logger log = Logger.getLogger(DefaultLDAPAuthenticator.class);
28 private final LDAPUserAdaptor userAdaptor;
29 private final LdapSearchProperties searchProperties;
30 private final RepositoryIdentifier repositoryIdentifier;
31 private final LdapConnectionProperties connectionProperties;
32 private final LdapFilterFactory filterFactory;
33 private final LdapContextFactory contextFactory;
34
35 public DefaultLDAPAuthenticator(RepositoryIdentifier repositoryIdentifier, LdapContextFactory contextFactory,
36 LdapSearchProperties searchProperties, LdapConnectionProperties connectionProperties,
37 LdapFilterFactory filterFactory)
38 {
39 this.repositoryIdentifier = repositoryIdentifier;
40 this.filterFactory = filterFactory;
41 this.searchProperties = searchProperties;
42 this.connectionProperties = connectionProperties;
43 this.contextFactory = contextFactory;
44 this.userAdaptor = new DefaultLDAPUserAdaptor(contextFactory, searchProperties, filterFactory);
45 }
46
47
48
49
50
51
52
53
54
55
56
57
58
59 public boolean authenticate(String username, String password) throws EntityException
60 {
61 if (UtilTimerStack.isActive())
62 UtilTimerStack.push(this.getClass().getName() + "_authenticate__" + username);
63
64
65 if (StringUtils.isEmpty(password))
66 {
67 if (log.isDebugEnabled())
68 log.debug("Cannot perform authentication on empty passwords.");
69
70 return false;
71 }
72
73 String userDN;
74 DirContext authCtx = null;
75
76 try
77 {
78 userDN = userAdaptor.getUserDN(username);
79 }
80 catch (EntityException e)
81 {
82 log.error("Could not construct DN to authenticate user: " + username, e);
83 return false;
84 }
85 try
86 {
87 Hashtable authEnv = contextFactory.getAuthenticationJndiEnvironment(userDN, password);
88 authCtx = new InitialDirContext(authEnv);
89
90
91 SearchControls ctls = new SearchControls();
92 ctls.setReturningAttributes(new String[]{searchProperties.getUsernameAttribute()});
93 ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
94
95 AndFilter filter = new AndFilter();
96 filter.and(filterFactory.getUserSearchFilter());
97 filter.and(new EqualsFilter(searchProperties.getUsernameAttribute(), username));
98
99 if (log.isDebugEnabled())
100 {
101 log.debug("Doing initial search to complete authentication, username: '" + username + "', " +
102 "base: '" + searchProperties.getBaseUserNamespace() + "' filter: '" + filter.encode() + "'");
103 }
104
105
106 authCtx.search(searchProperties.getBaseUserNamespace(), filter.encode(), ctls);
107 }
108 catch (AuthenticationException e)
109 {
110 if (log.isDebugEnabled())
111 log.debug("LDAP authentication failed, user: '" + username + "', constructed DN: '" + userDN + "'", e);
112 return false;
113 }
114 catch (NamingException e)
115 {
116 log.error("LDAP authentication error, user: '" + username + "', " +
117 "constructed DN: '" + userDN + "', connectionProperties: " + connectionProperties, e);
118 return false;
119 }
120 catch (Throwable t)
121 {
122 log.error("Error occurred in LDAP authentication for username: " + username, t);
123 return false;
124 }
125 finally
126 {
127 try
128 {
129 if (authCtx != null) authCtx.close();
130 }
131 catch (Exception e)
132 {
133 log.warn("Exception closing LDAP connection, possible resource leak", e);
134 }
135
136 if (UtilTimerStack.isActive())
137 UtilTimerStack.pop(this.getClass().getName() + "_authenticate__" + username);
138 }
139
140 return true;
141 }
142
143
144
145
146 public RepositoryIdentifier getRepository()
147 {
148 return repositoryIdentifier;
149 }
150 }