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: jones $'
12
 *     '$Date: 2001-11-26 14:36:47 -0800 (Mon, 26 Nov 2001) $'
13
 * '$Revision: 873 $'
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

    
34
import javax.naming.AuthenticationException;
35
import javax.naming.Context;
36
import javax.naming.NamingEnumeration;
37
import javax.naming.NamingException;
38
import javax.naming.SizeLimitExceededException;
39
import javax.naming.InitialContext;
40
import javax.naming.directory.InvalidSearchFilterException;
41
import javax.naming.directory.Attribute;
42
import javax.naming.directory.Attributes;
43
import javax.naming.directory.BasicAttribute;
44
import javax.naming.directory.BasicAttributes;
45
import javax.naming.directory.DirContext;
46
import javax.naming.directory.InitialDirContext;
47
import javax.naming.directory.SearchResult;
48
import javax.naming.directory.SearchControls;
49
import javax.naming.ReferralException;
50
import javax.naming.ldap.*;
51
import java.util.Iterator;
52
import java.util.HashMap;
53
import java.util.Hashtable;
54
import java.util.Enumeration;
55
import java.util.Set;
56
import java.util.Vector;
57

    
58
/**
59
 * An implementation of the AuthInterface interface that
60
 * allows Metacat to use the LDAP protocol for directory services.
61
 * The LDAP authentication service is used to determine if a user 
62
 * is authenticated, and whether they are a member of a particular group.
63
 */
64
public class AuthLdap implements AuthInterface {
65
  
66
  private MetaCatUtil util = new MetaCatUtil();
67
  private String ldapUrl;
68
  private String ldapsUrl;
69
  private String ldapBase;
70
  private String referral;
71

    
72
  /** 
73
   * Construct an AuthLdap
74
   */
75
  public AuthLdap() {
76

    
77
    // Read LDAP URI for directory service information
78
    this.ldapUrl = MetaCatUtil.getOption("ldapurl");
79
    this.ldapsUrl = MetaCatUtil.getOption("ldapsurl");
80
    this.ldapBase = MetaCatUtil.getOption("ldapbase");
81
    this.referral = MetaCatUtil.getOption("referral");
82
    //this.referral = "ignore";
83
    //System.out.println("LDAPBASE is: " + ldapBase);
84
  }
85

    
86
  /**
87
   * Determine if a user/password are valid according to the authentication
88
   * service.
89
   *
90
   * @param user the name of the principal to authenticate
91
   * @param password the password to use for authentication
92
   * @returns boolean true if authentication successful, false otherwise
93
   */
94
  public boolean authenticate(String user, String password)
95
                    throws ConnectException
96
  {
97
    System.out.println("ldap authenticating");
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
   
106
        // Check the usename as passed in
107
        authenticated = ldapAuthenticate(identifier, password);
108

    
109
        // if not found, try looking up a valid DN then auth again
110
        if (!authenticated) {
111
            identifier = getIdentifyingName(identifier,ldapUrl,ldapBase);
112
            authenticated = ldapAuthenticate(identifier+","+ldapBase, password);
113
        }
114

    
115
    } catch (NullPointerException e) {
116
      util.debugMessage("NullPointerException b' password is null");
117
      util.debugMessage("NullPointerException while authenticating in " + 
118
                        "AuthLdap.authenticate: " + e);
119
      throw new ConnectException(
120
      "NullPointerException while authenticating in " + 
121
                        "AuthLdap.authenticate: " + e);
122
    } catch (NamingException e) {
123
      util.debugMessage("Naming exception while authenticating in " + 
124
                        "AuthLdap.authenticate: " + e);
125
      e.printStackTrace();
126
    } catch (Exception e) {
127
      System.out.println(e.getMessage());
128
    }
129

    
130
    return authenticated;
131
  }
132

    
133
  /**
134
   * Connect to the LDAP directory and do the authentication using the
135
   * username and password as passed into the routine.
136
   *
137
   * @param identifier the distinguished name to check against LDAP
138
   * @param password the password for authentication
139
   */
140
  private boolean ldapAuthenticate(String identifier, String password)
