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-03 14:02:20 -0800 (Thu, 03 Jan 2002) $'
13
 * '$Revision: 888 $'
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, "throw");
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

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

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

    
575
    } catch(ReferralException re) {
576
      System.out.println("caught a referral to " + re.toString());
577
      try
578
      {
579
        Context c = handleReferral(env, re);
580
        DirContext dc = (DirContext)c;
581
        String[] attrIDs = {"cn"};
582
        // Specify the attributes to match.
583
        // Groups are objects with attribute objectclass=groupofuniquenames.
584
        // and have attribute uniquemember: uid=foruser,ldapbase.
585
        SearchControls ctls = new SearchControls();
586
        ctls.setReturningAttributes(attrIDs);
587
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
588
        
589
        String filter = null;
590
        String gfilter = "(objectClass=groupOfUniqueNames)";
591
        if (null == foruser) {
592
            filter = gfilter;
593
        } else {
594
            filter = "(& " + gfilter + "(uniqueMember=" + foruser + "))";
595
        }
596
        NamingEnumeration enum = dc.search(ldapBase, filter, ctls);
597

    
598
        // Print the groups
599
        Vector uvec = new Vector();
600
        while (enum.hasMore()) {
601
          SearchResult sr = (SearchResult)enum.next();
602
          uvec.add(sr.getName()+","+ldapBase);
603
        }
604

    
605
        // initialize groups[] and fill it
606
        groups = new String[uvec.size()];
607
        for (int i=0; i < uvec.size(); i++) {
608
          groups[i] = (String)uvec.elementAt(i); 
609
        }
610
        c.close();
611
        dc.close();
612
      }
613
      catch(Exception e)
614
      {
615
        System.out.println("returning groups as null");
616
        return groups;
617
      }
618
    } catch (NamingException e) {
619
      System.err.println("Problem getting groups in AuthLdap.getGroups 2:" + e);
620
      e.printStackTrace(System.err);
621
      throw new ConnectException(
622
      "Problem getting groups for a user in AuthLdap.getGroups:" + e);
623
    } 
624
    return groups;
625
  }
626

    
627
  /**
628
   * Get attributes describing a user or group
629
   *
630
   * @param foruser the user for which the attribute list is requested
631
   * @returns HashMap a map of attribute name to a Vector of values
632
   */
633
  public HashMap getAttributes(String foruser) 
634
         throws ConnectException
635
  {
636
    return getAttributes(null, null, foruser);
637
  }
638

    
639
  /**
640
   * Get attributes describing a user or group
641
   *
642
   * @param user the user for authenticating against the service
643
   * @param password the password for authenticating against the service
644
   * @param foruser the user whose attributes should be returned
645
   * @returns HashMap a map of attribute name to a Vector of values
646
   */
647
  public HashMap getAttributes(String user, String password, String foruser) 
648
         throws ConnectException
649
  {
650
    HashMap attributes = new HashMap();
651
    String ldapUrl = this.ldapUrl;
652
    String ldapBase = this.ldapBase;
653
    String userident = foruser;
654
    /*
655
    try { 
656
      this.ldapBase = userident.substring(userident.indexOf(",")+1);
657
      userident = userident.substring(0,userident.indexOf(","));
658
    } catch (StringIndexOutOfBoundsException e) {}
659
*/
660
    // Identify service provider to use
661
    Hashtable env = new Hashtable(11);
662
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
663
        "com.sun.jndi.ldap.LdapCtxFactory");
664
    env.put(Context.REFERRAL, referral);
665
    env.put(Context.PROVIDER_URL, ldapUrl);
666

    
667
    try {
668
      
669
      // Create the initial directory context
670
      DirContext ctx = new InitialDirContext(env);
671
        
672
      // Find out the identifying attribute for the user
673
      //userident = getIdentifyingName(userident,ldapUrl,ldapBase);
674

    
675
      // Ask for all attributes of the user 
676
      //Attributes attrs = ctx.getAttributes(userident);
677
      Attributes attrs = ctx.getAttributes(foruser);
678
 
679
      // Print all of the attributes
680
      NamingEnumeration en = attrs.getAll();
681
      while (en.hasMore()) {
682
        Attribute att = (Attribute)en.next();
683
        Vector values = new Vector();
684
        String attName = att.getID();
685
        NamingEnumeration attvalues = att.getAll();
686
        while (attvalues.hasMore()) {
687
          String value = (String)attvalues.next();
688
          values.add(value);
689
        }
690
        attributes.put(attName, values);
691
      }
692
  
693
      // Close the context when we're done
694
      ctx.close();
695
    } catch (NamingException e) {
696
      System.err.println("Problem getting attributes in " + 
697
              "AuthLdap.getAttributes:" + e);
698
      throw new ConnectException(
699
      "Problem getting attributes in AuthLdap.getAttributes:" + e);
700
    }
701

    
702
    return attributes;
703
  }
704

    
705
  /**
706
   * Get list of all subtrees holding Metacat's groups and users
707
   * starting from the Metacat LDAP root, 
708
   * i.e. ldap://dev.nceas.ucsb.edu/dc=ecoinformatics,dc=org
709
   */
