Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *    Purpose: An implementation of the AuthInterface interface that
4
 *             allows Metacat to use the LDAP protocol for
5
 *             directory services
6
 *  Copyright: 2000 Regents of the University of California and the
7
 *             National Center for Ecological Analysis and Synthesis
8
 *    Authors: Matt Jones
9
 *    Release: @release@
10
 *
11
 *   '$Author: berkley $'
12
 *     '$Date: 2002-01-15 08:46:37 -0800 (Tue, 15 Jan 2002) $'
13
 * '$Revision: 894 $'
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, Runnable {
65
  
66
  private MetaCatUtil util = new MetaCatUtil();
67
  private String ldapUrl;
68
  private String ldapsUrl;
69
  private String ldapBase;
70
  private String referral;
71
  private Context referralContext;
72
  Hashtable env = new Hashtable(11);
73
  private Context rContext;
74
  ReferralException refExc;
75

    
76
  /** 
77
   * Construct an AuthLdap
78
   */
79
  public AuthLdap() {
80

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

    
90
  /**
91
   * Determine if a user/password are valid according to the authentication
92
   * service.
93
   *
94
   * @param user the name of the principal to authenticate
95
   * @param password the password to use for authentication
96
   * @returns boolean true if authentication successful, false otherwise
97
   */
98
  public boolean authenticate(String user, String password)
99
                    throws ConnectException
100
  {
101
    System.out.println("ldap authenticating");
102
    String ldapUrl = this.ldapUrl;
103
    String ldapsUrl = this.ldapsUrl;
104
    String ldapBase = this.ldapBase;
105
    boolean authenticated = false;
106
    String identifier = user;
107

    
108
    try {
109
   
110
        // Check the usename as passed in
111
        authenticated = ldapAuthenticate(identifier, password);
112

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

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

    
134
    return authenticated;
135
  }
136

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

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

    
289
  /**
290
   * Get the identifying name for a given userid or name.  This is the name
291
   * that is used in conjunction withthe LDAP BaseDN to create a
292
   * distinguished name (dn) for the record
293
   *
294
   * @param user the user for which the identifying name is requested
295
   * @returns String the identifying name for the user, 
296
   *          or null if not found
297
   */
298
  private String getIdentifyingName(String user, String ldapUrl, String ldapBase) 
299
         throws NamingException
300
  {
301
    String identifier = null;
302

    
303
    // Identify service provider to use
304
    Hashtable env = new Hashtable(11);
305
    env.put(Context.INITIAL_CONTEXT_FACTORY,
306
            "com.sun.jndi.ldap.LdapCtxFactory");
307
    util.debugMessage("setting referrals to: " + referral);
308
    env.put(Context.REFERRAL, referral);
309
    env.put(Context.PROVIDER_URL, ldapUrl + ldapBase);
310
    //    non-secure LDAP context; dn are publicly readable
311
    //    env.put(Context.SECURITY_PROTOCOL, "ssl");
312
    try {
313
      
314
      // Bind to the LDAP server, in order to search for the right
315
      // distinguished name (dn) based on userid (uid) or common name (cn)
316
      DirContext ctx = new InitialDirContext(env);
317
      SearchControls ctls = new SearchControls();
318
      ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
319
      // Search for the user id or name using the uid, then cn and sn attributes
320
      // If we find a record, determine the dn for the record
321
      //System.out.println("Starting search phase...");
322

    
323
      String filter = "(" + user + ")";
324
      NamingEnumeration answer;
325
      try {
326
        answer = ctx.search("", filter, ctls);
327
        if (answer.hasMore()) {
328
          SearchResult sr = (SearchResult)answer.next();
329
          identifier = sr.getName();
330
          if ( !sr.isRelative() ) { 
331
            this.ldapUrl = identifier.substring(0,identifier.lastIndexOf("/")+1);
332
            this.ldapBase = identifier.substring(identifier.indexOf(",")+1);
333
            identifier = identifier.substring(identifier.lastIndexOf("/")+1,
334
                                              identifier.indexOf(","));
335
          }
336
          util.debugMessage("Found: " + identifier);
337
          return identifier;
338
        }
339
      } catch (InvalidSearchFilterException e) {}
340
      filter = "(uid=" + user + ")";
341
      answer = ctx.search("", filter, ctls);
342
      if (answer.hasMore()) {
343
        SearchResult sr = (SearchResult)answer.next();
344
        identifier = sr.getName();
345
        if ( !sr.isRelative() ) { 
346
          this.ldapUrl = identifier.substring(0,identifier.lastIndexOf("/")+1);
347
          this.ldapBase = identifier.substring(identifier.indexOf(",")+1);
348
          identifier = identifier.substring(identifier.lastIndexOf("/")+1,
349
                                            identifier.indexOf(","));
350
        }
351
        util.debugMessage("Found: " + identifier);
352
      } else {
353
        //Attributes matchAttrs2 = new BasicAttributes(true);
354
        //matchAttrs2.put(new BasicAttribute("cn", user));
355
        //NamingEnumeration answer2 = ctx.search("", matchAttrs2);
356
        filter = "(cn=" + user + ")";
357
        NamingEnumeration answer2 = ctx.search("", filter, ctls);
358
        if (answer2.hasMore()) {
359
          SearchResult sr = (SearchResult)answer2.next();
360
          identifier = sr.getName();
361
          if ( !sr.isRelative() ) { 
362
            this.ldapUrl = identifier.substring(0,identifier.lastIndexOf("/")+1);
363
            this.ldapBase = identifier.substring(identifier.indexOf(",")+1);
364
            identifier = identifier.substring(identifier.lastIndexOf("/")+1,
365
                                              identifier.indexOf(","));
366
          }
367
          util.debugMessage("Found: " + identifier);
368
        } else {
369
          //Attributes matchAttrs3 = new BasicAttributes(true);
370
          //matchAttrs3.put(new BasicAttribute("sn", user));
371
          //NamingEnumeration answer3 = ctx.search("", matchAttrs3);
372
          filter = "(sn=" + user + ")";
373
          NamingEnumeration answer3 = ctx.search("", filter, ctls);
374
          if (answer3.hasMore()) {
375
            SearchResult sr = (SearchResult)answer3.next();
376
            identifier = sr.getName();
377
            if ( !sr.isRelative() ) { 
378
              this.ldapUrl = identifier.substring(0,identifier.lastIndexOf("/")+1);
379
              this.ldapBase = identifier.substring(identifier.indexOf(",")+1);
380
              identifier = identifier.substring(identifier.lastIndexOf("/")+1,
381
                                                identifier.indexOf(","));
382
            }
383
            util.debugMessage("Found: " + identifier);
384
          }
385
        }
386
      }
387
      // Close the context when we're done the initial search
388
      ctx.close();
389
    } catch (NamingException e) {
390
      util.debugMessage("Naming exception while getting dn: " + e);
391
      throw new NamingException(
392
      "Naming exception in AuthLdap.getIdentifyingName: " + e);
393
    }
394
    return identifier;
395
  }
396

    
397
  /**
398
   * Get all users from the authentication service
399
   *
400
   * @param user the user for authenticating against the service
401
   * @param password the password for authenticating against the service
402
   * @returns string array of all of the user names
403
   */
404
  public String[] getUsers(String user, String password) 
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
    //env.put(Context.BATCHSIZE, "500");
416

    
417
    try {
418

    
419
        // Create the initial directory context
420
        DirContext ctx = new InitialDirContext(env);
421

    
422
        // Specify the attributes to match.
423
        // Users are objects that have the attribute objectclass=InetOrgPerson.
424
        SearchControls ctls = new SearchControls();
425
        String[] attrIDs = {"dn"};
426
        ctls.setReturningAttributes(attrIDs);
427
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
428
        //ctls.setCountLimit(1000);
429
        String filter = "(objectClass=inetOrgPerson)";
430
        NamingEnumeration enum = ctx.search(ldapBase, filter, ctls);
431
        
432
        // Store the users in a vector
433
        Vector uvec = new Vector();
434
        try {
435
            while (enum.hasMore()) {
436
                SearchResult sr = (SearchResult)enum.next();
437
                uvec.add(sr.getName()+","+ldapBase);
438
            }
439
        } catch (SizeLimitExceededException slee) {
440
            util.debugMessage("LDAP Server size limit exceeded. " +
441
                    "Returning incomplete record set.");
442
        }
443

    
444
        // initialize users[]; fill users[]
445
        users = new String[uvec.size()];
446
        for (int i=0; i < uvec.size(); i++) {
447
          users[i] = (String)uvec.elementAt(i); 
448
        }
449

    
450
        // Close the context when we're done
451
        ctx.close();
452

    
453
    } catch (NamingException e) {
454
      System.err.println("Problem getting users in AuthLdap.getUsers:" + e);
455
      e.printStackTrace(System.err);
456
      throw new ConnectException(
457
      "Problem getting users in AuthLdap.getUsers:" + e);
458
    }
459

    
460
    return users;
461
  }
462

    
463
  /**
464
   * Get the users for a particular group from the authentication service
465
   *
466
   * @param user the user for authenticating against the service
467
   * @param password the password for authenticating against the service
468
   * @param group the group whose user list should be returned
469
   * @returns string array of the user names belonging to the group
470
   */
471
  public String[] getUsers(String user, String password, String group) 
472
         throws ConnectException
473
  {
474
    String[] users = null;
475

    
476
    // Identify service provider to use
477
    Hashtable env = new Hashtable(11);
478
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
479
            "com.sun.jndi.ldap.LdapCtxFactory");
480
    env.put(Context.REFERRAL, referral);
481
    env.put(Context.PROVIDER_URL, ldapUrl);
482

    
483
    try {
484

    
485
        // Create the initial directory context
486
        DirContext ctx = new InitialDirContext(env);
487

    
488
        // Specify the ids of the attributes to return
489
        String[] attrIDs = {"uniqueMember"};
490

    
491
        Attributes answer = ctx.getAttributes(group, attrIDs);
492

    
493
        Vector uvec = new Vector();
494
        try {
495
            for (NamingEnumeration ae = answer.getAll(); ae.hasMore();) {
496
                Attribute attr = (Attribute)ae.next();
497
                for (NamingEnumeration e = attr.getAll(); 
498
                     e.hasMore();
499
                     uvec.add(e.next()) 
500
                    );
501
            }
502
        } catch (SizeLimitExceededException slee) {
503
            util.debugMessage("LDAP Server size limit exceeded. " +
504
                    "Returning incomplete record set.");
505
        }
506

    
507
        // initialize users[]; fill users[]
508
        users = new String[uvec.size()];
509
        for (int i=0; i < uvec.size(); i++) {
510
          users[i] = (String)uvec.elementAt(i); 
511
        }
512

    
513
        // Close the context when we're done
514
        ctx.close();
515

    
516
    } catch (NamingException e) {
517
      System.err.println("Problem getting users for a group in " +
518
              "AuthLdap.getUsers:" + e);
519
      throw new ConnectException(
520
      "Problem getting users for a group in AuthLdap.getUsers:" + e);
521
    }
522

    
523
    return users;
524
  }
525

    
526
  /**
527
   * Get all groups from the authentication service
528
   *
529
   * @param user the user for authenticating against the service
530
   * @param password the password for authenticating against the service
531
   * @returns string array of the group names
532
   */
533
  public String[] getGroups(String user, String password) 
534
         throws ConnectException
535
  {
536
      return getGroups(user, password, null);
537
  }
538

    
539
  /**
540
   * Get the groups for a particular user from the authentication service
541
   *
542
   * @param user the user for authenticating against the service
543
   * @param password the password for authenticating against the service
544
   * @param foruser the user whose group list should be returned
545
   * @returns string array of the group names
546
   */
547
  public String[] getGroups(String user, String password, String foruser) 
548
         throws ConnectException
549
  {
550
    //System.err.println("GG in get groups 2");
551
    String[] groups = null;
552

    
553
    // Identify service provider to use
554
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
555
            "com.sun.jndi.ldap.LdapCtxFactory");
556
    env.put(Context.REFERRAL, "throw");
557
    env.put(Context.PROVIDER_URL, ldapUrl);
558
    try {
559
        // Create the initial directory context
560
        DirContext ctx = new InitialDirContext(env);
561
        // Specify the ids of the attributes to return
562
        String[] attrIDs = {"cn"};
563
        // Specify the attributes to match.
564
        // Groups are objects with attribute objectclass=groupofuniquenames.
565
        // and have attribute uniquemember: uid=foruser,ldapbase.
566
        SearchControls ctls = new SearchControls();
567
        ctls.setReturningAttributes(attrIDs);
568
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
569
        
570
        String filter = null;
571
        String gfilter = "(objectClass=groupOfUniqueNames)";
572
        if (null == foruser) {
573
            filter = gfilter;
574
        } else {
575
            filter = "(& " + gfilter + "(uniqueMember=" + foruser + "))";
576
        }
577
        NamingEnumeration enum = ctx.search(ldapBase, filter, ctls);
578

    
579
        // Print the groups
580
        Vector uvec = new Vector();
581
        while (enum.hasMore()) {
582
          SearchResult sr = (SearchResult)enum.next();
583
          uvec.add(sr.getName()+","+ldapBase);
584
        }
585
      
586

    
587
        // initialize groups[] and fill it
588
        groups = new String[uvec.size()];
589
        for (int i=0; i < uvec.size(); i++) {
590
          groups[i] = (String)uvec.elementAt(i); 
591
        }
592

    
593
        // Close the context when we're done
594
        ctx.close();
595

    
596
    } catch(ReferralException re) {
597
      System.out.println("caught a referral to " + re.toString());
598
      try
599
      {
600
        refExc = re;
601
        Thread t = new Thread(this);
602
        System.out.println("Starting thread...");
603
        t.start();
604
        System.out.println("sleeping for 5 seconds.");
605
        Thread.sleep(5000); //this is a manual override of ldap's hideously long time
606
                     //out period.
607
        System.out.println("Awake after 5 seconds.");
608
        if(referralContext == null)
609
        {
610
          System.out.println("killing thread....returning null.");
611
          t.interrupt();
612
          System.out.println("thread killed.");
613
          return null;
614
        }
615
        DirContext dc = (DirContext)referralContext;
616
        String[] attrIDs = {"cn"};
617
        // Specify the attributes to match.
618
        // Groups are objects with attribute objectclass=groupofuniquenames.
619
        // and have attribute uniquemember: uid=foruser,ldapbase.
620
        SearchControls ctls = new SearchControls();
621
        ctls.setReturningAttributes(attrIDs);
622
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
623
        
624
        String filter = null;
625
        String gfilter = "(objectClass=groupOfUniqueNames)";
626
        if (null == foruser) {
627
            filter = gfilter;
628
        } else {
629
            filter = "(& " + gfilter + "(uniqueMember=" + foruser + "))";
630
        }
631
        NamingEnumeration enum = dc.search(ldapBase, filter, ctls);
632

    
633
        // Print the groups
634
        Vector uvec = new Vector();
635
        while (enum.hasMore()) {
636
          SearchResult sr = (SearchResult)enum.next();
637
          uvec.add(sr.getName()+","+ldapBase);
638
        }
639

    
640
        // initialize groups[] and fill it
641
        groups = new String[uvec.size()];
642
        for (int i=0; i < uvec.size(); i++) {
643
          groups[i] = (String)uvec.elementAt(i); 
644
        }
645
        referralContext.close();
646
        dc.close();
647
      }
648
      catch(Exception e)
649
      {
650
        System.out.println("returning groups as null");
651
        return groups;
652
      }
653
    } catch (NamingException e) {
654
      System.err.println("Problem getting groups in AuthLdap.getGroups 2:" + e);
655
      e.printStackTrace(System.err);
656
      throw new ConnectException(
657
      "Problem getting groups for a user in AuthLdap.getGroups:" + e);
658
    } 
659
    return groups;
660
  }