141
            throws ConnectException, NamingException, NullPointerException
142
  {
143
    double totStartTime = System.currentTimeMillis();
144
    boolean authenticated = false;
145
    if (identifier != null && !password.equals("")) {
146
    
147
        // Identify service provider to use
148
        Hashtable env = new Hashtable(11);
149
        env.put(Context.INITIAL_CONTEXT_FACTORY, 
150
            "com.sun.jndi.ldap.LdapCtxFactory");
151

    
152
        //System.out.println("referral: " + referral);
153
        // Now that we have the dn, we can authenticate, so
154
        // authenticate this time when opening the DirContext
155
        //System.out.println("referral=throw");
156
        env.put(Context.REFERRAL, "throw");
157
        /*CB:  Note that the above env.put statement does not use the referral 
158
          variable.  it is hard coded to 'throw'.  Matt: Is it ok to do this
159
          only here and not in every method?
160
        */
161
        //System.out.println("ldapsUrl: " + ldapsUrl + " ldapBase: " + ldapBase);
162
        env.put(Context.PROVIDER_URL, ldapsUrl + ldapBase);
163
        if ( !ldapsUrl.equals(ldapUrl) ) {
164
          // ldap is set on default port 389
165
          // ldaps is set on second port - 636 by default
166
          env.put(Context.SECURITY_PROTOCOL, "ssl");
167
        }
168
        env.put(Context.SECURITY_AUTHENTICATION, "simple");
169
        env.put(Context.SECURITY_PRINCIPAL, identifier);
170
        //System.out.println("Trying DN: " + identifier);
171
        env.put(Context.SECURITY_CREDENTIALS, password);
172
        // If our auth credentials are invalid, an exception will be thrown
173
        DirContext ctx = null;
174
        try {
175
          double startTime = System.currentTimeMillis();
176
          ctx = new InitialDirContext(env);
177
//          // StartTLS support from LDAPv3 with X.509 cert and with JSDKv1.4+
178
//          LdapContext ctx = new InitialLdapContext(env, null);
179
//          StartTlsResponse tls =
180
//            (StartTlsResponse)ctx.extendedOperation(new StartTlsRequest());
181
//          tls.negotiate();
182
          double stopTime = System.currentTimeMillis();
183
          System.out.println("Connection time thru " + ldapsUrl + " was: " +
184
                             (stopTime-startTime)/1000 + " seconds.");
185
          authenticated = true;
186
          //tls.close();
187
          ctx.close();
188
          this.ldapUrl = ldapUrl;
189
          this.ldapBase = ldapBase;
190
          //break;
191
        } catch (AuthenticationException ae) {
192
          authenticated = false;
193
//          if ( tls != null ) {
194
//            tls.close();
195
//          }
196
          if ( ctx != null ) {
197
            ctx.close();
198
          }
199
        } 
200
        catch (javax.naming.InvalidNameException ine) 
201
        {
202
            System.out.println("An invalid DN was provided!");
203
        } 
204
        catch(javax.naming.ReferralException re) 
205
        {
206
	        try
207
          {
208
            Context c = handleReferral(env, re);
209
            authenticated = true;
210
          }
211
          catch(Exception e)
212
          {
213
            authenticated = false;
214
          }
215
        }
216
    } else { 
217
        util.debugMessage("User not found");
218
    }
219
    double totStopTime = System.currentTimeMillis();
220
    System.out.println("total ldap authentication time: " + 
221
                      (totStopTime - totStartTime)/1000 + " seconds");
222
    return authenticated;
223
  }
224
  
225
  /**
226
   * handles a referral exception.  this method should be called from
227
   * within the catch statement of a ReferralException 
228
   */
229
  private Context handleReferral(Hashtable env, ReferralException re)
230
                  throws Exception 
