001package org.apache.archiva.redback.users.ldap;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022
023import org.apache.archiva.redback.common.ldap.connection.DefaultLdapConnection;
024import org.apache.archiva.redback.common.ldap.connection.LdapConnection;
025import org.apache.archiva.redback.common.ldap.user.LdapUser;
026import org.apache.archiva.redback.common.ldap.user.UserMapper;
027import org.apache.archiva.redback.configuration.UserConfiguration;
028import org.apache.archiva.redback.configuration.UserConfigurationKeys;
029import org.apache.archiva.redback.users.AbstractUserManager;
030import org.apache.archiva.redback.users.User;
031import org.apache.archiva.redback.users.UserExistsException;
032import org.apache.archiva.redback.users.UserManager;
033import org.apache.archiva.redback.users.UserManagerException;
034import org.apache.archiva.redback.users.UserNotFoundException;
035import org.apache.archiva.redback.common.ldap.MappingException;
036import org.apache.archiva.redback.common.ldap.connection.LdapConnectionFactory;
037import org.apache.archiva.redback.common.ldap.connection.LdapException;
038import org.apache.archiva.redback.users.UserQuery;
039import org.apache.archiva.redback.users.ldap.ctl.LdapController;
040import org.apache.archiva.redback.users.ldap.ctl.LdapControllerException;
041import org.apache.archiva.redback.users.ldap.service.LdapCacheService;
042import org.springframework.stereotype.Service;
043
044import javax.annotation.PostConstruct;
045import javax.inject.Inject;
046import javax.inject.Named;
047import javax.naming.NamingException;
048import javax.naming.directory.DirContext;
049import java.util.ArrayList;
050import java.util.Collections;
051import java.util.Date;
052import java.util.List;
053
054/**
055 * @author  jesse
056 */
057@Service("userManager#ldap")
058public class LdapUserManager
059    extends AbstractUserManager
060    implements UserManager
061{
062    @Inject
063    @Named(value = "ldapConnectionFactory#configurable")
064    private LdapConnectionFactory connectionFactory;
065
066    @Inject
067    private LdapController controller;
068
069    @Inject
070    @Named(value = "userMapper#ldap")
071    private UserMapper mapper;
072
073    @Inject
074    @Named(value = "userConfiguration#default")
075    private UserConfiguration userConf;
076
077    @Inject
078    private LdapCacheService ldapCacheService;
079
080    private User guestUser;
081
082    private boolean writableLdap = false;
083
084    @PostConstruct
085    public void initialize()
086    {
087        this.writableLdap = userConf.getBoolean( UserConfigurationKeys.LDAP_WRITABLE, this.writableLdap );
088        controller.initialize();
089    }
090
091    public boolean isReadOnly()
092    {
093        return !this.writableLdap;
094    }
095
096    public User addUser( User user )
097        throws UserManagerException
098    {
099        try
100        {
101            return addUser( user, true );
102        }
103        catch ( LdapException e )
104        {
105            throw new UserManagerException( e.getMessage(), e );
106        }
107    }
108
109    public void addUserUnchecked( User user )
110        throws UserManagerException
111    {
112        try
113        {
114            addUser( user, false );
115        }
116        catch ( LdapException e )
117        {
118            throw new UserManagerException( e.getMessage(), e );
119        }
120    }
121
122    private User addUser( User user, boolean checked )
123        throws LdapException
124    {
125        if ( user == null )
126        {
127            return null;
128        }
129        try
130        {
131            if (checked && userExists( user.getUsername() )){
132                throw new UserExistsException( "User exists already " + user.getUsername( ) );
133            }
134        }
135        catch ( UserManagerException e )
136        {
137            throw new LdapException( "Unexpected LDAP error " + e.getMessage( ) );
138        }
139
140        if ( isReadOnly() && GUEST_USERNAME.equals( user.getUsername() ) )
141        {
142            guestUser = user;
143            return guestUser;
144        }
145
146        user.setAccountCreationDate( new Date( ) );
147
148        LdapConnection ldapConnection = getLdapConnection();
149        try
150        {
151            DirContext context = ldapConnection.getDirContext();
152            controller.createUser( user, context, checked );
153        }
154        catch ( LdapControllerException e )
155        {
156            log.error( "Error mapping user: {} to LDAP attributes.", user.getUsername(), e );
157        }
158        catch ( MappingException e )
159        {
160            log.error( "Error mapping user: {} to LDAP attributes.", user.getUsername(), e );
161        }
162        finally
163        {
164            closeLdapConnection( ldapConnection );
165        }
166        return user;
167    }
168
169    public User createUser( String username, String fullName, String emailAddress )
170    {
171        return mapper.newUserInstance( username, fullName, emailAddress );
172    }
173
174    public UserQuery createUserQuery()
175    {
176        return new LdapUserQuery();
177    }
178
179
180    public void deleteUser( String username )
181        throws UserNotFoundException, UserManagerException
182    {
183        if ( username != null )
184        {
185            clearFromCache( username );
186        }
187        LdapConnection ldapConnection = null;
188        try
189        {
190            ldapConnection = getLdapConnection();
191            DirContext context = ldapConnection.getDirContext();
192            controller.removeUser( username, context );
193        }
194        catch ( LdapControllerException e )
195        {
196            log.error( "Failed to delete user: {}", username, e );
197        }
198        catch ( LdapException e )
199        {
200            throw new UserManagerException( e.getMessage(), e );
201        }
202        finally
203        {
204            closeLdapConnection( ldapConnection );
205        }
206    }
207
208    public void eraseDatabase()
209    {
210        // TODO Implement erase!
211    }
212
213    @Override
214    public User findUser( String username, boolean useCache )
215        throws UserNotFoundException, UserManagerException
216    {
217        if ( username == null )
218        {
219            throw new UserNotFoundException( "Unable to find user based on null username." );
220        }
221
222        if ( useCache )
223        {
224            // REDBACK-289/MRM-1488
225            // look for the user in the cache first
226            LdapUser ldapUser = ldapCacheService.getUser( username );
227            if ( ldapUser != null )
228            {
229                log.debug( "User {} found in cache.", username );
230                return ldapUser;
231            }
232        }
233        LdapConnection ldapConnection = null;
234
235        try
236        {
237            ldapConnection = getLdapConnection();
238            DirContext context = ldapConnection.getDirContext();
239            User user = controller.getUser( username, context );
240            if ( user == null )
241            {
242                throw new UserNotFoundException( "user with name " + username + " not found " );
243            }
244
245            // REDBACK-289/MRM-1488
246            log.debug( "Adding user {} to cache..", username );
247
248            ldapCacheService.addUser( (LdapUser) user );
249
250            return user;
251        }
252        catch ( LdapControllerException e )
253        {
254            log.error( "Failed to find user: {}", username, e );
255            return null;
256        }
257        catch ( LdapException e )
258        {
259            throw new UserManagerException( e.getMessage(), e );
260        }
261        catch ( MappingException e )
262        {
263            log.error( "Failed to map user: {}", username, e );
264            return null;
265        }
266        finally
267        {
268            closeLdapConnection( ldapConnection );
269        }
270    }
271
272    public User findUser( String username )
273        throws UserNotFoundException, UserManagerException
274    {
275        return findUser( username, true );
276    }
277
278    public List<User> findUsersByEmailKey( String emailKey, boolean orderAscending )
279        throws UserManagerException
280    {
281        LdapUserQuery query = new LdapUserQuery();
282        query.setEmail( emailKey );
283        query.setOrderBy( UserQuery.ORDER_BY_EMAIL );
284        query.setAscending( orderAscending );
285        return findUsersByQuery( query );
286    }
287
288    public List<User> findUsersByFullNameKey( String fullNameKey, boolean orderAscending )
289        throws UserManagerException
290    {
291        LdapUserQuery query = new LdapUserQuery();
292        query.setFullName( fullNameKey );
293        query.setOrderBy( UserQuery.ORDER_BY_FULLNAME );
294        query.setAscending( orderAscending );
295        return findUsersByQuery( query );
296    }
297
298    public List<User> findUsersByQuery( UserQuery query )
299        throws UserManagerException
300    {
301        if ( query == null )
302        {
303            return Collections.emptyList();
304        }
305
306        LdapConnection ldapConnection = null;
307
308        try
309        {
310            ldapConnection = getLdapConnection();
311            DirContext context = ldapConnection.getDirContext();
312            return controller.getUsersByQuery( (LdapUserQuery) query, context );
313        }
314        catch ( LdapControllerException e )
315        {
316            log.error( "Failed to find user", e );
317            return null;
318        }
319        catch ( MappingException e )
320        {
321            log.error( "Failed to map user", e );
322            return null;
323        }
324        catch ( LdapException e )
325        {
326            throw new UserManagerException( e.getMessage(), e );
327        }
328        finally
329        {
330            closeLdapConnection( ldapConnection );
331        }
332    }
333
334    /**
335     * @see org.apache.archiva.redback.users.UserManager#findUsersByUsernameKey(java.lang.String, boolean)
336     */
337    public List<User> findUsersByUsernameKey( String usernameKey, boolean orderAscending )
338        throws UserManagerException
339    {
340        LdapUserQuery query = new LdapUserQuery();
341        query.setUsername( usernameKey );
342        query.setOrderBy( UserQuery.ORDER_BY_USERNAME );
343        query.setAscending( orderAscending );
344        return findUsersByQuery( query );
345    }
346
347    public String getId()
348    {
349        return "ldap";
350    }
351
352    /**
353     * @see org.apache.archiva.redback.users.UserManager#getUsers()
354     */
355    public List<User> getUsers()
356    {
357        LdapConnection ldapConnection = null;
358
359        try
360        {
361            ldapConnection = getLdapConnection();
362            DirContext context = ldapConnection.getDirContext();
363            List<User> users = new ArrayList<User>( controller.getUsers( context ) );
364            //We add the guest user because it isn't in LDAP
365            try
366            {
367                User u = getGuestUser();
368                if ( u != null )
369                {
370                    users.add( u );
371                }
372            }
373            catch ( UserNotFoundException e )
374            {
375                //Nothing to do
376            }
377            return users;
378        }
379        /*catch ( LdapException e )
380        {
381            throw new UserManagerException( e.getMessage(), e );
382        }*/
383        catch ( Exception e )
384        {
385            log.error( e.getMessage(), e );
386        }
387        finally
388        {
389            closeLdapConnection( ldapConnection );
390        }
391        return Collections.emptyList();
392    }
393
394    public List<User> getUsers( boolean orderAscending )
395    {
396        return getUsers();
397    }
398
399    public User updateUser( User user )
400        throws UserNotFoundException, UserManagerException
401    {
402        return updateUser( user, false );
403    }
404
405    public User updateUser( User user, boolean passwordChangeRequired )
406        throws UserNotFoundException, UserManagerException
407    {
408        if ( user != null )
409        {
410            clearFromCache( user.getUsername() );
411        }
412
413        LdapConnection ldapConnection = null;
414
415        try
416        {
417            ldapConnection = getLdapConnection();
418            DirContext context = ldapConnection.getDirContext();
419            controller.updateUser( user, context );
420        }
421        catch ( LdapControllerException e )
422        {
423            log.error( "Failed to update user: {}", user.getUsername(), e );
424        }
425        catch ( MappingException e )
426        {
427            log.error( "Failed to update user: {}", user.getUsername(), e );
428        }
429        catch ( LdapException e )
430        {
431            throw new UserManagerException( e.getMessage(), e );
432        }
433        finally
434        {
435            closeLdapConnection( ldapConnection );
436        }
437        return user;
438    }
439
440    public boolean userExists( String principal )
441        throws UserManagerException
442    {
443        if ( principal == null )
444        {
445            return false;
446        }
447
448        // REDBACK-289/MRM-1488
449        // look for the user in the cache first
450        LdapUser ldapUser = ldapCacheService.getUser( principal );
451        if ( ldapUser != null )
452        {
453            log.debug( "User {} found in cache.", principal );
454            return true;
455        }
456
457        LdapConnection ldapConnection = null;
458
459        try
460        {
461            ldapConnection = getLdapConnection();
462            DirContext context = ldapConnection.getDirContext();
463            return controller.userExists( principal, context );
464        }
465        catch ( LdapControllerException e )
466        {
467            log.warn( "Failed to search for user: {}", principal, e );
468            return false;
469        }
470        catch ( LdapException e )
471        {
472            throw new UserManagerException( e.getMessage(), e );
473        }
474        finally
475        {
476            closeLdapConnection( ldapConnection );
477        }
478    }
479
480    private LdapConnection getLdapConnection()
481        throws LdapException
482    {
483        try
484        {
485            return connectionFactory.getConnection();
486        }
487        catch ( LdapException e )
488        {
489            log.warn( "failed to get a ldap connection {}", e.getMessage(), e );
490            throw new LdapException( "failed to get a ldap connection " + e.getMessage(), e );
491        }
492    }
493
494    private void closeLdapConnection( LdapConnection ldapConnection )
495    {
496        if ( ldapConnection != null )
497        {
498            try
499            {
500                ldapConnection.close();
501            }
502            catch ( NamingException e )
503            {
504                log.error( "Could not close connection: {}", e.getMessage( ), e );
505            }
506        }
507    }
508
509    // REDBACK-289/MRM-1488
510    private void clearFromCache( String username )
511    {
512        log.debug( "Removing user {} from cache..", username );
513        ldapCacheService.removeUser( username );
514
515        log.debug( "Removing userDn for user {} from cache..", username );
516        ldapCacheService.removeLdapUserDn( username );
517    }
518
519    public boolean isFinalImplementation()
520    {
521        return true;
522    }
523
524    public String getDescriptionKey()
525    {
526        return "archiva.redback.usermanager.ldap";
527    }
528}