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: tao $'
12
 *     '$Date: 2002-03-22 14:24:20 -0800 (Fri, 22 Mar 2002) $'
13
 * '$Revision: 976 $'
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 Context 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, null);
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
        
504
        String filter = null;
505
        String gfilter = "(objectClass=groupOfUniqueNames)";
506
        if (null == foruser) {
507
            filter = gfilter;
508
        } else {
509
            filter = "(& " + gfilter + "(uniqueMember=" + foruser + "))";
510
        }
511
        NamingEnumeration enum = ctx.search(ldapBase, filter, ctls);
512

    
513
        // Print the groups
514
        Vector uvec = new Vector();
515
        while (enum.hasMore()) {
516
          SearchResult sr = (SearchResult)enum.next();
517
          uvec.add(sr.getName()+","+ldapBase);
518
        }
519
      
520

    
521
        // initialize groups[] and fill it
522
        groups = new String[uvec.size()];
523
        for (int i=0; i < uvec.size(); i++) {
524
          groups[i] = (String)uvec.elementAt(i); 
525
        }
526

    
527
        // Close the context when we're done
528
        ctx.close();
529

    
530
    } catch (ReferralException re) {
531
      try
532
      {
533
        refExc = re;
534
        Thread t = new Thread(this);
535
        util.debugMessage("Starting thread...");
536
        t.start();
537
        util.debugMessage("sleeping for 5 seconds.");
538
        Thread.sleep(5000); 
539
        //this is a manual override of ldap's hideously long time
540
        //out period.
541
        util.debugMessage("Awake after 5 seconds.");
542
        if (referralContext == null)
543
        {
544
          t.interrupt();
545
          return null;
546
        }
547
        DirContext dc = (DirContext)referralContext;
548
        String[] attrIDs = {"cn"};
549
        // Specify the attributes to match.
550
        // Groups are objects with attribute objectclass=groupofuniquenames.
551
        // and have attribute uniquemember: uid=foruser,ldapbase.
552
        SearchControls ctls = new SearchControls();
553
        ctls.setReturningAttributes(attrIDs);
554
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
555
        
556
        String filter = null;
557
        String gfilter = "(objectClass=groupOfUniqueNames)";
558
        if (null == foruser) {
559
            filter = gfilter;
560
        } else {
561
            filter = "(& " + gfilter + "(uniqueMember=" + foruser + "))";
562
        }
563
        NamingEnumeration enum = dc.search(ldapBase, filter, ctls);
564

    
565
        // Print the groups
566
        Vector uvec = new Vector();
567
        while (enum.hasMore()) {
568
          SearchResult sr = (SearchResult)enum.next();
569
          uvec.add(sr.getName()+","+ldapBase);
570
        }
571

    
572
        // initialize groups[] and fill it
573
        groups = new String[uvec.size()];
574
        for (int i=0; i < uvec.size(); i++) {
575
          groups[i] = (String)uvec.elementAt(i); 
576
        }
577
        referralContext.close();
578
        dc.close();
579
      }
580
      catch (Exception e)
581
      {
582
        return groups;
583
      }
584
    } catch (NamingException e) {
585
      e.printStackTrace(System.err);
586
      throw new ConnectException(
587
      "Problem getting groups for a user in AuthLdap.getGroups:" + e);
588
    } 
589
    return groups;
590
  }
591

    
592
  /**
593
   * Get attributes describing a user or group
594
   *
595
   * @param foruser the user for which the attribute list is requested
596
   * @returns HashMap a map of attribute name to a Vector of values
597
   */
598
  public HashMap getAttributes(String foruser) 
599
         throws ConnectException
600
  {
601
    return getAttributes(null, null, foruser);
602
  }
603

    
604
  /**
605
   * Get attributes describing a user or group
606
   *
607
   * @param user the user for authenticating against the service
608
   * @param password the password for authenticating against the service
609
   * @param foruser the user whose attributes should be returned
610
   * @returns HashMap a map of attribute name to a Vector of values
611
   */
612
  public HashMap getAttributes(String user, String password, String foruser) 
613
         throws ConnectException
614
  {
615
    HashMap attributes = new HashMap();
616
    String ldapUrl = this.ldapUrl;
617
    String ldapBase = this.ldapBase;
618
    String userident = foruser;
619
   
620
    // Identify service provider to use
621
    Hashtable env = new Hashtable(11);
622
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
623
        "com.sun.jndi.ldap.LdapCtxFactory");
624
    env.put(Context.REFERRAL, referral);
