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-02-13 17:48:51 -0800 (Wed, 13 Feb 2002) $'
13
 * '$Revision: 935 $'
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

    
485
    // Identify service provider to use
486
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
487
            "com.sun.jndi.ldap.LdapCtxFactory");
488
    env.put(Context.REFERRAL, "throw");
489
    env.put(Context.PROVIDER_URL, ldapUrl);
490
    try {
491
        // Create the initial directory context
492
        DirContext ctx = new InitialDirContext(env);
493
        // Specify the ids of the attributes to return
494
        String[] attrIDs = {"cn"};
495
        // Specify the attributes to match.
496
        // Groups are objects with attribute objectclass=groupofuniquenames.
497
        // and have attribute uniquemember: uid=foruser,ldapbase.
498
        SearchControls ctls = new SearchControls();
499
        ctls.setReturningAttributes(attrIDs);
500
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
501
        
502
        String filter = null;
503
        String gfilter = "(objectClass=groupOfUniqueNames)";
504
        if (null == foruser) {
505
            filter = gfilter;
506
        } else {
507
            filter = "(& " + gfilter + "(uniqueMember=" + foruser + "))";
508
        }
509
        NamingEnumeration enum = ctx.search(ldapBase, filter, ctls);
510

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

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

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

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

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

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

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

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

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

    
657
    return attributes;
658
  }
659

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

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

    
678
    try {
679

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

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

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

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

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

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

    
738
    return trees;
739
  }
740

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

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

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

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

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

    
819
    AuthLdap authservice = new AuthLdap();
820

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

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

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

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

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

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

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

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