Project

General

Profile

1 503 bojilova
/**
2
 *  '$RCSfile$'
3
 *    Purpose: An implementation of the AuthInterface interface that
4
 *             allows Metacat to use the LDAP protocol for
5
 *             directory services
6
 *  Copyright: 2000 Regents of the University of California and the
7
 *             National Center for Ecological Analysis and Synthesis
8
 *    Authors: Matt Jones
9
 *    Release: @release@
10
 *
11
 *   '$Author$'
12
 *     '$Date$'
13
 * '$Revision$'
14 669 jones
 *
15
 * This program is free software; you can redistribute it and/or modify
16
 * it under the terms of the GNU General Public License as published by
17
 * the Free Software Foundation; either version 2 of the License, or
18
 * (at your option) any later version.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 * GNU General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU General Public License
26
 * along with this program; if not, write to the Free Software
27
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
28 503 bojilova
 */
29
30
package edu.ucsb.nceas.metacat;
31
32
import java.net.ConnectException;
33
import javax.naming.AuthenticationException;
34
import javax.naming.Context;
35 787 bojilova
import javax.naming.NamingEnumeration;
36
import javax.naming.NamingException;
37 873 jones
import javax.naming.SizeLimitExceededException;
38 730 bojilova
import javax.naming.InitialContext;
39 802 bojilova
import javax.naming.directory.InvalidSearchFilterException;
40 503 bojilova
import javax.naming.directory.Attribute;
41
import javax.naming.directory.Attributes;
42 504 jones
import javax.naming.directory.BasicAttribute;
43
import javax.naming.directory.BasicAttributes;
44
import javax.naming.directory.DirContext;
45
import javax.naming.directory.InitialDirContext;
46
import javax.naming.directory.SearchResult;
47 723 bojilova
import javax.naming.directory.SearchControls;
48 868 berkley
import javax.naming.ReferralException;
49 787 bojilova
import javax.naming.ldap.*;
50 503 bojilova
import java.util.Iterator;
51
import java.util.HashMap;
52
import java.util.Hashtable;
53 730 bojilova
import java.util.Enumeration;
54 503 bojilova
import java.util.Set;
55
import java.util.Vector;
56
57
/**
58
 * An implementation of the AuthInterface interface that
59
 * allows Metacat to use the LDAP protocol for directory services.
60
 * The LDAP authentication service is used to determine if a user
61
 * is authenticated, and whether they are a member of a particular group.
62
 */
