Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *    Purpose: An implementation of the AuthInterface interface that
4
 *             allows Metacat to use the LDAP protocol for
5
 *             directory services
6
 *  Copyright: 2000 Regents of the University of California and the
7
 *             National Center for Ecological Analysis and Synthesis
8
 *    Authors: Matt Jones
9
 *    Release: @release@
10
 *
11
 *   '$Author: tao $'
12
 *     '$Date: 2003-03-19 15:11:10 -0800 (Wed, 19 Mar 2003) $'
13
 * '$Revision: 1494 $'
14
 *
15
 * This program is free software; you can redistribute it and/or modify
16
 * it under the terms of the GNU General Public License as published by
17
 * the Free Software Foundation; either version 2 of the License, or
18
 * (at your option) any later version.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 * GNU General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU General Public License
26
 * along with this program; if not, write to the Free Software
27
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
28
 */
29

    
30
package edu.ucsb.nceas.metacat;
31

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

    
57
/**
58
 * An implementation of the AuthInterface interface that
59
 * allows Metacat to use the LDAP protocol for directory services.
60
 * The LDAP authentication service is used to determine if a user 
61
 * is authenticated, and whether they are a member of a particular group.
62
 */
63
public class AuthLdap implements AuthInterface, Runnable {
64
  private MetaCatUtil util = new MetaCatUtil();
65
  private String ldapUrl;
66
  private String ldapsUrl;
67
  private String ldapBase;
68
  private String referral;
69
  private Context referralContext;
70
  Hashtable env = new Hashtable(11);
71
  private Context rContext;
72
  private String userName;
73
  private String userPassword;
74
  ReferralException refExc;
75
  String uid=null;
76

    
77
  /** 
78
   * Construct an AuthLdap
79
   */
80
  public AuthLdap() {
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
  }
87

    
88
  /**
89
   * Determine if a user/password are valid according to the authentication
90
   * service.
91
   *
92
   * @param user the name of the principal to authenticate
93
   * @param password the password to use for authentication
94
   * @returns boolean true if authentication successful, false otherwise
95
   */
96
  public boolean authenticate(String user, String password)
97
                    throws ConnectException
98
  {
99
    String ldapUrl = this.ldapUrl;
100
    String ldapsUrl = this.ldapsUrl;
101
    String ldapBase = this.ldapBase;
102
    boolean authenticated = false;
103
    String identifier = user;
104
    //get uid here.
105
    uid=user.substring(0, user.indexOf(","));
106
  
107
    try {
108
        // Check the usename as passed in
109
        authenticated = ldapAuthenticate(identifier, password);
110
        // if not found, try looking up a valid DN then auth again
111
        if (!authenticated) {
112
            MetaCatUtil.debugMessage("Looking up DN for: " + identifier, 35);
113
            identifier = getIdentifyingName(identifier,ldapUrl,ldapBase);
114
            //System.out.println("identifier: "+identifier);
115
            String refUrl = "";
116
            String refBase = "";
117
            if (identifier.startsWith("ldap")) {
118
                refUrl = identifier.substring(0,
119
                               identifier.lastIndexOf("/")+1);
120
                MetaCatUtil.debugMessage("Ref ldapUrl: " + refUrl, 35);
121
                int position = identifier.indexOf(",");
122
                int position2 = identifier.indexOf(",", position+1);
123
                refBase = identifier.substring(position2+1);
124
                MetaCatUtil.debugMessage("Ref ldapBase: " + refBase, 35);
125
                identifier = identifier.substring(
126
                             identifier.lastIndexOf("/")+1);
127
                MetaCatUtil.debugMessage("Trying: " + identifier, 35);
128
                authenticated = ldapAuthenticate(identifier, password,
129
                                                 refUrl, refBase);
130
            } else {
131
                identifier = identifier+","+ldapBase;
132
                MetaCatUtil.debugMessage("Trying: " + identifier, 35);
133
                authenticated = ldapAuthenticate(identifier, password);
134
            }
135
            //authenticated = ldapAuthenticate(identifier, password);
136
        }
137
     
138
    } catch (NullPointerException e) {
139
      util.debugMessage("NullPointerException b' password is null", 30);
140
      util.debugMessage("NullPointerException while authenticating in " + 
141
                        "AuthLdap.authenticate: " + e, 30);
142
      throw new ConnectException(
143
      "NullPointerException while authenticating in " + 
144
                        "AuthLdap.authenticate: " + e);
145
    } catch (NamingException e) {
146
      util.debugMessage("Naming exception while authenticating in " + 
147
                        "AuthLdap.authenticate: " + e, 30);
148
      e.printStackTrace();
149
    } catch (Exception e) {
150
      util.debugMessage(e.getMessage(), 30);
151
    }
152
    return authenticated;
153
  }
154

    
155
  /**
156
   * Connect to the LDAP directory and do the authentication using the
157
   * username and password as passed into the routine.
158
   *
159
   * @param identifier the distinguished name to check against LDAP
160
   * @param password the password for authentication
161
   */
162
  private boolean ldapAuthenticate(String identifier, String password)
163
            throws ConnectException, NamingException, NullPointerException
164
  {
165
      return ldapAuthenticate(identifier, password, 
166
                              this.ldapsUrl, this.ldapBase);
167
  }
168

    
169
  /**
170
   * Connect to the LDAP directory and do the authentication using the
171
   * username and password as passed into the routine.
172
   *
173
   * @param identifier the distinguished name to check against LDAP
174
   * @param password the password for authentication
175
   */
176
  private boolean ldapAuthenticate(String identifier, String password,
177
            String directoryUrl, String searchBase)
178
            throws ConnectException, NamingException, NullPointerException
179
  {
180
    double totStartTime = System.currentTimeMillis();
181
    boolean authenticated = false;
182
    if (identifier != null && !password.equals("")) 
183
    {
184
    
185
        //Pass the username and password to run() method
186
        userName=identifier;
187
        userPassword=password;
188
        // Identify service provider to use
189
        Hashtable env = new Hashtable(11);
190
        env.put(Context.INITIAL_CONTEXT_FACTORY, 
191
              "com.sun.jndi.ldap.LdapCtxFactory");
192
        env.put(Context.REFERRAL, "throw");
193
        env.put(Context.PROVIDER_URL, directoryUrl + searchBase);
194
        if ( !ldapsUrl.equals(ldapUrl) ) 
195
        {
196
          // ldap is set on default port 389
197
          // ldaps is set on second port - 636 by default
198
          //env.put(Context.SECURITY_PROTOCOL, "ssl");
199
        }
200
        env.put(Context.SECURITY_AUTHENTICATION, "simple");
201
        env.put(Context.SECURITY_PRINCIPAL, identifier);
202
        env.put(Context.SECURITY_CREDENTIALS, password);
203
        // If our auth credentials are invalid, an exception will be thrown
204
        DirContext ctx = null;
205
        try 
206
        {
207
          double startTime = System.currentTimeMillis();
208
          //Here to check the autheration
209
          ctx = new InitialDirContext(env);
210
          double stopTime = System.currentTimeMillis();
211
          util.debugMessage("Connection time thru " + ldapsUrl + " was: " +
212
                             (stopTime-startTime)/1000 + " seconds.", 35);
213
          authenticated = true;
214
          //tls.close();
215
          ctx.close();
216
          this.ldapUrl = ldapUrl;
217
          this.ldapBase = ldapBase;
218
          //break;
219
        } 
220
        catch (AuthenticationException ae) 
221
        {
222
          authenticated = false;
223
          if ( ctx != null ) 
224
          {
225
            ctx.close();
226
          }
227
        } 
228
        catch (javax.naming.InvalidNameException ine) 
229
        {
230
            util.debugMessage("An invalid DN was provided!", 30);
231
        } 
232
        catch (javax.naming.ReferralException re) 
233
        {
234
          util.debugMessage("referral during authentication", 30);
235
          util.debugMessage("Referral information: "+re.getReferralInfo(), 30);
236
          try
237
          {
238
            refExc = re;
239

    
240
            Thread t = new Thread(this);
241
            t.start();
242
            Thread.sleep(5000); //this is a manual override of ldap's 
243
                                //hideously long time out period.
244
            util.debugMessage("Awake after 5 seconds.", 35);
245
            if (referralContext == null)
246
            {
247
              t.interrupt();
248
              authenticated = false;
249
            }
250
            else
251
            {
252
              authenticated = true;
253

    
254
            }
255
          }
256
          catch (Exception e)
257
          {
258
            authenticated = false;
259
          }
260
        }
261
    } 
262
    else 
263
    { 
264
        util.debugMessage("User not found", 30);
265
    }
266
    double totStopTime = System.currentTimeMillis();
267
    util.debugMessage("total ldap authentication time: " + 
268
                      (totStopTime - totStartTime)/1000 + " seconds", 35);
269
    return authenticated;
270
  }
271
  
272
  
273

    
274
  /**
275
   * Get the identifying name for a given userid or name.  This is the name
276
   * that is used in conjunction withthe LDAP BaseDN to create a
277
   * distinguished name (dn) for the record
278
   *
279
   * @param user the user for which the identifying name is requested
280
   * @returns String the identifying name for the user, 
281
   *          or null if not found
282
   */
283
  private String getIdentifyingName(String user, String ldapUrl,
284
                                    String ldapBase) throws NamingException
285
  {
286
    String identifier = null;
287
    // Identify service provider to use
288
    Hashtable env = new Hashtable(11);
289
    env.put(Context.INITIAL_CONTEXT_FACTORY,
290
            "com.sun.jndi.ldap.LdapCtxFactory");
291
    util.debugMessage("setting referrals to: " + referral, 35);
292
    env.put(Context.REFERRAL, referral);
293
    env.put(Context.PROVIDER_URL, ldapUrl + ldapBase);
294
    //    non-secure LDAP context; dn are publicly readable
295
    try {
296
      
297
      // Bind to the LDAP server, in order to search for the right
298
      // distinguished name (dn) based on userid (uid) or common name (cn)
299
      DirContext ctx = new InitialDirContext(env);
300
      SearchControls ctls = new SearchControls();
301
      ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
302
      // Search for the user id or name using the uid, then cn and sn 
303
      //attributes
304
      // If we find a record, determine the dn for the record
305
      // The following blocks need to be refactored into a subroutine
306
      // they have a ridiculous amount of redundancy
307

    
308
      // Parse out the uid and org components and look up the real DN
309
      // This assumes a dn like "uid=x,o=y,dc=someinst,dc=org"
310
      int position = user.indexOf(",");
311
      String comp1 = user.substring(0, position);
312
      MetaCatUtil.debugMessage("First comp is: " + comp1, 35);
313
      String comp2 = user.substring(position+1, 
314
                                    user.indexOf(",", position+1));
315
      MetaCatUtil.debugMessage("Second comp is: " + comp2, 35);
316

    
317
      String filter = "(&(" + comp1 + ")(" + comp2 + "))";
318
      MetaCatUtil.debugMessage("Filter is: " + filter, 35);
319
      MetaCatUtil.debugMessage("Provider URL is: " + ldapUrl + ldapBase, 35);
320
      NamingEnumeration answer;
321
      try {
322
        answer = ctx.search("", filter, ctls);
323
        if (answer.hasMore()) {
324
          SearchResult sr = (SearchResult)answer.next();
325
          identifier = sr.getName();
326
          util.debugMessage("Originally Found: " + identifier, 35);
327
          return identifier;
328
        }
329
      } catch (InvalidSearchFilterException e) {}
330

    
331
      // That failed, so check if it is just a username
332
      filter = "(" + user + ")";
333
      try {
334
        answer = ctx.search("", filter, ctls);
335
        if (answer.hasMore()) {
336
          SearchResult sr = (SearchResult)answer.next();
337
          identifier = sr.getName();
338
          if ( !sr.isRelative() ) { 
339
            this.ldapUrl = identifier.substring(0,
340
                                                identifier.lastIndexOf("/")+1);
341
            this.ldapBase = identifier.substring(identifier.indexOf(",")+1);
342
            identifier = identifier.substring(identifier.lastIndexOf("/")+1,
343
                                              identifier.indexOf(","));
344
          }
345
          util.debugMessage("Found: " + identifier, 35);
346
          return identifier;
347
        }
348
      } catch (InvalidSearchFilterException e) {}
349

    
350
      // Maybe its a user id (uid)
351
      filter = "(uid=" + user + ")";
352
      answer = ctx.search("", filter, ctls);
353
      if (answer.hasMore()) {
354
        SearchResult sr = (SearchResult)answer.next();
355
        identifier = sr.getName();
356
        if ( !sr.isRelative() ) { 
357
          this.ldapUrl = identifier.substring(0,identifier.lastIndexOf("/")+1);
358
          this.ldapBase = identifier.substring(identifier.indexOf(",")+1);
359
          identifier = identifier.substring(identifier.lastIndexOf("/")+1,
360
                                            identifier.indexOf(","));
361
        }
362
        util.debugMessage("Found: " + identifier, 35);
363
      } else {
364

    
365
        // maybe its just a common name
366
        filter = "(cn=" + user + ")";
367
        NamingEnumeration answer2 = ctx.search("", filter, ctls);
368
        if (answer2.hasMore()) {
369
          SearchResult sr = (SearchResult)answer2.next();
370
          identifier = sr.getName();
371
          if ( !sr.isRelative() ) { 
372
            this.ldapUrl = identifier.substring(0,
373
                                                identifier.lastIndexOf("/")+1);
374
            this.ldapBase = identifier.substring(identifier.indexOf(",")+1);
375
            identifier = identifier.substring(identifier.lastIndexOf("/")+1,
376
                                              identifier.indexOf(","));
377
          }
378
          util.debugMessage("Found: " + identifier, 35);
379
        } else {
380

    
381
          // ok, last resort, is it a surname?
382
          filter = "(sn=" + user + ")";
383
          NamingEnumeration answer3 = ctx.search("", filter, ctls);
384
          if (answer3.hasMore()) {
385
            SearchResult sr = (SearchResult)answer3.next();
386
            identifier = sr.getName();
387
            if ( !sr.isRelative() ) { 
388
              this.ldapUrl = identifier.substring(0,
389
                                                identifier.lastIndexOf("/")+1);
390
              this.ldapBase = identifier.substring(identifier.indexOf(",")+1);
391
              identifier = identifier.substring(identifier.lastIndexOf("/")+1,
392
                                                identifier.indexOf(","));
393
            }
394
            util.debugMessage("Found: " + identifier, 35);
395
          }
396
        }
397
      }
398
      // Close the context when we're done the initial search
399
      ctx.close();
400
    } catch (NamingException e) {
401
      util.debugMessage("Naming exception while getting dn: " + e, 35);
402
      throw new NamingException(
403
      "Naming exception in AuthLdap.getIdentifyingName: " + e);
404
    }
405
    return identifier;
406
  }
407

    
408
  /**
409
   * Get all users from the authentication service
410
   *
411
   * @param user the user for authenticating against the service
412
   * @param password the password for authenticating against the service
413
   * @returns string array of all of the user names
414
   */
415
  public String[] getUsers(String user, String password) 
416
         throws ConnectException
417
  {
418
    String[] users = null;
419

    
420
    // Identify service provider to use
421
    Hashtable env = new Hashtable(11);
422
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
423
            "com.sun.jndi.ldap.LdapCtxFactory");
424
    env.put(Context.REFERRAL, referral);
425
    env.put(Context.PROVIDER_URL, ldapUrl);
426
 
427
    try {
428

    
429
        // Create the initial directory context
430
        DirContext ctx = new InitialDirContext(env);
431

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

    
454
        // initialize users[]; fill users[]
455
        users = new String[uvec.size()];
456
        for (int i=0; i < uvec.size(); i++) {
457
          users[i] = (String)uvec.elementAt(i); 
458
        }
459

    
460
        // Close the context when we're done
461
        ctx.close();
462

    
463
    } catch (NamingException e) {
464
      util.debugMessage("Problem getting users in AuthLdap.getUsers:" + e, 35);
465
      //e.printStackTrace(System.err);
466
      throw new ConnectException(
467
      "Problem getting users in AuthLdap.getUsers:" + e);
468
    }
469

    
470
    return users;
471
  }
