1
2
3
4
5
6
7
8
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 }