Project

General

Profile

1
/**
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: berkley $'
12
 *     '$Date: 2002-04-04 13:36:34 -0800 (Thu, 04 Apr 2002) $'
13
 * '$Revision: 980 $'
14
 *
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
 */
29

    
30
package edu.ucsb.nceas.metacat;
31

    
32
import java.net.ConnectException;
33
import javax.naming.AuthenticationException;
34
import javax.naming.Context;
35
import javax.naming.NamingEnumeration;
36
import javax.naming.NamingException;
37
import javax.naming.SizeLimitExceededException;
38
import javax.naming.InitialContext;
39
import javax.naming.directory.InvalidSearchFilterException;
40
import javax.naming.directory.Attribute;
41
import javax.naming.directory.Attributes;
42
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
import javax.naming.directory.SearchControls;
48
import javax.naming.ReferralException;
49
import javax.naming.ldap.*;
50
import java.util.Iterator;
51
import java.util.HashMap;
52
import java.util.Hashtable;
53
import java.util.Enumeration;
54
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
public class AuthLdap implements AuthInterface, Runnable {
64
  private MetaCatUtil util = new MetaCatUtil();
65
  private String ldapUrl;
66
  private String ldapsUrl;
67
  private String ldapBase;
68
  private String referral;
69
  private DirContext referralContext;
70
  Hashtable env = new Hashtable(11);
71
  private Context rContext;
72
  private String userName;
73
  private String userPassword;
74
  ReferralException refExc;
75

    
76
  /** 
77
   * Construct an AuthLdap
78
   */
79
  public AuthLdap() {
80
    // Read LDAP URI for directory service information
81
    this.ldapUrl = MetaCatUtil.getOption("ldapurl");
82
    this.ldapsUrl = MetaCatUtil.getOption("ldapsurl");
83
    this.ldapBase = MetaCatUtil.getOption("ldapbase");
84
    this.referral = MetaCatUtil.getOption("referral");
85
  }
86

    
87
  /**
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
    String ldapUrl = this.ldapUrl;
99
    String ldapsUrl = this.ldapsUrl;
100
    String ldapBase = this.ldapBase;
101
    boolean authenticated = false;
102
    String identifier = user;
103

    
104
    try {
105
        // Check the usename as passed in
106
        authenticated = ldapAuthenticate(identifier, password);
107
    } 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
    } catch (NamingException e) {
115
      util.debugMessage("Naming exception while authenticating in " + 
116
                        "AuthLdap.authenticate: " + e);
117
      e.printStackTrace();
118
    } catch (Exception e) {
119
      util.debugMessage(e.getMessage());
120
    }
121
    return authenticated;
122
  }
123

    
124
  /**
125
   * 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
    double totStartTime = System.currentTimeMillis();
135
    boolean authenticated = false;
136
    if (identifier != null && !password.equals("")) 
137
    {
138
    
139
        //Pass the username and password to run() method
140
        userName=identifier;
141
        userPassword=password;
142
        // Identify service provider to use
143
        Hashtable env = new Hashtable(11);
144
        env.put(Context.INITIAL_CONTEXT_FACTORY, 
145
              "com.sun.jndi.ldap.LdapCtxFactory");
146
        env.put(Context.REFERRAL, "throw");
147
        env.put(Context.PROVIDER_URL, ldapsUrl + ldapBase);
148
        if ( !ldapsUrl.equals(ldapUrl) ) 
149
        {
150
          // 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
        try 
160
        {
161
          double startTime = System.currentTimeMillis();
162
          //Here to check the autheration
163
          ctx = new InitialDirContext(env);
164
          double stopTime = System.currentTimeMillis();
165
          util.debugMessage("Connection time thru " + ldapsUrl + " was: " +
166
                             (stopTime-startTime)/1000 + " seconds.");
167
          authenticated = true;
168
          //tls.close();
169
          ctx.close();
170
          this.ldapUrl = ldapUrl;
171
          this.ldapBase = ldapBase;
172
          //break;
173
        } 
174
        catch (AuthenticationException ae) 
175
        {
176
          authenticated = false;
177
          if ( ctx != null ) 
178
          {
179
            ctx.close();
180
          }
181
        } 
182
        catch (javax.naming.InvalidNameException ine) 
183
        {
184
            util.debugMessage("An invalid DN was provided!");
185
        } 
186
        catch (javax.naming.ReferralException re) 
187
        {
188
          util.debugMessage("referral during authentication");
189
          util.debugMessage("Referral information: "+re.getReferralInfo());
190
          try
191
          {
192
            refExc = re;
193

    
194
            Thread t = new Thread(this);
195
            t.start();
196
            Thread.sleep(5000); //this is a manual override of ldap's 
197
                                //hideously long time out period.
198
            util.debugMessage("Awake after 5 seconds.");
199
            if (referralContext == null)
200
            {
201
              t.interrupt();
202
              authenticated = false;
203
            }
204
            else
205
            {
206
              authenticated = true;
207

    
208
            }
209
          }
210
          catch (Exception e)
211
          {
212
            authenticated = false;
213
          }
214
        }
215
    } 
216
    else 
217
    { 
218
        util.debugMessage("User not found");
219
    }
220
    double totStopTime = System.currentTimeMillis();
221
    util.debugMessage("total ldap authentication time: " + 
222
                      (totStopTime - totStartTime)/1000 + " seconds");
223
    return authenticated;
224
  }
225
  
226
  
227

    
228
  /**
229
   * 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
  private String getIdentifyingName(String user, String ldapUrl,
238
                                    String ldapBase) throws NamingException
239
  {
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
    util.debugMessage("setting referrals to: " + referral);
246
    env.put(Context.REFERRAL, referral);
247
    env.put(Context.PROVIDER_URL, ldapUrl + ldapBase);
248
    //    non-secure LDAP context; dn are publicly readable
249
    try {
250
      
251
      // 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
      // Search for the user id or name using the uid, then cn and sn 
257
      //attributes
258
      // If we find a record, determine the dn for the record
259

    
260
      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
            this.ldapUrl = identifier.substring(0,
269
                                                identifier.lastIndexOf("/")+1);
270
            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
      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
            this.ldapUrl = identifier.substring(0,
298
                                                identifier.lastIndexOf("/")+1);
299
            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
              this.ldapUrl = identifier.substring(0,
312
                                                identifier.lastIndexOf("/")+1);
313
              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
   * Get all users from the authentication service
333
   *
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
   */
338
  public String[] getUsers(String user, String password) 
339
         throws ConnectException
340
  {
341
    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
    env.put(Context.REFERRAL, referral);
348
    env.put(Context.PROVIDER_URL, ldapUrl);
349
 
350
    try {
351

    
352
        // Create the initial directory context
353
        DirContext ctx = new InitialDirContext(env);
354

    
355
        // Specify the attributes to match.
356
        // Users are objects that have the attribute objectclass=InetOrgPerson.
357
        SearchControls ctls = new SearchControls();
358
        String[] attrIDs = {"dn"};
359
        ctls.setReturningAttributes(attrIDs);
360
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
361
        //ctls.setCountLimit(1000);
362
        String filter = "(objectClass=inetOrgPerson)";
363
        NamingEnumeration enum = ctx.search(ldapBase, filter, ctls);
364
        
365
        // Store the users in a vector
366
        Vector uvec = new Vector();
367
        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
        }
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
      util.debugMessage("Problem getting users in AuthLdap.getUsers:" + e);
388
      e.printStackTrace(System.err);
389
      throw new ConnectException(
390
      "Problem getting users in AuthLdap.getUsers:" + e);
391
    }
392

    
393
    return users;
394
  }
