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