625
    env.put(Context.PROVIDER_URL, ldapUrl);
626

    
627
    try {
628
      
629
      // Create the initial directory context
630
      DirContext ctx = new InitialDirContext(env);
631
        
632
      // Ask for all attributes of the user 
633
      //Attributes attrs = ctx.getAttributes(userident);
634
      Attributes attrs = ctx.getAttributes(foruser);
635
 
636
      // Print all of the attributes
637
      NamingEnumeration en = attrs.getAll();
638
      while (en.hasMore()) {
639
        Attribute att = (Attribute)en.next();
640
        Vector values = new Vector();
641
        String attName = att.getID();
642
        NamingEnumeration attvalues = att.getAll();
643
        while (attvalues.hasMore()) {
644
          String value = (String)attvalues.next();
645
          values.add(value);
646
        }
647
        attributes.put(attName, values);
648
      }
649
  
650
      // Close the context when we're done
651
      ctx.close();
652
    } catch (NamingException e) {
653
      util.debugMessage("Problem getting attributes in " + 
654
              "AuthLdap.getAttributes:" + e);
655
      throw new ConnectException(
656
      "Problem getting attributes in AuthLdap.getAttributes:" + e);
657
    }
658

    
659
    return attributes;
660
  }
661

    
662
  /**
663
   * Get list of all subtrees holding Metacat's groups and users
664
   * starting from the Metacat LDAP root, 
665
   * i.e. ldap://dev.nceas.ucsb.edu/dc=ecoinformatics,dc=org
666
   */
667
  private Hashtable getSubtrees(String user, String password,
668
                                String ldapUrl, String ldapBase)
669
                throws ConnectException
670
  {
671
    Hashtable trees = new Hashtable();
672

    
673
    // Identify service provider to use
674
    Hashtable env = new Hashtable(11);
675
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
676
            "com.sun.jndi.ldap.LdapCtxFactory");
677
    env.put(Context.REFERRAL, referral);
678
    env.put(Context.PROVIDER_URL, ldapUrl + ldapBase);
679

    
680
    try {
681

    
682
        // Create the initial directory context
683
        DirContext ctx = new InitialDirContext(env);
684

    
685
        // Specify the ids of the attributes to return
686
        String[] attrIDs = {"o","ref"};
687
        SearchControls ctls = new SearchControls();
688
        ctls.setReturningAttributes(attrIDs);
689
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
690
          
691
        // Specify the attributes to match.
692
        // Subtrees from the main server are found as objects with attribute
693
        // objectclass=organization or objectclass=referral to the subtree
694
        // resided on other server.
695
        String filter = "(|(objectclass=organization)(objectclass=referral))";
696

    
697
        // Search for objects in the current context
698
        NamingEnumeration enum = ctx.search("", filter, ctls);
699

    
700
        // Print the subtrees' <ldapURL, baseDN>
701
        while (enum.hasMore()) {
702
          SearchResult sr = (SearchResult)enum.next();
703
          Attributes attrs = sr.getAttributes();
704
          NamingEnumeration enum1 = attrs.getAll(); // "dc" and "ref" attrs
705
          if (enum1.hasMore()) {
706
            Attribute attr = (Attribute)enum1.next();
707
            String attrValue = (String)attr.get();
708
            String attrName = (String)attr.getID();
709
 
710
            if ( enum1.hasMore() ) {
711
              attr = (Attribute)enum1.next();
712
              String refValue = (String)attr.get();
713
              String refName = (String)attr.getID();
714
               if ( ldapBase.startsWith(refName + "=" + refValue) ) {
715
                trees.put(ldapBase,
716
                         attrValue.substring(0,attrValue.lastIndexOf("/")+1) );
717
              } else {
718
                trees.put(refName + "=" + refValue + "," + ldapBase,
719
                         attrValue.substring(0,attrValue.lastIndexOf("/")+1) );
720
              }
721
 
722
            } else if ( ldapBase.startsWith(attrName + "=" + attrValue) ) {
723
                trees.put(ldapBase, ldapUrl);
724
            } else {              
725
                trees.put(attrName + "=" + attrValue + "," + ldapBase, ldapUrl);               
726
            }
727
          }
728
        }
729

    
730
        // Close the context when we're done
731
        ctx.close();
732

    
733
    } catch (NamingException e) {
734
      util.debugMessage("Problem getting subtrees in AuthLdap.getSubtrees:" 
735
                        + e);
736
      throw new ConnectException(
737
      "Problem getting subtrees in AuthLdap.getSubtrees:" + e);
738
    }