472

    
473
  /**
474
   * Get the users for a particular group from the authentication service
475
   *
476
   * @param user the user for authenticating against the service
477
   * @param password the password for authenticating against the service
478
   * @param group the group whose user list should be returned
479
   * @returns string array of the user names belonging to the group
480
   */
481
  public String[] getUsers(String user, String password, String group) 
482
         throws ConnectException
483
  {
484
    String[] users = null;
485

    
486
    // Identify service provider to use
487
    Hashtable env = new Hashtable(11);
488
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
489
            "com.sun.jndi.ldap.LdapCtxFactory");
490
    env.put(Context.REFERRAL, referral);
491
    env.put(Context.PROVIDER_URL, ldapUrl);
492

    
493
    try {
494

    
495
        // Create the initial directory context
496
        DirContext ctx = new InitialDirContext(env);
497

    
498
        // Specify the ids of the attributes to return
499
        String[] attrIDs = {"uniqueMember"};
500

    
501
        Attributes answer = ctx.getAttributes(group, attrIDs);
502

    
503
        Vector uvec = new Vector();
504
        try {
505
            for (NamingEnumeration ae = answer.getAll(); ae.hasMore();) {
506
                Attribute attr = (Attribute)ae.next();
507
                for (NamingEnumeration e = attr.getAll(); 
508
                     e.hasMore();
509
                     uvec.add(e.next()) 
510
                    );
511
            }
512
        } catch (SizeLimitExceededException slee) {
513
            util.debugMessage("LDAP Server size limit exceeded. " +
514
                    "Returning incomplete record set.", 35);
515
        }
516

    
517
        // initialize users[]; fill users[]
518
        users = new String[uvec.size()];
519
        for (int i=0; i < uvec.size(); i++) {
520
          users[i] = (String)uvec.elementAt(i); 
521
        }
522

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

    
526
    } catch (NamingException e) {
527
      util.debugMessage("Problem getting users for a group in " +
528
              "AuthLdap.getUsers:" + e, 30);
529
      throw new ConnectException(
530
      "Problem getting users for a group in AuthLdap.getUsers:" + e);
531
    }