395

    
396
  /**
397
   * Get the users for a particular group from the authentication service
398
   *
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
   */
404
  public String[] getUsers(String user, String password, String group) 
405
         throws ConnectException
406
  {
407
    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
    env.put(Context.REFERRAL, referral);
414
    env.put(Context.PROVIDER_URL, ldapUrl);
415

    
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
        String[] attrIDs = {"uniqueMember"};
423

    
424
        Attributes answer = ctx.getAttributes(group, attrIDs);
425

    
426
        Vector uvec = new Vector();
427
        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
        }
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
      util.debugMessage("Problem getting users for a group in " +
451
              "AuthLdap.getUsers:" + e);
452
      throw new ConnectException(
453
      "Problem getting users for a group in AuthLdap.getUsers:" + e);
454
    }
455

    
456
    return users;
457
  }
458

    
459
  /**
460
   * Get all groups from the authentication service
461
   *
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
   */
466
  public String[] getGroups(String user, String password) 
467
         throws ConnectException
468
  {
469
      return getGroups(user, password, user);
470
  }
471

    
472
  /**
473
   * Get the groups for a particular user from the authentication service
474
   *
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
   */
480
  public String[] getGroups(String user, String password, String foruser) 
481
         throws ConnectException
482
  {
483
    String[] groups = null;
484
    //Pass the username and password to run() method
485
    userName=user;
486
    userPassword=password;
487
    // Identify service provider to use
488
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
489
            "com.sun.jndi.ldap.LdapCtxFactory");
