]> source.dussan.org Git - archiva.git/blob
4fd536edb3fe41ebdb6e9b0f7bf5f591c46b7bfb
[archiva.git] /
1 package org.apache.archiva.redback.authentication.ldap;
2
3 /*
4  * Licensed to the Apache Software Foundation (ASF) under one
5  * or more contributor license agreements.  See the NOTICE file
6  * distributed with this work for additional information
7  * regarding copyright ownership.  The ASF licenses this file
8  * to you under the Apache License, Version 2.0 (the
9  * "License"); you may not use this file except in compliance
10  * with the License.  You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  * KIND, either express or implied.  See the License for the
18  * specific language governing permissions and limitations
19  * under the License.
20  */
21
22 import org.apache.archiva.redback.common.ldap.UserMapper;
23 import org.apache.archiva.redback.common.ldap.connection.LdapConnectionFactory;
24 import org.apache.archiva.redback.configuration.UserConfiguration;
25 import org.apache.commons.lang.StringUtils;
26 import org.apache.archiva.redback.authentication.AuthenticationDataSource;
27 import org.apache.archiva.redback.authentication.AuthenticationException;
28 import org.apache.archiva.redback.authentication.AuthenticationResult;
29 import org.apache.archiva.redback.authentication.Authenticator;
30 import org.apache.archiva.redback.authentication.PasswordBasedAuthenticationDataSource;
31 import org.apache.archiva.redback.common.ldap.connection.LdapConnection;
32 import org.apache.archiva.redback.common.ldap.connection.LdapException;
33 import org.apache.archiva.redback.users.ldap.service.LdapCacheService;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36 import org.springframework.stereotype.Service;
37
38 import javax.inject.Inject;
39 import javax.inject.Named;
40 import javax.naming.NamingEnumeration;
41 import javax.naming.NamingException;
42 import javax.naming.directory.DirContext;
43 import javax.naming.directory.SearchControls;
44 import javax.naming.directory.SearchResult;
45
46 /**
47  * LdapBindAuthenticator:
48  *
49  * @author: Jesse McConnell <jesse@codehaus.org>
50  */
51 @Service( "authenticator#ldap" )
52 public class LdapBindAuthenticator
53     implements Authenticator
54 {
55
56     private Logger log = LoggerFactory.getLogger( getClass() );
57
58     @Inject
59     @Named( value = "userMapper#ldap" )
60     private UserMapper mapper;
61
62     @Inject
63     @Named( value = "ldapConnectionFactory#configurable" )
64     private LdapConnectionFactory connectionFactory;
65
66     @Inject
67     @Named( value = "userConfiguration" )
68     private UserConfiguration config;
69
70     @Inject
71     private LdapCacheService ldapCacheService;
72
73     public String getId()
74     {
75         return "LdapBindAuthenticator";
76     }
77
78     public AuthenticationResult authenticate( AuthenticationDataSource s )
79         throws AuthenticationException
80     {
81         PasswordBasedAuthenticationDataSource source = (PasswordBasedAuthenticationDataSource) s;
82
83         if ( !config.getBoolean( "ldap.bind.authenticator.enabled" ) || (
84             !config.getBoolean( "ldap.bind.authenticator.allowEmptyPasswords", false ) && StringUtils.isEmpty(
85                 source.getPassword() ) ) )
86         {
87             return new AuthenticationResult( false, source.getPrincipal(), null );
88         }
89
90         SearchControls ctls = new SearchControls();
91
92         ctls.setCountLimit( 1 );
93
94         ctls.setDerefLinkFlag( true );
95         ctls.setSearchScope( SearchControls.SUBTREE_SCOPE );
96
97         String filter = "(&(objectClass=" + mapper.getUserObjectClass() + ")"
98             + ( mapper.getUserFilter() != null ? mapper.getUserFilter() : "" ) + "(" + mapper.getUserIdAttribute() + "="
99             + source.getPrincipal() + "))";
100
101         log.info( "Searching for users with filter: \'{}\'" + " from base dn: {}", filter, mapper.getUserBaseDn() );
102                                                               
103         LdapConnection ldapConnection = getLdapConnection();
104         LdapConnection authLdapConnection = null;
105         NamingEnumeration<SearchResult> results = null;
106         try
107         {
108             // check the cache for user's userDn in the ldap server
109             String userDn = ldapCacheService.getLdapUserDn( source.getPrincipal() );
110             
111             if( userDn == null )
112             {
113                 log.debug( "userDn for user {} not found in cache. Retrieving from ldap server..", source.getPrincipal() );
114
115                 DirContext context = ldapConnection.getDirContext();
116
117                 results = context.search( mapper.getUserBaseDn(), filter, ctls );
118
119                 log.info( "Found user?: {}", results.hasMoreElements() );
120
121                 if ( results.hasMoreElements() )
122                 {
123                     SearchResult result = results.nextElement();
124
125                     userDn = result.getNameInNamespace();
126
127                     log.debug( "Adding userDn {} for user {} to the cache..", userDn, source.getPrincipal() );
128
129                     // REDBACK-289/MRM-1488 cache the ldap user's userDn to lessen calls to ldap server
130                     ldapCacheService.addLdapUserDn( source.getPrincipal(), userDn );
131                 }
132                 else
133                 {
134                     return new AuthenticationResult( false, source.getPrincipal(), null );
135                 }
136             }
137
138             log.info( "Attempting Authenication: + {}", userDn );
139
140             authLdapConnection = connectionFactory.getConnection( userDn, source.getPassword() );
141
142             return new AuthenticationResult( true, source.getPrincipal(), null );
143         }
144         catch ( LdapException e )
145         {
146             return new AuthenticationResult( false, source.getPrincipal(), e );
147         }
148         catch ( NamingException e )
149         {
150             return new AuthenticationResult( false, source.getPrincipal(), e );
151         }
152         finally
153         {
154             closeNamingEnumeration( results );
155             closeLdapConnection( ldapConnection );
156             if ( authLdapConnection != null )
157             {
158                 closeLdapConnection( authLdapConnection );
159             }
160         }
161     }
162
163     public boolean supportsDataSource( AuthenticationDataSource source )
164     {
165         return ( source instanceof PasswordBasedAuthenticationDataSource );
166     }
167
168     private LdapConnection getLdapConnection()
169     {
170         try
171         {
172             return connectionFactory.getConnection();
173         }
174         catch ( LdapException e )
175         {
176             log.warn( "failed to get a ldap connection " + e.getMessage(), e );
177             throw new RuntimeException( "failed to get a ldap connection " + e.getMessage(), e );
178         }
179     }
180
181     private void closeLdapConnection( LdapConnection ldapConnection )
182     {
183         if ( ldapConnection != null )
184         {
185             ldapConnection.close();
186         }
187     }
188
189     private void closeNamingEnumeration( NamingEnumeration<SearchResult> results )
190     {
191         try
192         {
193             if ( results != null )
194             {
195                 results.close();
196             }
197         }
198         catch ( NamingException e )
199         {
200             log.warn( "skip exception closing naming search result " + e.getMessage() );
201         }
202     }
203 }