63 893 berkley
public class AuthLdap implements AuthInterface, Runnable {
64 787 bojilova
  private MetaCatUtil util = new MetaCatUtil();
65 728 bojilova
  private String ldapUrl;
66 787 bojilova
  private String ldapsUrl;
67 728 bojilova
  private String ldapBase;
68 865 jones
  private String referral;
69 991 tao
  private Context referralContext;
70 893 berkley
  Hashtable env = new Hashtable(11);
71
  private Context rContext;
72 934 tao
  private String userName;
73
  private String userPassword;
74 893 berkley
  ReferralException refExc;
75 1004 tao
  String uid=null;
76 503 bojilova
77 728 bojilova
  /**
78
   * Construct an AuthLdap
79
   */
80
  public AuthLdap() {
81
    // Read LDAP URI for directory service information
82 787 bojilova
    this.ldapUrl = MetaCatUtil.getOption("ldapurl");
83
    this.ldapsUrl = MetaCatUtil.getOption("ldapsurl");
84
    this.ldapBase = MetaCatUtil.getOption("ldapbase");
85 865 jones
    this.referral = MetaCatUtil.getOption("referral");
86 728 bojilova
  }
87
88 503 bojilova
  /**
89
   * Determine if a user/password are valid according to the authentication
90
   * service.
91
   *
92
   * @param user the name of the principal to authenticate
93
   * @param password the password to use for authentication
94
   * @returns boolean true if authentication successful, false otherwise
95
   */
96
  public boolean authenticate(String user, String password)
97
                    throws ConnectException
98
  {
99 730 bojilova
    String ldapUrl = this.ldapUrl;
100 787 bojilova
    String ldapsUrl = this.ldapsUrl;
101 730 bojilova
    String ldapBase = this.ldapBase;
102 503 bojilova
    boolean authenticated = false;
103 802 bojilova
    String identifier = user;
104 1004 tao
    //get uid here.
105
    uid=user.substring(0, user.indexOf(","));
106
107 503 bojilova
    try {
108 852 jones
        // Check the usename as passed in
109
        authenticated = ldapAuthenticate(identifier, password);
110 1004 tao
        // if not found, try looking up a valid DN then auth again
111
        if (!authenticated) {
112 1005 jones
            MetaCatUtil.debugMessage("Looking up DN for: " + identifier);
113 1004 tao
            identifier = getIdentifyingName(identifier,ldapUrl,ldapBase);
114 1006 tao
            //System.out.println("identifier: "+identifier);
115 1005 jones
            String refUrl = "";
116
            String refBase = "";
117
            if (identifier.startsWith("ldap")) {
118
                refUrl = identifier.substring(0,
119
                               identifier.lastIndexOf("/")+1);
120
                MetaCatUtil.debugMessage("Ref ldapUrl: " + refUrl);
121
                int position = identifier.indexOf(",");
122
                int position2 = identifier.indexOf(",", position+1);
123
                refBase = identifier.substring(position2+1);
124
                MetaCatUtil.debugMessage("Ref ldapBase: " + refBase);
125
                identifier = identifier.substring(
126
                             identifier.lastIndexOf("/")+1);
127
                MetaCatUtil.debugMessage("Trying: " + identifier);
128
                authenticated = ldapAuthenticate(identifier, password,
129
                                                 refUrl, refBase);
130
            } else {
131
                identifier = identifier+","+ldapBase;
132
                MetaCatUtil.debugMessage("Trying: " + identifier);
133
                authenticated = ldapAuthenticate(identifier, password);
134
            }
135
            //authenticated = ldapAuthenticate(identifier, password);
136 1004 tao
        }
137
138 740 bojilova
    } catch (NullPointerException e) {
139
      util.debugMessage("NullPointerException b' password is null");
140
      util.debugMessage("NullPointerException while authenticating in " +
141
                        "AuthLdap.authenticate: " + e);
142
      throw new ConnectException(
143
      "NullPointerException while authenticating in " +
144
                        "AuthLdap.authenticate: " + e);
145 503 bojilova
    } catch (NamingException e) {
146 675 berkley
      util.debugMessage("Naming exception while authenticating in " +
147
                        "AuthLdap.authenticate: " + e);
148 852 jones
      e.printStackTrace();
149 730 bojilova
    } catch (Exception e) {
150 934 tao
      util.debugMessage(e.getMessage());
151 503 bojilova
    }
152
    return authenticated;
153
  }
154
155
  /**
156 852 jones
   * Connect to the LDAP directory and do the authentication using the
157
   * username and password as passed into the routine.
158
   *
159
   * @param identifier the distinguished name to check against LDAP
160
   * @param password the password for authentication
161
   */
162
  private boolean ldapAuthenticate(String identifier, String password)
163
            throws ConnectException, NamingException, NullPointerException
164
  {
165 1005 jones
      return ldapAuthenticate(identifier, password,
166
                              this.ldapsUrl, this.ldapBase);
167
  }
168
169
  /**
170
   * Connect to the LDAP directory and do the authentication using the
171
   * username and password as passed into the routine.
172
   *
173
   * @param identifier the distinguished name to check against LDAP
174
   * @param password the password for authentication
175
   */
176
  private boolean ldapAuthenticate(String identifier, String password,
177
            String directoryUrl, String searchBase)
178
            throws ConnectException, NamingException, NullPointerException
179
  {
180 867 berkley
    double totStartTime = System.currentTimeMillis();
181 852 jones
    boolean authenticated = false;
182 927 tao
    if (identifier != null && !password.equals(""))
183 934 tao
    {
184 852 jones
185 934 tao
        //Pass the username and password to run() method
186
        userName=identifier;
187
        userPassword=password;
188
        // Identify service provider to use
189 852 jones
        Hashtable env = new Hashtable(11);
190
        env.put(Context.INITIAL_CONTEXT_FACTORY,
191 934 tao
              "com.sun.jndi.ldap.LdapCtxFactory");
192 866 berkley
        env.put(Context.REFERRAL, "throw");
193 1005 jones
        env.put(Context.PROVIDER_URL, directoryUrl + searchBase);
194 927 tao
        if ( !ldapsUrl.equals(ldapUrl) )
195 934 tao
        {
196 852 jones
          // ldap is set on default port 389
197
          // ldaps is set on second port - 636 by default
198 1005 jones
          //env.put(Context.SECURITY_PROTOCOL, "ssl");
199 852 jones
        }
200
        env.put(Context.SECURITY_AUTHENTICATION, "simple");
201
        env.put(Context.SECURITY_PRINCIPAL, identifier);
202
        env.put(Context.SECURITY_CREDENTIALS, password);
203
        // If our auth credentials are invalid, an exception will be thrown
204
        DirContext ctx = null;
205 927 tao
        try
206
        {
207 852 jones
          double startTime = System.currentTimeMillis();
208 927 tao
          //Here to check the autheration
209 852 jones
          ctx = new InitialDirContext(env);
210
          double stopTime = System.currentTimeMillis();
211 934 tao
          util.debugMessage("Connection time thru " + ldapsUrl + " was: " +
212 852 jones
                             (stopTime-startTime)/1000 + " seconds.");
213
          authenticated = true;
214
          //tls.close();
215
          ctx.close();
216
          this.ldapUrl = ldapUrl;
217
          this.ldapBase = ldapBase;
218
          //break;
219 927 tao
        }
220
        catch (AuthenticationException ae)
221
        {
222 852 jones
          authenticated = false;
223 927 tao
          if ( ctx != null )
224
          {
225 852 jones
            ctx.close();
226
          }
227 868 berkley
        }
228
        catch (javax.naming.InvalidNameException ine)
229
        {
230 934 tao
            util.debugMessage("An invalid DN was provided!");
231 868 berkley
        }
232 935 tao
        catch (javax.naming.ReferralException re)
233 868 berkley
        {
234 934 tao
          util.debugMessage("referral during authentication");
235
          util.debugMessage("Referral information: "+re.getReferralInfo());
236
          try
237 868 berkley
          {
238 893 berkley
            refExc = re;
239 934 tao
240 893 berkley
            Thread t = new Thread(this);
241
            t.start();
242 915 berkley
            Thread.sleep(5000); //this is a manual override of ldap's
243
                                //hideously long time out period.
244 934 tao
            util.debugMessage("Awake after 5 seconds.");
245 935 tao
            if (referralContext == null)
246 893 berkley
            {
247
              t.interrupt();
248
              authenticated = false;
249
            }
250
            else
251
            {
252
              authenticated = true;
253 934 tao
254 893 berkley
            }
255 868 berkley
          }
256 935 tao
          catch (Exception e)
257 868 berkley
          {
258
            authenticated = false;
259
          }
260
        }
261 927 tao
    }
262 934 tao
    else
263
    {
264 852 jones
        util.debugMessage("User not found");
265
    }
266 867 berkley
    double totStopTime = System.currentTimeMillis();
267 934 tao
    util.debugMessage("total ldap authentication time: " +
268 867 berkley
                      (totStopTime - totStartTime)/1000 + " seconds");
269 852 jones
    return authenticated;
270
  }
271 868 berkley
272 934 tao
273 852 jones
274
  /**
275 730 bojilova
   * Get the identifying name for a given userid or name.  This is the name
276
   * that is used in conjunction withthe LDAP BaseDN to create a
277
   * distinguished name (dn) for the record
278
   *
279
   * @param user the user for which the identifying name is requested
280
   * @returns String the identifying name for the user,
281
   *          or null if not found
282
   */
283 934 tao
  private String getIdentifyingName(String user, String ldapUrl,
284
                                    String ldapBase) throws NamingException
285 730 bojilova
  {
286
    String identifier = null;
287
    // Identify service provider to use
288
    Hashtable env = new Hashtable(11);
289
    env.put(Context.INITIAL_CONTEXT_FACTORY,
290
            "com.sun.jndi.ldap.LdapCtxFactory");
291 866 berkley
    util.debugMessage("setting referrals to: " + referral);
292 865 jones
    env.put(Context.REFERRAL, referral);
293 730 bojilova
    env.put(Context.PROVIDER_URL, ldapUrl + ldapBase);
294 867 berkley
    //    non-secure LDAP context; dn are publicly readable
295 730 bojilova
    try {
296 867 berkley
297 730 bojilova
      // Bind to the LDAP server, in order to search for the right
298
      // distinguished name (dn) based on userid (uid) or common name (cn)
299
      DirContext ctx = new InitialDirContext(env);
300
      SearchControls ctls = new SearchControls();
301
      ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
302 934 tao
      // Search for the user id or name using the uid, then cn and sn
303
      //attributes
304 730 bojilova
      // If we find a record, determine the dn for the record
305 1005 jones
      // The following blocks need to be refactored into a subroutine
306
      // they have a ridiculous amount of redundancy
307 730 bojilova
308 1005 jones
      // Parse out the uid and org components and look up the real DN
309
      // This assumes a dn like "uid=x,o=y,dc=someinst,dc=org"
310
      int position = user.indexOf(",");
311
      String comp1 = user.substring(0, position);
312
      MetaCatUtil.debugMessage("First comp is: " + comp1);
313
      String comp2 = user.substring(position+1,
314
                                    user.indexOf(",", position+1));
315
      MetaCatUtil.debugMessage("Second comp is: " + comp2);
316
317
      String filter = "(&(" + comp1 + ")(" + comp2 + "))";
318
      MetaCatUtil.debugMessage("Filter is: " + filter);
319
      MetaCatUtil.debugMessage("Provider URL is: " + ldapUrl + ldapBase);
320 802 bojilova
      NamingEnumeration answer;
321
      try {
322
        answer = ctx.search("", filter, ctls);
323
        if (answer.hasMore()) {
324
          SearchResult sr = (SearchResult)answer.next();
325
          identifier = sr.getName();
326 1005 jones
          util.debugMessage("Originally Found: " + identifier);
327
          return identifier;
328
        }
329
      } catch (InvalidSearchFilterException e) {}
330
331
      // That failed, so check if it is just a username
332
      filter = "(" + user + ")";
333
      try {
334
        answer = ctx.search("", filter, ctls);
335
        if (answer.hasMore()) {
336
          SearchResult sr = (SearchResult)answer.next();
337
          identifier = sr.getName();
338 802 bojilova
          if ( !sr.isRelative() ) {
339 934 tao
            this.ldapUrl = identifier.substring(0,
340
                                                identifier.lastIndexOf("/")+1);
341 802 bojilova
            this.ldapBase = identifier.substring(identifier.indexOf(",")+1);
342
            identifier = identifier.substring(identifier.lastIndexOf("/")+1,
343
                                              identifier.indexOf(","));
344
          }
345
          util.debugMessage("Found: " + identifier);
346
          return identifier;
347
        }
348
      } catch (InvalidSearchFilterException e) {}
349 1005 jones
350
      // Maybe its a user id (uid)
351 802 bojilova
      filter = "(uid=" + user + ")";
352
      answer = ctx.search("", filter, ctls);
353 730 bojilova
      if (answer.hasMore()) {
354
        SearchResult sr = (SearchResult)answer.next();
355
        identifier = sr.getName();
356
        if ( !sr.isRelative() ) {
357
          this.ldapUrl = identifier.substring(0,identifier.lastIndexOf("/")+1);
358
          this.ldapBase = identifier.substring(identifier.indexOf(",")+1);
359
          identifier = identifier.substring(identifier.lastIndexOf("/")+1,
360
                                            identifier.indexOf(","));
361
        }
362
        util.debugMessage("Found: " + identifier);
363
      } else {
364 1005 jones
365
        // maybe its just a common name
366 730 bojilova
        filter = "(cn=" + user + ")";
367
        NamingEnumeration answer2 = ctx.search("", filter, ctls);
368
        if (answer2.hasMore()) {
369
          SearchResult sr = (SearchResult)answer2.next();
370
          identifier = sr.getName();
371
          if ( !sr.isRelative() ) {
372 934 tao
            this.ldapUrl = identifier.substring(0,
373
                                                identifier.lastIndexOf("/")+1);
374 730 bojilova
            this.ldapBase = identifier.substring(identifier.indexOf(",")+1);
375
            identifier = identifier.substring(identifier.lastIndexOf("/")+1,
376
                                              identifier.indexOf(","));
377
          }
378
          util.debugMessage("Found: " + identifier);
379
        } else {
380 1005 jones
381
          // ok, last resort, is it a surname?
382 730 bojilova
          filter = "(sn=" + user + ")";
383
          NamingEnumeration answer3 = ctx.search("", filter, ctls);
384
          if (answer3.hasMore()) {
385
            SearchResult sr = (SearchResult)answer3.next();
386
            identifier = sr.getName();
387
            if ( !sr.isRelative() ) {
388 934 tao
              this.ldapUrl = identifier.substring(0,
389
                                                identifier.lastIndexOf("/")+1);
390 730 bojilova
              this.ldapBase = identifier.substring(identifier.indexOf(",")+1);
391
              identifier = identifier.substring(identifier.lastIndexOf("/")+1,
392
                                                identifier.indexOf(","));
393
            }
394
            util.debugMessage("Found: " + identifier);
395
          }
396
        }
397
      }
398
      // Close the context when we're done the initial search
399
      ctx.close();
400
    } catch (NamingException e) {
401
      util.debugMessage("Naming exception while getting dn: " + e);
402
      throw new NamingException(
403
      "Naming exception in AuthLdap.getIdentifyingName: " + e);
404
    }
405
    return identifier;
406
  }
407
408
  /**
409 503 bojilova
   * Get all users from the authentication service
410 871 jones
   *
411
   * @param user the user for authenticating against the service
412
   * @param password the password for authenticating against the service
413
   * @returns string array of all of the user names
414 503 bojilova
   */
415 514 jones
  public String[] getUsers(String user, String password)
416
         throws ConnectException
417 503 bojilova
  {
418 723 bojilova
    String[] users = null;
419
420
    // Identify service provider to use
421
    Hashtable env = new Hashtable(11);
422
    env.put(Context.INITIAL_CONTEXT_FACTORY,
423
            "com.sun.jndi.ldap.LdapCtxFactory");
424 865 jones
    env.put(Context.REFERRAL, referral);
425 871 jones
    env.put(Context.PROVIDER_URL, ldapUrl);
426 934 tao
427 723 bojilova
    try {
428
429
        // Create the initial directory context
430
        DirContext ctx = new InitialDirContext(env);
431
432 726 bojilova
        // Specify the attributes to match.
433
        // Users are objects that have the attribute objectclass=InetOrgPerson.
434 871 jones
        SearchControls ctls = new SearchControls();
435
        String[] attrIDs = {"dn"};
436
        ctls.setReturningAttributes(attrIDs);
437
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
438 873 jones
        //ctls.setCountLimit(1000);
439 871 jones
        String filter = "(objectClass=inetOrgPerson)";
440
        NamingEnumeration enum = ctx.search(ldapBase, filter, ctls);
441
442 873 jones
        // Store the users in a vector
443 723 bojilova
        Vector uvec = new Vector();
444 873 jones
        try {
445
            while (enum.hasMore()) {
446
                SearchResult sr = (SearchResult)enum.next();
447
                uvec.add(sr.getName()+","+ldapBase);
448
            }
449
        } catch (SizeLimitExceededException slee) {
450
            util.debugMessage("LDAP Server size limit exceeded. " +
451
                    "Returning incomplete record set.");
452 723 bojilova
        }
453
454
        // initialize users[]; fill users[]
455
        users = new String[uvec.size()];
456
        for (int i=0; i < uvec.size(); i++) {
457
          users[i] = (String)uvec.elementAt(i);
458
        }
459
460
        // Close the context when we're done
461
        ctx.close();
462
463
    } catch (NamingException e) {
464 934 tao
      util.debugMessage("Problem getting users in AuthLdap.getUsers:" + e);
465 871 jones
      e.printStackTrace(System.err);
466 723 bojilova
      throw new ConnectException(
467 728 bojilova
      "Problem getting users in AuthLdap.getUsers:" + e);
468 723 bojilova
    }
469
470
    return users;
471 503 bojilova
  }
472
473
  /**
474
   * Get the users for a particular group from the authentication service
475 871 jones
   *
476
   * @param user the user for authenticating against the service
477
   * @param password the password for authenticating against the service
478
   * @param group the group whose user list should be returned
479
   * @returns string array of the user names belonging to the group
480 503 bojilova
   */
481 514 jones
  public String[] getUsers(String user, String password, String group)
482
         throws ConnectException
483 503 bojilova
  {
484 723 bojilova
    String[] users = null;
485
486
    // Identify service provider to use
487
    Hashtable env = new Hashtable(11);
488
    env.put(Context.INITIAL_CONTEXT_FACTORY,
489
            "com.sun.jndi.ldap.LdapCtxFactory");
490 865 jones
    env.put(Context.REFERRAL, referral);
491 871 jones
    env.put(Context.PROVIDER_URL, ldapUrl);
492 723 bojilova
493
    try {
494
495
        // Create the initial directory context
496
        DirContext ctx = new InitialDirContext(env);
497
498
        // Specify the ids of the attributes to return
499 871 jones
        String[] attrIDs = {"uniqueMember"};
500 723 bojilova
501 871 jones
        Attributes answer = ctx.getAttributes(group, attrIDs);
502 723 bojilova
503
        Vector uvec = new Vector();
504 873 jones
        try {
505
            for (NamingEnumeration ae = answer.getAll(); ae.hasMore();) {
506
                Attribute attr = (Attribute)ae.next();
507
                for (NamingEnumeration e = attr.getAll();
508
                     e.hasMore();
509
                     uvec.add(e.next())
510
                    );
511
            }
512
        } catch (SizeLimitExceededException slee) {
513
            util.debugMessage("LDAP Server size limit exceeded. " +
514
                    "Returning incomplete record set.");
515 723 bojilova
        }
516
517
        // initialize users[]; fill users[]
518
        users = new String[uvec.size()];
519
        for (int i=0; i < uvec.size(); i++) {
520
          users[i] = (String)uvec.elementAt(i);
521
        }
522
523
        // Close the context when we're done
524
        ctx.close();
525
526
    } catch (NamingException e) {
527 934 tao
      util.debugMessage("Problem getting users for a group in " +
528 871 jones
              "AuthLdap.getUsers:" + e);
529 723 bojilova
      throw new ConnectException(
530 728 bojilova
      "Problem getting users for a group in AuthLdap.getUsers:" + e);
531 723 bojilova
    }
532
533
    return users;
534 503 bojilova
  }
535
536
  /**
537
   * Get all groups from the authentication service
538 871 jones
   *
539
   * @param user the user for authenticating against the service
540
   * @param password the password for authenticating against the service
541
   * @returns string array of the group names
542 503 bojilova
   */
543 514 jones
  public String[] getGroups(String user, String password)
544
         throws ConnectException
545 503 bojilova
  {
546 991 tao
      return getGroups(user, password, null);
547 503 bojilova
  }
548
549
  /**
550
   * Get the groups for a particular user from the authentication service
551 871 jones
   *
552
   * @param user the user for authenticating against the service
553
   * @param password the password for authenticating against the service
554
   * @param foruser the user whose group list should be returned
555
   * @returns string array of the group names
556 503 bojilova
   */
557 514 jones
  public String[] getGroups(String user, String password, String foruser)
558
         throws ConnectException
559 503 bojilova
  {
560 1000 berkley
    Vector uvec = new Vector();
561 976 tao
    //Pass the username and password to run() method
562
    userName=user;
563
    userPassword=password;
564 723 bojilova
    // Identify service provider to use
565
    env.put(Context.INITIAL_CONTEXT_FACTORY,
566
            "com.sun.jndi.ldap.LdapCtxFactory");
567 888 berkley
    env.put(Context.REFERRAL, "throw");
568 871 jones
    env.put(Context.PROVIDER_URL, ldapUrl);
569 723 bojilova
    try {
570
        // Create the initial directory context
571
        DirContext ctx = new InitialDirContext(env);
572
        // Specify the ids of the attributes to return
573 726 bojilova
        String[] attrIDs = {"cn"};
574 723 bojilova
        // Specify the attributes to match.
575 726 bojilova
        // Groups are objects with attribute objectclass=groupofuniquenames.
576 842 bojilova
        // and have attribute uniquemember: uid=foruser,ldapbase.
577 871 jones
        SearchControls ctls = new SearchControls();
578
        ctls.setReturningAttributes(attrIDs);
579
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
580 991 tao
581 871 jones
        String filter = null;
582
        String gfilter = "(objectClass=groupOfUniqueNames)";
583
        if (null == foruser) {
584
            filter = gfilter;
585
        } else {
586
            filter = "(& " + gfilter + "(uniqueMember=" + foruser + "))";
587
        }
588 1000 berkley
        MetaCatUtil.debug("searching for groups: " + filter);
589 991 tao
        NamingEnumeration enum = ctx.search(ldapBase, filter, ctls);
590
591
        // Print the groups
592 1000 berkley
        MetaCatUtil.debug("getting group results.");
593 991 tao
        while (enum.hasMore()) {
594 1000 berkley
          SearchResult sr = (SearchResult)enum.next();
595 991 tao
          uvec.add(sr.getName()+","+ldapBase);
596 1000 berkley
          MetaCatUtil.debug("group " + sr.getName() + " added to Group vector");
597 723 bojilova
        }
598
        // Close the context when we're done
599
        ctx.close();
600 991 tao
601 1000 berkley
    }
602
    catch (ReferralException re)
603
    {
604
      refExc = re;
605
      Thread t = new Thread(new GetGroup());
606
      util.debugMessage("Starting thread...");
607
      t.start();
608
      util.debugMessage("sleeping for 5 seconds.");
609 991 tao
      try
610
      {
611 1000 berkley
        Thread.sleep(5000);
612
      }
613
      catch(InterruptedException ie)
614
      {
615
        MetaCatUtil.debug("main thread interrupted: " + ie.getMessage());
616
      }
617
      //this is a manual override of jndi's hideously long time
618
      //out period.
619
      util.debugMessage("Awake after 5 seconds.");
620
      if (referralContext == null)
621
      {
622
        util.debug("thread timed out...returning groups: " + uvec.toString());
623
        String groups[] = new String[uvec.size()];
624
        for(int i=0; i<uvec.size(); i++)
625 991 tao
        {
626 1000 berkley
          groups[i] = (String)uvec.elementAt(i);
627 991 tao
        }
628 1000 berkley
        t.interrupt();
629
        return groups;
630
      }
631
      DirContext dc = (DirContext)referralContext;
632
      String[] attrIDs = {"cn"};
633
      // Specify the attributes to match.
634
      // Groups are objects with attribute objectclass=groupofuniquenames.
635
      // and have attribute uniquemember: uid=foruser,ldapbase.
636
      SearchControls ctls = new SearchControls();
637
      ctls.setReturningAttributes(attrIDs);
638
      ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
639
640
      String filter = null;
641
      String gfilter = "(objectClass=groupOfUniqueNames)";
642
      if (null == foruser) {
643
          filter = gfilter;
644
      } else {
645
          filter = "(& " + gfilter + "(uniqueMember=" + foruser + "))";
646
      }
647
648
      try
649
      {
650 991 tao
        NamingEnumeration enum = dc.search(ldapBase, filter, ctls);
651
        // Print the groups
652
        while (enum.hasMore()) {
653
          SearchResult sr = (SearchResult)enum.next();
654
          uvec.add(sr.getName()+","+ldapBase);
655
        }
656 1000 berkley
657 991 tao
        referralContext.close();
658
        dc.close();
659
      }
660 1000 berkley
      catch(NamingException ne)
661 991 tao
      {
662 1000 berkley
        MetaCatUtil.debug("Naming Exception in AuthLdap.getGroups");
663 991 tao
      }
664
    } catch (NamingException e) {
665 868 berkley
      e.printStackTrace(System.err);
666 1006 tao
      String groups[] = new String[uvec.size()];
667
      for(int i=0; i<uvec.size(); i++)
668
      {
669
        groups[i] = (String)uvec.elementAt(i);
670
      }
671
      return groups;
672
       /*throw new ConnectException(
673
      "Problem getting groups for a user in AuthLdap.getGroups:" + e);*/
674 888 berkley
    }
675 1000 berkley
676
    MetaCatUtil.debug("The user is in the following groups: " + uvec.toString());
677
    String groups[] = new String[uvec.size()];
678
    for(int i=0; i<uvec.size(); i++)
679
    {
680
      groups[i] = (String)uvec.elementAt(i);
681
    }
682 723 bojilova
    return groups;
683 503 bojilova
  }
684
685
  /**
686
   * Get attributes describing a user or group
687
   *
688 871 jones
   * @param foruser the user for which the attribute list is requested
689 503 bojilova
   * @returns HashMap a map of attribute name to a Vector of values
690
   */
691 514 jones
  public HashMap getAttributes(String foruser)
692 503 bojilova
         throws ConnectException
693
  {
694 514 jones
    return getAttributes(null, null, foruser);
695 503 bojilova
  }
696
697
  /**
698
   * Get attributes describing a user or group
699
   *
700 871 jones
   * @param user the user for authenticating against the service
701 503 bojilova
   * @param password the password for authenticating against the service
702 871 jones
   * @param foruser the user whose attributes should be returned
703 503 bojilova
   * @returns HashMap a map of attribute name to a Vector of values
704
   */
705 514 jones
  public HashMap getAttributes(String user, String password, String foruser)
706 503 bojilova
         throws ConnectException
707
  {
708
    HashMap attributes = new HashMap();
709 802 bojilova
    String ldapUrl = this.ldapUrl;
710
    String ldapBase = this.ldapBase;
711
    String userident = foruser;
712 934 tao
713 503 bojilova
    // Identify service provider to use
714
    Hashtable env = new Hashtable(11);
715
    env.put(Context.INITIAL_CONTEXT_FACTORY,
716
        "com.sun.jndi.ldap.LdapCtxFactory");
717 865 jones
    env.put(Context.REFERRAL, referral);
718 871 jones
    env.put(Context.PROVIDER_URL, ldapUrl);
719 503 bojilova
720
    try {
721 787 bojilova
722 503 bojilova
      // Create the initial directory context
723
      DirContext ctx = new InitialDirContext(env);
724
725 504 jones
      // Ask for all attributes of the user
726 871 jones
      //Attributes attrs = ctx.getAttributes(userident);
727
      Attributes attrs = ctx.getAttributes(foruser);
728 723 bojilova
729 503 bojilova
      // Print all of the attributes
730
      NamingEnumeration en = attrs.getAll();
731
      while (en.hasMore()) {
732
        Attribute att = (Attribute)en.next();
733
        Vector values = new Vector();
734
        String attName = att.getID();
735
        NamingEnumeration attvalues = att.getAll();
736
        while (attvalues.hasMore()) {
737
          String value = (String)attvalues.next();
738
          values.add(value);
739
        }
740
        attributes.put(attName, values);
741
      }
742
743
      // Close the context when we're done
744
      ctx.close();
745
    } catch (NamingException e) {
746 934 tao
      util.debugMessage("Problem getting attributes in " +
747 871 jones
              "AuthLdap.getAttributes:" + e);
748 723 bojilova
      throw new ConnectException(
749
      "Problem getting attributes in AuthLdap.getAttributes:" + e);
750 503 bojilova
    }
751
752
    return attributes;
753
  }
754
755
  /**
756 730 bojilova
   * Get list of all subtrees holding Metacat's groups and users
757
   * starting from the Metacat LDAP root,
758
   * i.e. ldap://dev.nceas.ucsb.edu/dc=ecoinformatics,dc=org
759 504 jones
   */
760 730 bojilova
  private Hashtable getSubtrees(String user, String password,
761
                                String ldapUrl, String ldapBase)
762
                throws ConnectException
763 504 jones
  {
764 730 bojilova
    Hashtable trees = new Hashtable();
765 504 jones
766
    // Identify service provider to use
767
    Hashtable env = new Hashtable(11);
768 730 bojilova
    env.put(Context.INITIAL_CONTEXT_FACTORY,
769 504 jones
            "com.sun.jndi.ldap.LdapCtxFactory");
770 865 jones
    env.put(Context.REFERRAL, referral);
771 504 jones
    env.put(Context.PROVIDER_URL, ldapUrl + ldapBase);
772
773
    try {
774
775 730 bojilova
        // Create the initial directory context
776
        DirContext ctx = new InitialDirContext(env);
777
778
        // Specify the ids of the attributes to return
779
        String[] attrIDs = {"o","ref"};
780
        SearchControls ctls = new SearchControls();
781
        ctls.setReturningAttributes(attrIDs);
782
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
783
784
        // Specify the attributes to match.
785
        // Subtrees from the main server are found as objects with attribute
786
        // objectclass=organization or objectclass=referral to the subtree
787
        // resided on other server.
788
        String filter = "(|(objectclass=organization)(objectclass=referral))";
789
790
        // Search for objects in the current context
791
        NamingEnumeration enum = ctx.search("", filter, ctls);
792
793
        // Print the subtrees' <ldapURL, baseDN>
794
        while (enum.hasMore()) {
795
          SearchResult sr = (SearchResult)enum.next();
796
          Attributes attrs = sr.getAttributes();
797
          NamingEnumeration enum1 = attrs.getAll(); // "dc" and "ref" attrs
798
          if (enum1.hasMore()) {
799
            Attribute attr = (Attribute)enum1.next();
800
            String attrValue = (String)attr.get();
801
            String attrName = (String)attr.getID();
802 934 tao
803 730 bojilova
            if ( enum1.hasMore() ) {
804
              attr = (Attribute)enum1.next();
805
              String refValue = (String)attr.get();
806
              String refName = (String)attr.getID();
807 934 tao
               if ( ldapBase.startsWith(refName + "=" + refValue) ) {
808 730 bojilova
                trees.put(ldapBase,
809 934 tao
                         attrValue.substring(0,attrValue.lastIndexOf("/")+1) );
810 730 bojilova
              } else {
811
                trees.put(refName + "=" + refValue + "," + ldapBase,
812 934 tao
                         attrValue.substring(0,attrValue.lastIndexOf("/")+1) );
813 730 bojilova
              }
814 934 tao
815 730 bojilova
            } else if ( ldapBase.startsWith(attrName + "=" + attrValue) ) {
816
                trees.put(ldapBase, ldapUrl);
817
            } else {
818 934 tao
                trees.put(attrName + "=" + attrValue + "," + ldapBase, ldapUrl);
819 730 bojilova
            }
820 504 jones
          }
821
        }
822 730 bojilova
823
        // Close the context when we're done
824
        ctx.close();
825
826 504 jones
    } catch (NamingException e) {
827 934 tao
      util.debugMessage("Problem getting subtrees in AuthLdap.getSubtrees:"
828
                        + e);
829 730 bojilova
      throw new ConnectException(
830
      "Problem getting subtrees in AuthLdap.getSubtrees:" + e);
831 504 jones
    }
832
833 730 bojilova
    return trees;
834 504 jones
  }
835
836
  /**
837 730 bojilova
   * Get all groups and users from authentication scheme.
838 725 bojilova
   * The output is formatted in XML.
839 730 bojilova
   * @param user the user which requests the information
840
   * @param password the user's password
841 725 bojilova
   */
842 730 bojilova
  public String getPrincipals(String user, String password)
843 725 bojilova
                throws ConnectException
844
  {
845
    StringBuffer out = new StringBuffer();
846 726 bojilova
    Vector usersIn = new Vector();
847 725 bojilova
848
    out.append("<?xml version=\"1.0\"?>\n");
849 730 bojilova
    out.append("<principals>\n");
850 725 bojilova
851 730 bojilova
    /*
852
     * get all subtrees first in the current dir context
853
     * and then the Metacat users under them
854
     */
855
    Hashtable subtrees = getSubtrees(user,password,this.ldapUrl,this.ldapBase);
856
857
    Enumeration enum = subtrees.keys();
858
    while ( enum.hasMoreElements() ) {
859
      this.ldapBase = (String)enum.nextElement();
860
      this.ldapUrl = (String)subtrees.get(ldapBase);
861
862
      out.append("  <authSystem URI=\"" +
863
                 this.ldapUrl + this.ldapBase + "\">\n");
864
865
      // get all groups for directory context
866
      String[] groups = getGroups(user, password);
867
868
      // for the groups and users that belong to them
869 976 tao
      if ( groups!=null && groups.length > 0 ) {
870 730 bojilova
        for (int i=0; i < groups.length; i++ ) {
871
          out.append("    <group>\n");
872 802 bojilova
          out.append("      <groupname>" + groups[i] + "</groupname>\n");
873 730 bojilova
          String[] usersForGroup = getUsers(user,password,groups[i]);
874
          for (int j=0; j < usersForGroup.length; j++ ) {
875
            usersIn.addElement(usersForGroup[j]);
876
            out.append("      <user>\n");
877 934 tao
            out.append("        <username>" + usersForGroup[j] +
878
                        "</username>\n");
879 730 bojilova
            out.append("      </user>\n");
880
          }
881
          out.append("    </group>\n");
882
        }
883
      }
884
      // for the users not belonging to any group
885
      String[] users = getUsers(user, password);
886
      for (int j=0; j < users.length; j++ ) {
887
        if ( !usersIn.contains(users[j]) ) {
888 725 bojilova
          out.append("    <user>\n");
889 802 bojilova
          out.append("      <username>" + users[j] + "</username>\n");
890 725 bojilova
          out.append("    </user>\n");
891
        }
892
      }
893 730 bojilova
894
      out.append("  </authSystem>\n");
895
      if ( !usersIn.isEmpty() ) {
896
        usersIn.removeAllElements();
897
        usersIn.trimToSize();
898 725 bojilova
      }
899 730 bojilova
900 725 bojilova
    }
901
    out.append("</principals>");
902
    return out.toString();
903
  }
904
905
  /**
906 503 bojilova
   * Test method for the class
907
   */
908
  public static void main(String[] args) {
909
910 504 jones
    // Provide a user, such as: "Matt Jones", or "jones"
911 503 bojilova
    String user = args[0];
912
    String password = args[1];
913
914 1005 jones
    MetaCatUtil.debugMessage("Creating session...");
915 503 bojilova
    AuthLdap authservice = new AuthLdap();
916 1005 jones
    MetaCatUtil.debugMessage("Session exists...");
917 873 jones
918 503 bojilova
    boolean isValid = false;
919
    try {
920 1005 jones
      MetaCatUtil.debugMessage("Authenticating...");
921 503 bojilova
      isValid = authservice.authenticate(user, password);
922
      if (isValid) {
923 991 tao
        MetaCatUtil.debugMessage("Authentication successful for: " + user );
924 503 bojilova
      } else {
925 991 tao
        MetaCatUtil.debugMessage("Authentication failed for: " + user);
926 503 bojilova
      }
927 725 bojilova
928 871 jones
      // Get attributes for the user
929 503 bojilova
      if (isValid) {
930 991 tao
        MetaCatUtil.debugMessage("\nGetting attributes for user....");
931
        HashMap userInfo = authservice.getAttributes(user, password, user);
932 503 bojilova
        // Print all of the attributes
933
        Iterator attList = (Iterator)(((Set)userInfo.keySet()).iterator());
934
        while (attList.hasNext()) {
935
          String att = (String)attList.next();
936
          Vector values = (Vector)userInfo.get(att);
937
          Iterator attvalues = values.iterator();
938
          while (attvalues.hasNext()) {
939
            String value = (String)attvalues.next();
940 991 tao
            MetaCatUtil.debugMessage(att + ": " + value);
941 503 bojilova
          }
942
        }
943 871 jones
      }
944 723 bojilova
945 871 jones
      // get the groups
946
      if (isValid) {
947 991 tao
        MetaCatUtil.debugMessage("\nGetting all groups....");
948
        String[] groups = authservice.getGroups(user, password);
949
        MetaCatUtil.debugMessage("Groups found: " + groups.length);
950 871 jones
        for (int i=0; i < groups.length; i++) {
951 991 tao
            MetaCatUtil.debugMessage("Group " + i + ": " + groups[i]);
952 871 jones
        }
953 503 bojilova
      }
954 725 bojilova
955 871 jones
      // get the groups for the user
956
      String savedGroup = null;
957
      if (isValid) {
958 991 tao
        MetaCatUtil.debugMessage("\nGetting groups for user....");
959
        String[] groups = authservice.getGroups(user, password, user);
960
        MetaCatUtil.debugMessage("Groups found: " + groups.length);
961 871 jones
        for (int i=0; i < groups.length; i++) {
962 991 tao
            MetaCatUtil.debugMessage("Group " + i + ": " + groups[i]);
963 871 jones
            savedGroup = groups[i];
964
        }
965
      }
966
967
      // get the users for a group
968
      if (isValid) {
969 991 tao
        MetaCatUtil.debugMessage("\nGetting users for group....");
970
        MetaCatUtil.debugMessage("Group: " + savedGroup);
971 871 jones
        String[] users = authservice.getUsers(user, password, savedGroup);
972 991 tao
        MetaCatUtil.debugMessage("Users found: " + users.length);
973 871 jones
        for (int i=0; i < users.length; i++) {
974 991 tao
            MetaCatUtil.debugMessage("User " + i + ": " + users[i]);
975 871 jones
        }
976
      }
977
978
      // get all users
979
      if (isValid) {
980 991 tao
        MetaCatUtil.debugMessage("\nGetting all users ....");
981 871 jones
        String[] users = authservice.getUsers(user, password);
982 991 tao
        MetaCatUtil.debugMessage("Users found: " + users.length);
983 934 tao
984 871 jones
      }
985 873 jones
986 726 bojilova
      // get the whole list groups and users in XML format
987 725 bojilova
      if (isValid) {
988 991 tao
        MetaCatUtil.debugMessage("\nTrying principals....");
989 730 bojilova
        authservice = new AuthLdap();
990 725 bojilova
        String out = authservice.getPrincipals(user, password);
991 802 bojilova
        java.io.File f = new java.io.File("principals.xml");
992 725 bojilova
        java.io.FileWriter fw = new java.io.FileWriter(f);
993
        java.io.BufferedWriter buff = new java.io.BufferedWriter(fw);
994
        buff.write(out);
995
        buff.flush();
996
        buff.close();
997
        fw.close();
998 991 tao
        MetaCatUtil.debugMessage("\nFinished getting principals.");
999 725 bojilova
      }
1000 873 jones
1001 503 bojilova
    } catch (ConnectException ce) {
1002 991 tao
      MetaCatUtil.debugMessage(ce.getMessage());
1003 725 bojilova
    } catch (java.io.IOException ioe) {
1004 991 tao
      MetaCatUtil.debugMessage("I/O Error writing to file principals.txt");
1005 503 bojilova
    }
1006
  }
1007 893 berkley
1008 934 tao
  /**
1009
   * This method will be called by start a thread.
1010
   * It can handle if a referral exception happend.
1011
   */
1012 893 berkley
  public void run()
1013
  {
1014
    referralContext = null;
1015 934 tao
    DirContext refDirContext=null;
1016
    boolean moreReferrals=true;
1017 1004 tao
    String referralInfo=null;
1018 934 tao
    //set a while loop is because we don't know if a referral excption contains
1019
    //another referral exception
1020 935 tao
    while (moreReferrals)
1021 934 tao
    {
1022
      try
1023 1004 tao
      {
1024 934 tao
        //revise environment variable
1025 1004 tao
        referralInfo=(String)refExc.getReferralInfo();
1026
        env.put(Context.PROVIDER_URL,refExc.getReferralInfo());
1027 1006 tao
        //System.out.println("referral info: "+referralInfo);
1028 1005 jones
        //if (referralInfo.indexOf("piscoweb")!=-1)
1029
        //{
1030
          //userName=uid+",o=PISCO,dc=piscoweb,dc=org";
1031
        //}
1032 934 tao
        env.put(Context.INITIAL_CONTEXT_FACTORY,
1033
                "com.sun.jndi.ldap.LdapCtxFactory");
1034 1006 tao
        //System.out.println("principal: "+userName);
1035 934 tao
        env.put(Context.SECURITY_PRINCIPAL, userName);
1036
        env.put(Context.SECURITY_CREDENTIALS, userPassword);
1037 991 tao
        env.put(Context.REFERRAL, "throw");
1038 980 berkley
        //get a context object for referral in the new envriment
1039 991 tao
        rContext = refExc.getReferralContext(env);
1040 934 tao
        //casting the context to dircontext and it will create a
1041
        //autherntication or naming exception if DN and password is incorrect
1042 991 tao
        referralContext=rContext;
1043
        refDirContext=(DirContext)rContext;
1044
        refDirContext.close();
1045 934 tao
        //get context and jump out the while loop
1046
        moreReferrals=false;
1047
      }//try
1048 991 tao
      //if referral have another referral excption
1049 935 tao
      catch (ReferralException re)
1050 934 tao
      {
1051
        //keep running in while loop
1052
        moreReferrals=true;
1053
        //assign refExc to new referral exception re
1054
        refExc=re;
1055
      }
1056 991 tao
      //catch a authentication exception
1057 935 tao
      catch (AuthenticationException ae)
1058 934 tao
      {
1059
        util.debugMessage("Error running referral handler thread: " +
1060 928 tao
                          ae.getMessage());
1061 934 tao
        //check if has another referral
1062
        moreReferrals=refExc.skipReferral();
1063
        //don't get the context
1064
        referralContext = null;
1065
      }
1066 991 tao
      //catch a naming exception
1067 935 tao
      catch (NamingException ne)
1068 934 tao
      {
1069
        util.debugMessage("Error running referral handler thread: " +
1070 928 tao
                          ne.getMessage());
1071 934 tao
        //check if has another referral
1072
        moreReferrals=refExc.skipReferral();
1073
        //don't get context
1074
        referralContext = null;
1075
      }
1076
    }//while
1077 930 tao
  }//run()
1078 991 tao
1079
  private class GetGroup implements Runnable
1080
  {
1081
    public void run()
1082
    {
1083
      referralContext = null;
1084 1000 berkley
      MetaCatUtil.debug("getting groups context");
1085 991 tao
      DirContext refDirContext=null;
1086
      boolean moreReferrals=true;
1087
      //set a while loop is because we don't know if a referral excption
1088
      //contains another referral exception
1089
      while (moreReferrals)
1090
      {
1091
        try
1092
        {
1093
          //revise environment variable
1094
          String refInfo = null;
1095
          refInfo = (String)refExc.getReferralInfo();
1096
          if(refInfo != null)
1097
          {
1098 1000 berkley
            MetaCatUtil.debug("Referral in thread to: " +
1099
                              refInfo.toString());
1100 991 tao
          }
1101
          else
1102
          {
1103 1000 berkley
            MetaCatUtil.debug("getting refInfo Manually");
1104 991 tao
            refInfo = (String)refExc.getReferralContext().getEnvironment().
1105
                                                  get(Context.PROVIDER_URL);
1106
          }
1107 1000 berkley
          MetaCatUtil.debug("refInfo: " + refInfo);
1108 991 tao
1109
          env.put(Context.INITIAL_CONTEXT_FACTORY,
1110
              "com.sun.jndi.ldap.LdapCtxFactory");
1111
          env.put(Context.REFERRAL, "throw");
1112
          env.put(Context.PROVIDER_URL, refInfo);
1113
1114 1000 berkley
          MetaCatUtil.debug("creating referralContext");
1115 991 tao
          referralContext = new InitialDirContext(env);
1116 1000 berkley
          MetaCatUtil.debug("referralContext created");
1117 991 tao
          //get context and jump out the while loop
1118
          moreReferrals=false;
1119
        }//try
1120
        catch (ReferralException re)
1121
        {
1122
          //keep running in while loop
1123
          moreReferrals=true;
1124
          //assign refExc to new referral exception re
1125
          refExc=re;
1126
        }
1127
        catch (AuthenticationException ae)
1128
        {
1129
          util.debugMessage("Error running referral handler thread: " +
1130
                          ae.getMessage());
1131
          //check if has another referral
1132
          moreReferrals=refExc.skipReferral();
1133
          //don't get the context
1134
          referralContext = null;
1135
        }
1136
        catch (NamingException ne)
1137
        {
1138
          util.debugMessage("Error running referral handler thread: " +
1139
                          ne.getMessage());
1140
          //check if has another referral
1141
          moreReferrals=refExc.skipReferral();
1142
          //don't get context
1143
          referralContext = null;
1144
        }
1145
      }//while
1146
    }//run()
1147
 }
1148 503 bojilova
}