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-21 18:10:19 -0800 (Wed, 21 Nov 2001) $'
13
 * '$Revision: 872 $'
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.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 {
64
  
65
  private MetaCatUtil util = new MetaCatUtil();
66
  private String ldapUrl;
67
  private String ldapsUrl;
68
  private String ldapBase;
69
  private String referral;
70

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

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

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

    
103
    try {
104
   
105
        // Check the usename as passed in
106
        authenticated = ldapAuthenticate(identifier, password);
107

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

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

    
129
    return authenticated;
130
  }
131

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

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

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

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

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

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

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

    
392
    try {
393

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

    
397
        // Specify the attributes to match.
398
        // Users are objects that have the attribute objectclass=InetOrgPerson.
399
        SearchControls ctls = new SearchControls();
400
        String[] attrIDs = {"dn"};
401
        ctls.setReturningAttributes(attrIDs);
402
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
403
        String filter = "(objectClass=inetOrgPerson)";
404
        NamingEnumeration enum = ctx.search(ldapBase, filter, ctls);
405
        
406
        // Print the users
407
        Vector uvec = new Vector();
408
        while (enum.hasMore()) {
409
          SearchResult sr = (SearchResult)enum.next();
410
          uvec.add(sr.getName()+","+ldapBase);
411
        }
412

    
413
        // initialize users[]; fill users[]
414
        users = new String[uvec.size()];
415
        for (int i=0; i < uvec.size(); i++) {
416
          users[i] = (String)uvec.elementAt(i); 
417
        }
418

    
419
        // Close the context when we're done
420
        ctx.close();
421

    
422
    } catch (NamingException e) {
423
      System.err.println("Problem getting users in AuthLdap.getUsers:" + e);
424
      e.printStackTrace(System.err);
425
      throw new ConnectException(
426
      "Problem getting users in AuthLdap.getUsers:" + e);
427
    }
428

    
429
    return users;
430
  }
431

    
432
  /**
433
   * Get the users for a particular group from the authentication service
434
   *
435
   * @param user the user for authenticating against the service
436
   * @param password the password for authenticating against the service
437
   * @param group the group whose user list should be returned
438
   * @returns string array of the user names belonging to the group
439
   */
440
  public String[] getUsers(String user, String password, String group) 
441
         throws ConnectException
442
  {
443
    String[] users = null;
444

    
445
    // Identify service provider to use
446
    Hashtable env = new Hashtable(11);
447
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
448
            "com.sun.jndi.ldap.LdapCtxFactory");
449
    env.put(Context.REFERRAL, referral);
450
    env.put(Context.PROVIDER_URL, ldapUrl);
451

    
452
    try {
453

    
454
        // Create the initial directory context
455
        DirContext ctx = new InitialDirContext(env);
456

    
457
        // Specify the ids of the attributes to return
458
        String[] attrIDs = {"uniqueMember"};
459

    
460
        Attributes answer = ctx.getAttributes(group, attrIDs);
461

    
462
        Vector uvec = new Vector();
463
        for (NamingEnumeration ae = answer.getAll(); ae.hasMore();) {
464
            Attribute attr = (Attribute)ae.next();
465
            for (NamingEnumeration e = attr.getAll(); e.hasMore();
466
                 uvec.add(e.next()) 
467
                 );
468
        }
469

    
470
        // initialize users[]; fill users[]
471
        users = new String[uvec.size()];
472
        for (int i=0; i < uvec.size(); i++) {
473
          users[i] = (String)uvec.elementAt(i); 
474
        }
475

    
476
        // Close the context when we're done
477
        ctx.close();
478

    
479
    } catch (NamingException e) {
480
      System.err.println("Problem getting users for a group in " +
481
              "AuthLdap.getUsers:" + e);
482
      throw new ConnectException(
483
      "Problem getting users for a group in AuthLdap.getUsers:" + e);
484
    }
485

    
486
    return users;
487
  }
488

    
489
  /**
490
   * Get all groups from the authentication service
491
   *
492
   * @param user the user for authenticating against the service
493
   * @param password the password for authenticating against the service
494
   * @returns string array of the group names
495
   */
496
  public String[] getGroups(String user, String password) 
497
         throws ConnectException
498
  {
499
      return getGroups(user, password, null);
500
  }
501

    
502
  /**
503
   * Get the groups for a particular user from the authentication service
504
   *
505
   * @param user the user for authenticating against the service
506
   * @param password the password for authenticating against the service
507
   * @param foruser the user whose group list should be returned
508
   * @returns string array of the group names
509
   */
510
  public String[] getGroups(String user, String password, String foruser) 