661

    
662
  /**
663
   * Get attributes describing a user or group
664
   *
665
   * @param foruser the user for which the attribute list is requested
666
   * @returns HashMap a map of attribute name to a Vector of values
667
   */
668
  public HashMap getAttributes(String foruser) 
669
         throws ConnectException
670
  {
671
    return getAttributes(null, null, foruser);
672
  }
673

    
674
  /**
675
   * Get attributes describing a user or group
676
   *
677
   * @param user the user for authenticating against the service
678
   * @param password the password for authenticating against the service
679
   * @param foruser the user whose attributes should be returned
680
   * @returns HashMap a map of attribute name to a Vector of values
681
   */
682
  public HashMap getAttributes(String user, String password, String foruser) 
683
         throws ConnectException
684
  {
685
    HashMap attributes = new HashMap();
686
    String ldapUrl = this.ldapUrl;
687
    String ldapBase = this.ldapBase;
688
    String userident = foruser;
689
    /*
690
    try { 
691
      this.ldapBase = userident.substring(userident.indexOf(",")+1);
692
      userident = userident.substring(0,userident.indexOf(","));
693
    } catch (StringIndexOutOfBoundsException e) {}
694
*/
695
    // Identify service provider to use
696
    Hashtable env = new Hashtable(11);
697
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
698
        "com.sun.jndi.ldap.LdapCtxFactory");