490
    env.put(Context.REFERRAL, "throw");
491
    env.put(Context.PROVIDER_URL, ldapUrl);
492
    try {
493
        // Create the initial directory context
494
        DirContext ctx = new InitialDirContext(env);
495
        // Specify the ids of the attributes to return
496
        String[] attrIDs = {"cn"};
497
        // Specify the attributes to match.
498
        // Groups are objects with attribute objectclass=groupofuniquenames.
499
        // and have attribute uniquemember: uid=foruser,ldapbase.
500
        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
        
511
        Vector uvec = new Vector();
512
        
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
        }
523
        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

    
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
    }  
602
    catch (NamingException e) 
603
    {
604
      e.printStackTrace(System.err);
605
      throw new ConnectException(
606
      "Problem getting groups for a user in AuthLdap.getGroups:" + e);
607
    } 
608
    return groups;
609
  }
610

    
611
  /**
612
   * Get attributes describing a user or group
613
   *
614
   * @param foruser the user for which the attribute list is requested
615
   * @returns HashMap a map of attribute name to a Vector of values
616
   */
617
  public HashMap getAttributes(String foruser) 
618
         throws ConnectException
619
  {
620
    return getAttributes(null, null, foruser);
621
  }
622

    
623
  /**
624
   * Get attributes describing a user or group
625
   *
626
   * @param user the user for authenticating against the service
627
   * @param password the password for authenticating against the service
628
   * @param foruser the user whose attributes should be returned
629
   * @returns HashMap a map of attribute name to a Vector of values
630
   */
631
  public HashMap getAttributes(String user, String password, String foruser) 
632
         throws ConnectException
633
  {
634
    HashMap attributes = new HashMap();
635
    String ldapUrl = this.ldapUrl;
636
    String ldapBase = this.ldapBase;
637
    String userident = foruser;
638
   
639
    // 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
    env.put(Context.REFERRAL, referral);
644
    env.put(Context.PROVIDER_URL, ldapUrl);
645

    
646
    try {
647
      
648
      // Create the initial directory context
649
      DirContext ctx = new InitialDirContext(env);
650
        
651
      // Ask for all attributes of the user 
652
      //Attributes attrs = ctx.getAttributes(userident);
653
      Attributes attrs = ctx.getAttributes(foruser);
654
 
655
      // 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
      util.debugMessage("Problem getting attributes in " + 
673
              "AuthLdap.getAttributes:" + e);
674
      throw new ConnectException(
675
      "Problem getting attributes in AuthLdap.getAttributes:" + e);
676
    }
677

    
678
    return attributes;
679
  }