532

    
533
    return users;
534
  }
535

    
536
  /**
537
   * Get all groups from the authentication service
538
   *
539
   * @param user the user for authenticating against the service
540
   * @param password the password for authenticating against the service
541
   * @returns string array of the group names
542
   */
543
  public String[] getGroups(String user, String password) 
544
         throws ConnectException
545
  {
546
      return getGroups(user, password, null);
547
  }
548

    
549
  /**
550
   * Get the groups for a particular user from the authentication service
551
   *
552
   * @param user the user for authenticating against the service
553
   * @param password the password for authenticating against the service
554
   * @param foruser the user whose group list should be returned
555
   * @returns string array of the group names
556
   */
557
  public String[] getGroups(String user, String password, String foruser) 
558
         throws ConnectException
559
  {
560
    Vector uvec = new Vector();
561
    //Pass the username and password to run() method
562
    userName=user;
563
    userPassword=password;
564
    // Identify service provider to use
565
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
566
            "com.sun.jndi.ldap.LdapCtxFactory");
567
    env.put(Context.REFERRAL, "throw");
568
    env.put(Context.PROVIDER_URL, ldapUrl);
569
    try {
570
        // Create the initial directory context
571
        DirContext ctx = new InitialDirContext(env);
572
        // Specify the ids of the attributes to return
573
        String[] attrIDs = {"cn"};
574
        // Specify the attributes to match.
575
        // Groups are objects with attribute objectclass=groupofuniquenames.
576
        // and have attribute uniquemember: uid=foruser,ldapbase.
577
        SearchControls ctls = new SearchControls();
578
        ctls.setReturningAttributes(attrIDs);
579
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
580
        
581
        String filter = null;
582
        String gfilter = "(objectClass=groupOfUniqueNames)";
583
        if (null == foruser) {
584
            filter = gfilter;
585
        } else {
586
            filter = "(& " + gfilter + "(uniqueMember=" + foruser + "))";
587
        }
588
        MetaCatUtil.debugMessage("searching for groups: " + filter, 35);
589
        NamingEnumeration enum = ctx.search(ldapBase, filter, ctls);
590

    
591
        // Print the groups
592
        MetaCatUtil.debugMessage("getting group results.", 50);
593
        while (enum.hasMore()) {
594
          SearchResult sr = (SearchResult)enum.next();          
595
          uvec.add(sr.getName()+","+ldapBase);
596
          MetaCatUtil.debugMessage("group " + sr.getName() + 
597
                                  " added to Group vector", 35);
598
        }
599
        // Close the context when we're done
600
        ctx.close();
601

    
602
    } 