739

    
740
    return trees;
741
  }
742

    
743
  /**
744
   * Get all groups and users from authentication scheme.
745
   * The output is formatted in XML.
746
   * @param user the user which requests the information
747
   * @param password the user's password
748
   */
749
  public String getPrincipals(String user, String password)
750
                throws ConnectException
751
  {
752
    StringBuffer out = new StringBuffer();
753
    Vector usersIn = new Vector();
754
    
755
    out.append("<?xml version=\"1.0\"?>\n");
756
    out.append("<principals>\n");
757
    
758
    /*
759
     * get all subtrees first in the current dir context 
760
     * and then the Metacat users under them
761
     */
762
    Hashtable subtrees = getSubtrees(user,password,this.ldapUrl,this.ldapBase);
763
    
764
    Enumeration enum = subtrees.keys();
765
    while ( enum.hasMoreElements() ) {
766
      this.ldapBase = (String)enum.nextElement();
767
      this.ldapUrl = (String)subtrees.get(ldapBase);
768
      
769
      out.append("  <authSystem URI=\"" + 
770
                 this.ldapUrl + this.ldapBase + "\">\n");
771

    
772
      // get all groups for directory context
773
      String[] groups = getGroups(user, password);
774

    
775
      // for the groups and users that belong to them
776
      if ( groups!=null && groups.length > 0 ) {
777
        for (int i=0; i < groups.length; i++ ) {
778
          out.append("    <group>\n");
779
          out.append("      <groupname>" + groups[i] + "</groupname>\n");
780
          String[] usersForGroup = getUsers(user,password,groups[i]);
781
          for (int j=0; j < usersForGroup.length; j++ ) {
782
            usersIn.addElement(usersForGroup[j]);
783
            out.append("      <user>\n");
784
            out.append("        <username>" + usersForGroup[j] + 
785
                        "</username>\n");
786
            out.append("      </user>\n");
787
          }
788
          out.append("    </group>\n");
789
        }
790
      }
791
      // for the users not belonging to any group
792
      String[] users = getUsers(user, password);
793
      for (int j=0; j < users.length; j++ ) {
794
        if ( !usersIn.contains(users[j]) ) {
795
          out.append("    <user>\n");
796
          out.append("      <username>" + users[j] + "</username>\n");
797
          out.append("    </user>\n");
798
        }
799
      }
800
    
801
      out.append("  </authSystem>\n");
802
      if ( !usersIn.isEmpty() ) {
803
        usersIn.removeAllElements();
804
        usersIn.trimToSize();
805
      }
806
    
807
    }
808
    out.append("</principals>");
809
    return out.toString();
810
  }
811

    
812
  /**
813
   * Test method for the class
814
   */