680

    
681
  /**
682
   * 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
   */
686
  private Hashtable getSubtrees(String user, String password,
687
                                String ldapUrl, String ldapBase)
688
                throws ConnectException
689
  {
690
    Hashtable trees = new Hashtable();
691

    
692
    // Identify service provider to use
693
    Hashtable env = new Hashtable(11);
694
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
695
            "com.sun.jndi.ldap.LdapCtxFactory");
696
    env.put(Context.REFERRAL, referral);
697
    env.put(Context.PROVIDER_URL, ldapUrl + ldapBase);
698

    
699
    try {
700

    
701
        // 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
 
729
            if ( enum1.hasMore() ) {
730
              attr = (Attribute)enum1.next();
731
              String refValue = (String)attr.get();
732
              String refName = (String)attr.getID();
733
               if ( ldapBase.startsWith(refName + "=" + refValue) ) {
734
                trees.put(ldapBase,
735
                         attrValue.substring(0,attrValue.lastIndexOf("/")+1) );
736
              } else {
737
                trees.put(refName + "=" + refValue + "," + ldapBase,
738
                         attrValue.substring(0,attrValue.lastIndexOf("/")+1) );
739
              }
740
 
741
            } else if ( ldapBase.startsWith(attrName + "=" + attrValue) ) {
742
                trees.put(ldapBase, ldapUrl);
743
            } else {              
744
                trees.put(attrName + "=" + attrValue + "," + ldapBase, ldapUrl);               
745
            }
746
          }
747
        }
748

    
749
        // Close the context when we're done
750
        ctx.close();
751

    
752
    } catch (NamingException e) {
753
      util.debugMessage("Problem getting subtrees in AuthLdap.getSubtrees:" 
754
                        + e);
755
      throw new ConnectException(
756
      "Problem getting subtrees in AuthLdap.getSubtrees:" + e);
757
    }
758

    
759
    return trees;
760
  }
761

    
762
  /**
763
   * Get all groups and users from authentication scheme.
764
   * The output is formatted in XML.
765
   * @param user the user which requests the information
766
   * @param password the user's password
767
   */
768
  public String getPrincipals(String user, String password)
769
                throws ConnectException