603
    catch (ReferralException re) 
604
    {
605
      refExc = re;
606
      Thread t = new Thread(new GetGroup());
607
      util.debugMessage("Starting thread...", 50);
608
      t.start();
609
      util.debugMessage("sleeping for 5 seconds.", 50);
610
      try
611
      {
612
        Thread.sleep(5000);
613
      }
614
      catch(InterruptedException ie)
615
      {
616
        MetaCatUtil.debugMessage("main thread interrupted: " + ie.getMessage(), 30);
617
      }
618
      //this is a manual override of jndi's hideously long time
619
      //out period.
620
      util.debugMessage("Awake after 5 seconds.", 40);
621
      if (referralContext == null)
622
      {
623
        util.debugMessage("thread timed out...returning groups: " + uvec.toString(), 35);
624
        String groups[] = new String[uvec.size()];
625
        for(int i=0; i<uvec.size(); i++)
626
        {
627
          groups[i] = (String)uvec.elementAt(i);
628
        }
629
        t.interrupt();
630
        return groups;
631
      }
632
      DirContext dc = (DirContext)referralContext;
633
      String[] attrIDs = {"cn"};
634
      // Specify the attributes to match.
635
      // Groups are objects with attribute objectclass=groupofuniquenames.
636
      // and have attribute uniquemember: uid=foruser,ldapbase.
637
      SearchControls ctls = new SearchControls();
638
      ctls.setReturningAttributes(attrIDs);
639
      ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
640
      
641
      String filter = null;
642
      String gfilter = "(objectClass=groupOfUniqueNames)";
643
      if (null == foruser) {
644
          filter = gfilter;
645
      } else {
646
          filter = "(& " + gfilter + "(uniqueMember=" + foruser + "))";
647
      }
648
      
649
      try
650
      {
651
        NamingEnumeration enum = dc.search(ldapBase, filter, ctls);
652
        // Print the groups
653
        while (enum.hasMore()) {
654
          SearchResult sr = (SearchResult)enum.next();
655
          uvec.add(sr.getName()+","+ldapBase);
656
        }
657
        
658
        referralContext.close();
659
        dc.close();
660
      }
661
      catch(NamingException ne)
662
      {
663
        MetaCatUtil.debugMessage("Naming Exception in AuthLdap.getGroups", 30);
664
      }
665
    } catch (NamingException e) {
666
      e.printStackTrace(System.err);
667
      String groups[] = new String[uvec.size()];
668
      for(int i=0; i<uvec.size(); i++)
669
      {
670
        groups[i] = (String)uvec.elementAt(i);
671
      }
672
      return groups;
673
       /*throw new ConnectException(
674
      "Problem getting groups for a user in AuthLdap.getGroups:" + e);*/
675
    } 