511
         throws ConnectException
512
  {
513
    //System.err.println("GG in get groups 2");
514
    String[] groups = null;
515

    
516
    // Identify service provider to use
517
    Hashtable env = new Hashtable(11);
518
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
519
            "com.sun.jndi.ldap.LdapCtxFactory");
520
    env.put(Context.REFERRAL, referral);
521
    env.put(Context.PROVIDER_URL, ldapUrl);
522
    try {
523

    
524
        // Create the initial directory context
525
        DirContext ctx = new InitialDirContext(env);
526
        // Specify the ids of the attributes to return
527
        String[] attrIDs = {"cn"};
528
        // Specify the attributes to match.
529
        // Groups are objects with attribute objectclass=groupofuniquenames.
530
        // and have attribute uniquemember: uid=foruser,ldapbase.
531
        SearchControls ctls = new SearchControls();
532
        ctls.setReturningAttributes(attrIDs);
533
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
534
        
535
        String filter = null;
536
        String gfilter = "(objectClass=groupOfUniqueNames)";
537
        if (null == foruser) {
538
            filter = gfilter;
539
        } else {
540
            filter = "(& " + gfilter + "(uniqueMember=" + foruser + "))";
541
        }
542
        NamingEnumeration enum = ctx.search(ldapBase, filter, ctls);
543

    
544
        // Print the groups
545
        Vector uvec = new Vector();
546
        while (enum.hasMore()) {
547
          SearchResult sr = (SearchResult)enum.next();
548
          uvec.add(sr.getName()+","+ldapBase);
549
        }
550

    
551
        // initialize groups[] and fill it
552
        groups = new String[uvec.size()];
553
        for (int i=0; i < uvec.size(); i++) {
554
          groups[i] = (String)uvec.elementAt(i); 
555
        }
556

    
557
        // Close the context when we're done
558
        ctx.close();
559

    
560
    } catch (NamingException e) {
561
      System.err.println("Problem getting groups in AuthLdap.getGroups 2:" + e);
562
      e.printStackTrace(System.err);
563
      throw new ConnectException(
564
      "Problem getting groups for a user in AuthLdap.getGroups:" + e);
565
    }
566
    return groups;
567
  }
568

    
569
  /**
570
   * Get attributes describing a user or group
571
   *
572
   * @param foruser the user for which the attribute list is requested
573
   * @returns HashMap a map of attribute name to a Vector of values
574
   */
575
  public HashMap getAttributes(String foruser) 
576
         throws ConnectException
577
  {
578
    return getAttributes(null, null, foruser);
579
  }
580

    
581
  /**
582
   * Get attributes describing a user or group
583
   *
584
   * @param user the user for authenticating against the service
585
   * @param password the password for authenticating against the service
586
   * @param foruser the user whose attributes should be returned
587
   * @returns HashMap a map of attribute name to a Vector of values
588
   */
589
  public HashMap getAttributes(String user, String password, String foruser) 
590
         throws ConnectException
591
  {
592
    HashMap attributes = new HashMap();
593
    String ldapUrl = this.ldapUrl;
594
    String ldapBase = this.ldapBase;
595
    String userident = foruser;
596
    /*
597
    try { 
598
      this.ldapBase = userident.substring(userident.indexOf(",")+1);
599
      userident = userident.substring(0,userident.indexOf(","));
600
    } catch (StringIndexOutOfBoundsException e) {}
601
*/
602
    // Identify service provider to use
603
    Hashtable env = new Hashtable(11);
604
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
605
        "com.sun.jndi.ldap.LdapCtxFactory");
606
    env.put(Context.REFERRAL, referral);
607
    env.put(Context.PROVIDER_URL, ldapUrl);
608

    
609
    try {
610
      
611
      // Create the initial directory context
612
      DirContext ctx = new InitialDirContext(env);
613
        
614
      // Find out the identifying attribute for the user
615
      //userident = getIdentifyingName(userident,ldapUrl,ldapBase);
616

    
617
      // Ask for all attributes of the user 
618
      //Attributes attrs = ctx.getAttributes(userident);
619
      Attributes attrs = ctx.getAttributes(foruser);
620
 
621
      // Print all of the attributes
622
      NamingEnumeration en = attrs.getAll();
623
      while (en.hasMore()) {
624
        Attribute att = (Attribute)en.next();
625
        Vector values = new Vector();
626
        String attName = att.getID();
627
        NamingEnumeration attvalues = att.getAll();
628
        while (attvalues.hasMore()) {
629
          String value = (String)attvalues.next();
630
          values.add(value);
631
        }
632
        attributes.put(attName, values);
633
      }
634
  
635
      // Close the context when we're done
636
      ctx.close();
637
    } catch (NamingException e) {
638
      System.err.println("Problem getting attributes in " + 
639
              "AuthLdap.getAttributes:" + e);
640
      throw new ConnectException(
641
      "Problem getting attributes in AuthLdap.getAttributes:" + e);
642
    }