231
  {
232
    System.out.println("referral to : " + re.getReferralInfo().toString());
233
    boolean referralSuccess = false;
234
    while(referralSuccess != true)
235
    {
236
      try
237
      {
238
        /*
239
         Matt, I think this is right but I'm not sure...please check me to make
240
         sure I didn't do something wrong here.
241
        */
242
        double refStartTime = System.currentTimeMillis();
243
        Context refctx = re.getReferralContext(env);
244
        referralSuccess = true;
245
        refctx.close();
246
        this.ldapUrl = ldapUrl;
247
        this.ldapBase = ldapBase;
248
        double refStopTime = System.currentTimeMillis();
249
        System.out.println("total referral time: " + 
250
                          (refStopTime - refStartTime)/1000 + " seconds");
251
        return refctx;
252
      }
253
      catch(ReferralException e)
254
      {
255
          System.out.println("Referring to: " + 
256
                             re.getReferralInfo().toString());
257
      }
258
      catch(Exception e)
259
      {
260
        throw e;
261
      }
262
    }
263
    return null; //this should never get called
264
  }
265

    
266
  /**
267
   * Get the identifying name for a given userid or name.  This is the name
268
   * that is used in conjunction withthe LDAP BaseDN to create a
269
   * distinguished name (dn) for the record
270
   *
271
   * @param user the user for which the identifying name is requested
272
   * @returns String the identifying name for the user, 
273
   *          or null if not found
274
   */
275
  private String getIdentifyingName(String user, String ldapUrl, String ldapBase) 
276
         throws NamingException
277
  {
278
    String identifier = null;
279

    
280
    // Identify service provider to use
281
    Hashtable env = new Hashtable(11);
282
    env.put(Context.INITIAL_CONTEXT_FACTORY,
283
            "com.sun.jndi.ldap.LdapCtxFactory");
284
    util.debugMessage("setting referrals to: " + referral);
285
    env.put(Context.REFERRAL, referral);
286
    env.put(Context.PROVIDER_URL, ldapUrl + ldapBase);
287
    //    non-secure LDAP context; dn are publicly readable
288
    //    env.put(Context.SECURITY_PROTOCOL, "ssl");
289
    try {
290
      
291
      // Bind to the LDAP server, in order to search for the right
292
      // distinguished name (dn) based on userid (uid) or common name (cn)
293
      DirContext ctx = new InitialDirContext(env);
294
      SearchControls ctls = new SearchControls();
295
      ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
296
      // Search for the user id or name using the uid, then cn and sn attributes
297
      // If we find a record, determine the dn for the record
298
      //System.out.println("Starting search phase...");
299

    
300
      String filter = "(" + user + ")";
301
      NamingEnumeration answer;
302
      try {
303
        answer = ctx.search("", filter, ctls);
304
        if (answer.hasMore()) {
305
          SearchResult sr = (SearchResult)answer.next();
306
          identifier = sr.getName();
307
          if ( !sr.isRelative() ) { 
308
            this.ldapUrl = identifier.substring(0,identifier.lastIndexOf("/")+1);
309
            this.ldapBase = identifier.substring(identifier.indexOf(",")+1);
310
            identifier = identifier.substring(identifier.lastIndexOf("/")+1,
311
                                              identifier.indexOf(","));
312
          }
313
          util.debugMessage("Found: " + identifier);
314
          return identifier;
315
        }
316
      } catch (InvalidSearchFilterException e) {}
317
      filter = "(uid=" + user + ")";
318
      answer = ctx.search("", filter, ctls);
319
      if (answer.hasMore()) {
320
        SearchResult sr = (SearchResult)answer.next();
321
        identifier = sr.getName();
322
        if ( !sr.isRelative() ) { 
323
          this.ldapUrl = identifier.substring(0,identifier.lastIndexOf("/")+1);
324
          this.ldapBase = identifier.substring(identifier.indexOf(",")+1);
325
          identifier = identifier.substring(identifier.lastIndexOf("/")+1,
326
                                            identifier.indexOf(","));
327
        }
328
        util.debugMessage("Found: " + identifier);
329
      } else {
330
        //Attributes matchAttrs2 = new BasicAttributes(true);
331
        //matchAttrs2.put(new BasicAttribute("cn", user));
332
        //NamingEnumeration answer2 = ctx.search("", matchAttrs2);
333
        filter = "(cn=" + user + ")";
334
        NamingEnumeration answer2 = ctx.search("", filter, ctls);
335
        if (answer2.hasMore()) {
336
          SearchResult sr = (SearchResult)answer2.next();
337
          identifier = sr.getName();
338
          if ( !sr.isRelative() ) { 
339
            this.ldapUrl = identifier.substring(0,identifier.lastIndexOf("/")+1);
340
            this.ldapBase = identifier.substring(identifier.indexOf(",")+1);
341
            identifier = identifier.substring(identifier.lastIndexOf("/")+1,
342
                                              identifier.indexOf(","));
343
          }
344
          util.debugMessage("Found: " + identifier);
345
        } else {
346
          //Attributes matchAttrs3 = new BasicAttributes(true);
347
          //matchAttrs3.put(new BasicAttribute("sn", user));
348
          //NamingEnumeration answer3 = ctx.search("", matchAttrs3);
349
          filter = "(sn=" + user + ")";
350
          NamingEnumeration answer3 = ctx.search("", filter, ctls);
351
          if (answer3.hasMore()) {
352
            SearchResult sr = (SearchResult)answer3.next();
353
            identifier = sr.getName();
354
            if ( !sr.isRelative() ) { 
355
              this.ldapUrl = identifier.substring(0,identifier.lastIndexOf("/")+1);
356
              this.ldapBase = identifier.substring(identifier.indexOf(",")+1);
357
              identifier = identifier.substring(identifier.lastIndexOf("/")+1,
358
                                                identifier.indexOf(","));
359
            }
360
            util.debugMessage("Found: " + identifier);
361
          }
362
        }
363
      }
364
      // Close the context when we're done the initial search
365
      ctx.close();
366
    } catch (NamingException e) {
367
      util.debugMessage("Naming exception while getting dn: " + e);
368
      throw new NamingException(
369
      "Naming exception in AuthLdap.getIdentifyingName: " + e);
370
    }
371
    return identifier;
372
  }