676
    
677
    MetaCatUtil.debugMessage("The user is in the following groups: " + 
678
                              uvec.toString(), 35);
679
    String groups[] = new String[uvec.size()];
680
    for(int i=0; i<uvec.size(); i++)
681
    {
682
      groups[i] = (String)uvec.elementAt(i);
683
    }
684
    return groups;
685
  }
686

    
687
  /**
688
   * Get attributes describing a user or group
689
   *
690
   * @param foruser the user for which the attribute list is requested
691
   * @returns HashMap a map of attribute name to a Vector of values
692
   */
693
  public HashMap getAttributes(String foruser) 
694
         throws ConnectException
695
  {
696
    return getAttributes(null, null, foruser);
697
  }
698

    
699
  /**
700
   * Get attributes describing a user or group
701
   *
702
   * @param user the user for authenticating against the service
703
   * @param password the password for authenticating against the service
704
   * @param foruser the user whose attributes should be returned
705
   * @returns HashMap a map of attribute name to a Vector of values
706
   */
707
  public HashMap getAttributes(String user, String password, String foruser) 
708
         throws ConnectException
709
  {
710
    HashMap attributes = new HashMap();
711
    String ldapUrl = this.ldapUrl;
712
    String ldapBase = this.ldapBase;
713
    String userident = foruser;
714
   
715
    // Identify service provider to use
716
    Hashtable env = new Hashtable(11);
717
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
718
        "com.sun.jndi.ldap.LdapCtxFactory");
