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  
13  import java.util.*;
14  
15  import javax.naming.*;
16  import javax.naming.directory.*;
17  import javax.persistence.Entity;
18  import javax.persistence.Transient;
19  
20  import org.apache.log4j.Logger;
21  
22  
23  /** A group of users defined by an LDAP VO.
24   * <p>
25   * The query should be :
26   * <ul>
27   *   <li>A People ou (i.e. ou=People,o=atlas,dc=eu-datagrid,dc=org). All the 
28   *   object found in that category will have to have a 'description' attribute
29   *   which will contain the certificate DN</li>
30   *   <li>A group ou (i.e. ou=usatlas,o=atlas,dc=eu-datagrid,dc=org). The ou
31   *   object will need to have a 'member' property with the list of people in 
32   *   the VO that are in the group. Each member will be an object in
33   *   ou=People</li>
34   *   <li>A root o (i.e. o=atlas,dc=eu-datagrid,dc=org). A ou=People object will
35   *   be expected, and it will behave like the first option</li>
36   * </ul>
37   *
38   * @author Gabriele Carcassi, Jay Packard
39   */
40  @Entity
41  public class LDAPUserGroup extends UserGroup {
42  	static protected Logger log = Logger.getLogger(LDAPUserGroup.class);
43  	static protected Logger gumsAdminLog = Logger.getLogger(GUMS.gumsAdminLogName);
44  
45  	protected String peopleContext = null;
46  
47  	// persistent variables
48  	protected String server;
49  	protected String certDNField = "description";
50  	protected String memberUidField = "memberUid";
51  	protected String uidField = "uid";
52  	protected String peopleTree;
53  	protected String peopleObject = "ou=People";
54  	protected String groupTree;
55  
56  	public LDAPUserGroup() {
57  		super();
58  		hasCache = true;
59  	}
60  	
61  	public LDAPUserGroup(Configuration configuration, String name) {
62  		super(configuration, name);
63  		hasCache = true;
64  	}
65  	
66  	/**
67  	 * Getter for property certDN
68  	 * 
69  	 * @return cert DN as string
70  	 */
71  	 public String getCertDNField() {
72  		 return certDNField;
73  	 }
74  
75  	 /**
76  	  * Getter for property groupTree
77  	  *
78  	  * @return groupTree as string
79  	  */
80  	 public String getGroupTree() {
81  		 return groupTree;
82  	 }
83  
84  	 public String getMemberUidField() {
85  		 return memberUidField;
86  	 }
87  
88  	 /**
89  	  * Getter for property peopleTree
90  	  *
91  	  * @return peopleTree as string
92  	  */
93  	 public String getPeopleTree() {
94  		 return peopleTree;
95  	 }
96  
97  	 /**
98  	  * The LDAP query used to retrieveGetter for property query.
99  	  * 
100 	  * @return The LDAP query used. i.e. "ou=usatlas,o=atlas,dc=eu-datagrid,dc=org"
101 	  */
102 	 @Transient
103 	 public String getQuery() {
104 		 return peopleTree;
105 	 }
106 
107 	 /**
108 	  * Returns the name of the LDAP server used to retrieve the list of users.
109 	  * 
110 	  * @return The name of the server server. i.e. "grid-vo.nikhef.nl"
111 	  */
112 	 public String getServer() {
113 		 return this.server;
114 	 }
115 
116 	 public String getUidField() {
117 		 return uidField;
118 	 }
119 
120 	 /**
121 	  * Setter for property certDN
122 	  * 
123 	  * @param certDN as string
124 	  */
125 	 @ConfigFieldAnnotation(label="Certificate DN Field", example="description")  
126 	 public void setCertDNField(String certDNField) {
127 		 this.certDNField = certDNField;
128 	 }
129 
130 	 /**
131 	  * Setter for property groupTree
132 	  *
133 	  * @param groupTree as string
134 	  */
135 	 @ConfigFieldAnnotation(label="Group Tree", example="ou=Group,dc=usatlas,dc=bnl,dc=gov")
136 	 public void setGroupTree(String groupTree) {
137 		 this.groupTree = groupTree;
138 	 }
139 
140 	 @ConfigFieldAnnotation(label="Member Field", example="memberUid", help="field containing user ID in 'Group' object")
141 	 public void setMemberUidField(String memberUidField) {
142 		 this.memberUidField = memberUidField;
143 	 }
144 
145 	 /**
146 	  * Setter for property peopleTree
147 	  *
148 	  * @param peopleTree as string
149 	  */
150 	 @ConfigFieldAnnotation(label="People Tree", example="ou=People,dc=usatlas,dc=bnl,dc=gov")
151 	 public void setPeopleTree(String peopleTree) {
152 		 if (peopleTree == null)
153 			 return;
154 		 this.peopleTree = peopleTree;
155 		 this.peopleObject = peopleTree.substring(0, peopleTree.indexOf(','));
156 		 this.peopleContext = peopleTree.substring(peopleTree.indexOf(',')+1);
157 	 }
158 
159 	 /**
160 	  * Changes the LDAP query used to retrieveGetter for property query.
161 	  * 
162 	  * @param query The LDAP query used. i.e. "ou=usatlas,o=atlas,dc=eu-datagrid,dc=org"
163 	  * @deprecated
164 	  */
165 	 public void setQuery(String query) {
166 		 if (query == null)
167 			 return;
168 		 if (query.startsWith(peopleObject+",")) {
169 			 peopleTree = query;
170 			 peopleObject = query.substring(0, query.indexOf(','));
171 			 peopleContext = query.substring(query.indexOf(',')+1);
172 		 } else if (query.startsWith("ou=")) {
173 			 peopleObject = "ou=People";
174 			 peopleContext = query.substring(query.indexOf(',')+1);
175 			 peopleTree = peopleObject + "," + peopleContext;
176 		 } else if (query.startsWith("o=")) {
177 			 peopleObject = "ou=People";
178 			 peopleContext = query;
179 			 peopleTree = peopleObject + "," + peopleContext;
180 		 } else {
181 			 throw new IllegalArgumentException("The query is not understood by the LDAP group. It is expected to start with \"ou=...\" or \"o=...\"");
182 		 }
183 	 }
184 
185 	 /**
186 	  * Changes the LDAP server used to retrieve the list of users.
187 	  * @param server The name of the server server. i.e. "grid-vo.nikhef.nl"
188 	  */
189 	 @ConfigFieldAnnotation(label="LDAP Server", example="grid-vo.nikhef.nl") 
190 	 public void setServer(String server) {
191 		 this.server = server;
192 	 }
193 
194 	 @ConfigFieldAnnotation(label="Account UID Field", example="uid")
195 	 public void setUidField(String uidField) {
196 		 this.uidField = uidField;
197 	 }
198 
199 	 private Set<GridUser> retrieveGroupMembers(DirContext rootCtx, Attribute members) throws javax.naming.NamingException {
200 		 Map<String, String> people = retrievePeopleMap(rootCtx);
201 		 NamingEnumeration<?> names = members.getAll();
202 		 Set<GridUser> users = new HashSet<GridUser>();
203 		 while (names.hasMore()) {
204 			 // Converting the people to the DN, by looking up the person description attribute
205 			 String ldapName = (String)names.next();
206 			 ldapName = ldapName.trim();
207 			 String certDN = people.get(ldapName);
208 			 if (certDN == null) {
209 				 gumsAdminLog.warn("Member of a LDAP VO group not mapped to any certificate: '" + ldapName + "'");
210 			 } else {
211 				 users.add(new GridUser(certDN));
212 			 }
213 		 }
214 		 if (users.isEmpty())
215 			 gumsAdminLog.warn("The following group returned no members: " + this);
216 		 return users;
217 	 }
218 
219 	 /**
220 	  * Returns the list of member retrieved from the LDAP server. The members are not saved in the database.
221 	  * Must be synchronized since the System properties are being set
222 	  * 
223 	  * @return A list of VOEntry objects representing the members.
224 	  */
225 	 protected synchronized Set<GridUser> retrieveMembers() {
226 		 java.util.Properties properties = retrieveProperties();
227 		 log.info("Retrieving members from '" + properties.getProperty("java.naming.provider.url") + "'  '" + peopleTree + "' '" + groupTree + "'");
228 		 try {
229 			 javax.naming.directory.DirContext jndiCtx = new javax.naming.directory.InitialDirContext(properties);
230 			 if (!peopleTree.equals("") && groupTree.equals("")) {
231 				 DirContext rootCtx = (DirContext) jndiCtx.lookup(peopleContext);
232 				 return retrieveVOMembers(rootCtx);
233 			 }
234 			 else if (!groupTree.equals("") && !peopleTree.equals("")) {
235 				 Attributes atts = jndiCtx.getAttributes(groupTree);
236 				 Attribute members = atts.get(memberUidField);
237 				 if (members == null) {
238 					 String message = "Couldn't retrieve the list of members from the LDAP group: missing attribute member";
239 					 GUMS.gumsAdminEmailLog.put("ldapUpdateProblem", message, false);
240 					 throw new RuntimeException(message);
241 				 }
242 				 DirContext rootCtx = (DirContext) jndiCtx.lookup(peopleContext);
243 				 return retrieveGroupMembers(rootCtx, members);            
244 			 }
245 		 } catch (javax.naming.NamingException e) {
246 			 String message = "Couldn't retrieve users from LDAP server: " + e;
247 			 /*            if (e.getRootCause() != null) {
248                 message += " caused by [" + e.getRootCause().getMessage() + "]";
249             }*/
250 			 log.error("Couldn't retrieve LDAP users: ", e);
251 			 throw new RuntimeException(message, e);
252 		 }
253 		 return null;
254 	 }
255 	 
256 	 protected Map<String, String> retrievePeopleMap(DirContext ldap) throws javax.naming.NamingException {
257 		 NamingEnumeration<?> people = ldap.search(peopleObject, "("+certDNField+"=*)", null);
258 		 Map<String, String> map = new Hashtable<String, String>();
259 		 while (people.hasMore()) {
260 			 SearchResult person = (SearchResult) people.next();
261 			 Attributes personAtts = person.getAttributes();
262 			 String ldapDN = (String)personAtts.get(uidField).get();//person.getName();
263 			 //if (person.isRelative()) {
264 			 //ldapDN = ldapDN + "," + peopleObject + "," + ldap.getNameInNamespace();
265 			 //}
266 			 String certDN = (String) personAtts.get(certDNField).get();
267 			 if (certDN.startsWith("subject=")) {
268 				 certDN = certDN.substring(8);
269 			 }
270 			 certDN = certDN.trim();
271 			 map.put(ldapDN, certDN);
272 		 }
273 		 return map;
274 	 }
275 
276 	 private Properties retrieveProperties() {
277 		 Properties properties = new java.util.Properties();
278 		 properties.put(Context.PROVIDER_URL, "ldap://"+server);
279 		 properties.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
280 		 properties.put(Context.SECURITY_PROTOCOL, "none");
281 		 return properties;
282 	 }
283 
284 	 private Set<GridUser> retrieveVOMembers(DirContext rootCtx) throws NamingException {
285 		 Map<String, String> people = retrievePeopleMap(rootCtx);
286 		 List<String> list = new ArrayList<String>(people.values());
287 		 if (list.isEmpty()) {
288 			 gumsAdminLog.warn("The following group returned no members: " + this);
289 		 }
290 		 Iterator<String> iter = list.iterator();
291 		 Set<GridUser> users = new HashSet<GridUser>();
292 		 while (iter.hasNext()) {
293 			 String dn = (String) iter.next();
294 			 users.add(new GridUser(dn));
295 		 }
296 		 if (list.isEmpty())
297 			 gumsAdminLog.warn("The following group returned no members: " + this);
298 		 return users;
299 	 }    
300 
301 }