373

    
374
  /**
375
   * Get all users from the authentication service
376
   *
377
   * @param user the user for authenticating against the service
378
   * @param password the password for authenticating against the service
379
   * @returns string array of all of the user names
380
   */
381
  public String[] getUsers(String user, String password) 
382
         throws ConnectException
383
  {
384
    String[] users = null;
385

    
386
    // Identify service provider to use
387
    Hashtable env = new Hashtable(11);
388
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
389
            "com.sun.jndi.ldap.LdapCtxFactory");
390
    env.put(Context.REFERRAL, referral);
391
    env.put(Context.PROVIDER_URL, ldapUrl);
392
    //env.put(Context.BATCHSIZE, "500");
393

    
394
    try {
395

    
396
        // Create the initial directory context
397
        DirContext ctx = new InitialDirContext(env);
398

    
399
        // Specify the attributes to match.
400
        // Users are objects that have the attribute objectclass=InetOrgPerson.
401
        SearchControls ctls = new SearchControls();
402
        String[] attrIDs = {"dn"};
403
        ctls.setReturningAttributes(attrIDs);
404
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
405
        //ctls.setCountLimit(1000);
406
        String filter = "(objectClass=inetOrgPerson)";
407
        NamingEnumeration enum = ctx.search(ldapBase, filter, ctls);
408
        
409
        // Store the users in a vector
410
        Vector uvec = new Vector();
411
        try {
412
            while (enum.hasMore()) {
413
                SearchResult sr = (SearchResult)enum.next();
414
                uvec.add(sr.getName()+","+ldapBase);
415
            }
416
        } catch (SizeLimitExceededException slee) {
417
            util.debugMessage("LDAP Server size limit exceeded. " +
418
                    "Returning incomplete record set.");
419
        }
420

    
421
        // initialize users[]; fill users[]
422
        users = new String[uvec.size()];
423
        for (int i=0; i < uvec.size(); i++) {
424
          users[i] = (String)uvec.elementAt(i); 
425
        }
426

    
427
        // Close the context when we're done
428
        ctx.close();
429

    
430
    } catch (NamingException e) {
431
      System.err.println("Problem getting users in AuthLdap.getUsers:" + e);
432
      e.printStackTrace(System.err);
433
      throw new ConnectException(
434
      "Problem getting users in AuthLdap.getUsers:" + e);
435
    }