719
    env.put(Context.REFERRAL, referral);
720
    env.put(Context.PROVIDER_URL, ldapUrl);
721

    
722
    try {
723
      
724
      // Create the initial directory context
725
      DirContext ctx = new InitialDirContext(env);
726
        
727
      // Ask for all attributes of the user 
728
      //Attributes attrs = ctx.getAttributes(userident);
729
      Attributes attrs = ctx.getAttributes(foruser);
730
 
731
      // Print all of the attributes
732
      NamingEnumeration en = attrs.getAll();
733
      while (en.hasMore()) {
734
        Attribute att = (Attribute)en.next();
735
        Vector values = new Vector();
736
        String attName = att.getID();
737
        NamingEnumeration attvalues = att.getAll();
738
        while (attvalues.hasMore()) {
739
          String value = (String)attvalues.next();
740
          values.add(value);
741
        }
742
        attributes.put(attName, values);
743
      }
744
  
745
      // Close the context when we're done
746
      ctx.close();
747
    } catch (NamingException e) {
748
      util.debugMessage("Problem getting attributes in " + 
749
              "AuthLdap.getAttributes:" + e, 35);
750
      throw new ConnectException(
751
      "Problem getting attributes in AuthLdap.getAttributes:" + e);
752
    }
753

    
754
    return attributes;
755
  }
756

    
757
  /**
758
   * Get list of all subtrees holding Metacat's groups and users
759
   * starting from the Metacat LDAP root, 
760
   * i.e. ldap://dev.nceas.ucsb.edu/dc=ecoinformatics,dc=org
761
   */
762
  private Hashtable getSubtrees(String user, String password,
763
                                String ldapUrl, String ldapBase)
764
                throws ConnectException
765
  {
766
    Hashtable trees = new Hashtable();
767

    
768
    // Identify service provider to use
769
    Hashtable env = new Hashtable(11);
770
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
771
            "com.sun.jndi.ldap.LdapCtxFactory");
772
    env.put(Context.REFERRAL, referral);
773
    env.put(Context.PROVIDER_URL, ldapUrl + ldapBase);
774

    
775
    try {
776

    
777
        // Create the initial directory context
778
        DirContext ctx = new InitialDirContext(env);
779

    
780
        // Specify the ids of the attributes to return
781
        String[] attrIDs = {"o","ref"};
782
        SearchControls ctls = new SearchControls();
783
        ctls.setReturningAttributes(attrIDs);
784
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
785
          
786
        // Specify the attributes to match.
787
        // Subtrees from the main server are found as objects with attribute
788
        // objectclass=organization or objectclass=referral to the subtree
789
        // resided on other server.
790
        String filter = "(|(objectclass=organization)(objectclass=referral))";
791

    
792
        // Search for objects in the current context
793
        NamingEnumeration enum = ctx.search("", filter, ctls);
794

    
795
        // Print the subtrees' <ldapURL, baseDN>
796
        while (enum.hasMore()) {
797
          SearchResult sr = (SearchResult)enum.next();
798
          Attributes attrs = sr.getAttributes();
799
          NamingEnumeration enum1 = attrs.getAll(); // "dc" and "ref" attrs
800
          if (enum1.hasMore()) {
801
            Attribute attr = (Attribute)enum1.next();
802
            String attrValue = (String)attr.get();
803
            String attrName = (String)attr.getID();
804
 
805
            if ( enum1.hasMore() ) {
806
              attr = (Attribute)enum1.next();
807
              String refValue = (String)attr.get();
808
              String refName = (String)attr.getID();
809
               if ( ldapBase.startsWith(refName + "=" + refValue) ) {
810
                trees.put(ldapBase,
811
                         attrValue.substring(0,attrValue.lastIndexOf("/")+1) );
812
              } else {
813
                trees.put(refName + "=" + refValue + "," + ldapBase,
814
                         attrValue.substring(0,attrValue.lastIndexOf("/")+1) );
815
              }
816
 
817
            } else if ( ldapBase.startsWith(attrName + "=" + attrValue) ) {
818
                trees.put(ldapBase, ldapUrl);
819
            } else {              
820
                trees.put(attrName + "=" + attrValue + "," + ldapBase, ldapUrl);               
821
            }
822
          }
823
        }
824

    
825
        // Close the context when we're done
826
        ctx.close();
827

    
828
    } catch (NamingException e) {
829
      util.debugMessage("Problem getting subtrees in AuthLdap.getSubtrees:" 
830
                        + e, 30);
831
      throw new ConnectException(
832
      "Problem getting subtrees in AuthLdap.getSubtrees:" + e);
833
    }