699
    env.put(Context.REFERRAL, referral);
700
    env.put(Context.PROVIDER_URL, ldapUrl);
701

    
702
    try {
703
      
704
      // Create the initial directory context
705
      DirContext ctx = new InitialDirContext(env);
706
        
707
      // Find out the identifying attribute for the user
708
      //userident = getIdentifyingName(userident,ldapUrl,ldapBase);
709

    
710
      // Ask for all attributes of the user 
711
      //Attributes attrs = ctx.getAttributes(userident);
712
      Attributes attrs = ctx.getAttributes(foruser);
713
 
714
      // Print all of the attributes
715
      NamingEnumeration en = attrs.getAll();
716
      while (en.hasMore()) {
717
        Attribute att = (Attribute)en.next();
718
        Vector values = new Vector();
719
        String attName = att.getID();
720
        NamingEnumeration attvalues = att.getAll();
721
        while (attvalues.hasMore()) {
722
          String value = (String)attvalues.next();
723
          values.add(value);
724
        }
725
        attributes.put(attName, values);
726
      }
727
  
728
      // Close the context when we're done
729
      ctx.close();
730
    } catch (NamingException e) {
731
      System.err.println("Problem getting attributes in " + 
732
              "AuthLdap.getAttributes:" + e);
733
      throw new ConnectException(
734
      "Problem getting attributes in AuthLdap.getAttributes:" + e);
735
    }