436

    
437
    return users;
438
  }
439

    
440
  /**
441
   * Get the users for a particular group from the authentication service
442
   *
443
   * @param user the user for authenticating against the service
444
   * @param password the password for authenticating against the service
445
   * @param group the group whose user list should be returned
446
   * @returns string array of the user names belonging to the group
447
   */
448
  public String[] getUsers(String user, String password, String group) 
449
         throws ConnectException
450
  {
451
    String[] users = null;
452

    
453
    // Identify service provider to use
454
    Hashtable env = new Hashtable(11);
455
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
456
            "com.sun.jndi.ldap.LdapCtxFactory");
457
    env.put(Context.REFERRAL, referral);
458
    env.put(Context.PROVIDER_URL, ldapUrl);
459

    
460
    try {
461

    
462
        // Create the initial directory context
463
        DirContext ctx = new InitialDirContext(env);
464

    
465
        // Specify the ids of the attributes to return
466
        String[] attrIDs = {"uniqueMember"};
467

    
468
        Attributes answer = ctx.getAttributes(group, attrIDs);
469

    
470
        Vector uvec = new Vector();
471
        try {
472
            for (NamingEnumeration ae = answer.getAll(); ae.hasMore();) {
473
                Attribute attr = (Attribute)ae.next();
474
                for (NamingEnumeration e = attr.getAll(); 
475
                     e.hasMore();
476
                     uvec.add(e.next()) 
477
                    );
478
            }
479
        } catch (SizeLimitExceededException slee) {
480
            util.debugMessage("LDAP Server size limit exceeded. " +
481
                    "Returning incomplete record set.");
482
        }
483

    
484
        // initialize users[]; fill users[]
485
        users = new String[uvec.size()];
486
        for (int i=0; i < uvec.size(); i++) {
487
          users[i] = (String)uvec.elementAt(i); 
488
        }
489

    
490
        // Close the context when we're done
491
        ctx.close();
492

    
493
    } catch (NamingException e) {
494
      System.err.println("Problem getting users for a group in " +
495
              "AuthLdap.getUsers:" + e);
496
      throw new ConnectException(
497
      "Problem getting users for a group in AuthLdap.getUsers:" + e);
498
    }
499

    
500
    return users;
501
  }
502

    
503
  /**
504
   * Get all groups from the authentication service
505
   *
506
   * @param user the user for authenticating against the service
507
   * @param password the password for authenticating against the service
508
   * @returns string array of the group names
509
   */
510
  public String[] getGroups(String user, String password) 
511
         throws ConnectException
512
  {
513
      return getGroups(user, password, null);
514
  }
515

    
516
  /**
517
   * Get the groups for a particular user from the authentication service
518
   *
519
   * @param user the user for authenticating against the service
520
   * @param password the password for authenticating against the service
521
   * @param foruser the user whose group list should be returned
522
   * @returns string array of the group names
523
   */
524
  public String[] getGroups(String user, String password, String foruser) 
525
         throws ConnectException
526
  {
527
    //System.err.println("GG in get groups 2");
528
    String[] groups = null;
529

    
530
    // Identify service provider to use
531
    Hashtable env = new Hashtable(11);
532
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
533
            "com.sun.jndi.ldap.LdapCtxFactory");
534
    env.put(Context.REFERRAL, referral);
535
    env.put(Context.PROVIDER_URL, ldapUrl);