834

    
835
    return trees;
836
  }
837

    
838
  /**
839
   * Get all groups and users from authentication scheme.
840
   * The output is formatted in XML.
841
   * @param user the user which requests the information
842
   * @param password the user's password
843
   */
844
  public String getPrincipals(String user, String password)
845
                throws ConnectException
846
  {
847
    StringBuffer out = new StringBuffer();
848
    Vector usersIn = new Vector();
849
    
850
    out.append("<?xml version=\"1.0\"?>\n");
851
    out.append("<principals>\n");
852
    
853
    /*
854
     * get all subtrees first in the current dir context 
855
     * and then the Metacat users under them
856
     */
857
    Hashtable subtrees = getSubtrees(user,password,this.ldapUrl,this.ldapBase);
858
    
859
    Enumeration enum = subtrees.keys();
860
    while ( enum.hasMoreElements() ) {
861
      this.ldapBase = (String)enum.nextElement();
862
      this.ldapUrl = (String)subtrees.get(ldapBase);
863
      
864
      out.append("  <authSystem URI=\"" + 
865
                 this.ldapUrl + this.ldapBase + "\">\n");
866

    
867
      // get all groups for directory context
868
      String[] groups = getGroups(user, password);
869

    
870
      // for the groups and users that belong to them
871
      if ( groups!=null && groups.length > 0 ) {
872
        for (int i=0; i < groups.length; i++ ) {
873
          out.append("    <group>\n");
874
          out.append("      <groupname>" + groups[i] + "</groupname>\n");
875
          String[] usersForGroup = getUsers(user,password,groups[i]);
876
          for (int j=0; j < usersForGroup.length; j++ ) {
877
            usersIn.addElement(usersForGroup[j]);
878
            out.append("      <user>\n");
879
            out.append("        <username>" + usersForGroup[j] + 
880
                        "</username>\n");
881
            out.append("      </user>\n");
882
          }
883
          out.append("    </group>\n");
884
        }
885
      }
886
      // for the users not belonging to any group
887
      String[] users = getUsers(user, password);
888
      for (int j=0; j < users.length; j++ ) {
889
        if ( !usersIn.contains(users[j]) ) {
890
          out.append("    <user>\n");
891
          out.append("      <username>" + users[j] + "</username>\n");
892
          out.append("    </user>\n");
893
        }
894
      }
895
    
896
      out.append("  </authSystem>\n");
897
      if ( !usersIn.isEmpty() ) {
898
        usersIn.removeAllElements();
899
        usersIn.trimToSize();
900
      }
901
    
902
    }
903
    out.append("</principals>");
904
    return out.toString();
905
  }
906

    
907
  /**
908
   * Test method for the class
909
   */
910
  public static void main(String[] args) {
911

    
912
    // Provide a user, such as: "Matt Jones", or "jones"
913
    String user = args[0];
914
    String password = args[1];
915

    
916
    MetaCatUtil.debugMessage("Creating session...", 20);
917
    AuthLdap authservice = new AuthLdap();
918
    MetaCatUtil.debugMessage("Session exists...", 20);
919
                
920
    boolean isValid = false;
921
    try {
922
      MetaCatUtil.debugMessage("Authenticating...", 20);
923
      isValid = authservice.authenticate(user, password);
924
      if (isValid) {
925
        MetaCatUtil.debugMessage("Authentication successful for: " + user, 20 );
926
      } else {
927
        MetaCatUtil.debugMessage("Authentication failed for: " + user, 20);
928
      }
929

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

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

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

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

    
980
      // get all users
981
      if (isValid) {
982
        MetaCatUtil.debugMessage("\nGetting all users ....", 20);
983
        String[] users = authservice.getUsers(user, password);
984
        MetaCatUtil.debugMessage("Users found: " + users.length, 20);
985
        
986
      }
987

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

    
1003
    } catch (ConnectException ce) {
1004
      MetaCatUtil.debugMessage(ce.getMessage(), 30);
1005
    } catch (java.io.IOException ioe) {
1006
      MetaCatUtil.debugMessage("I/O Error writing to file principals.txt", 20);
1007
    }
1008
  }
1009
  
1010
  /**
1011
   * This method will be called by start a thread.
1012
   * It can handle if a referral exception happend.
1013
   */ 
1014
  public void run()