710
  private Hashtable getSubtrees(String user, String password,
711
                                String ldapUrl, String ldapBase)
712
                throws ConnectException
713
  {
714
    Hashtable trees = new Hashtable();
715

    
716
    // Identify service provider to use
717
    Hashtable env = new Hashtable(11);
718
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
719
            "com.sun.jndi.ldap.LdapCtxFactory");
720
    env.put(Context.REFERRAL, referral);
721
    env.put(Context.PROVIDER_URL, ldapUrl + ldapBase);
722

    
723
    try {
724

    
725
        // Create the initial directory context
726
        DirContext ctx = new InitialDirContext(env);
727

    
728
        // Specify the ids of the attributes to return
729
        String[] attrIDs = {"o","ref"};
730
        SearchControls ctls = new SearchControls();
731
        ctls.setReturningAttributes(attrIDs);
732
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
733
          
734
        // Specify the attributes to match.
735
        // Subtrees from the main server are found as objects with attribute
736
        // objectclass=organization or objectclass=referral to the subtree
737
        // resided on other server.
738
        String filter = "(|(objectclass=organization)(objectclass=referral))";
739

    
740
        // Search for objects in the current context
741
        NamingEnumeration enum = ctx.search("", filter, ctls);
742

    
743
        // Print the subtrees' <ldapURL, baseDN>
744
        while (enum.hasMore()) {
745
          SearchResult sr = (SearchResult)enum.next();
746
          Attributes attrs = sr.getAttributes();
747
          NamingEnumeration enum1 = attrs.getAll(); // "dc" and "ref" attrs
748
          if (enum1.hasMore()) {
749
            Attribute attr = (Attribute)enum1.next();
750
            String attrValue = (String)attr.get();
751
            String attrName = (String)attr.getID();
752
            //System.out.println(attrName + "=" + attrValue);
753
            if ( enum1.hasMore() ) {
754
              attr = (Attribute)enum1.next();
755
              String refValue = (String)attr.get();
756
              String refName = (String)attr.getID();
757
              //System.out.println(refName + "=" + refValue);
758
              if ( ldapBase.startsWith(refName + "=" + refValue) ) {
759
                trees.put(ldapBase,
760
                          attrValue.substring(0,attrValue.lastIndexOf("/")+1) );
761
              } else {
762
                trees.put(refName + "=" + refValue + "," + ldapBase,
763
                          attrValue.substring(0,attrValue.lastIndexOf("/")+1) );
764
              }
765
              //System.out.println("REFERRAL:" + attrValue);
766
            } else if ( ldapBase.startsWith(attrName + "=" + attrValue) ) {
767
                trees.put(ldapBase, ldapUrl);
768
            } else {              
769
                trees.put(attrName + "=" + attrValue + "," + ldapBase, ldapUrl);
770
                //System.out.println(ldapUrl + attrName + "=" + attrValue + "," + ldapBase);
771
            }
772
          }
773
        }
774

    
775
        // Close the context when we're done
776
        ctx.close();
777

    
778
    } catch (NamingException e) {
779
      System.err.println("Problem getting subtrees in AuthLdap.getSubtrees:" + e);
780
      throw new ConnectException(
781
      "Problem getting subtrees in AuthLdap.getSubtrees:" + e);
782
    }
783

    
784
    //System.out.println("number of subtrees:" + trees.size());
785
    return trees;
786
  }
787

    
788
  /**
789
   * Get all groups and users from authentication scheme.
790
   * The output is formatted in XML.
791
   * @param user the user which requests the information
792
   * @param password the user's password
793
   */
794
  public String getPrincipals(String user, String password)
795
                throws ConnectException
796
  {
797
    StringBuffer out = new StringBuffer();
798
    Vector usersIn = new Vector();
799
    
800
    out.append("<?xml version=\"1.0\"?>\n");
801
    out.append("<principals>\n");
802
    
803
    /*
804
     * get all subtrees first in the current dir context 
805
     * and then the Metacat users under them
806
     */
807
    Hashtable subtrees = getSubtrees(user,password,this.ldapUrl,this.ldapBase);
808
    
809
    Enumeration enum = subtrees.keys();
810
    while ( enum.hasMoreElements() ) {
811
      this.ldapBase = (String)enum.nextElement();
812
      this.ldapUrl = (String)subtrees.get(ldapBase);
813
      
814
      out.append("  <authSystem URI=\"" + 
815
                 this.ldapUrl + this.ldapBase + "\">\n");
816

    
817
      // get all groups for directory context
818
      String[] groups = getGroups(user, password);
819

    
820
      // for the groups and users that belong to them
821
      if ( groups.length > 0 ) {
822
        for (int i=0; i < groups.length; i++ ) {
823
          out.append("    <group>\n");
824
          out.append("      <groupname>" + groups[i] + "</groupname>\n");
825
          String[] usersForGroup = getUsers(user,password,groups[i]);
826
          for (int j=0; j < usersForGroup.length; j++ ) {
827
            usersIn.addElement(usersForGroup[j]);
828
            out.append("      <user>\n");
829
            out.append("        <username>" + usersForGroup[j] + "</username>\n");
830
            out.append("      </user>\n");
831
          }
832
          out.append("    </group>\n");
833
        }
834
      }
835
      // for the users not belonging to any group
836
      String[] users = getUsers(user, password);
837
      for (int j=0; j < users.length; j++ ) {
838
        if ( !usersIn.contains(users[j]) ) {
839
          out.append("    <user>\n");
840
          out.append("      <username>" + users[j] + "</username>\n");
841
          out.append("    </user>\n");
842
        }
843
      }
844
    
845
      out.append("  </authSystem>\n");
846
      if ( !usersIn.isEmpty() ) {
847
        usersIn.removeAllElements();
848
        usersIn.trimToSize();
849
      }
850
    
851
    }
852
    out.append("</principals>");
853
    return out.toString();
854
  }