815
  public static void main(String[] args) {
816

    
817
    // Provide a user, such as: "Matt Jones", or "jones"
818
    String user = args[0];
819
    String password = args[1];
820

    
821
    AuthLdap authservice = new AuthLdap();
822

    
823
                
824
    boolean isValid = false;
825
    try {
826
      isValid = authservice.authenticate(user, password);
827
      if (isValid) {
828
        MetaCatUtil.debugMessage("Authentication successful for: " + user );
829
      } else {
830
        MetaCatUtil.debugMessage("Authentication failed for: " + user);
831
      }
832

    
833
      // Get attributes for the user
834
      if (isValid) {
835
        MetaCatUtil.debugMessage("\nGetting attributes for user....");
836
        HashMap userInfo = authservice.getAttributes(user, password, user);
837
        // Print all of the attributes
838
        Iterator attList = (Iterator)(((Set)userInfo.keySet()).iterator());
839
        while (attList.hasNext()) {
840
          String att = (String)attList.next();
841
          Vector values = (Vector)userInfo.get(att);
842
          Iterator attvalues = values.iterator();
843
          while (attvalues.hasNext()) {
844
            String value = (String)attvalues.next();
845
            MetaCatUtil.debugMessage(att + ": " + value);
846
          }
847
        }
848
      }
849

    
850
      // get the groups
851
      if (isValid) {
852
        MetaCatUtil.debugMessage("\nGetting all groups....");
853
        String[] groups = authservice.getGroups(user, password);
854
        MetaCatUtil.debugMessage("Groups found: " + groups.length);
855
        for (int i=0; i < groups.length; i++) {
856
            MetaCatUtil.debugMessage("Group " + i + ": " + groups[i]);
857
        }
858
      }
859

    
860
      // get the groups for the user
861
      String savedGroup = null;
862
      if (isValid) {
863
        MetaCatUtil.debugMessage("\nGetting groups for user....");
864
        String[] groups = authservice.getGroups(user, password, user);
865
        MetaCatUtil.debugMessage("Groups found: " + groups.length);
866
        for (int i=0; i < groups.length; i++) {
867
            MetaCatUtil.debugMessage("Group " + i + ": " + groups[i]);
868
            savedGroup = groups[i];
869
        }
870
      }
871

    
872
      // get the users for a group
873
      if (isValid) {
874
        MetaCatUtil.debugMessage("\nGetting users for group....");
875
        MetaCatUtil.debugMessage("Group: " + savedGroup);
876
        String[] users = authservice.getUsers(user, password, savedGroup);
877
        MetaCatUtil.debugMessage("Users found: " + users.length);
878
        for (int i=0; i < users.length; i++) {
879
            MetaCatUtil.debugMessage("User " + i + ": " + users[i]);
880
        }
881
      }
882

    
883
      // get all users
884
      if (isValid) {
885
        MetaCatUtil.debugMessage("\nGetting all users ....");
886
        String[] users = authservice.getUsers(user, password);
887
        MetaCatUtil.debugMessage("Users found: " + users.length);
888
        
889
      }
890

    
891
      // get the whole list groups and users in XML format
892
      if (isValid) {
893
        MetaCatUtil.debugMessage("\nTrying principals....");
894
        authservice = new AuthLdap();
895
        String out = authservice.getPrincipals(user, password);
896
        java.io.File f = new java.io.File("principals.xml");
897
        java.io.FileWriter fw = new java.io.FileWriter(f);
898
        java.io.BufferedWriter buff = new java.io.BufferedWriter(fw);
899
        buff.write(out);
900
        buff.flush();
901
        buff.close();
902
        fw.close();
903
        MetaCatUtil.debugMessage("\nFinished getting principals.");
904
      }
905

    
906
    } catch (ConnectException ce) {
907
      MetaCatUtil.debugMessage(ce.getMessage());
908
    } catch (java.io.IOException ioe) {
909
      MetaCatUtil.debugMessage("I/O Error writing to file principals.txt");
910
    }
911
  }
912
  
913
  /**
914
   * This method will be called by start a thread.
915
   * It can handle if a referral exception happend.
916
   */ 
917
  public void run()
918
  {
919
    referralContext = null;
920
    DirContext refDirContext=null;
921
    boolean moreReferrals=true;
922
    //set a while loop is because we don't know if a referral excption contains
923
    //another referral exception
924
    while (moreReferrals)
925
    {
926
      try
927
      {
928
        //revise environment variable
929
        env.put(Context.PROVIDER_URL, refExc.getReferralInfo());
930
        env.put(Context.INITIAL_CONTEXT_FACTORY, 
931
                "com.sun.jndi.ldap.LdapCtxFactory");
932
        env.put(Context.SECURITY_PRINCIPAL, userName);
933
        env.put(Context.SECURITY_CREDENTIALS, userPassword);
934
        env.put(Context.REFERRAL, "throw");
935
        //get a context object for referral in the new envriment
936
        rContext = refExc.getReferralContext(env);
937
        //casting the context to dircontext and it will create a
938
        //autherntication or naming exception if DN and password is incorrect
939
        referralContext=rContext;
940
        refDirContext=(DirContext)rContext;
941
        refDirContext.close();
942
        //get context and jump out the while loop
943
        moreReferrals=false;
944
      }//try
945
      //if referral have another referral excption
946
      catch (ReferralException re)
947
      {
948
        //keep running in while loop
949
        moreReferrals=true;
950
        //assign refExc to new referral exception re
951
        refExc=re;
952
      }
953
      //catch a authentication exception
954
      catch (AuthenticationException ae)
955
      {
956
        util.debugMessage("Error running referral handler thread: " + 
957
                          ae.getMessage());
958
        //check if has another referral
959
        moreReferrals=refExc.skipReferral();
960
        //don't get the context
961
        referralContext = null;
962
      }
963
      //catch a naming exception
964
      catch (NamingException ne)
965
      {
966
        util.debugMessage("Error running referral handler thread: " + 
967
                          ne.getMessage());
968
        //check if has another referral
969
        moreReferrals=refExc.skipReferral();
970
        //don't get context
971
        referralContext = null;		
972
      }
973
    }//while
974
  }//run()
975
}
(6-6/40)