736

    
737
    return attributes;
738
  }
739

    
740
  /**
741
   * Get list of all subtrees holding Metacat's groups and users
742
   * starting from the Metacat LDAP root, 
743
   * i.e. ldap://dev.nceas.ucsb.edu/dc=ecoinformatics,dc=org
744
   */
745
  private Hashtable getSubtrees(String user, String password,
746
                                String ldapUrl, String ldapBase)
747
                throws ConnectException
748
  {
749
    Hashtable trees = new Hashtable();
750

    
751
    // Identify service provider to use
752
    Hashtable env = new Hashtable(11);
753
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
754
            "com.sun.jndi.ldap.LdapCtxFactory");
755
    env.put(Context.REFERRAL, referral);
756
    env.put(Context.PROVIDER_URL, ldapUrl + ldapBase);
757

    
758
    try {
759

    
760
        // Create the initial directory context
761
        DirContext ctx = new InitialDirContext(env);
762

    
763
        // Specify the ids of the attributes to return
764
        String[] attrIDs = {"o","ref"};
765
        SearchControls ctls = new SearchControls();
766
        ctls.setReturningAttributes(attrIDs);
767
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
768
          
769
        // Specify the attributes to match.
770
        // Subtrees from the main server are found as objects with attribute
771
        // objectclass=organization or objectclass=referral to the subtree
772
        // resided on other server.
773
        String filter = "(|(objectclass=organization)(objectclass=referral))";
774

    
775
        // Search for objects in the current context
776
        NamingEnumeration enum = ctx.search("", filter, ctls);
777

    
778
        // Print the subtrees' <ldapURL, baseDN>
779
        while (enum.hasMore()) {
780
          SearchResult sr = (SearchResult)enum.next();
781
          Attributes attrs = sr.getAttributes();
782
          NamingEnumeration enum1 = attrs.getAll(); // "dc" and "ref" attrs
783
          if (enum1.hasMore()) {
784
            Attribute attr = (Attribute)enum1.next();
785
            String attrValue = (String)attr.get();
786
            String attrName = (String)attr.getID();
787
            //System.out.println(attrName + "=" + attrValue);
788
            if ( enum1.hasMore() ) {
789
              attr = (Attribute)enum1.next();
790
              String refValue = (String)attr.get();
791
              String refName = (String)attr.getID();
792
              //System.out.println(refName + "=" + refValue);
793
              if ( ldapBase.startsWith(refName + "=" + refValue) ) {
794
                trees.put(ldapBase,
795
                          attrValue.substring(0,attrValue.lastIndexOf("/")+1) );
796
              } else {
797
                trees.put(refName + "=" + refValue + "," + ldapBase,
798
                          attrValue.substring(0,attrValue.lastIndexOf("/")+1) );
799
              }
800
              //System.out.println("REFERRAL:" + attrValue);
801
            } else if ( ldapBase.startsWith(attrName + "=" + attrValue) ) {
802
                trees.put(ldapBase, ldapUrl);
803
            } else {              
804
                trees.put(attrName + "=" + attrValue + "," + ldapBase, ldapUrl);
805
                //System.out.println(ldapUrl + attrName + "=" + attrValue + "," + ldapBase);
806
            }
807
          }
808
        }
809

    
810
        // Close the context when we're done
811
        ctx.close();
812

    
813
    } catch (NamingException e) {
814
      System.err.println("Problem getting subtrees in AuthLdap.getSubtrees:" + e);
815
      throw new ConnectException(
816
      "Problem getting subtrees in AuthLdap.getSubtrees:" + e);
817
    }