536
    try {
537

    
538
        // Create the initial directory context
539
        DirContext ctx = new InitialDirContext(env);
540
        // Specify the ids of the attributes to return
541
        String[] attrIDs = {"cn"};
542
        // Specify the attributes to match.
543
        // Groups are objects with attribute objectclass=groupofuniquenames.
544
        // and have attribute uniquemember: uid=foruser,ldapbase.
545
        SearchControls ctls = new SearchControls();
546
        ctls.setReturningAttributes(attrIDs);
547
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
548
        
549
        String filter = null;
550
        String gfilter = "(objectClass=groupOfUniqueNames)";
551
        if (null == foruser) {
552
            filter = gfilter;
553
        } else {
554
            filter = "(& " + gfilter + "(uniqueMember=" + foruser + "))";
555
        }
556
        NamingEnumeration enum = ctx.search(ldapBase, filter, ctls);
557

    
558
        // Print the groups
559
        Vector uvec = new Vector();
560
        while (enum.hasMore()) {
561
          SearchResult sr = (SearchResult)enum.next();
562
          uvec.add(sr.getName()+","+ldapBase);
563
        }
564

    
565
        // initialize groups[] and fill it
566
        groups = new String[uvec.size()];
567
        for (int i=0; i < uvec.size(); i++) {
568
          groups[i] = (String)uvec.elementAt(i); 
569
        }
570

    
571
        // Close the context when we're done
572
        ctx.close();
573

    
574
    } catch (NamingException e) {
575
      System.err.println("Problem getting groups in AuthLdap.getGroups 2:" + e);
576
      e.printStackTrace(System.err);
577
      throw new ConnectException(
578
      "Problem getting groups for a user in AuthLdap.getGroups:" + e);
579
    }
580
    return groups;
581
  }
582

    
583
  /**
584
   * Get attributes describing a user or group
585
   *
586
   * @param foruser the user for which the attribute list is requested
587
   * @returns HashMap a map of attribute name to a Vector of values
588
   */
589
  public HashMap getAttributes(String foruser) 
590
         throws ConnectException
591
  {
592
    return getAttributes(null, null, foruser);
593
  }
594

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

    
623
    try {
624
      
625
      // Create the initial directory context
626
      DirContext ctx = new InitialDirContext(env);
627
        
628
      // Find out the identifying attribute for the user
629
      //userident = getIdentifyingName(userident,ldapUrl,ldapBase);
630

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

    
658
    return attributes;
659
  }
660

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

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

    
679
    try {
680

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

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

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

    
699
        // Print the subtrees' <ldapURL, baseDN>
700
        while (enum.hasMore()) {
701
          SearchResult sr = (SearchResult)enum.next();
702
          Attributes attrs = sr.getAttributes();
703
          NamingEnumeration enum1 = attrs.getAll(); // "dc" and "ref" attrs
704
          if (enum1.hasMore()) {
705
            Attribute attr = (Attribute)enum1.next();
706
            String attrValue = (String)attr.get();
707
            String attrName = (String)attr.getID();
708
            //System.out.println(attrName + "=" + attrValue);
709
            if ( enum1.hasMore() ) {
710
              attr = (Attribute)enum1.next();
711
              String refValue = (String)attr.get();
712
              String refName = (String)attr.getID();
713
              //System.out.println(refName + "=" + refValue);
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
              //System.out.println("REFERRAL:" + attrValue);
722
            } else if ( ldapBase.startsWith(attrName + "=" + attrValue) ) {
723
                trees.put(ldapBase, ldapUrl);
724
            } else {              
725
                trees.put(attrName + "=" + attrValue + "," + ldapBase, ldapUrl);
726
                //System.out.println(ldapUrl + attrName + "=" + attrValue + "," + ldapBase);
727
            }
728
          }
729
        }
730

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

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

    
740
    //System.out.println("number of subtrees:" + trees.size());
741
    return trees;
742
  }
743

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

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

    
776
      // for the groups and users that belong to them
