KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sslexplorer > activedirectory > ActiveDirectoryUserDatabase


1 /*
2  * SSL-Explorer
3  *
4  * Copyright (C) 2003-2006 3SP LTD. All Rights Reserved
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2 of
9  * the License, or (at your option) any later version.
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */

19             
20 package com.sslexplorer.activedirectory;
21
22 import java.security.PrivilegedAction JavaDoc;
23 import java.util.ArrayList JavaDoc;
24 import java.util.Collection JavaDoc;
25 import java.util.Collections JavaDoc;
26 import java.util.Date JavaDoc;
27 import java.util.HashMap JavaDoc;
28 import java.util.LinkedList JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.Map JavaDoc;
31 import java.util.Properties JavaDoc;
32 import java.util.StringTokenizer JavaDoc;
33
34 import javax.management.relation.RoleNotFoundException JavaDoc;
35 import javax.naming.Context JavaDoc;
36 import javax.naming.NamingException JavaDoc;
37 import javax.naming.directory.Attribute JavaDoc;
38 import javax.naming.directory.Attributes JavaDoc;
39 import javax.naming.directory.SearchResult JavaDoc;
40 import javax.naming.ldap.InitialLdapContext JavaDoc;
41 import javax.security.auth.login.AppConfigurationEntry JavaDoc;
42 import javax.security.auth.login.Configuration JavaDoc;
43 import javax.security.auth.login.LoginContext JavaDoc;
44
45 import org.apache.commons.logging.Log;
46 import org.apache.commons.logging.LogFactory;
47
48 import com.sslexplorer.boot.ContextHolder;
49 import com.sslexplorer.core.CoreEvent;
50 import com.sslexplorer.core.CoreJAASConfiguration;
51 import com.sslexplorer.core.CoreListener;
52 import com.sslexplorer.core.CoreServlet;
53 import com.sslexplorer.policyframework.Principal;
54 import com.sslexplorer.properties.Property;
55 import com.sslexplorer.properties.PropertyChangeEvent;
56 import com.sslexplorer.properties.impl.userattributes.UserAttributeKey;
57 import com.sslexplorer.realms.Realm;
58 import com.sslexplorer.security.DefaultUserDatabase;
59 import com.sslexplorer.security.InvalidLoginCredentialsException;
60 import com.sslexplorer.security.Role;
61 import com.sslexplorer.security.User;
62 import com.sslexplorer.security.UserDatabaseException;
63 import com.sslexplorer.security.UserNotFoundException;
64
65 /**
66  * <p>
67  * Microsoft Active Directory implementation of {@link com.sslexplorer.security.UserDatabase}.
68  * @author Lee Painter <a HREF="mailto:lee@3sp.com">&lt;lee@3sp.com&gt;</a>
69  * @author Brett Smith <a HREF="mailto:brett@3sp.com">&lt;brett@3sp.com&gt;</a>
70  */