855

    
856
  /**
857
   * Test method for the class
858
   */
859
  public static void main(String[] args) {
860

    
861
    // Provide a user, such as: "Matt Jones", or "jones"
862
    String user = args[0];
863
    String password = args[1];
864

    
865
    AuthLdap authservice = new AuthLdap();
866

    
867
/*
868
    // Get the list of supported controls
869
    try {
870
        // Create initial context
871
        DirContext dctx = new InitialDirContext();
872
        
873
        // Read supportedcontrol from root DSE
874
        MetaCatUtil util = new MetaCatUtil();
875
        String ldapurl = util.getOption("ldapurl");
876
        Attributes attrs = dctx.getAttributes(
877
            ldapurl, new String[]{"supportedcontrol"});
878
        
879
        System.out.println(attrs);
880
        // Close the context when we're done
881
        dctx.close();
882
    } catch (NamingException e) {
883
        e.printStackTrace();
884
    }
885
*/               
886
                
887
    boolean isValid = false;
888
    try {
889
      isValid = authservice.authenticate(user, password);
890
      if (isValid) {
891
        System.out.println("Authentication successful for: " + user );
892
      } else {
893
        System.out.println("Authentication failed for: " + user);
894
      }
895

    
896
      // Get attributes for the user
897
      if (isValid) {
898
        System.out.println("\nGetting attributes for user....");
899
        HashMap userInfo = authservice.getAttributes(user, password, user);
900
        // Print all of the attributes
901
        Iterator attList = (Iterator)(((Set)userInfo.keySet()).iterator());
902
        while (attList.hasNext()) {
903
          String att = (String)attList.next();
904
          Vector values = (Vector)userInfo.get(att);
905
          Iterator attvalues = values.iterator();
906
          while (attvalues.hasNext()) {
907
            String value = (String)attvalues.next();
908
            System.out.println(att + ": " + value);
909
          }
910
        }
911
      }
912

    
913
      // get the groups
914
      if (isValid) {
915
        System.out.println("\nGetting all groups....");
916
        String[] groups = authservice.getGroups(user, password);
917
        System.out.println("Groups found: " + groups.length);
918
        for (int i=0; i < groups.length; i++) {
919
            System.out.println("Group " + i + ": " + groups[i]);
920
        }
921
      }
922

    
923
      // get the groups for the user
924
      String savedGroup = null;
925
      if (isValid) {
926
        System.out.println("\nGetting groups for user....");
927
        String[] groups = authservice.getGroups(user, password, user);
928
        System.out.println("Groups found: " + groups.length);
929
        for (int i=0; i < groups.length; i++) {
930
            System.out.println("Group " + i + ": " + groups[i]);
931
            savedGroup = groups[i];
932
        }
933
      }
934

    
935
      // get the users for a group
936
      if (isValid) {
937
        System.out.println("\nGetting users for group....");
938
        System.out.println("Group: " + savedGroup);
939
        String[] users = authservice.getUsers(user, password, savedGroup);
940
        System.out.println("Users found: " + users.length);
941
        for (int i=0; i < users.length; i++) {
942
            System.out.println("User " + i + ": " + users[i]);
943
        }
944
      }
945

    
946
      // get all users
947
      if (isValid) {
948
        System.out.println("\nGetting all users ....");
949
        String[] users = authservice.getUsers(user, password);
950
        System.out.println("Users found: " + users.length);
951
        for (int i=0; i < users.length; i++) {
952
            //System.out.println("User " + i + ": " + users[i]);
953
        }
954
      }
955

    
956
      // get the whole list groups and users in XML format
957
      if (isValid) {
958
        System.out.println("\nTrying principals....");
959
        authservice = new AuthLdap();
960
        String out = authservice.getPrincipals(user, password);
961
        java.io.File f = new java.io.File("principals.xml");
962
        java.io.FileWriter fw = new java.io.FileWriter(f);
963
        java.io.BufferedWriter buff = new java.io.BufferedWriter(fw);
964
        buff.write(out);
965
        buff.flush();
966
        buff.close();
967
        fw.close();
968
        System.out.println("\nFinished getting principals.");
969
      }
970

    
971
    } catch (ConnectException ce) {
972
      System.err.println(ce.getMessage());
973
    } catch (java.io.IOException ioe) {
974
      System.err.println("I/O Error writing to file principals.txt");
975
    }
976
  }
977
}
(6-6/40)