818

    
819
    //System.out.println("number of subtrees:" + trees.size());
820
    return trees;
821
  }
822

    
823
  /**
824
   * Get all groups and users from authentication scheme.
825
   * The output is formatted in XML.
826
   * @param user the user which requests the information
827
   * @param password the user's password
828
   */
829
  public String getPrincipals(String user, String password)
830
                throws ConnectException
831
  {
832
    StringBuffer out = new StringBuffer();
833
    Vector usersIn = new Vector();
834
    
835
    out.append("<?xml version=\"1.0\"?>\n");
836
    out.append("<principals>\n");
837
    
838
    /*
839
     * get all subtrees first in the current dir context 
840
     * and then the Metacat users under them
841
     */
842
    Hashtable subtrees = getSubtrees(user,password,this.ldapUrl,this.ldapBase);
843
    
844
    Enumeration enum = subtrees.keys();
845
    while ( enum.hasMoreElements() ) {
846
      this.ldapBase = (String)enum.nextElement();
847
      this.ldapUrl = (String)subtrees.get(ldapBase);
848
      
849
      out.append("  <authSystem URI=\"" + 
850
                 this.ldapUrl + this.ldapBase + "\">\n");
851

    
852
      // get all groups for directory context
853
      String[] groups = getGroups(user, password);
854

    
855
      // for the groups and users that belong to them
856
      if ( groups.length > 0 ) {
857
        for (int i=0; i < groups.length; i++ ) {
858
          out.append("    <group>\n");
859
          out.append("      <groupname>" + groups[i] + "</groupname>\n");
860
          String[] usersForGroup = getUsers(user,password,groups[i]);
861
          for (int j=0; j < usersForGroup.length; j++ ) {
862
            usersIn.addElement(usersForGroup[j]);
863
            out.append("      <user>\n");
864
            out.append("        <username>" + usersForGroup[j] + "</username>\n");
865
            out.append("      </user>\n");
866
          }
867
          out.append("    </group>\n");
868
        }
869
      }
870
      // for the users not belonging to any group
871
      String[] users = getUsers(user, password);
872
      for (int j=0; j < users.length; j++ ) {
873
        if ( !usersIn.contains(users[j]) ) {
874
          out.append("    <user>\n");
875
          out.append("      <username>" + users[j] + "</username>\n");
876
          out.append("    </user>\n");
877
        }
878
      }
879
    
880
      out.append("  </authSystem>\n");
881
      if ( !usersIn.isEmpty() ) {
882
        usersIn.removeAllElements();
883
        usersIn.trimToSize();
884
      }
885
    
886
    }
887
    out.append("</principals>");
888
    return out.toString();
889
  }