71 public class ActiveDirectoryUserDatabase extends DefaultUserDatabase implements CoreListener {
72     private static final String JavaDoc OBJECT_SID_ATTRIBUTE = "objectSID";
73     private static final String JavaDoc HOME_DIRECTORY_ATTRIBUTE = "homeDirectory";
74     private static final String JavaDoc HOME_DRIVE_ATTRIBUTE = "homeDrive";
75     private static final String JavaDoc MEMBER_OF_ATTIBUTE = "memberOf";
76     private static final String JavaDoc PWD_LAST_SET_ATTRIBUTE = "pwdLastSet";
77     private static final String JavaDoc GROUPNAME_FILTER_ATTRIBUTE = "%GROUPNAME%";
78     private static final String JavaDoc USERNAME_FILTER_PARAMETER = "%USERNAME%";
79     
80     public static final String JavaDoc SAM_ACCOUNT_NAME_ATTRIBUTE = "sAMAccountName";
81     public static final String JavaDoc COMMON_NAME_ATTRIBUTE = "cn";
82     public static final String JavaDoc DISPLAY_NAME_ATTRIBUTE = "displayName";
83     public static final String JavaDoc USER_PRINCIPAL_NAME_ATTRIBUTE = "userPrincipalName";
84     public static final String JavaDoc MAIL_ATTRIBUTE = "mail";
85     public static final String JavaDoc USER_ACCOUNT_CONTROL_ATTRIBUTE = "userAccountControl";
86     public static final String JavaDoc PRIMARY_GROUP_ID_ATTRIBUTE = "primaryGroupId";
87     public static final String JavaDoc OBJECT_CLASS_ATTRIBUTE = "objectClass";
88     
89     private static final String JavaDoc USER_FILTER = "(&(!(" + OBJECT_CLASS_ATTRIBUTE + "=computer))(" + OBJECT_CLASS_ATTRIBUTE + "=user)(|(" + SAM_ACCOUNT_NAME_ATTRIBUTE + "=" + USERNAME_FILTER_PARAMETER + ")(" + USER_PRINCIPAL_NAME_ATTRIBUTE + "=" + USERNAME_FILTER_PARAMETER + ")))";
90     private static final String JavaDoc GROUP_FILTER = "(&(" + OBJECT_CLASS_ATTRIBUTE + "=group)(" + COMMON_NAME_ATTRIBUTE + "=" + GROUPNAME_FILTER_ATTRIBUTE + "))";
91     private static final String JavaDoc USER_ATTRS[] = {SAM_ACCOUNT_NAME_ATTRIBUTE, USER_PRINCIPAL_NAME_ATTRIBUTE, DISPLAY_NAME_ATTRIBUTE, MAIL_ATTRIBUTE, HOME_DIRECTORY_ATTRIBUTE, HOME_DRIVE_ATTRIBUTE, MEMBER_OF_ATTIBUTE, PRIMARY_GROUP_ID_ATTRIBUTE, PWD_LAST_SET_ATTRIBUTE, USER_ACCOUNT_CONTROL_ATTRIBUTE};
92     private static final String JavaDoc GROUP_ATTRS[] = {COMMON_NAME_ATTRIBUTE, OBJECT_SID_ATTRIBUTE, MEMBER_OF_ATTIBUTE};
93     private static final String JavaDoc WILDCARD_SEARCH = "*";
94     private static final Log logger = LogFactory.getLog(ActiveDirectoryUserDatabase.class);
95
96     private ActiveDirectoryUserDatabaseConfiguration configuration;
97     private UserContainer userContainer = UserContainer.EMPTY_CACHE;
98     private GroupContainer groupContainer = GroupContainer.EMPTY_CACHE;
99     private ThreadRunner threadRunner;
100     
101     /** Constructor */
102     public ActiveDirectoryUserDatabase() {
103         super("Active Directory", false, false);
104         addJAASConfiguration();
105     }
106     
107     private void addJAASConfiguration() {
108         Map JavaDoc<String JavaDoc, String JavaDoc> parameters = new HashMap JavaDoc<String JavaDoc, String JavaDoc>();
109         parameters.put("client", "TRUE");
110         parameters.put("debug", String.valueOf(logger.isDebugEnabled()).toUpperCase());
111         parameters.put("useSubjectCredsOnly", "FALSE");
112         parameters.put("useTicketCache", "FALSE");
113         parameters.put("refreshKrb5Config", "TRUE");
114             
115         AppConfigurationEntry JavaDoc entry = new AppConfigurationEntry JavaDoc("com.sun.security.auth.module.Krb5LoginModule", AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, parameters);
116         CoreJAASConfiguration config = (CoreJAASConfiguration) Configuration.getConfiguration();
117         config.addAppConfigurationEntry(ActiveDirectoryUserDatabase.class.getName(), entry);
118     }
119     
120     protected ActiveDirectoryUserDatabaseConfiguration getConfiguration() {
121         return configuration;
122     }
123
124     protected UserContainer getUserContainer() {
125         return userContainer;
126     }
127     
128     protected GroupContainer getGroupContainer() {
129         return groupContainer;
130     }
131     
132     /*
133      * (non-Javadoc)
134      * @see com.sslexplorer.security.UserDatabase#logon(java.lang.String, java.lang.String)
135      */

136     public User logon(String JavaDoc username, String JavaDoc password) throws UserDatabaseException, InvalidLoginCredentialsException {
137         ActiveDirectoryUser user = null;
138         try {
139             user = (ActiveDirectoryUser) getAccount(username);
140         } catch (Exception JavaDoc e) {
141             logger.error("Failed to logon", e);
142             throw new UserDatabaseException("Failed to logon", e);
143         }
144         // this needs to be outside the try/catch, otherwise the specific exception is turned into a general one
145
assertValidCredentials(user, password);
146         return user;
147     }
148           
149     private void assertValidCredentials(ActiveDirectoryUser user, String JavaDoc password) throws InvalidLoginCredentialsException {
150         if (!areCredentialsValid(user, password)) {
151             throw new InvalidLoginCredentialsException("Invalid username or password.");
152         }
153     }
154     
155     /*
156      * (non-Javadoc)
157      * @see com.sslexplorer.security.UserDatabase#listAllUsers(java.lang.String)
158      */

159     public User[] listAllUsers(String JavaDoc filter) throws Exception JavaDoc {
160         if (logger.isDebugEnabled()) {
161             logger.debug("List all users with filter '" + filter + "'");
162         }
163         Collection JavaDoc<ActiveDirectoryUser> users = userContainer.retrievePrincipals(filter);
164         return users.toArray(new User[users.size()]);
165     }
166     
167     /*
168      * (non-Javadoc)
169      * @see com.sslexplorer.security.UserDatabase#getAccount(java.lang.String)
170      */

171     public User getAccount(String JavaDoc username) throws UserNotFoundException, Exception JavaDoc {
172         if (logger.isDebugEnabled()) {
173             logger.debug("Getting account " + username);
174         }
175
176         username = configuration.isUsernamesAreCaseSensitive() ? username : username.toLowerCase();
177         if (!userContainer.containsPrincipal(username)) {
178             loadUsers(username, false);
179         }
180         if (!userContainer.containsPrincipal(username)) {
181             throw new UserNotFoundException(username + " is not a valid user!");
182         }
183         return userContainer.retrievePrincipal(username);
184     }
185
186     /*
187      * (non-Javadoc)
188      * @see com.sslexplorer.security.UserDatabase#getRole(java.lang.String)
189      */

190     public Role getRole(String JavaDoc groupName) throws Exception JavaDoc {
191         if (logger.isDebugEnabled()) {
192             logger.debug("Getting group " + groupName);
193         }
194
195         if (!groupContainer.containsPrincipal(groupName)) {
196             loadRoles(groupName, false);
197         }
198         if (!groupContainer.containsPrincipal(groupName)) {
199             throw new RoleNotFoundException JavaDoc(groupName + " is not a valid group!");
200         }
201
202         ActiveDirectoryGroup group = groupContainer.retrievePrincipal(groupName);
203         groupContainer.buildHierarchy(group.getOriginalDn());
204         return group;
205     }
206
207     /*
208      * (non-Javadoc)
209      * @see com.sslexplorer.security.UserDatabase#listAllRoles(java.lang.String)
210      */

211     public Role[] listAllRoles(String JavaDoc filter) throws UserDatabaseException {
212         if (logger.isDebugEnabled()) {
213             logger.debug("List all groups with filter '" + filter + "'");
214         }
215         Collection JavaDoc<ActiveDirectoryGroup> groups = groupContainer.retrievePrincipals(filter);
216         return groups.toArray(new Role[groups.size()]);
217     }
218
219     /*
220      * (non-Javadoc)
221      * @see com.sslexplorer.security.UserDatabase#checkPassword(java.lang.String,java.lang.String)
222      */

223     public boolean checkPassword(String JavaDoc username, String JavaDoc password) throws UserDatabaseException, InvalidLoginCredentialsException {
224         try {
225             ActiveDirectoryUser user = (ActiveDirectoryUser) getAccount(username);
226             return areCredentialsValid(user, password);
227         } catch (UserNotFoundException e) {
228             throw new UserDatabaseException("Failed to check password", e);
229         } catch (Exception JavaDoc e) {
230             throw new UserDatabaseException("Failed to check password", e);
231         }
232     }
233
234     /*
235      * (non-Javadoc)
236      * @see com.sslexplorer.security.UserDatabase#listAvailablePrincipals()
237      */

238     public Principal[] listAvailablePrincipals() throws Exception JavaDoc {
239         if (logger.isDebugEnabled()) {
240             logger.debug("Listing available principals");
241         }
242         return listAllUsers(WILDCARD_SEARCH);
243     }
244     
245     private void loadUsers(final String JavaDoc filter, final boolean removeMissingEntries) throws UserDatabaseException {
246         configuration.doAs(new RetryPrivilegedAction() {
247             protected Object JavaDoc doIt(InitialLdapContext JavaDoc context) throws Exception JavaDoc {
248                 loadUsers(filter, context, removeMissingEntries);
249                 return null;
250             }
251         });
252     }
253     
254     private void loadUsers(String JavaDoc filter, InitialLdapContext JavaDoc context, boolean removeMissingEntries) throws Exception JavaDoc {
255         final Collection JavaDoc<String JavaDoc> usernames = userContainer.retrievePrincipalNames();
256         final LinkedList JavaDoc<Exception JavaDoc> exceptions = new LinkedList JavaDoc<Exception JavaDoc>();
257         PagedResultMapper mapper = new PagedResultMapper ()
258         {
259             public void mapSearchResult(SearchResult JavaDoc searchResult) throws Exception JavaDoc {
260                 String JavaDoc dn = searchResult.getNameInNamespace();
261                 ActiveDirectoryUser user = populateActiveDirectoryUser(dn, searchResult.getAttributes());
262                 String JavaDoc key = userContainer.storePrincipal(user);
263                 usernames.remove(key);
264                 if (logger.isDebugEnabled()) {
265                     logger.debug("Found user " + user);
266                 }
267             }
268
269             public void processSearchResultException(SearchResult JavaDoc searchResult, Exception JavaDoc e) {
270                 String JavaDoc dn = searchResult.getNameInNamespace();
271                 logger.error("Problem retrieving user " + dn, e);
272             }
273
274             public void processException(Exception JavaDoc e) {
275                 logger.error("Problem retrieving users", e);
276                 exceptions.add(e);
277             }
278         };
279         String JavaDoc escapedFilter = ActiveDirectoryUserDatabaseConfiguration.getEscapedDn(filter, true);
280         String JavaDoc replacedFilter = USER_FILTER.replaceAll(USERNAME_FILTER_PARAMETER, escapedFilter);
281         configuration.search(context, replacedFilter, USER_ATTRS, mapper);
282         
283         if (removeMissingEntries) {
284             userContainer.updateRemovedPrincipals(usernames);
285         }
286         
287         if (!exceptions.isEmpty()) {
288             Exception JavaDoc e = exceptions.getLast();
289             logger.error(exceptions.size() + " exceptions occurred, throwing the last one", e);
290             throw e;
291         }
292     }
293
294     private void loadRoles(final String JavaDoc filter, final boolean removeMissingEntries) throws UserDatabaseException {
295         configuration.doAs(new RetryPrivilegedAction() {
296             @Override JavaDoc
297             protected Object JavaDoc doIt(InitialLdapContext JavaDoc context) throws Exception JavaDoc {
298                 loadRoles(filter, context, removeMissingEntries);
299                 return null;
300             }
301
302             @Override JavaDoc
303             protected InitialLdapContext JavaDoc getContext(String JavaDoc url) throws Exception JavaDoc {
304                 Map JavaDoc<String JavaDoc, String JavaDoc> extraProperties = Collections.<String JavaDoc, String JavaDoc> singletonMap("java.naming.ldap.attributes.binary", OBJECT_SID_ATTRIBUTE);
305                 return configuration.getAuthenticatedContext(url, extraProperties);
306             }
307         });
308         groupContainer.buildHierarchy();
309     }
310     
311     private void loadRoles(String JavaDoc filter, InitialLdapContext JavaDoc context, boolean removeMissingEntries) throws Exception JavaDoc {
312         final Collection JavaDoc<String JavaDoc> groupNames = groupContainer.retrievePrincipalNames();
313         final LinkedList JavaDoc<Exception JavaDoc> exceptions = new LinkedList JavaDoc<Exception JavaDoc>();
314         PagedResultMapper mapper = new PagedResultMapper ()
315         {
316             public void mapSearchResult(SearchResult JavaDoc searchResult) throws Exception JavaDoc {
317                 String JavaDoc dn = searchResult.getNameInNamespace();
318                 Attributes JavaDoc attributes = searchResult.getAttributes();
319                 String JavaDoc commonName = getAttributeValue(attributes, COMMON_NAME_ATTRIBUTE);
320                 if (commonName.length() != 0) {
321                     Long JavaDoc rid = ActiveDirectoryGroup.getRIDFromSID((byte[]) attributes.get(OBJECT_SID_ATTRIBUTE).get());
322                     ActiveDirectoryGroup group = new ActiveDirectoryGroup(commonName, dn, getEscapedDn(dn), rid, getRealm());
323                     String JavaDoc[] parents = getParents(attributes);
324                     String JavaDoc key = groupContainer.storeGroup(group, parents);
325                     groupNames.remove(key);
326                 }
327             }
328             
329             private String JavaDoc[] getParents(Attributes JavaDoc attributes) throws NamingException JavaDoc {
330                 List JavaDoc<String JavaDoc> parents = new ArrayList JavaDoc<String JavaDoc>();
331                 Attribute JavaDoc memberOfAttribute = attributes.get(MEMBER_OF_ATTIBUTE);
332                 if (memberOfAttribute != null) {
333                     for (int index = 0; index < memberOfAttribute.size(); index++) {
334                         String JavaDoc parentDn = (String JavaDoc) memberOfAttribute.get(index);
335                         if (configuration.isDnValid(parentDn)) {
336                             parents.add(parentDn); // valid parent so record
337
}
338                     }
339                 }
340                 return parents.toArray(new String JavaDoc[parents.size()]);
341             }
342
343             public void processSearchResultException(SearchResult JavaDoc searchResult, Exception JavaDoc e) {
344                 String JavaDoc dn = searchResult.getNameInNamespace();
345                 logger.error("Problem retrieving group " + dn, e);
346             }
347
348             public void processException(Exception JavaDoc e) {
349                 logger.error("Problem retrieving groups", e);
350                 exceptions.add(e);
351             }
352         };
353         String JavaDoc escapedFilter = ActiveDirectoryUserDatabaseConfiguration.getEscapedDn(filter, true);
354         String JavaDoc replacedFilter = GROUP_FILTER.replaceAll(GROUPNAME_FILTER_ATTRIBUTE, escapedFilter);
355         configuration.search(context, replacedFilter, GROUP_ATTRS, mapper);
356         
357         if (removeMissingEntries) {
358             groupContainer.updateRemovedGroups(groupNames);
359         }
360         
361         if (!exceptions.isEmpty()) {
362             Exception JavaDoc e = exceptions.getLast();
363             logger.error(exceptions.size() + " exceptions occurred, throwing the last one", e);
364             throw e;
365         }
366     }
367
368     private boolean areCredentialsValid(ActiveDirectoryUser user, String JavaDoc password) {
369         if (logger.isDebugEnabled()) {
370             logger.debug("Authenticating with user " + user + " and password = '********'");
371         }
372         
373         try {
374             if (configuration.isUserAuthenticationGssApi()) {
375                 LoginContext JavaDoc context = configuration.createLoginContext(user.getUserPrincipalName(), password);
376                 ActiveDirectoryUserDatabaseConfiguration.logoutContext(context);
377             } else {
378                 return areSimpleCredentialsValid(user.getPrincipalName(), password);
379             }
380             return true;
381         } catch (Exception JavaDoc e) {
382             logger.error("Failure to authenticate user", e);
383             return false;
384         }
385     }
386     
387     private boolean areSimpleCredentialsValid(final String JavaDoc username, final String JavaDoc password) {
388         RetryPrivilegedAction action = new RetryPrivilegedAction() {
389             protected Object JavaDoc doIt(InitialLdapContext JavaDoc context) throws Exception JavaDoc {
390                 return null;
391             }
392
393             protected InitialLdapContext JavaDoc getContext(String JavaDoc url) throws Exception JavaDoc {
394                 String JavaDoc userDn = ((ActiveDirectoryUser) getAccount(username)).getOriginalDn();
395                 Map JavaDoc<String JavaDoc, String JavaDoc> variables = new HashMap JavaDoc<String JavaDoc, String JavaDoc>(3);
396                 variables.put(Context.SECURITY_AUTHENTICATION, configuration.getUserAuthenticationType());
397                 variables.put(Context.SECURITY_PRINCIPAL, userDn);
398                 variables.put(Context.SECURITY_CREDENTIALS, password);
399                 return configuration.getInitialContext(url, variables);
400             }
401         };
402         Object JavaDoc result = action.run();
403         boolean isThrowable = (result instanceof Throwable JavaDoc);
404         if (isThrowable && logger.isErrorEnabled()) {
405             logger.error("Failure to authenticate user", (Throwable JavaDoc) result);
406         }
407         return !isThrowable;
408     }
409     
410     /**
411      * Get a user account given a DN. <code>null</code> will be returned if no such account can be found.
412      * @param dn dn
413      * @return user account
414      * @throws Exception on any error
415      */

416     public User getAccountFromDN(final String JavaDoc dn) throws Exception JavaDoc {
417         if (dn.indexOf("CN") > -1 && dn.indexOf("DC") > -1) {
418             // This looks like a DN so do a lookup
419
if (logger.isDebugEnabled()) {
420                 logger.debug("Looking up account using DN: " + dn);
421             }
422                 
423             return (User) configuration.doAs(new RetryPrivilegedAction(false) {
424                 protected Object JavaDoc doIt(InitialLdapContext JavaDoc context) throws Exception JavaDoc {
425                     return getAccountFromDN(dn, context);
426                 }
427             });
428         }
429     
430         throw new Exception JavaDoc("Certificate requires subject to be DN of Active Directory user");
431     }
432     
433     private User getAccountFromDN(String JavaDoc dn, InitialLdapContext JavaDoc context) throws NamingException JavaDoc {
434         String JavaDoc actualDN = null;
435         for (StringTokenizer JavaDoc tokens = new StringTokenizer JavaDoc(dn, ","); tokens.hasMoreTokens();) {
436             String JavaDoc elm = tokens.nextToken().trim();
437             if (elm.toUpperCase().startsWith("CN") || elm.toUpperCase().startsWith("OU") || elm.toUpperCase().startsWith("DC")) {
438                 actualDN = (actualDN == null ? "" : actualDN + ",") + elm;
439             }
440         }
441         
442         try {
443             Attributes JavaDoc attributes = context.getAttributes(actualDN, USER_ATTRS);
444             return populateActiveDirectoryUser(dn, attributes);
445         } catch (Exception JavaDoc e) {
446             logger.error("Cannot locate user for DN " + dn, e);
447             throw new NamingException JavaDoc("User not found for DN " + dn);
448         }
449     }
450
451     private ActiveDirectoryUser populateActiveDirectoryUser(String JavaDoc dn, Attributes JavaDoc attributes) throws NamingException JavaDoc, UserDatabaseException {
452         if (attributes == null) {
453             throw new NamingException JavaDoc("No attributes for " + dn);
454         }
455         
456         String JavaDoc username = getAttributeValue(attributes, SAM_ACCOUNT_NAME_ATTRIBUTE);
457         String JavaDoc userPrincipalName = getAttributeValue(attributes, USER_PRINCIPAL_NAME_ATTRIBUTE);
458         String JavaDoc email = getAttributeValue(attributes, MAIL_ATTRIBUTE);
459         String JavaDoc fullName = getAttributeValue(attributes, DISPLAY_NAME_ATTRIBUTE);
460         Date JavaDoc lastPasswordChange = isPasswordChangeAllowed(attributes) ? getPasswordLastSetDate(attributes) : new Date JavaDoc();
461         ActiveDirectoryUser user = new ActiveDirectoryUser(username, userPrincipalName, email, fullName, dn, getEscapedDn(dn), lastPasswordChange, getRealm());
462         
463         String JavaDoc homeDirectory = getAttributeValue(attributes, User.USER_ATTR_HOME_DIRECTORY);
464         if (homeDirectory.length() != 0) {
465             Property.setProperty(new UserAttributeKey(user, User.USER_ATTR_HOME_DIRECTORY), homeDirectory, null);
466         }
467         
468         String JavaDoc homeDrive = getAttributeValue(attributes, User.USER_ATTR_HOME_DRIVE);
469         if (homeDrive.length() != 0) {
470             Property.setProperty(new UserAttributeKey(user, User.USER_ATTR_HOME_DRIVE), homeDrive, null);
471         }
472
473         ActiveDirectoryGroup[] groups = getGroups(attributes);
474         if (logger.isDebugEnabled()) {
475             logger.debug("User belongs to " + groups.length + " groups");
476         }
477         user.setRoles(groups);
478         return user;
479     }
480     
481     private static Date JavaDoc getPasswordLastSetDate(Attributes JavaDoc attributes) throws NamingException JavaDoc {
482         try {
483             String JavaDoc attributeValue = getAttributeValue(attributes, PWD_LAST_SET_ATTRIBUTE);
484             long parseLong = Long.parseLong(attributeValue);
485             return ActiveDirectoryUser.adTimeToJavaDate(parseLong);
486         } catch (NumberFormatException JavaDoc e) {
487             return new Date JavaDoc();
488         }
489     }
490     
491     /**
492      * @param attributes
493      * @return true if the last password change date should be required
494      * @throws NamingException
495      */

496     protected boolean isPasswordChangeAllowed(Attributes JavaDoc attributes) throws NamingException JavaDoc {
497         return false;
498     }
499     
500     private ActiveDirectoryGroup[] getGroups(Attributes JavaDoc attributes) throws NamingException JavaDoc {
501         Collection JavaDoc<ActiveDirectoryGroup> groups = new ArrayList JavaDoc<ActiveDirectoryGroup>();
502         String JavaDoc primaryGroupId = getAttributeValue(attributes, PRIMARY_GROUP_ID_ATTRIBUTE);
503         if (primaryGroupId.length() != 0) {
504             Long JavaDoc rid = new Long JavaDoc(Long.parseLong(primaryGroupId));
505             
506             if (logger.isDebugEnabled()) {
507                 logger.debug("Users primaryGroupId is " + rid.toString());
508             }
509             
510             ActiveDirectoryGroup group = groupContainer.getByRid(rid);
511             if (group != null) {
512                 if (logger.isDebugEnabled()) {
513                     logger.debug("Users primary group is " + group.getOriginalDn());
514                 }
515                 groups.add(group);
516             } else {
517                 if (logger.isDebugEnabled()) {
518                     logger.debug("Could not find primary group " + rid.toString());
519                 }
520             }
521         }
522         
523         Attribute JavaDoc memberOfAttribute = attributes.get(MEMBER_OF_ATTIBUTE);
524         if (memberOfAttribute != null) {
525             for (int index = 0; index < memberOfAttribute.size(); index++) {
526                 String JavaDoc groupDn = (String JavaDoc) memberOfAttribute.get(index);
527                 if (logger.isDebugEnabled()) {
528                     logger.debug("Checking if user is a member of " + groupDn + " a valid group");
529                 }
530                 
531                 if (groupContainer.containsDn(groupDn)) {
532                     ActiveDirectoryGroup group = (ActiveDirectoryGroup) groupContainer.getGroupByDn(groupDn);
533                     if (group != null && !groups.contains(group)) {
534                         groups.add(group);
535                         
536                         if (logger.isDebugEnabled()) {
537                             logger.debug("Member of " + groupDn + " [" + group.getPrincipalName() + "]");
538                         }
539                         
540                         /**
541                          * Add the parent groups for each group since the user
542                          * effectively belongs to those groups too.
543                          */

544                         if (group.getParents() != null) {
545                             for (int parentIndex = 0; parentIndex < group.getParents().length; parentIndex++) {
546                                 ActiveDirectoryGroup parentGroup = group.getParents()[parentIndex];
547                                 if (parentGroup == null) {
548                                     if (logger.isDebugEnabled()) {
549                                         logger.debug("Found NULL parent group");
550                                     }
551                                 } else if (!groups.contains(parentGroup)) {
552                                     groups.add(parentGroup);
553                                 }
554                             }
555                         }
556                     } else {
557                         if (logger.isInfoEnabled()) {
558                             logger.info("Could not find group " + groupDn);
559                         }
560                     }
561                 }
562             }
563         }
564         return groups.toArray(new ActiveDirectoryGroup[groups.size()]);
565     }
566
567     /*
568      * (non-Javadoc)
569      * @see com.sslexplorer.core.CoreListener#coreEvent(com.sslexplorer.core.CoreEvent)
570      */

571     public void coreEvent(CoreEvent event) {
572         // When in install mode, the wizard looks after re-initialising the user database
573
if (!ContextHolder.getContext().isSetupMode() && event instanceof PropertyChangeEvent) {
574             PropertyChangeEvent changeEvent = (PropertyChangeEvent) event;
575             if (changeEvent.getDefinition().getName().startsWith("activeDirectory.")) {
576                 if (logger.isInfoEnabled()) {
577                     logger.info("Active Directory configuration changed. Re-initialising");
578                 }
579                 try {
580                     configuration.refresh();
581                     initialise();
582                 } catch (Exception JavaDoc e) {
583                     logger.error("Failed to re-initialise Active Directory.", e);
584                 }
585             }
586         }
587     }
588
589     public void open(CoreServlet controllingServlet, Realm realm) throws Exception JavaDoc {
590         try {
591             super.open(controllingServlet, realm);
592             CoreServlet.getServlet().addCoreListener(this);
593             initConfiguration();
594             threadRunner = new ThreadRunner("CacheUpdater", getCacheUpdaterJob(), configuration.getTimeToLive());
595             threadRunner.start();
596             initialise();
597         } catch (Exception JavaDoc e) {
598             close();
599             throw e;
600         }
601     }
602
603     private void initConfiguration() throws Exception JavaDoc {
604         configuration = buildConfiguration();
605         configuration.postInitialize();
606         userContainer = configuration.createUserContainer();
607         groupContainer = configuration.createRoleContainer();
608     }
609     
610     protected ActiveDirectoryUserDatabaseConfiguration buildConfiguration() throws IllegalArgumentException JavaDoc, Exception JavaDoc {
611         ActiveDirectoryUserDatabaseConfiguration configuration = new ActiveDirectoryUserDatabaseConfiguration(getRealm(), new Properties JavaDoc());
612         configuration.setProtocolType(ActiveDirectoryUserDatabaseConfiguration.PLAIN_PROTOCOL);
613         configuration.setServiceAuthenticationType(ActiveDirectoryUserDatabaseConfiguration.GSSAPI_AUTHENTICATION_METHOD);
614         return configuration;
615     }
616     
617     private Runnable JavaDoc getCacheUpdaterJob() {
618         return new Runnable JavaDoc(){
619             public void run() {
620                 if (logger.isInfoEnabled()) {
621                     logger.info("Caching items");
622                     logger.info("Caching roles");
623                 }
624                 
625                 try {
626                     loadRoles(WILDCARD_SEARCH, true);
627                 } catch (UserDatabaseException e) {
628                     logger.error("Error updating cached roles", e);
629                 }
630                 
631                 if (logger.isInfoEnabled()) {
632                     logger.info("Finished caching roles");
633                     logger.info("Caching users");
634                 }
635                 
636                 // once we have the roles, we'll be able to find the users role assignments
637

638                 try {
639                     loadUsers(WILDCARD_SEARCH, true);
640                 } catch (UserDatabaseException e) {
641                     logger.error("Error updating cached users", e);
642                 }
643
644                 if (logger.isInfoEnabled()) {
645                     logger.info("Finished caching users");
646                     logger.info("Finished caching items");
647                 }
648             }
649         };
650     }
651     
652     /**
653      * I have changed this to obtain the service account user account details. The previous code was not fully contacting the
654      * LDAP server, it actually only performed a kerberos login so some errors were not being detected.
655      * @throws Exception
656      */

657     private void initialise() throws Exception JavaDoc {
658         try {
659             String JavaDoc serviceAccountName = configuration.getServiceAccountName();
660             if (configuration.isServiceAuthenticationGssApi()) {
661                 getAccount(serviceAccountName);
662             } else {
663                 getAttributeValue(serviceAccountName, SAM_ACCOUNT_NAME_ATTRIBUTE);
664             }
665         } catch (Exception JavaDoc e) {
666             logger.error("Could not get the service account login context. All Active Directory features will be unavailable. You should check your Service Account Username and Password settings.", e);
667             close(); // if we can't talk to the server we shouldn't be classed as open
668
throw e;
669         }
670     }
671     
672     /*
673      * (non-Javadoc)
674      * @see com.sslexplorer.boot.Database#close()
675      */

676     public void close() throws Exception JavaDoc {
677         super.close();
678         CoreServlet.getServlet().removeCoreListener(this);
679         if (threadRunner != null) {
680             threadRunner.stop();
681         }
682         userContainer.close();
683         groupContainer.close();
684     }
685     
686     /*
687      * (non-Javadoc)
688      * @see com.sslexplorer.security.UserDatabase#logout(com.sslexplorer.security.User)
689      */

690     public void logout(User user) {
691     }
692         
693     /**
694      * The application must supply a PrivilegedAction that is to be run inside a
695      * Subject.doAs() or Subject.doAsPrivileged().
696      */

697     protected abstract class RetryPrivilegedAction implements PrivilegedAction JavaDoc {
698         private final boolean returnExceptionNotNull;
699         private final String JavaDoc hosts;
700         
701         protected RetryPrivilegedAction() {
702             this(true);
703         }
704
705         protected RetryPrivilegedAction(boolean returnExceptionNotNull) {
706             this.returnExceptionNotNull = returnExceptionNotNull;
707             hosts = configuration.getContactableActiveDirectories();
708         }
709         
710         public final Object JavaDoc run() {
711             long startTime = System.currentTimeMillis();
712             if (logger.isDebugEnabled()) {
713                 logger.debug("Starting Timed Operation");
714             }
715             
716             InitialLdapContext JavaDoc context = null;
717             try {
718                 context = getContext(hosts);
719                 Object JavaDoc doIt = doIt(context);
720                 return doIt;
721             } catch (Exception JavaDoc e) {
722                 logger.error("Failed to run action.", e);
723                 return returnExceptionNotNull ? e : null;
724             } finally {
725                 close(context);
726                 
727                 long finishTime = System.currentTimeMillis();
728                 if(logger.isDebugEnabled()) {
729                     logger.debug("Finished Timed Operation in " + ((finishTime - startTime) / 1000) + " seconds");
730                 }
731             }
732         }
733         
734         protected InitialLdapContext JavaDoc getContext(String JavaDoc url) throws Exception JavaDoc {
735             return configuration.getAuthenticatedContext(url);
736         }
737         
738         protected abstract Object JavaDoc doIt(InitialLdapContext JavaDoc context) throws Exception JavaDoc;
739     }
740     
741     protected String JavaDoc getAttributeValue(String JavaDoc dn, String JavaDoc attributeName) throws NamingException JavaDoc, UserDatabaseException {
742         Attributes JavaDoc attributes = getAttributes(dn, new String JavaDoc[]{attributeName});
743         return getAttributeValue(attributes, attributeName);
744     }
745     
746     private Attributes JavaDoc getAttributes(final String JavaDoc dn, final String JavaDoc[] attributes) throws UserDatabaseException {
747         return (Attributes JavaDoc) configuration.doAs(new RetryPrivilegedAction(){
748             protected Object JavaDoc doIt(InitialLdapContext JavaDoc context) throws Exception JavaDoc {
749                 return context.getAttributes(dn, attributes);
750             }
751         });
752     }
753     
754     protected static String JavaDoc getAttributeValue(Attributes JavaDoc attributes, String JavaDoc attributeName) throws NamingException JavaDoc {
755         if (attributes == null) {
756             return null;
757         }
758         Attribute JavaDoc attribute = attributes.get(attributeName);
759         return attribute == null ? "" : (String JavaDoc) attribute.get();
760     }
761     
762     /**
763      * As some characters are escaped via a backslash, we need
764      * to add some more slashes to get this to work.
765      * @param dn
766      * @return String
767      */

768     private static String JavaDoc getEscapedDn(String JavaDoc dn) {
769         String JavaDoc escapeDn = dn.replaceAll("\\\\", "\\\\\\\\");
770         String JavaDoc escapeForwardSlash = escapeDn.replaceAll("/", "\\\\/");
771         return escapeForwardSlash;
772     }
773     
774     protected static void close(InitialLdapContext JavaDoc context) {
775         if (context != null) {
776             try {
777                 context.close();
778             } catch (NamingException JavaDoc e) {
779                 // ignore
780
}
781         }
782     }
783 }
Popular Tags