643

    
644
    return attributes;
645
  }
646

    
647
  /**
648
   * Get list of all subtrees holding Metacat's groups and users
649
   * starting from the Metacat LDAP root, 
650
   * i.e. ldap://dev.nceas.ucsb.edu/dc=ecoinformatics,dc=org
651
   */
652
  private Hashtable getSubtrees(String user, String password,
653
                                String ldapUrl, String ldapBase)
654
                throws ConnectException
655
  {
656
    Hashtable trees = new Hashtable();
657

    
658
    // Identify service provider to use
659
    Hashtable env = new Hashtable(11);
660
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
661
            "com.sun.jndi.ldap.LdapCtxFactory");
662
    env.put(Context.REFERRAL, referral);
663
    env.put(Context.PROVIDER_URL, ldapUrl + ldapBase);
664

    
665
    try {
666

    
667
        // Create the initial directory context
668
        DirContext ctx = new InitialDirContext(env);
669

    
670
        // Specify the ids of the attributes to return
671
        String[] attrIDs = {"o","ref"};
672
        SearchControls ctls = new SearchControls();
673
        ctls.setReturningAttributes(attrIDs);
674
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
675
          
676
        // Specify the attributes to match.
677
        // Subtrees from the main server are found as objects with attribute
678
        // objectclass=organization or objectclass=referral to the subtree
679
        // resided on other server.
680
        String filter = "(|(objectclass=organization)(objectclass=referral))";
681

    
682
        // Search for objects in the current context
683
        NamingEnumeration enum = ctx.search("", filter, ctls);
684

    
685
        // Print the subtrees' <ldapURL, baseDN>
686
        while (enum.hasMore()) {
687
          SearchResult sr = (SearchResult)enum.next();
688
          Attributes attrs = sr.getAttributes();
689
          NamingEnumeration enum1 = attrs.getAll(); // "dc" and "ref" attrs
690
          if (enum1.hasMore()) {
691
            Attribute attr = (Attribute)enum1.next();
692
            String attrValue = (String)attr.get();
693
            String attrName = (String)attr.getID();
694
            //System.out.println(attrName + "=" + attrValue);
695
            if ( enum1.hasMore() ) {
696
              attr = (Attribute)enum1.next();
697
              String refValue = (String)attr.get();
698
              String refName = (String)attr.getID();
699
              //System.out.println(refName + "=" + refValue);
700
              if ( ldapBase.startsWith(refName + "=" + refValue) ) {
701
                trees.put(ldapBase,
702
                          attrValue.substring(0,attrValue.lastIndexOf("/")+1) );
703
              } else {
704
                trees.put(refName + "=" + refValue + "," + ldapBase,
705
                          attrValue.substring(0,attrValue.lastIndexOf("/")+1) );
706
              }
707
              //System.out.println("REFERRAL:" + attrValue);
708
            } else if ( ldapBase.startsWith(attrName + "=" + attrValue) ) {
709
                trees.put(ldapBase, ldapUrl);
710
            } else {              
711
                trees.put(attrName + "=" + attrValue + "," + ldapBase, ldapUrl);
712
                //System.out.println(ldapUrl + attrName + "=" + attrValue + "," + ldapBase);
713
            }
714
          }
715
        }
716

    
717
        // Close the context when we're done
718
        ctx.close();
719

    
720
    } catch (NamingException e) {
721
      System.err.println("Problem getting subtrees in AuthLdap.getSubtrees:" + e);
722
      throw new ConnectException(
723
      "Problem getting subtrees in AuthLdap.getSubtrees:" + e);
724
    }
725

    
726
    //System.out.println("number of subtrees:" + trees.size());
727
    return trees;
728
  }
729

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

    
759
      // get all groups for directory context
760
      String[] groups = getGroups(user, password);
761

    
762
      // for the groups and users that belong to them
763
      if ( groups.length > 0 ) {
764
        for (int i=0; i < groups.length; i++ ) {
765
          out.append("    <group>\n");
766
          out.append("      <groupname>" + groups[i] + "</groupname>\n");
767
          String[] usersForGroup = getUsers(user,password,groups[i]);
768
          for (int j=0; j < usersForGroup.length; j++ ) {
769
            usersIn.addElement(usersForGroup[j]);
770
            out.append("      <user>\n");
771
            out.append("        <username>" + usersForGroup[j] + "</username>\n");
772
            out.append("      </user>\n");
773
          }
774
          out.append("    </group>\n");
775
        }
776
      }
