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.commons.logging.*;
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      private Log log = LogFactory.getLog(LDAPUserGroup.class);
48      private Log resourceAdminLog = LogFactory.getLog(GUMS.resourceAdminLog);
49      private UserGroupDB db;
50      private String persistenceFactory = "";    
51      private Configuration conf;
52      private String server = "";
53      private String query = "";
54      private String certDNField = "description";
55  	protected ConfigurationStore confStore;
56      
57      /**
58       * Create a new ldap user group. This empty constructor is needed by the XML Digestor.
59       */
60  	public LDAPUserGroup() {
61      	super();
62      }
63  	
64      /**
65       * Create a new ldap user group with a configuration.
66       */
67      public LDAPUserGroup(Configuration configuration) {
68  		super(configuration);
69  	}
70   
71      /**
72       * Create a new ldap user group with a configuration and a name.
73       */
74  	public LDAPUserGroup(Configuration configuration, String name) {
75  		super(configuration, name);
76  	}
77      
78  	public UserGroup clone(Configuration configuration) {
79      	LDAPUserGroup userGroup = new LDAPUserGroup(configuration, new String(getName()));
80      	userGroup.setDescription(new String(getDescription()));
81      	userGroup.setPersistenceFactory(new String(persistenceFactory));
82      	userGroup.setAccess(new String(getAccess()));
83      	userGroup.setQuery(new String(getQuery()));
84      	userGroup.setServer(new String(getServer()));
85      	userGroup.setCertDNField(new String(certDNField));
86      	return userGroup;
87      }
88      
89      public boolean equals(Object obj) {
90          if (obj instanceof LDAPUserGroup) {
91              LDAPUserGroup group = (LDAPUserGroup) obj;
92              if ((server == null ? group.server == null : server.equals(group.server)) &&
93                 (query == null ? group.query == null : query.equals(group.query)) && 
94                 (getName() == null ? group.getName() == null : getName().equals(group.getName())) && 
95                 (persistenceFactory == null ? group.persistenceFactory == null : persistenceFactory.equals(group.persistenceFactory))) {
96                  return true;
97              }
98          }
99          return false;
100     }
101     
102     /**
103      * Getter for property certDN
104      * 
105      * @return cert DN as string
106      */
107     public String getCertDNField() {
108         return certDNField;
109     }
110     
111     public java.util.List getMemberList() {
112         return getDB().retrieveMembers();
113     }
114     
115     /**
116      * Getter for property persistenceFactory
117      * 
118      * @return persistenceFactory as string
119      */
120     public String getPersistenceFactory() {
121         return persistenceFactory;
122     }
123     
124     /**
125      * The LDAP query used to retrieveGetter for property query.
126      * 
127      * @return The LDAP query used. i.e. "ou=usatlas,o=atlas,dc=eu-datagrid,dc=org"
128      */
129     public String getQuery() {
130         return this.query;
131     }
132     
133     /**
134      * Returns the name of the LDAP server used to retrieve the list of users.
135      * 
136      * @return The name of the server server. i.e. "grid-vo.nikhef.nl"
137      */
138     public String getServer() {
139         return this.server;
140     }
141     
142     public String getType() {
143 		return "ldap";
144 	}
145     
146     public int hashCode() {
147         return query.hashCode();
148     }
149     
150     public boolean isInGroup(GridUser user) {
151         return getDB().isMemberInGroup(user);
152     }
153     
154     public Map retrievePeopleMap(DirContext ldap) throws javax.naming.NamingException {
155         NamingEnumeration people = ldap.search("ou=People", "("+certDNField+"=*)", null);
156         Map map = new Hashtable();
157         while (people.hasMore()) {
158             SearchResult person = (SearchResult) people.next();
159             Attributes personAtts = person.getAttributes();
160             String ldapDN = person.getName();
161             if (person.isRelative()) {
162                 ldapDN = ldapDN + ",ou=People," + ldap.getNameInNamespace();
163             }
164             
165             String certDN = (String) personAtts.get(certDNField).get();
166             if (certDN.startsWith("subject=")) {
167                 certDN = certDN.substring(8);
168             }
169             certDN = certDN.trim();
170             map.put(ldapDN, certDN);
171         }
172         return map;
173     }
174     
175     /**
176      * Setter for property certDN
177      * 
178      * @param certDN as string
179      */
180     public void setCertDNField(String certDNField) {
181     	this.certDNField = certDNField;
182     }
183     
184     /**
185      * Setter for property persistenceFactory
186      * 
187      * @param persistenceFactory as string
188      */
189     public void setPersistenceFactory(String persistenceFactory) {
190         this.persistenceFactory = persistenceFactory;
191     }
192     
193     /**
194      * Changes the LDAP query used to retrieveGetter for property query.
195      * 
196      * @param query The LDAP query used. i.e. "ou=usatlas,o=atlas,dc=eu-datagrid,dc=org"
197      */
198     public void setQuery(String query) {
199         this.query = query;
200     }
201     
202     /**
203      * Changes the LDAP server used to retrieve the list of users.
204      * @param server The name of the server server. i.e. "grid-vo.nikhef.nl"
205      */
206     public void setServer(String server) {
207         this.server = server;
208     }
209     
210     public String toString() {
211         return "LDAPGroup: ldap://"+server+"/"+query;
212     }
213     
214     public String toString(String bgColor) {
215     	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>";
216     }
217     
218     public String toXML() {
219     	return "\t\t<ldapUserGroup\n"+
220 		"\t\t\tname='"+getName()+"'\n"+
221 		"\t\t\taccess='"+accessTypes[accessIndex]+"'\n" +
222 		"\t\t\tdescription='"+getDescription()+"'\n"+
223     	"\t\t\tserver='"+server+"'\n" +
224 		"\t\t\tquery='"+query+"'\n" +
225 		"\t\t\tcertDNField='"+certDNField+"'\n" +
226 		"\t\t\tpersistenceFactory='"+persistenceFactory+"'/>\n\n";
227     }
228     
229     public void updateMembers() {
230     	getDB().loadUpdatedList(retrieveMembers());
231     }
232     
233     private UserGroupDB getDB() {
234     	if (db==null)
235             db = getConfiguration().getPersistenceFactory(persistenceFactory).retrieveUserGroupDB( getName() );
236     	return db;
237     }
238 
239     private List retrieveGroupMembers(DirContext rootCtx, Attribute members) throws javax.naming.NamingException {
240         Map people = retrievePeopleMap(rootCtx);
241         NamingEnumeration names = members.getAll();
242         List list = new ArrayList();
243         while (names.hasMore()) {
244             // Converting the people to the DN, by looking up the person description attribute
245             String ldapName = (String) names.next();
246             ldapName = ldapName.trim();
247             String certDN = (String) people.get(ldapName);
248             if (certDN == null) {
249                 resourceAdminLog.warn("Member of a LDAP VO group not mapped to any certificate: '" + ldapName + "'");
250             } else {
251                 list.add(new GridUser(certDN, null));
252             }
253         }
254         if (list.isEmpty()) {
255             resourceAdminLog.warn("The following group returned no members: " + this);
256         }
257         return list;
258     }
259     
260     /**
261      * Returns the list of member retrieved from the LDAP server. The members are not saved in the database.
262      * Must be synchronized since the System properties are being set
263      * 
264      * @return A list of VOEntry objects representing the members.
265      */
266     private synchronized List retrieveMembers() {
267         java.util.Properties properties = retrieveProperties();
268         log.info("Retrieving members from '" + properties.getProperty("java.naming.provider.url") + "'  '" + query + "'");
269         try {
270             javax.naming.directory.DirContext jndiCtx = new javax.naming.directory.InitialDirContext(properties);
271             if (query.startsWith("ou=People,")) {
272                 String voRoot = query.substring(query.indexOf(',')+1);
273                 DirContext rootCtx = (DirContext) jndiCtx.lookup(voRoot);
274                 return retrieveVOMembers(rootCtx);
275             } else if (query.startsWith("ou=")) {
276                 Attributes atts = jndiCtx.getAttributes(query);
277                 Attribute members = atts.get("member");
278                 if (members == null) {
279                     throw new RuntimeException("Couldn't retrieve the list of members from the LDAP group: missing attribute member");
280                 }
281                 String voRoot = query.substring(query.indexOf(',')+1);
282                 DirContext rootCtx = (DirContext) jndiCtx.lookup(voRoot);
283                 return retrieveGroupMembers(rootCtx, members);
284             } else if (query.startsWith("o=")) {
285                 DirContext rootCtx = (DirContext) jndiCtx.lookup(query);
286                 return retrieveVOMembers(rootCtx);
287             } else {
288                 throw new IllegalArgumentException("The query is not understood by the LDAP group. It is expected to start with \"ou=...\" or \"o=...\"");
289             }
290 
291         } catch (javax.naming.NamingException e) {
292             String message = "Couldn't retrieve users from LDAP server: " + e;
293 /*            if (e.getRootCause() != null) {
294                 message += " caused by [" + e.getRootCause().getMessage() + "]";
295             }*/
296             log.error("Couldn't retrieve LDAP users: ", e);
297             throw new RuntimeException(message, e);
298         }
299     }
300     
301     private Properties retrieveProperties() {
302         Properties properties = new java.util.Properties();
303         properties.put(Context.PROVIDER_URL, "ldap://"+server);
304         properties.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
305         properties.put(Context.SECURITY_PROTOCOL, "none");
306         return properties;
307     }    
308     
309     private List retrieveVOMembers(DirContext rootCtx) throws NamingException {
310         Map people = retrievePeopleMap(rootCtx);
311         List list = new ArrayList(people.values());
312         if (list.isEmpty()) {
313             resourceAdminLog.warn("The following group returned no members: " + this);
314         }
315         Iterator iter = list.iterator();
316         List users = new ArrayList();
317         while (iter.hasNext()) {
318             String dn = (String) iter.next();
319             users.add(new GridUser(dn, null));
320         }
321         if (list.isEmpty()) {
322             resourceAdminLog.warn("The following group returned no members: " + this);
323         }
324         return users;
325     }
326 }