890

    
891
  /**
892
   * Test method for the class
893
   */
894
  public static void main(String[] args) {
895

    
896
    // Provide a user, such as: "Matt Jones", or "jones"
897
    String user = args[0];
898
    String password = args[1];
899

    
900
    AuthLdap authservice = new AuthLdap();
901

    
902
/*
903
    // Get the list of supported controls
904
    try {
905
        // Create initial context
906
        DirContext dctx = new InitialDirContext();
907
        
908
        // Read supportedcontrol from root DSE
909
        MetaCatUtil util = new MetaCatUtil();
910
        String ldapurl = util.getOption("ldapurl");
911
        Attributes attrs = dctx.getAttributes(
912
            ldapurl, new String[]{"supportedcontrol"});
913
        
914
        System.out.println(attrs);
915
        // Close the context when we're done
916
        dctx.close();
917
    } catch (NamingException e) {
918
        e.printStackTrace();
919
    }
920
*/               
921
                
922
    boolean isValid = false;
923
    try {
924
      isValid = authservice.authenticate(user, password);
925
      if (isValid) {
926
        System.out.println("Authentication successful for: " + user );
927
      } else {
928
        System.out.println("Authentication failed for: " + user);
929
      }
930

    
931
      // Get attributes for the user
932
      if (isValid) {
933
        System.out.println("\nGetting attributes for user....");
934
        HashMap userInfo = authservice.getAttributes(user, password, user);
935
        // Print all of the attributes
936
        Iterator attList = (Iterator)(((Set)userInfo.keySet()).iterator());
937
        while (attList.hasNext()) {
938
          String att = (String)attList.next();
939
          Vector values = (Vector)userInfo.get(att);
940
          Iterator attvalues = values.iterator();
941
          while (attvalues.hasNext()) {
942
            String value = (String)attvalues.next();
943
            System.out.println(att + ": " + value);
944
          }
945
        }
946
      }
947

    
948
      // get the groups
949
      if (isValid) {
950
        System.out.println("\nGetting all groups....");
951
        String[] groups = authservice.getGroups(user, password);
952
        System.out.println("Groups found: " + groups.length);
953
        for (int i=0; i < groups.length; i++) {
954
            System.out.println("Group " + i + ": " + groups[i]);
955
        }
956
      }
957

    
958
      // get the groups for the user
959
      String savedGroup = null;
960
      if (isValid) {
961
        System.out.println("\nGetting groups for user....");
962
        String[] groups = authservice.getGroups(user, password, user);
963
        System.out.println("Groups found: " + groups.length);
964
        for (int i=0; i < groups.length; i++) {
965
            System.out.println("Group " + i + ": " + groups[i]);
966
            savedGroup = groups[i];
967
        }
968
      }
969

    
970
      // get the users for a group
971
      if (isValid) {
972
        System.out.println("\nGetting users for group....");
973
        System.out.println("Group: " + savedGroup);
974
        String[] users = authservice.getUsers(user, password, savedGroup);
975
        System.out.println("Users found: " + users.length);
976
        for (int i=0; i < users.length; i++) {
977
            System.out.println("User " + i + ": " + users[i]);
978
        }
979
      }
980

    
981
      // get all users
982
      if (isValid) {
983
        System.out.println("\nGetting all users ....");
984
        String[] users = authservice.getUsers(user, password);
985
        System.out.println("Users found: " + users.length);
986
        for (int i=0; i < users.length; i++) {
987
            //System.out.println("User " + i + ": " + users[i]);
988
        }
989
      }
990

    
991
      // get the whole list groups and users in XML format
992
      if (isValid) {
993
        System.out.println("\nTrying principals....");
994
        authservice = new AuthLdap();
995
        String out = authservice.getPrincipals(user, password);
996
        java.io.File f = new java.io.File("principals.xml");
997
        java.io.FileWriter fw = new java.io.FileWriter(f);
998
        java.io.BufferedWriter buff = new java.io.BufferedWriter(fw);
999
        buff.write(out);
1000
        buff.flush();
1001
        buff.close();
1002
        fw.close();
1003
        System.out.println("\nFinished getting principals.");
1004
      }
1005

    
1006
    } catch (ConnectException ce) {
1007
      System.err.println(ce.getMessage());
1008
    } catch (java.io.IOException ioe) {
1009
      System.err.println("I/O Error writing to file principals.txt");
1010
    }
1011
  }
1012
  
1013
  public void run()
1014
  {
1015
    referralContext = null;
1016
    try
1017
    {
1018
      System.out.println("running thread....");
1019
      rContext = refExc.getReferralContext(env);
1020
      referralContext = handleReferral(env, rContext);
1021
      System.out.println("exiting thread...");
1022
    }
1023
    catch(Exception e)
1024
    {
1025
      System.out.println("Error running referral handler thread: " + 
1026
                          e.getMessage());
1027
      e.printStackTrace();
1028
      referralContext = null;
1029
    }
1030
  }
1031
}
(6-6/40)