View Javadoc

1   /*
2    * LDAPGroup.java
3    *
4    * Created on May 25, 2004, 11:57 AM
5    */
6   
7   package gov.bnl.gums.userGroup;
8   
9   import gov.bnl.gums.GUMS;
10  import gov.bnl.gums.GridUser;
11  import gov.bnl.gums.configuration.Configuration;
12  import gov.bnl.gums.configuration.ConfigurationStore;
13  import gov.bnl.gums.db.UserGroupDB;
14  
15  import java.util.*;
16  
17  import javax.naming.*;
18  import javax.naming.directory.*;
19  
20  import org.apache.log4j.Logger;
21  
22  /** A group of users defined by an LDAP VO.
23   * <p>
24   * The query should be :
25   * <ul>
26   *   <li>A People ou (i.e. ou=People,o=atlas,dc=eu-datagrid,dc=org). All the 
27   *   object found in that category will have to have a 'description' attribute
28   *   which will contain the certificate DN</li>
29   *   <li>A group ou (i.e. ou=usatlas,o=atlas,dc=eu-datagrid,dc=org). The ou
30   *   object will need to have a 'member' property with the list of people in 
31   *   the VO that are in the group. Each member will be an object in
32   *   ou=People</li>
33   *   <li>A root o (i.e. o=atlas,dc=eu-datagrid,dc=org). A ou=People object will
34   *   be expected, and it will behave like the first option</li>
35   * </ul>
36   *
37   * @author Gabriele Carcassi, Jay Packard
38   */
39  public class LDAPUserGroup extends UserGroup {
40  	/**
41  	 * @return user friendly string representation of the property type called statically 
42  	 */
43  	static public String getTypeStatic() {
44  		return "ldap";
45  	}
46  
47  	protected Logger log = Logger.getLogger(LDAPUserGroup.class);
48  	protected Logger gumsAdminLog = Logger.getLogger(GUMS.gumsAdminLogName);
49  	protected ConfigurationStore confStore;    
50  	protected UserGroupDB db;
51  	protected Configuration conf;
52  	protected String persistenceFactory = "";    
53  	protected String server = "";
54  	protected String certDNField = "description";
55  	protected String memberUidField = "memberUid";
56  	protected String uidField = "uid";
57  	protected String peopleTree = "";
58  	protected String peopleObject = "ou=People";
59  	protected String peopleContext = null;
60  	protected String groupTree = "";
61  
62  	/**
63  	 * Create a new ldap user group. This empty constructor is needed by the XML Digestor.
64  	 */
65  	public LDAPUserGroup() {
66  		super();
67  	}
68  
69  	/**
70  	 * Create a new ldap user group with a configuration.
71  	 */
72  	public LDAPUserGroup(Configuration configuration) {
73  		super(configuration);
74  	}
75  
76  	/**
77  	 * Create a new ldap user group with a configuration and a name.
78  	 */
79  	public LDAPUserGroup(Configuration configuration, String name) {
80  		super(configuration, name);
81  	}
82  
83  	public UserGroup clone(Configuration configuration) {
84  		LDAPUserGroup userGroup = new LDAPUserGroup(configuration, new String(getName()));
85  		userGroup.setDescription(new String(getDescription()));
86  		userGroup.setPersistenceFactory(new String(persistenceFactory));
87  		userGroup.setAccess(new String(getAccess()));
88  		userGroup.setPeopleTree(new String(getPeopleTree()));
89  		userGroup.setGroupTree(new String(getGroupTree()));
90  		userGroup.setMemberUidField(new String(getMemberUidField()));
91  		userGroup.setUidField(new String(getUidField()));
92  		userGroup.setServer(new String(getServer()));
93  		userGroup.setCertDNField(new String(certDNField));
94  		return userGroup;
95  	}
96  
97  	public boolean equals(Object obj) {
98  		if (obj instanceof LDAPUserGroup) {
99  			LDAPUserGroup group = (LDAPUserGroup) obj;
100 			if ((server == null ? group.server == null : server.equals(group.server)) &&
101 					(peopleTree == null ? group.peopleTree == null : peopleTree.equals(group.peopleTree)) && 
102 					(groupTree == null ? group.groupTree == null : groupTree.equals(group.groupTree)) &&
103 					(certDNField == null ? group.certDNField == null : certDNField.equals(group.certDNField)) &&
104 					(memberUidField == null ? group.memberUidField == null : memberUidField.equals(group.memberUidField)) &&
105 					(uidField == null ? group.uidField == null : uidField.equals(group.uidField)) &&
106 					(getName() == null ? group.getName() == null : getName().equals(group.getName())) && 
107 					(persistenceFactory == null ? group.persistenceFactory == null : persistenceFactory.equals(group.persistenceFactory))) {
108 				return true;
109 			}
110 		}
111 		return false;
112 	}
113 
114 	/**
115 	 * Getter for property certDN
116 	 * 
117 	 * @return cert DN as string
118 	 */
119 	 public String getCertDNField() {
120 		 return certDNField;
121 	 }
122 
123 	 /**
124 	  * Getter for property groupTree
125 	  *
126 	  * @return groupTree as string
127 	  */
128 	 public String getGroupTree() {
129 		 return groupTree;
130 	 }
131 
132 	 public java.util.List getMemberList() {
133 		 return getDB().retrieveMembers();
134 	 }
135 
136 	 public String getMemberUidField() {
137 		 return memberUidField;
138 	 }
139 
140 	 /**
141 	  * Getter for property peopleTree
142 	  *
143 	  * @return peopleTree as string
144 	  */
145 	 public String getPeopleTree() {
146 		 return peopleTree;
147 	 }
148 
149 	 /**
150 	  * Getter for property persistenceFactory
151 	  * 
152 	  * @return persistenceFactory as string
153 	  */
154 	 public String getPersistenceFactory() {
155 		 return persistenceFactory;
156 	 }
157 
158 	 /**
159 	  * The LDAP query used to retrieveGetter for property query.
160 	  * 
161 	  * @return The LDAP query used. i.e. "ou=usatlas,o=atlas,dc=eu-datagrid,dc=org"
162 	  */
163 	 public String getQuery() {
164 		 return peopleTree;
165 	 }
166 
167 	 /**
168 	  * Returns the name of the LDAP server used to retrieve the list of users.
169 	  * 
170 	  * @return The name of the server server. i.e. "grid-vo.nikhef.nl"
171 	  */
172 	 public String getServer() {
173 		 return this.server;
174 	 }
175 
176 	 public String getType() {
177 		 return "ldap";
178 	 }
179 
180 	 public String getUidField() {
181 		 return uidField;
182 	 }
183 
184 	 public int hashCode() {
185 		 return new String(peopleTree + " " + groupTree).hashCode();
186 	 }
187 
188 	 public boolean isInGroup(GridUser user) {
189 		 return getDB().isMemberInGroup(user);
190 	 }
191 
192 	 /**
193 	  * Setter for property certDN
194 	  * 
195 	  * @param certDN as string
196 	  */
197 	 public void setCertDNField(String certDNField) {
198 		 this.certDNField = certDNField;
199 	 }
200 
201 	 /**
202 	  * Setter for property groupTree
203 	  *
204 	  * @param groupTree as string
205 	  */
206 	 public void setGroupTree(String groupTree) {
207 		 this.groupTree = groupTree;
208 	 }
209 
210 	 public void setMemberUidField(String memberUidField) {
211 		 this.memberUidField = memberUidField;
212 	 }
213 
214 	 /**
215 	  * Setter for property peopleTree
216 	  *
217 	  * @param peopleTree as string
218 	  */
219 	 public void setPeopleTree(String peopleTree) {
220 		 this.peopleTree = peopleTree;
221 		 this.peopleObject = peopleTree.substring(0, peopleTree.indexOf(','));
222 		 this.peopleContext = peopleTree.substring(peopleTree.indexOf(',')+1);
223 	 }
224 	 
225 	 /**
226 	  * Setter for property persistenceFactory
227 	  *
228 	  * @param persistenceFactory as string
229 	  */
230 	 public void setPersistenceFactory(String persistenceFactory) {
231 		 this.persistenceFactory = persistenceFactory;
232 	 }
233 
234 	 /**
235 	  * Changes the LDAP query used to retrieveGetter for property query.
236 	  * 
237 	  * @param query The LDAP query used. i.e. "ou=usatlas,o=atlas,dc=eu-datagrid,dc=org"
238 	  * @deprecated
239 	  */
240 	 public void setQuery(String query) {
241 		 if (query.startsWith(peopleObject+",")) {
242 			 peopleTree = query;
243 			 peopleObject = query.substring(0, query.indexOf(','));
244 			 peopleContext = query.substring(query.indexOf(',')+1);
245 		 } else if (query.startsWith("ou=")) {
246 			 peopleObject = "ou=People";
247 			 peopleContext = query.substring(query.indexOf(',')+1);
248 			 peopleTree = peopleObject + "," + peopleContext;
249 		 } else if (query.startsWith("o=")) {
250 			 peopleObject = "ou=People";
251 			 peopleContext = query;
252 			 peopleTree = peopleObject + "," + peopleContext;
253 		 } else {
254 			 throw new IllegalArgumentException("The query is not understood by the LDAP group. It is expected to start with \"ou=...\" or \"o=...\"");
255 		 }
256 	 }
257 
258 	 /**
259 	  * Changes the LDAP server used to retrieve the list of users.
260 	  * @param server The name of the server server. i.e. "grid-vo.nikhef.nl"
261 	  */
262 	 public void setServer(String server) {
263 		 this.server = server;
264 	 }
265 
266 	 public void setUidField(String uidField) {
267 		 this.uidField = uidField;
268 	 }
269 
270 	 public String toString() {
271 		 return "LDAPGroup: ldap://"+server+"/"+peopleTree+","+groupTree;
272 	 }
273 
274 	 public String toString(String bgColor) {
275 		 return "<td bgcolor=\""+bgColor+"\"><a href=\"userGroups.jsp?command=edit&name=" + getName() + "\">" + getName() + "</a></td><td bgcolor=\""+bgColor+"\">" + getType() + "</td><td bgcolor=\""+bgColor+"\">&nbsp;</td><td bgcolor=\""+bgColor+"\">&nbsp;</td><td bgcolor=\""+bgColor+"\"></td><td bgcolor=\""+bgColor+"\">&nbsp;</td>";
276 	 }
277 
278 	 public String toXML() {
279 		 return "\t\t<ldapUserGroup\n"+
280 		 "\t\t\tname='"+getName()+"'\n"+
281 		 "\t\t\taccess='"+accessTypes[accessIndex]+"'\n" +
282 		 "\t\t\tdescription='"+getDescription()+"'\n"+
283 		 "\t\t\tserver='"+server+"'\n" +
284 		 "\t\t\tpeopleTree='"+peopleTree+"'\n" +
285 		 "\t\t\tgroupTree='"+groupTree+"'\n" +
286 		 "\t\t\tmemberUidField='"+memberUidField+"'\n" +
287 		 "\t\t\tuidField='" + uidField+"'\n" +
288 		 "\t\t\tcertDNField='"+certDNField+"'\n" +
289 		 "\t\t\tpersistenceFactory='"+persistenceFactory+"'/>\n\n";
290 	 }
291 
292 	 public void updateMembers() {
293 		 getDB().loadUpdatedList(retrieveMembers());
294 	 }
295 
296 	 private UserGroupDB getDB() {
297 		 if (db==null)
298 			 db = getConfiguration().getPersistenceFactory(persistenceFactory).retrieveUserGroupDB( getName() );
299 		 return db;
300 	 }
301 
302 	 private List retrieveGroupMembers(DirContext rootCtx, Attribute members) throws javax.naming.NamingException {
303 		 Map people = retrievePeopleMap(rootCtx);
304 		 NamingEnumeration names = members.getAll();
305 		 List list = new ArrayList();
306 		 while (names.hasMore()) {
307 			 // Converting the people to the DN, by looking up the person description attribute
308 			 String ldapName = (String) names.next();
309 			 ldapName = ldapName.trim();
310 			 String certDN = (String) people.get(ldapName);
311 			 if (certDN == null) {
312 				 gumsAdminLog.warn("Member of a LDAP VO group not mapped to any certificate: '" + ldapName + "'");
313 			 } else {
314 				 list.add(new GridUser(certDN, null));
315 			 }
316 		 }
317 		 if (list.isEmpty()) {
318 			 gumsAdminLog.warn("The following group returned no members: " + this);
319 		 }
320 		 return list;
321 	 }
322 
323 	 /**
324 	  * Returns the list of member retrieved from the LDAP server. The members are not saved in the database.
325 	  * Must be synchronized since the System properties are being set
326 	  * 
327 	  * @return A list of VOEntry objects representing the members.
328 	  */
329 	 private synchronized List retrieveMembers() {
330 		 java.util.Properties properties = retrieveProperties();
331 		 log.info("Retrieving members from '" + properties.getProperty("java.naming.provider.url") + "'  '" + peopleTree + "' '" + groupTree + "'");
332 		 try {
333 			 javax.naming.directory.DirContext jndiCtx = new javax.naming.directory.InitialDirContext(properties);
334 			 if (!peopleTree.equals("") && groupTree.equals("")) {
335 				 DirContext rootCtx = (DirContext) jndiCtx.lookup(peopleContext);
336 				 return retrieveVOMembers(rootCtx);
337 			 }
338 			 else if (!groupTree.equals("") && !peopleTree.equals("")) {
339 				 Attributes atts = jndiCtx.getAttributes(groupTree);
340 				 Attribute members = atts.get(memberUidField);
341 				 if (members == null) {
342 					 String message = "Couldn't retrieve the list of members from the LDAP group: missing attribute member";
343 					 GUMS.gumsAdminEmailLog.put("ldapUpdateProblem", message, false);
344 					 throw new RuntimeException(message);
345 				 }
346 				 DirContext rootCtx = (DirContext) jndiCtx.lookup(peopleContext);
347 				 return retrieveGroupMembers(rootCtx, members);            
348 			 }
349 		 } catch (javax.naming.NamingException e) {
350 			 String message = "Couldn't retrieve users from LDAP server: " + e;
351 			 /*            if (e.getRootCause() != null) {
352                 message += " caused by [" + e.getRootCause().getMessage() + "]";
353             }*/
354 			 log.error("Couldn't retrieve LDAP users: ", e);
355 			 throw new RuntimeException(message, e);
356 		 }
357 		 return null;
358 	 }
359 
360 	 private Properties retrieveProperties() {
361 		 Properties properties = new java.util.Properties();
362 		 properties.put(Context.PROVIDER_URL, "ldap://"+server);
363 		 properties.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
364 		 properties.put(Context.SECURITY_PROTOCOL, "none");
365 		 return properties;
366 	 }
367 
368 	 private List retrieveVOMembers(DirContext rootCtx) throws NamingException {
369 		 Map people = retrievePeopleMap(rootCtx);
370 		 List list = new ArrayList(people.values());
371 		 if (list.isEmpty()) {
372 			 gumsAdminLog.warn("The following group returned no members: " + this);
373 		 }
374 		 Iterator iter = list.iterator();
375 		 List users = new ArrayList();
376 		 while (iter.hasNext()) {
377 			 String dn = (String) iter.next();
378 			 users.add(new GridUser(dn, null));
379 		 }
380 		 if (list.isEmpty()) {
381 			 gumsAdminLog.warn("The following group returned no members: " + this);
382 		 }
383 		 return users;
384 	 }    
385 
386 	 protected Map retrievePeopleMap(DirContext ldap) throws javax.naming.NamingException {
387 		 NamingEnumeration people = ldap.search(peopleObject, "("+certDNField+"=*)", null);
388 		 Map map = new Hashtable();
389 		 while (people.hasMore()) {
390 			 SearchResult person = (SearchResult) people.next();
391 			 Attributes personAtts = person.getAttributes();
392 			 String ldapDN = (String)personAtts.get(uidField).get();//person.getName();
393 			 //if (person.isRelative()) {
394 			 //ldapDN = ldapDN + "," + peopleObject + "," + ldap.getNameInNamespace();
395 			 //}
396 			 String certDN = (String) personAtts.get(certDNField).get();
397 			 if (certDN.startsWith("subject=")) {
398 				 certDN = certDN.substring(8);
399 			 }
400 			 certDN = certDN.trim();
401 			 map.put(ldapDN, certDN);
402 		 }
403 		 return map;
404 	 }
405 }