770
  {
771
    StringBuffer out = new StringBuffer();
772
    Vector usersIn = new Vector();
773
    
774
    out.append("<?xml version=\"1.0\"?>\n");
775
    out.append("<principals>\n");
776
    
777
    /*
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
      if ( groups!=null && groups.length > 0 ) {
796
        for (int i=0; i < groups.length; i++ ) {
797
          out.append("    <group>\n");
798
          out.append("      <groupname>" + groups[i] + "</groupname>\n");
799
          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
            out.append("        <username>" + usersForGroup[j] + 
804
                        "</username>\n");
805
            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
          out.append("    <user>\n");
815
          out.append("      <username>" + users[j] + "</username>\n");
816
          out.append("    </user>\n");
817
        }
818
      }
819
    
820
      out.append("  </authSystem>\n");
821
      if ( !usersIn.isEmpty() ) {
822
        usersIn.removeAllElements();
823
        usersIn.trimToSize();
824
      }
825
    
826
    }
827
    out.append("</principals>");
828
    return out.toString();
829
  }
830

    
831
  /**
832
   * Test method for the class
833
   */
834
  public static void main(String[] args) {
835

    
836
    // Provide a user, such as: "Matt Jones", or "jones"
837
    String user = args[0];
838
    String password = args[1];
839
    String foruser = null;
840
    
841
    if(args.length == 3)
842
      foruser = args[2];
843

    
844
    AuthLdap authservice = new AuthLdap();
845

    
846
                
847
    boolean isValid = false;
848
    try {
849
      System.out.println("authenticating user " + user);
850
      isValid = authservice.authenticate(user, password);
851
      if (isValid) {
852
        System.out.println("Authentication successful for: " + user );
853
      } else {
854
        System.out.println("Authentication failed for: " + user);
855
      }
856

    
857
      // Get attributes for the user
858
      if (isValid) {
859
        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
        // 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
            System.out.println(att + ": " + value);
878
          }
879
        }
880
      }
881

    
882
      // get the groups
883
      if (isValid) {
884
        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
        for (int i=0; i < groups.length; i++) {
896
            System.out.println("Group " + i + ": " + groups[i]);
897
        }
898
      }
899

    
900
      // get the groups for the user
901
      String savedGroup = null;
902
      if (isValid) {
903
        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
        for (int i=0; i < groups.length; i++) {
915
            System.out.println("Group " + i + ": " + groups[i]);
916
            savedGroup = groups[i];
917
        }
918
      }
919

    
920
      // get the users for a group
921
      if (isValid) {
922
        System.out.println("\nGetting users for group....");
923
        System.out.println("Group: " + savedGroup);
924
        String[] users = authservice.getUsers(user, password, savedGroup);
925
        System.out.println("Users found: " + users.length);
926
        for (int i=0; i < users.length; i++) {
927
            System.out.println("User " + i + ": " + users[i]);
928
        }
929
      }
930

    
931
      // get all users
932
      if (isValid) {
933
        System.out.println("\nGetting all users ....");
934
        String[] users = authservice.getUsers(user, password);
935
        System.out.println("Users found: " + users.length);
936
        
937
      }
938

    
939
      // get the whole list groups and users in XML format
940
      if (isValid) {
941
        System.out.println("\nTrying principals....");
942
        authservice = new AuthLdap();
943
        String out = authservice.getPrincipals(user, password);
944
        java.io.File f = new java.io.File("principals.xml");
945
        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
        System.out.println("\nFinished getting principals.");
952
      }
953

    
954
    } catch (ConnectException ce) {
955
      System.out.println(ce.getMessage());
956
    } catch (java.io.IOException ioe) {
957
      System.out.println("I/O Error writing to file principals.txt");
958
    }
959
  }
960
  
961
  /**
962
   * This method will be called by start a thread.
963
   * It can handle if a referral exception happend.
964
   */ 
965
  public void run()
966
  {
967
    referralContext = null;
968
    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
    while (moreReferrals)
973
    {
974
      try
975
      {
976
        //revise environment variable
977
        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
        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
        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
        env.put(Context.REFERRAL, "throw");
1004
        env.put(Context.PROVIDER_URL, refInfo);
1005
        
1006
        referralContext = new InitialDirContext(env);
1007
        //casting the context to dircontext and it will create a
1008
        //autherntication or naming exception if DN and password is incorrect
1009
        //referralContext=rContext;
1010
        //refDirContext=(DirContext)rContext;
1011
        //refDirContext.close();
1012
        //get context and jump out the while loop
1013
        moreReferrals=false;
1014
      }//try
1015
      catch (ReferralException re)
1016
      {
1017
        //keep running in while loop
1018
        moreReferrals=true;
1019
        //assign refExc to new referral exception re
1020
        refExc=re;
1021
      }
1022
      catch (AuthenticationException ae)
1023
      {
1024
        util.debugMessage("Error running referral handler thread: " + 
1025
                          ae.getMessage());
1026
        //check if has another referral
1027
        moreReferrals=refExc.skipReferral();
1028
        //don't get the context
1029
        referralContext = null;
1030
      }
1031
      catch (NamingException ne)
1032
      {
1033
        util.debugMessage("Error running referral handler thread: " + 
1034
                          ne.getMessage());
1035
        //check if has another referral
1036
        moreReferrals=refExc.skipReferral();
1037
        //don't get context
1038
        referralContext = null;		
1039
      }
1040
    }//while
1041
  }//run()
1042
}
(6-6/40)