777
      if ( groups.length > 0 ) {
778
        for (int i=0; i < groups.length; i++ ) {
779
          out.append("    <group>\n");
780
          out.append("      <groupname>" + groups[i] + "</groupname>\n");
781
          String[] usersForGroup = getUsers(user,password,groups[i]);
782
          for (int j=0; j < usersForGroup.length; j++ ) {
783
            usersIn.addElement(usersForGroup[j]);
784
            out.append("      <user>\n");
785
            out.append("        <username>" + usersForGroup[j] + "</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
    // Get the list of supported controls
825
    try {
826
        // Create initial context
827
        DirContext dctx = new InitialDirContext();
828
        
829
        // Read supportedcontrol from root DSE
830
        MetaCatUtil util = new MetaCatUtil();
831
        String ldapurl = util.getOption("ldapurl");
832
        Attributes attrs = dctx.getAttributes(
833
            ldapurl, new String[]{"supportedcontrol"});
834
        
835
        System.out.println(attrs);
836
        // Close the context when we're done
837
        dctx.close();
838
    } catch (NamingException e) {
839
        e.printStackTrace();
840
    }
841
*/               
842
                
843
    boolean isValid = false;
844
    try {
845
      isValid = authservice.authenticate(user, password);
846
      if (isValid) {
847
        System.out.println("Authentication successful for: " + user );
848
      } else {
849
        System.out.println("Authentication failed for: " + user);
850
      }
851

    
852
      // Get attributes for the user
853
      if (isValid) {
854
        System.out.println("\nGetting attributes for user....");
855
        HashMap userInfo = authservice.getAttributes(user, password, user);
856
        // Print all of the attributes
857
        Iterator attList = (Iterator)(((Set)userInfo.keySet()).iterator());
858
        while (attList.hasNext()) {
859
          String att = (String)attList.next();
860
          Vector values = (Vector)userInfo.get(att);
861
          Iterator attvalues = values.iterator();
862
          while (attvalues.hasNext()) {
863
            String value = (String)attvalues.next();
864
            System.out.println(att + ": " + value);
865
          }
866
        }
867
      }
868

    
869
      // get the groups
870
      if (isValid) {
871
        System.out.println("\nGetting all groups....");
872
        String[] groups = authservice.getGroups(user, password);
873
        System.out.println("Groups found: " + groups.length);
874
        for (int i=0; i < groups.length; i++) {
875
            System.out.println("Group " + i + ": " + groups[i]);
876
        }
877
      }
878

    
879
      // get the groups for the user
880
      String savedGroup = null;
881
      if (isValid) {
882
        System.out.println("\nGetting groups for user....");
883
        String[] groups = authservice.getGroups(user, password, user);
884
        System.out.println("Groups found: " + groups.length);
885
        for (int i=0; i < groups.length; i++) {
886
            System.out.println("Group " + i + ": " + groups[i]);
887
            savedGroup = groups[i];
888
        }
889
      }
890

    
891
      // get the users for a group
892
      if (isValid) {
893
        System.out.println("\nGetting users for group....");
894
        System.out.println("Group: " + savedGroup);
895
        String[] users = authservice.getUsers(user, password, savedGroup);
896
        System.out.println("Users found: " + users.length);
897
        for (int i=0; i < users.length; i++) {
898
            System.out.println("User " + i + ": " + users[i]);
899
        }
900
      }
901

    
902
      // get all users
903
      if (isValid) {
904
        System.out.println("\nGetting all users ....");
905
        String[] users = authservice.getUsers(user, password);
906
        System.out.println("Users found: " + users.length);
907
        for (int i=0; i < users.length; i++) {
908
            //System.out.println("User " + i + ": " + users[i]);
909
        }
910
      }
911

    
912
      // get the whole list groups and users in XML format
913
      if (isValid) {
914
        System.out.println("\nTrying principals....");
915
        authservice = new AuthLdap();
916
        String out = authservice.getPrincipals(user, password);
917
        java.io.File f = new java.io.File("principals.xml");
918
        java.io.FileWriter fw = new java.io.FileWriter(f);
919
        java.io.BufferedWriter buff = new java.io.BufferedWriter(fw);
920
        buff.write(out);
921
        buff.flush();
922
        buff.close();
923
        fw.close();
924
        System.out.println("\nFinished getting principals.");
925
      }
926

    
927
    } catch (ConnectException ce) {
928
      System.err.println(ce.getMessage());
929
    } catch (java.io.IOException ioe) {
930
      System.err.println("I/O Error writing to file principals.txt");
931
    }
932
  }
933
}
(6-6/40)