View Javadoc

1   /*
2    * LDAPMapDB.java
3    *
4    * Created on September 23, 2005, 11:29 AM
5    *
6    * To change this template, choose Tools | Options and locate the template under
7    * the Source Creation and Management node. Right-click the template and choose
8    * Open. You can then make changes to the template in the Source Editor.
9    */
10  
11  package gov.bnl.gums.ldap;
12  
13  import gov.bnl.gums.AccountPoolMapperDB;
14  import gov.bnl.gums.ManualAccountMapperDB;
15  import java.util.Hashtable;
16  import java.util.List;
17  import java.util.Map;
18  import javax.naming.NameAlreadyBoundException;
19  import javax.naming.NamingEnumeration;
20  import javax.naming.directory.*;
21  import javax.naming.ldap.Control;
22  import javax.naming.ldap.LdapContext;
23  import javax.naming.ldap.PagedResultsControl;
24  import org.apache.commons.logging.Log;
25  import org.apache.commons.logging.LogFactory;
26  
27  /***
28   *
29   * @author carcassi
30   */
31  public class LDAPMapDB implements AccountPoolMapperDB, ManualAccountMapperDB {
32      private Log log = LogFactory.getLog(LDAPMapDB.class);
33  
34      private LDAPPersistenceFactory factory;
35      private String map;
36      private String mapDN;
37      private String group;
38      private List secondaryGroups;
39      
40      /***
41       * Creates a new LDAP map, named "map=map" in the defaultGumsOU.
42       * @param factory the LDAP factory that will provide LDAP connectivity
43       * @param map the name of the map
44       */
45      public LDAPMapDB(LDAPPersistenceFactory factory, String map) {
46          this.factory = factory;
47          this.map = map;
48          this.mapDN = "map=" + map + "," + factory.getDefaultGumsOU();
49          createGroupIfNotExists();
50          log.trace("LDAPMapDB object create: map '" + map + "' factory " + factory);
51      }
52  
53      /***
54       * Creates a new LDAP map, named "map=map" in the defaultGumsOU.
55       * When accounts are assigned they will be associated with the gids for the
56       * UNIX groups given.
57       *
58       * @param factory the LDAP factory that will provide LDAP connectivity
59       * @param map the name of the map
60       * @param group the UNIX primary group for the accounts assigned
61       * @param secondaryGroups the UNIX secondary groups for the accounts assigned
62       */
63      public LDAPMapDB(LDAPPersistenceFactory factory, String map, String group, List secondaryGroups) {
64          this(factory, map);
65          this.group = group;
66          this.secondaryGroups = secondaryGroups;
67          log.trace("LDAPMapDB object create: map '" + map + "' factory " + factory + " primary group '" + group + "' secondary groups '" + secondaryGroups + "'");
68      }
69      
70      /***
71       * Checks whether the map actually exists in LDAP.
72       * @return true if it exists
73       */
74      boolean doesMapExist() {
75          DirContext context = factory.retrieveContext();
76          try {
77              SearchControls ctrls = new SearchControls();
78              ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE);
79              log.trace("Checking if LDAP map '" + map + "' exists");
80              NamingEnumeration result = context.search(factory.getDefaultGumsOU(), "(map={0})", new Object[] {map}, ctrls);
81              return result.hasMore();
82          } catch (Exception e) {
83              log.error("Couldn't determine if LDAP map exists '" + map + "'", e);
84              throw new RuntimeException("Couldn't determine if LDAP map exists '" + map + "': " + e.getMessage(), e);
85          } finally {
86              factory.releaseContext(context);
87          }
88      }
89      
90      /***
91       * Creates the group in LDAP if it doesn't exists.
92       */
93      void createGroupIfNotExists() {
94          if (!doesMapExist()) {
95              factory.createMap(map, mapDN);
96              log.trace("LDAP group '" + map + "' didn't exist, and it was created");
97          }
98      }
99  
100     /***
101      * Retrieve the mapping for the certificate DN of the user.
102      * @param userDN full certificate DN of the user
103      * @return the UNIX account to be mapped to
104      */
105     public String retrieveMapping(String userDN) {
106         DirContext context = factory.retrieveContext();
107         try {
108             SearchControls ctrls = new SearchControls();
109             ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE);
110             NamingEnumeration result = context.search(mapDN, "(user={0})", new Object[] {userDN}, ctrls);
111             if (result.hasMore()) {
112                 SearchResult res = (SearchResult) result.next();
113                 Attributes atts = res.getAttributes();
114                 Attribute map = atts.get("account");
115                 if (map == null)
116                     return null;
117                 String account = (String) map.get();
118                 log.trace("Retrieved map entry in map '" + map + "' for user '" + userDN + "' to account '" + account + "'");
119                 return account;
120             }
121             return null;
122         } catch (Exception e) {
123             log.error("Couldn't retrieve entry from LDAP map '" + map + "' for user '" + userDN + "'", e);
124             throw new RuntimeException("Couldn't retrieve entry from LDAP map '" + map + "' for user '" + userDN + "': " + e.getMessage(), e);
125         } finally {
126             factory.releaseContext(context);
127         }
128     }
129 
130     /***
131      * Remove the mapping associated to the certificate DN of the user provided.
132      * @param userDN full certificate DN of the user
133      * @return true if a mapping was actually removed
134      */
135     public boolean removeMapping(String userDN) {
136         try {
137             return factory.removeMapEntry(userDN, map, mapDN);
138         } catch (RuntimeException e) {
139             if (e.getCause() instanceof NoSuchAttributeException)
140                 return false;
141             throw e;
142         }
143     }
144 
145     /***
146      * For an account pool, frees the account that was associated to a particular
147      * user.
148      * @param userDN full certificate DN of the user
149      */
150     public void unassignUser(String userDN) {
151         factory.removeMapEntry(userDN, map, mapDN);
152     }
153 
154     /***
155      * Retrieves the account associated with the certificate DN of the user.
156      * @param userDN full certificate DN of the user
157      * @return the UNIX account or null if no associated account was found
158      */
159     public String retrieveAccount(String userDN) {
160         String account = retrieveMapping(userDN);
161         log.trace("Retrieving account from LDAP map '" + map + "' for user '" + userDN + "' account '" + account + "'");
162         if (account != null) {
163             factory.retrieveAssigner().reassignGroups(account, group, secondaryGroups);
164             log.trace("Reassigned gids for user '" + userDN + "' account '" + account + "'");
165         }
166         return account;
167     }
168 
169     /***
170      * Assigns a new account taken from the pool to the certificate DN of the user.
171      * @param userDN full certificate DN of the user
172      * @return the UNIX account or null if no free account was found
173      */
174     public String assignAccount(String userDN) {
175         DirContext context = factory.retrieveContext();
176         String account = null;
177         Control[] controlsBackup = null;
178         try {
179             LdapContext ldapContext = (LdapContext) context;
180             controlsBackup = ldapContext.getRequestControls();
181             ldapContext.setRequestControls(new Control[] {new PagedResultsControl(100, Control.CRITICAL)});
182             SearchControls ctrls = new SearchControls();
183             ctrls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
184             NamingEnumeration result = context.search(mapDN, "(!(user=*))", null, ctrls);
185             while (result.hasMore()) {
186                 SearchResult res = (SearchResult) result.next();
187                 Attributes atts = res.getAttributes();
188                 Attribute accounts = atts.get("account");
189                 if (accounts != null) {
190                     String newAccount = (String) accounts.get();
191                     if (account == null) {
192                         account = newAccount;
193                     } else {
194                         if (account.compareTo(newAccount) > 0) {
195                             account = newAccount;
196                         }
197                     }
198                 }
199             }
200         } catch (Exception e) {
201             log.error("Couldn't assign account from LDAP map '" + map + "' to user '" + userDN + "'", e);
202             throw new RuntimeException("Couldn't assign account from LDAP map '" + map + "' to user '" + userDN + "': " + e.getMessage(), e);
203         } finally {
204             if (controlsBackup != null) {
205                 try {
206                     ((LdapContext) context).setRequestControls(controlsBackup);
207                 } catch (Exception e) {
208                     log.error("Couldn't reset controls", e);
209                 }
210             }
211             factory.releaseContext(context);
212         }
213         if (account != null) {
214             factory.addMapEntry(userDN, account, map, mapDN);
215             log.trace("Assigned account for LDAP map '" + map + "' user '" + userDN + "' account '" + account + "'");
216             factory.retrieveAssigner().assignGroups(account, group, secondaryGroups);
217             log.trace("Assigned gids for user '" + userDN + "' account '" + account + "'");
218         } else {
219             log.trace("No account to assign for LDAP map '" + map + "' user '" + userDN + "' account '" + account + "'");
220         }
221         return account;
222     }
223 
224     /***
225      * Adds an account to the pool of accounts available.
226      * @param account a UNIX account
227      */
228     public void addAccount(String account) {
229         try {
230             factory.createAccountInMap(account, map, mapDN);
231         } catch (Exception e) {
232             if (e.getCause() instanceof NameAlreadyBoundException) {
233                 throw new IllegalArgumentException("Account '" + account + "' is already present in LDAP pool '" + map + "'");
234             }
235         }
236     }
237 
238     /***
239      * This is not supported anymore
240      * @param date ignored
241      * @return nothing
242      * @throws UnsupportedOperationException
243      */
244     public java.util.List retrieveUsersNotUsedSince(java.util.Date date) {
245         throw new UnsupportedOperationException("retrieveUsersNotUsedSince is not supported anymore");
246     }
247 
248     /***
249      * Creates a new mapping in the map, associating a certificate DN with an account.
250      * @param userDN full certificate DN of the user
251      * @param account a UNIX account
252      */
253     public void createMapping(String userDN, String account) {
254         factory.addMapEntry(userDN, account, map, mapDN);
255     }
256 
257     /***
258      * Retrieves the full association of certificates DNs to accounts that
259      * the map has stored.
260      * @return a Map with the certificate DN as the key and the account as the value
261      */
262     public java.util.Map retrieveAccountMap() {
263         DirContext context = factory.retrieveContext();
264         Map map = new Hashtable();
265         try {
266             SearchControls ctrls = new SearchControls();
267             ctrls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
268             NamingEnumeration result = context.search(mapDN, "(user=*)", null, ctrls);
269             while (result.hasMore()) {
270                 SearchResult res = (SearchResult) result.next();
271                 Attributes atts = res.getAttributes();
272                 Attribute accounts = atts.get("account");
273                 if (accounts != null) {
274                     String account = (String) accounts.get();
275                     Attribute users = atts.get("user");
276                     String user = (String) users.get();
277                     map.put(user, account);
278                 }
279             }
280             log.trace("Retrieved LDAP map '" + map + "'");
281             return map;
282         } catch (Exception e) {
283             log.error("Couldn't retrieve LDAP map '" + map + "'", e);
284             throw new RuntimeException("Couldn't retrieve LDAP map '" + map + "': " + e.getMessage(), e);
285         } finally {
286             factory.releaseContext(context);
287         }
288     }
289     
290     public void resetAccountPool() {
291         DirContext context = factory.retrieveContext();
292         try {
293             SearchControls ctrls = new SearchControls();
294             ctrls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
295             NamingEnumeration result = context.search(mapDN, "(user=*)", null, ctrls);
296             while (result.hasMore()) {
297                 SearchResult res = (SearchResult) result.next();
298                 Attributes atts = res.getAttributes();
299                 Attribute accounts = atts.get("account");
300                 if (accounts != null) {
301                     String account = (String) accounts.get();
302                     Attribute users = atts.get("user");
303                     String user = (String) users.get();
304                     ModificationItem[] mods = new ModificationItem[1];
305                     mods[0] = new ModificationItem(context.REMOVE_ATTRIBUTE,
306                         new BasicAttribute("user", user));
307                     context.modifyAttributes("account=" + account + "," + mapDN, mods);
308                 }
309             }
310             log.trace("Reset LDAP account pool '" + map + "'");
311         } catch (Exception e) {
312             log.error("Couldn't reset LDAP account pool '" + map + "'", e);
313             throw new RuntimeException("Couldn't reset LDAP account pool '" + map + "': " + e.getMessage(), e);
314         } finally {
315             factory.releaseContext(context);
316         }
317     }
318     
319 }