777
      // for the users not belonging to any group
778
      String[] users = getUsers(user, password);
779
      for (int j=0; j < users.length; j++ ) {
780
        if ( !usersIn.contains(users[j]) ) {
781
          out.append("    <user>\n");
782
          out.append("      <username>" + users[j] + "</username>\n");
783
          out.append("    </user>\n");
784
        }
785
      }
786
    
787
      out.append("  </authSystem>\n");
788
      if ( !usersIn.isEmpty() ) {
789
        usersIn.removeAllElements();
790
        usersIn.trimToSize();
791
      }
792
    
793
    }
794
    out.append("</principals>");
795
    return out.toString();
796
  }
797

    
798
  /**
799
   * Test method for the class
800
   */
801
  public static void main(String[] args) {
802

    
803
    // Provide a user, such as: "Matt Jones", or "jones"
804
    String user = args[0];
805
    String password = args[1];
806

    
807
    AuthLdap authservice = new AuthLdap();
808

    
809
    boolean isValid = false;
810
    try {
811
      isValid = authservice.authenticate(user, password);
812
      if (isValid) {
813
        System.out.println("Authentication successful for: " + user );
814
      } else {
815
        System.out.println("Authentication failed for: " + user);
816
      }
817

    
818
      // Get attributes for the user
819
      if (isValid) {
820
        System.out.println("\nGetting attributes for user....");
821
        HashMap userInfo = authservice.getAttributes(user, password, user);
822
        // Print all of the attributes
823
        Iterator attList = (Iterator)(((Set)userInfo.keySet()).iterator());
824
        while (attList.hasNext()) {
825
          String att = (String)attList.next();
826
          Vector values = (Vector)userInfo.get(att);
827
          Iterator attvalues = values.iterator();
828
          while (attvalues.hasNext()) {
829
            String value = (String)attvalues.next();
830
            System.out.println(att + ": " + value);
831
          }
832
        }
833
      }
834

    
835
      // get the groups
836
      if (isValid) {
837
        System.out.println("\nGetting all groups....");
838
        String[] groups = authservice.getGroups(user, password);
839
        System.out.println("Groups found: " + groups.length);
840
        for (int i=0; i < groups.length; i++) {
841
            System.out.println("Group " + i + ": " + groups[i]);
842
        }
843
      }
844

    
845
      // get the groups for the user
846
      String savedGroup = null;
847
      if (isValid) {
848
        System.out.println("\nGetting groups for user....");
849
        String[] groups = authservice.getGroups(user, password, user);
850
        System.out.println("Groups found: " + groups.length);
851
        for (int i=0; i < groups.length; i++) {
852
            System.out.println("Group " + i + ": " + groups[i]);
853
            savedGroup = groups[i];
854
        }
855
      }
856

    
857
      // get the users for a group
858
      if (isValid) {
859
        System.out.println("\nGetting users for group....");
860
        System.out.println("Group: " + savedGroup);
861
        String[] users = authservice.getUsers(user, password, savedGroup);
862
        System.out.println("Users found: " + users.length);
863
        for (int i=0; i < users.length; i++) {
864
            System.out.println("User " + i + ": " + users[i]);
865
        }
866
      }
867

    
868
      // get all users
869
      if (isValid) {
870
        System.out.println("\nGetting all users ....");
871
        String[] users = authservice.getUsers(user, password);
872
        System.out.println("Users found: " + users.length);
873
        for (int i=0; i < users.length; i++) {
874
            System.out.println("User " + i + ": " + users[i]);
875
        }
876
      }
877
/*
878
      // get the whole list groups and users in XML format
879
      if (isValid) {
880
        System.out.println("\nTrying principals....");
881
        authservice = new AuthLdap();
882
        String out = authservice.getPrincipals(user, password);
883
        java.io.File f = new java.io.File("principals.xml");
884
        java.io.FileWriter fw = new java.io.FileWriter(f);
885
        java.io.BufferedWriter buff = new java.io.BufferedWriter(fw);
886
        buff.write(out);
887
        buff.flush();
888
        buff.close();
889
        fw.close();
890
      }
891
*/
892
    } catch (ConnectException ce) {
893
      System.err.println(ce.getMessage());
894
    } catch (java.io.IOException ioe) {
895
      System.err.println("I/O Error writing to file principals.txt");
896
    }
897
  }
898
}
(6-6/40)