1015
  {
1016
    referralContext = null;
1017
    DirContext refDirContext=null;
1018
    boolean moreReferrals=true;
1019
    String referralInfo=null;
1020
    //set a while loop is because we don't know if a referral excption contains
1021
    //another referral exception
1022
    while (moreReferrals)
1023
    {
1024
      try
1025
      {  
1026
        //revise environment variable
1027
        referralInfo=(String)refExc.getReferralInfo();
1028
        env.put(Context.PROVIDER_URL,refExc.getReferralInfo());
1029
        //System.out.println("referral info: "+referralInfo);
1030
        //if (referralInfo.indexOf("piscoweb")!=-1)
1031
        //{
1032
          //userName=uid+",o=PISCO,dc=piscoweb,dc=org";
1033
        //}
1034
        env.put(Context.INITIAL_CONTEXT_FACTORY, 
1035
                "com.sun.jndi.ldap.LdapCtxFactory");
1036
        //System.out.println("principal: "+userName);
1037
        env.put(Context.SECURITY_PRINCIPAL, userName);
1038
        env.put(Context.SECURITY_CREDENTIALS, userPassword);
1039
        env.put(Context.REFERRAL, "throw");
1040
        //get a context object for referral in the new envriment
1041
        rContext = refExc.getReferralContext(env);
1042
        //casting the context to dircontext and it will create a
1043
        //autherntication or naming exception if DN and password is incorrect
1044
        referralContext=rContext;
1045
        refDirContext=(DirContext)rContext;
1046
        refDirContext.close();
1047
        //get context and jump out the while loop
1048
        moreReferrals=false;
1049
      }//try
1050
      //if referral have another referral excption
1051
      catch (ReferralException re)
1052
      {
1053
        //keep running in while loop
1054
        moreReferrals=true;
1055
        //assign refExc to new referral exception re
1056
        refExc=re;
1057
      }
1058
      //catch a authentication exception
1059
      catch (AuthenticationException ae)
1060
      {
1061
        util.debugMessage("Error running referral handler thread: " + 
1062
                          ae.getMessage(), 20);
1063
        //check if has another referral
1064
        moreReferrals=refExc.skipReferral();
1065
        //don't get the context
1066
        referralContext = null;
1067
      }
1068
      //catch a naming exception
1069
      catch (NamingException ne)
1070
      {
1071
        util.debugMessage("Error running referral handler thread: " + 
1072
                          ne.getMessage(), 20);
1073
        //check if has another referral
1074
        moreReferrals=refExc.skipReferral();
1075
        //don't get context
1076
        referralContext = null;		
1077
      }
1078
    }//while
1079
  }//run()
1080
  
1081
  private class GetGroup implements Runnable
1082
  {
1083
    public void run()
1084
    {
1085
      referralContext = null;
1086
      MetaCatUtil.debugMessage("getting groups context", 50);
1087
      DirContext refDirContext=null;
1088
      boolean moreReferrals=true;
1089
      //set a while loop is because we don't know if a referral excption 
1090
      //contains another referral exception
1091
      while (moreReferrals)
1092
      {
1093
        try
1094
        {
1095
          //revise environment variable
1096
          String refInfo = null;
1097
          refInfo = (String)refExc.getReferralInfo();
1098
          if(refInfo != null)
1099
          {
1100
            MetaCatUtil.debugMessage("Referral in thread to: " + 
1101
                              refInfo.toString(), 40);
1102
          }
1103
          else
1104
          {
1105
            MetaCatUtil.debugMessage("getting refInfo Manually", 50);
1106
            refInfo = (String)refExc.getReferralContext().getEnvironment().
1107
                                                  get(Context.PROVIDER_URL);
1108
          }
1109
          MetaCatUtil.debugMessage("refInfo: " + refInfo, 40);
1110
        
1111
          env.put(Context.INITIAL_CONTEXT_FACTORY, 
1112
              "com.sun.jndi.ldap.LdapCtxFactory");
1113
          env.put(Context.REFERRAL, "throw");
1114
          env.put(Context.PROVIDER_URL, refInfo);
1115
        
1116
          MetaCatUtil.debugMessage("creating referralContext", 40);
1117
          referralContext = new InitialDirContext(env);
1118
          MetaCatUtil.debugMessage("referralContext created", 40);
1119
          //get context and jump out the while loop
1120
          moreReferrals=false;
1121
        }//try
1122
        catch (ReferralException re)
1123
        {
1124
          //keep running in while loop
1125
          moreReferrals=true;
1126
          //assign refExc to new referral exception re
1127
          refExc=re;
1128
        }
1129
        catch (AuthenticationException ae)
1130
        {
1131
          util.debugMessage("Error running referral handler thread: " + 
1132
                          ae.getMessage(), 50);
1133
          //check if has another referral
1134
          moreReferrals=refExc.skipReferral();
1135
          //don't get the context
1136
          referralContext = null;
1137
        }
1138
        catch (NamingException ne)
1139
        {
1140
          util.debugMessage("Error running referral handler thread: " + 
1141
                          ne.getMessage(), 50);
1142
          //check if has another referral
1143
          moreReferrals=refExc.skipReferral();
1144
          //don't get context
1145
          referralContext = null;		
1146
        }
1147
      }//while
1148
    }//run()
1149
 }
1150
}
(12-12/58)