Project

General

Profile

1 503 bojilova
/**
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$'
12
 *     '$Date$'
13
 * '$Revision$'
14 669 jones
 *
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 503 bojilova
 */
29
30
package edu.ucsb.nceas.metacat;
31
32
import java.net.ConnectException;
33
import javax.naming.AuthenticationException;
34
import javax.naming.Context;
35 787 bojilova
import javax.naming.NamingEnumeration;
36
import javax.naming.NamingException;
37 873 jones
import javax.naming.SizeLimitExceededException;
38 730 bojilova
import javax.naming.InitialContext;
39 802 bojilova
import javax.naming.directory.InvalidSearchFilterException;
40 503 bojilova
import javax.naming.directory.Attribute;
41
import javax.naming.directory.Attributes;
42 504 jones
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 723 bojilova
import javax.naming.directory.SearchControls;
48 868 berkley
import javax.naming.ReferralException;
49 787 bojilova
import javax.naming.ldap.*;
50 503 bojilova
import java.util.Iterator;
51
import java.util.HashMap;
52
import java.util.Hashtable;
53 730 bojilova
import java.util.Enumeration;
54 503 bojilova
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 893 berkley
public class AuthLdap implements AuthInterface, Runnable {
64 787 bojilova
  private MetaCatUtil util = new MetaCatUtil();
65 728 bojilova
  private String ldapUrl;
66 787 bojilova
  private String ldapsUrl;
67 728 bojilova
  private String ldapBase;
68 865 jones
  private String referral;
69 934 tao
  private Context referralContext;
70 893 berkley
  Hashtable env = new Hashtable(11);
71
  private Context rContext;
72 934 tao
  private String userName;
73
  private String userPassword;
74 893 berkley
  ReferralException refExc;
75 503 bojilova
76 728 bojilova
  /**
77
   * Construct an AuthLdap
78
   */
79
  public AuthLdap() {
80
    // Read LDAP URI for directory service information
81 787 bojilova
    this.ldapUrl = MetaCatUtil.getOption("ldapurl");
82
    this.ldapsUrl = MetaCatUtil.getOption("ldapsurl");
83
    this.ldapBase = MetaCatUtil.getOption("ldapbase");
84 865 jones
    this.referral = MetaCatUtil.getOption("referral");
85 728 bojilova
  }
86
87 503 bojilova
  /**
88
   * Determine if a user/password are valid according to the authentication
89
   * service.
90
   *
91
   * @param user the name of the principal to authenticate
92
   * @param password the password to use for authentication
93
   * @returns boolean true if authentication successful, false otherwise
94
   */
95
  public boolean authenticate(String user, String password)
96
                    throws ConnectException
97
  {
98 730 bojilova
    String ldapUrl = this.ldapUrl;
99 787 bojilova
    String ldapsUrl = this.ldapsUrl;
100 730 bojilova
    String ldapBase = this.ldapBase;
101 503 bojilova
    boolean authenticated = false;
102 802 bojilova
    String identifier = user;
103 503 bojilova
104
    try {
105 852 jones
        // Check the usename as passed in
106
        authenticated = ldapAuthenticate(identifier, password);
107 740 bojilova
    } catch (NullPointerException e) {
108
      util.debugMessage("NullPointerException b' password is null");
109
      util.debugMessage("NullPointerException while authenticating in " +
110
                        "AuthLdap.authenticate: " + e);
111
      throw new ConnectException(
112
      "NullPointerException while authenticating in " +
113
                        "AuthLdap.authenticate: " + e);
114 503 bojilova
    } catch (NamingException e) {
115 675 berkley
      util.debugMessage("Naming exception while authenticating in " +
116
                        "AuthLdap.authenticate: " + e);
117 852 jones
      e.printStackTrace();
118 730 bojilova
    } catch (Exception e) {
119 934 tao
      util.debugMessage(e.getMessage());
120 503 bojilova
    }
121
    return authenticated;
122
  }
123
124
  /**
125 852 jones
   * Connect to the LDAP directory and do the authentication using the
126
   * username and password as passed into the routine.
127
   *
128
   * @param identifier the distinguished name to check against LDAP
129
   * @param password the password for authentication
130
   */
131
  private boolean ldapAuthenticate(String identifier, String password)
132
            throws ConnectException, NamingException, NullPointerException
133
  {
134 867 berkley
    double totStartTime = System.currentTimeMillis();
135 852 jones
    boolean authenticated = false;
136 927 tao
    if (identifier != null && !password.equals(""))
137 934 tao
    {
138 852 jones
139 934 tao
        //Pass the username and password to run() method
140
        userName=identifier;
141
        userPassword=password;
142
        // Identify service provider to use
143 852 jones
        Hashtable env = new Hashtable(11);
144
        env.put(Context.INITIAL_CONTEXT_FACTORY,
145 934 tao
              "com.sun.jndi.ldap.LdapCtxFactory");
146 866 berkley
        env.put(Context.REFERRAL, "throw");
147 852 jones
        env.put(Context.PROVIDER_URL, ldapsUrl + ldapBase);
148 927 tao
        if ( !ldapsUrl.equals(ldapUrl) )
149 934 tao
        {
150 852 jones
          // ldap is set on default port 389
151
          // ldaps is set on second port - 636 by default
152
          env.put(Context.SECURITY_PROTOCOL, "ssl");
153
        }
154
        env.put(Context.SECURITY_AUTHENTICATION, "simple");
155
        env.put(Context.SECURITY_PRINCIPAL, identifier);
156
        env.put(Context.SECURITY_CREDENTIALS, password);
157
        // If our auth credentials are invalid, an exception will be thrown
158
        DirContext ctx = null;
159 927 tao
        try
160
        {
161 852 jones
          double startTime = System.currentTimeMillis();
162 927 tao
          //Here to check the autheration
163 852 jones
          ctx = new InitialDirContext(env);
164
          double stopTime = System.currentTimeMillis();
165 934 tao
          util.debugMessage("Connection time thru " + ldapsUrl + " was: " +
166 852 jones
                             (stopTime-startTime)/1000 + " seconds.");
167
          authenticated = true;
168
          //tls.close();
169
          ctx.close();
170
          this.ldapUrl = ldapUrl;
171
          this.ldapBase = ldapBase;
172
          //break;
173 927 tao
        }
174
        catch (AuthenticationException ae)
175
        {
176 852 jones
          authenticated = false;
177 927 tao
          if ( ctx != null )
178
          {
179 852 jones
            ctx.close();
180
          }
181 868 berkley
        }
182
        catch (javax.naming.InvalidNameException ine)
183
        {
184 934 tao
            util.debugMessage("An invalid DN was provided!");
185 868 berkley
        }
186 935 tao
        catch (javax.naming.ReferralException re)
187 868 berkley
        {
188 934 tao
          util.debugMessage("referral during authentication");
189
          util.debugMessage("Referral information: "+re.getReferralInfo());
190
          try
191 868 berkley
          {
192 893 berkley
            refExc = re;
193 934 tao
194 893 berkley
            Thread t = new Thread(this);
195
            t.start();
196 915 berkley
            Thread.sleep(5000); //this is a manual override of ldap's
197
                                //hideously long time out period.
198 934 tao
            util.debugMessage("Awake after 5 seconds.");
199 935 tao
            if (referralContext == null)
200 893 berkley
            {
201
              t.interrupt();
202
              authenticated = false;
203
            }
204
            else
205
            {
206
              authenticated = true;
207 934 tao
208 893 berkley
            }
209 868 berkley
          }
210 935 tao
          catch (Exception e)
211 868 berkley
          {
212
            authenticated = false;
213
          }
214
        }
215 927 tao
    }
216 934 tao
    else
217
    {
218 852 jones
        util.debugMessage("User not found");
219
    }
220 867 berkley
    double totStopTime = System.currentTimeMillis();
221 934 tao
    util.debugMessage("total ldap authentication time: " +
222 867 berkley
                      (totStopTime - totStartTime)/1000 + " seconds");
223 852 jones
    return authenticated;
224
  }
225 868 berkley
226 934 tao
227 852 jones
228
  /**
229 730 bojilova
   * Get the identifying name for a given userid or name.  This is the name
230
   * that is used in conjunction withthe LDAP BaseDN to create a
231
   * distinguished name (dn) for the record
232
   *
233
   * @param user the user for which the identifying name is requested
234
   * @returns String the identifying name for the user,
235
   *          or null if not found
236
   */
237 934 tao
  private String getIdentifyingName(String user, String ldapUrl,
238
                                    String ldapBase) throws NamingException
239 730 bojilova
  {
240
    String identifier = null;
241
    // Identify service provider to use
242
    Hashtable env = new Hashtable(11);
243
    env.put(Context.INITIAL_CONTEXT_FACTORY,
244
            "com.sun.jndi.ldap.LdapCtxFactory");
245 866 berkley
    util.debugMessage("setting referrals to: " + referral);
246 865 jones
    env.put(Context.REFERRAL, referral);
247 730 bojilova
    env.put(Context.PROVIDER_URL, ldapUrl + ldapBase);
248 867 berkley
    //    non-secure LDAP context; dn are publicly readable
249 730 bojilova
    try {
250 867 berkley
251 730 bojilova
      // Bind to the LDAP server, in order to search for the right
252
      // distinguished name (dn) based on userid (uid) or common name (cn)
253
      DirContext ctx = new InitialDirContext(env);
254
      SearchControls ctls = new SearchControls();
255
      ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
256 934 tao
      // Search for the user id or name using the uid, then cn and sn
257
      //attributes
258 730 bojilova
      // If we find a record, determine the dn for the record
259
260 802 bojilova
      String filter = "(" + user + ")";
261
      NamingEnumeration answer;
262
      try {
263
        answer = ctx.search("", filter, ctls);
264
        if (answer.hasMore()) {
265
          SearchResult sr = (SearchResult)answer.next();
266
          identifier = sr.getName();
267
          if ( !sr.isRelative() ) {
268 934 tao
            this.ldapUrl = identifier.substring(0,
269
                                                identifier.lastIndexOf("/")+1);
270 802 bojilova
            this.ldapBase = identifier.substring(identifier.indexOf(",")+1);
271
            identifier = identifier.substring(identifier.lastIndexOf("/")+1,
272
                                              identifier.indexOf(","));
273
          }
274
          util.debugMessage("Found: " + identifier);
275
          return identifier;
276
        }
277
      } catch (InvalidSearchFilterException e) {}
278
      filter = "(uid=" + user + ")";
279
      answer = ctx.search("", filter, ctls);
280 730 bojilova
      if (answer.hasMore()) {
281
        SearchResult sr = (SearchResult)answer.next();
282
        identifier = sr.getName();
283
        if ( !sr.isRelative() ) {
284
          this.ldapUrl = identifier.substring(0,identifier.lastIndexOf("/")+1);
285
          this.ldapBase = identifier.substring(identifier.indexOf(",")+1);
286
          identifier = identifier.substring(identifier.lastIndexOf("/")+1,
287
                                            identifier.indexOf(","));
288
        }
289
        util.debugMessage("Found: " + identifier);
290
      } else {
291
        filter = "(cn=" + user + ")";
292
        NamingEnumeration answer2 = ctx.search("", filter, ctls);
293
        if (answer2.hasMore()) {
294
          SearchResult sr = (SearchResult)answer2.next();
295
          identifier = sr.getName();
296
          if ( !sr.isRelative() ) {
297 934 tao
            this.ldapUrl = identifier.substring(0,
298
                                                identifier.lastIndexOf("/")+1);
299 730 bojilova
            this.ldapBase = identifier.substring(identifier.indexOf(",")+1);
300
            identifier = identifier.substring(identifier.lastIndexOf("/")+1,
301
                                              identifier.indexOf(","));
302
          }
303
          util.debugMessage("Found: " + identifier);
304
        } else {
305
          filter = "(sn=" + user + ")";
306
          NamingEnumeration answer3 = ctx.search("", filter, ctls);
307
          if (answer3.hasMore()) {
308
            SearchResult sr = (SearchResult)answer3.next();
309
            identifier = sr.getName();
310
            if ( !sr.isRelative() ) {
311 934 tao
              this.ldapUrl = identifier.substring(0,
312
                                                identifier.lastIndexOf("/")+1);
313 730 bojilova
              this.ldapBase = identifier.substring(identifier.indexOf(",")+1);
314
              identifier = identifier.substring(identifier.lastIndexOf("/")+1,
315
                                                identifier.indexOf(","));
316
            }
317
            util.debugMessage("Found: " + identifier);
318
          }
319
        }
320
      }
321
      // Close the context when we're done the initial search
322
      ctx.close();
323
    } catch (NamingException e) {
324
      util.debugMessage("Naming exception while getting dn: " + e);
325
      throw new NamingException(
326
      "Naming exception in AuthLdap.getIdentifyingName: " + e);
327
    }
328
    return identifier;
329
  }
330
331
  /**
332 503 bojilova
   * Get all users from the authentication service
333 871 jones
   *
334
   * @param user the user for authenticating against the service
335
   * @param password the password for authenticating against the service
336
   * @returns string array of all of the user names
337 503 bojilova
   */
338 514 jones
  public String[] getUsers(String user, String password)
339
         throws ConnectException
340 503 bojilova
  {
341 723 bojilova
    String[] users = null;
342
343
    // Identify service provider to use
344
    Hashtable env = new Hashtable(11);
345
    env.put(Context.INITIAL_CONTEXT_FACTORY,
346
            "com.sun.jndi.ldap.LdapCtxFactory");
347 865 jones
    env.put(Context.REFERRAL, referral);
348 871 jones
    env.put(Context.PROVIDER_URL, ldapUrl);
349 934 tao
350 723 bojilova
    try {
351
352
        // Create the initial directory context
353
        DirContext ctx = new InitialDirContext(env);
354
355 726 bojilova
        // Specify the attributes to match.
356
        // Users are objects that have the attribute objectclass=InetOrgPerson.
357 871 jones
        SearchControls ctls = new SearchControls();
358
        String[] attrIDs = {"dn"};
359
        ctls.setReturningAttributes(attrIDs);
360
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
361 873 jones
        //ctls.setCountLimit(1000);
362 871 jones
        String filter = "(objectClass=inetOrgPerson)";
363
        NamingEnumeration enum = ctx.search(ldapBase, filter, ctls);
364
365 873 jones
        // Store the users in a vector
366 723 bojilova
        Vector uvec = new Vector();
367 873 jones
        try {
368
            while (enum.hasMore()) {
369
                SearchResult sr = (SearchResult)enum.next();
370
                uvec.add(sr.getName()+","+ldapBase);
371
            }
372
        } catch (SizeLimitExceededException slee) {
373
            util.debugMessage("LDAP Server size limit exceeded. " +
374
                    "Returning incomplete record set.");
375 723 bojilova
        }
376
377
        // initialize users[]; fill users[]
378
        users = new String[uvec.size()];
379
        for (int i=0; i < uvec.size(); i++) {
380
          users[i] = (String)uvec.elementAt(i);
381
        }
382
383
        // Close the context when we're done
384
        ctx.close();
385
386
    } catch (NamingException e) {
387 934 tao
      util.debugMessage("Problem getting users in AuthLdap.getUsers:" + e);
388 871 jones
      e.printStackTrace(System.err);
389 723 bojilova
      throw new ConnectException(
390 728 bojilova
      "Problem getting users in AuthLdap.getUsers:" + e);
391 723 bojilova
    }
392
393
    return users;
394 503 bojilova
  }
395
396
  /**
397
   * Get the users for a particular group from the authentication service
398 871 jones
   *
399
   * @param user the user for authenticating against the service
400
   * @param password the password for authenticating against the service
401
   * @param group the group whose user list should be returned
402
   * @returns string array of the user names belonging to the group
403 503 bojilova
   */
404 514 jones
  public String[] getUsers(String user, String password, String group)
405
         throws ConnectException
406 503 bojilova
  {
407 723 bojilova
    String[] users = null;
408
409
    // Identify service provider to use
410
    Hashtable env = new Hashtable(11);
411
    env.put(Context.INITIAL_CONTEXT_FACTORY,
412
            "com.sun.jndi.ldap.LdapCtxFactory");
413 865 jones
    env.put(Context.REFERRAL, referral);
414 871 jones
    env.put(Context.PROVIDER_URL, ldapUrl);
415 723 bojilova
416
    try {
417
418
        // Create the initial directory context
419
        DirContext ctx = new InitialDirContext(env);
420
421
        // Specify the ids of the attributes to return
422 871 jones
        String[] attrIDs = {"uniqueMember"};
423 723 bojilova
424 871 jones
        Attributes answer = ctx.getAttributes(group, attrIDs);
425 723 bojilova
426
        Vector uvec = new Vector();
427 873 jones
        try {
428
            for (NamingEnumeration ae = answer.getAll(); ae.hasMore();) {
429
                Attribute attr = (Attribute)ae.next();
430
                for (NamingEnumeration e = attr.getAll();
431
                     e.hasMore();
432
                     uvec.add(e.next())
433
                    );
434
            }
435
        } catch (SizeLimitExceededException slee) {
436
            util.debugMessage("LDAP Server size limit exceeded. " +
437
                    "Returning incomplete record set.");
438 723 bojilova
        }
439
440
        // initialize users[]; fill users[]
441
        users = new String[uvec.size()];
442
        for (int i=0; i < uvec.size(); i++) {
443
          users[i] = (String)uvec.elementAt(i);
444
        }
445
446
        // Close the context when we're done
447
        ctx.close();
448
449
    } catch (NamingException e) {
450 934 tao
      util.debugMessage("Problem getting users for a group in " +
451 871 jones
              "AuthLdap.getUsers:" + e);
452 723 bojilova
      throw new ConnectException(
453 728 bojilova
      "Problem getting users for a group in AuthLdap.getUsers:" + e);
454 723 bojilova
    }
455
456
    return users;
457 503 bojilova
  }
458
459
  /**
460
   * Get all groups from the authentication service
461 871 jones
   *
462
   * @param user the user for authenticating against the service
463
   * @param password the password for authenticating against the service
464
   * @returns string array of the group names
465 503 bojilova
   */
466 514 jones
  public String[] getGroups(String user, String password)
467
         throws ConnectException
468 503 bojilova
  {
469 871 jones
      return getGroups(user, password, null);
470 503 bojilova
  }
471
472
  /**
473
   * Get the groups for a particular user from the authentication service
474 871 jones
   *
475
   * @param user the user for authenticating against the service
476
   * @param password the password for authenticating against the service
477
   * @param foruser the user whose group list should be returned
478
   * @returns string array of the group names
479 503 bojilova
   */
480 514 jones
  public String[] getGroups(String user, String password, String foruser)
481
         throws ConnectException
482 503 bojilova
  {
483 723 bojilova
    String[] groups = null;
484
485
    // Identify service provider to use
486
    env.put(Context.INITIAL_CONTEXT_FACTORY,
487
            "com.sun.jndi.ldap.LdapCtxFactory");
488 888 berkley
    env.put(Context.REFERRAL, "throw");
489 871 jones
    env.put(Context.PROVIDER_URL, ldapUrl);
490 723 bojilova
    try {
491
        // Create the initial directory context
492
        DirContext ctx = new InitialDirContext(env);
493
        // Specify the ids of the attributes to return
494 726 bojilova
        String[] attrIDs = {"cn"};
495 723 bojilova
        // Specify the attributes to match.
496 726 bojilova
        // Groups are objects with attribute objectclass=groupofuniquenames.
497 842 bojilova
        // and have attribute uniquemember: uid=foruser,ldapbase.
498 871 jones
        SearchControls ctls = new SearchControls();
499
        ctls.setReturningAttributes(attrIDs);
500
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
501
502
        String filter = null;
503
        String gfilter = "(objectClass=groupOfUniqueNames)";
504
        if (null == foruser) {
505
            filter = gfilter;
506
        } else {
507
            filter = "(& " + gfilter + "(uniqueMember=" + foruser + "))";
508
        }
509
        NamingEnumeration enum = ctx.search(ldapBase, filter, ctls);
510
511
        // Print the groups
512 723 bojilova
        Vector uvec = new Vector();
513
        while (enum.hasMore()) {
514
          SearchResult sr = (SearchResult)enum.next();
515 871 jones
          uvec.add(sr.getName()+","+ldapBase);
516 723 bojilova
        }
517 888 berkley
518 723 bojilova
519
        // initialize groups[] and fill it
520
        groups = new String[uvec.size()];
521
        for (int i=0; i < uvec.size(); i++) {
522
          groups[i] = (String)uvec.elementAt(i);
523
        }
524
525
        // Close the context when we're done
526
        ctx.close();
527
528 935 tao
    } catch (ReferralException re) {
529 888 berkley
      try
530
      {
531 893 berkley
        refExc = re;
532
        Thread t = new Thread(this);
533 934 tao
        util.debugMessage("Starting thread...");
534 893 berkley
        t.start();
535 934 tao
        util.debugMessage("sleeping for 5 seconds.");
536
        Thread.sleep(5000);
537
        //this is a manual override of ldap's hideously long time
538
        //out period.
539
        util.debugMessage("Awake after 5 seconds.");
540 935 tao
        if (referralContext == null)
541 893 berkley
        {
542
          t.interrupt();
543
          return null;
544
        }
545
        DirContext dc = (DirContext)referralContext;
546 888 berkley
        String[] attrIDs = {"cn"};
547
        // Specify the attributes to match.
548
        // Groups are objects with attribute objectclass=groupofuniquenames.
549
        // and have attribute uniquemember: uid=foruser,ldapbase.
550
        SearchControls ctls = new SearchControls();
551
        ctls.setReturningAttributes(attrIDs);
552
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
553
554
        String filter = null;
555
        String gfilter = "(objectClass=groupOfUniqueNames)";
556
        if (null == foruser) {
557
            filter = gfilter;
558
        } else {
559
            filter = "(& " + gfilter + "(uniqueMember=" + foruser + "))";
560
        }
561
        NamingEnumeration enum = dc.search(ldapBase, filter, ctls);
562
563
        // Print the groups
564
        Vector uvec = new Vector();
565
        while (enum.hasMore()) {
566
          SearchResult sr = (SearchResult)enum.next();
567
          uvec.add(sr.getName()+","+ldapBase);
568
        }
569
570
        // initialize groups[] and fill it
571
        groups = new String[uvec.size()];
572
        for (int i=0; i < uvec.size(); i++) {
573
          groups[i] = (String)uvec.elementAt(i);
574
        }
575 893 berkley
        referralContext.close();
576 888 berkley
        dc.close();
577
      }
578 935 tao
      catch (Exception e)
579 888 berkley
      {
580
        return groups;
581
      }
582 723 bojilova
    } catch (NamingException e) {
583 868 berkley
      e.printStackTrace(System.err);
584 723 bojilova
      throw new ConnectException(
585 728 bojilova
      "Problem getting groups for a user in AuthLdap.getGroups:" + e);
586 888 berkley
    }
587 723 bojilova
    return groups;
588 503 bojilova
  }
589
590
  /**
591
   * Get attributes describing a user or group
592
   *
593 871 jones
   * @param foruser the user for which the attribute list is requested
594 503 bojilova
   * @returns HashMap a map of attribute name to a Vector of values
595
   */
596 514 jones
  public HashMap getAttributes(String foruser)
597 503 bojilova
         throws ConnectException
598
  {
599 514 jones
    return getAttributes(null, null, foruser);
600 503 bojilova
  }
601
602
  /**
603
   * Get attributes describing a user or group
604
   *
605 871 jones
   * @param user the user for authenticating against the service
606 503 bojilova
   * @param password the password for authenticating against the service
607 871 jones
   * @param foruser the user whose attributes should be returned
608 503 bojilova
   * @returns HashMap a map of attribute name to a Vector of values
609
   */
610 514 jones
  public HashMap getAttributes(String user, String password, String foruser)
611 503 bojilova
         throws ConnectException
612
  {
613
    HashMap attributes = new HashMap();
614 802 bojilova
    String ldapUrl = this.ldapUrl;
615
    String ldapBase = this.ldapBase;
616
    String userident = foruser;
617 934 tao
618 503 bojilova
    // Identify service provider to use
619
    Hashtable env = new Hashtable(11);
620
    env.put(Context.INITIAL_CONTEXT_FACTORY,
621
        "com.sun.jndi.ldap.LdapCtxFactory");
622 865 jones
    env.put(Context.REFERRAL, referral);
623 871 jones
    env.put(Context.PROVIDER_URL, ldapUrl);
624 503 bojilova
625
    try {
626 787 bojilova
627 503 bojilova
      // Create the initial directory context
628
      DirContext ctx = new InitialDirContext(env);
629
630 504 jones
      // Ask for all attributes of the user
631 871 jones
      //Attributes attrs = ctx.getAttributes(userident);
632
      Attributes attrs = ctx.getAttributes(foruser);
633 723 bojilova
634 503 bojilova
      // Print all of the attributes
635
      NamingEnumeration en = attrs.getAll();
636
      while (en.hasMore()) {
637
        Attribute att = (Attribute)en.next();
638
        Vector values = new Vector();
639
        String attName = att.getID();
640
        NamingEnumeration attvalues = att.getAll();
641
        while (attvalues.hasMore()) {
642
          String value = (String)attvalues.next();
643
          values.add(value);
644
        }
645
        attributes.put(attName, values);
646
      }
647
648
      // Close the context when we're done
649
      ctx.close();
650
    } catch (NamingException e) {
651 934 tao
      util.debugMessage("Problem getting attributes in " +
652 871 jones
              "AuthLdap.getAttributes:" + e);
653 723 bojilova
      throw new ConnectException(
654
      "Problem getting attributes in AuthLdap.getAttributes:" + e);
655 503 bojilova
    }
656
657
    return attributes;
658
  }
659
660
  /**
661 730 bojilova
   * Get list of all subtrees holding Metacat's groups and users
662
   * starting from the Metacat LDAP root,
663
   * i.e. ldap://dev.nceas.ucsb.edu/dc=ecoinformatics,dc=org
664 504 jones
   */
665 730 bojilova
  private Hashtable getSubtrees(String user, String password,
666
                                String ldapUrl, String ldapBase)
667
                throws ConnectException
668 504 jones
  {
669 730 bojilova
    Hashtable trees = new Hashtable();
670 504 jones
671
    // Identify service provider to use
672
    Hashtable env = new Hashtable(11);
673 730 bojilova
    env.put(Context.INITIAL_CONTEXT_FACTORY,
674 504 jones
            "com.sun.jndi.ldap.LdapCtxFactory");
675 865 jones
    env.put(Context.REFERRAL, referral);
676 504 jones
    env.put(Context.PROVIDER_URL, ldapUrl + ldapBase);
677
678
    try {
679
680 730 bojilova
        // Create the initial directory context
681
        DirContext ctx = new InitialDirContext(env);
682
683
        // Specify the ids of the attributes to return
684
        String[] attrIDs = {"o","ref"};
685
        SearchControls ctls = new SearchControls();
686
        ctls.setReturningAttributes(attrIDs);
687
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
688
689
        // Specify the attributes to match.
690
        // Subtrees from the main server are found as objects with attribute
691
        // objectclass=organization or objectclass=referral to the subtree
692
        // resided on other server.
693
        String filter = "(|(objectclass=organization)(objectclass=referral))";
694
695
        // Search for objects in the current context
696
        NamingEnumeration enum = ctx.search("", filter, ctls);
697
698
        // Print the subtrees' <ldapURL, baseDN>
699
        while (enum.hasMore()) {
700
          SearchResult sr = (SearchResult)enum.next();
701
          Attributes attrs = sr.getAttributes();
702
          NamingEnumeration enum1 = attrs.getAll(); // "dc" and "ref" attrs
703
          if (enum1.hasMore()) {
704
            Attribute attr = (Attribute)enum1.next();
705
            String attrValue = (String)attr.get();
706
            String attrName = (String)attr.getID();
707 934 tao
708 730 bojilova
            if ( enum1.hasMore() ) {
709
              attr = (Attribute)enum1.next();
710
              String refValue = (String)attr.get();
711
              String refName = (String)attr.getID();
712 934 tao
               if ( ldapBase.startsWith(refName + "=" + refValue) ) {
713 730 bojilova
                trees.put(ldapBase,
714 934 tao
                         attrValue.substring(0,attrValue.lastIndexOf("/")+1) );
715 730 bojilova
              } else {
716
                trees.put(refName + "=" + refValue + "," + ldapBase,
717 934 tao
                         attrValue.substring(0,attrValue.lastIndexOf("/")+1) );
718 730 bojilova
              }
719 934 tao
720 730 bojilova
            } else if ( ldapBase.startsWith(attrName + "=" + attrValue) ) {
721
                trees.put(ldapBase, ldapUrl);
722
            } else {
723 934 tao
                trees.put(attrName + "=" + attrValue + "," + ldapBase, ldapUrl);
724 730 bojilova
            }
725 504 jones
          }
726
        }
727 730 bojilova
728
        // Close the context when we're done
729
        ctx.close();
730
731 504 jones
    } catch (NamingException e) {
732 934 tao
      util.debugMessage("Problem getting subtrees in AuthLdap.getSubtrees:"
733
                        + e);
734 730 bojilova
      throw new ConnectException(
735
      "Problem getting subtrees in AuthLdap.getSubtrees:" + e);
736 504 jones
    }
737
738 730 bojilova
    return trees;
739 504 jones
  }
740
741
  /**
742 730 bojilova
   * Get all groups and users from authentication scheme.
743 725 bojilova
   * The output is formatted in XML.
744 730 bojilova
   * @param user the user which requests the information
745
   * @param password the user's password
746 725 bojilova
   */
747 730 bojilova
  public String getPrincipals(String user, String password)
748 725 bojilova
                throws ConnectException
749
  {
750
    StringBuffer out = new StringBuffer();
751 726 bojilova
    Vector usersIn = new Vector();
752 725 bojilova
753
    out.append("<?xml version=\"1.0\"?>\n");
754 730 bojilova
    out.append("<principals>\n");
755 725 bojilova
756 730 bojilova
    /*
757
     * get all subtrees first in the current dir context
758
     * and then the Metacat users under them
759
     */
760
    Hashtable subtrees = getSubtrees(user,password,this.ldapUrl,this.ldapBase);
761
762
    Enumeration enum = subtrees.keys();
763
    while ( enum.hasMoreElements() ) {
764
      this.ldapBase = (String)enum.nextElement();
765
      this.ldapUrl = (String)subtrees.get(ldapBase);
766
767
      out.append("  <authSystem URI=\"" +
768
                 this.ldapUrl + this.ldapBase + "\">\n");
769
770
      // get all groups for directory context
771
      String[] groups = getGroups(user, password);
772
773
      // for the groups and users that belong to them
774
      if ( groups.length > 0 ) {
775
        for (int i=0; i < groups.length; i++ ) {
776
          out.append("    <group>\n");
777 802 bojilova
          out.append("      <groupname>" + groups[i] + "</groupname>\n");
778 730 bojilova
          String[] usersForGroup = getUsers(user,password,groups[i]);
779
          for (int j=0; j < usersForGroup.length; j++ ) {
780
            usersIn.addElement(usersForGroup[j]);
781
            out.append("      <user>\n");
782 934 tao
            out.append("        <username>" + usersForGroup[j] +
783
                        "</username>\n");
784 730 bojilova
            out.append("      </user>\n");
785
          }
786
          out.append("    </group>\n");
787
        }
788
      }
789
      // for the users not belonging to any group
790
      String[] users = getUsers(user, password);
791
      for (int j=0; j < users.length; j++ ) {
792
        if ( !usersIn.contains(users[j]) ) {
793 725 bojilova
          out.append("    <user>\n");
794 802 bojilova
          out.append("      <username>" + users[j] + "</username>\n");
795 725 bojilova
          out.append("    </user>\n");
796
        }
797
      }
798 730 bojilova
799
      out.append("  </authSystem>\n");
800
      if ( !usersIn.isEmpty() ) {
801
        usersIn.removeAllElements();
802
        usersIn.trimToSize();
803 725 bojilova
      }
804 730 bojilova
805 725 bojilova
    }
806
    out.append("</principals>");
807
    return out.toString();
808
  }
809
810
  /**
811 503 bojilova
   * Test method for the class
812
   */
813
  public static void main(String[] args) {
814
815 504 jones
    // Provide a user, such as: "Matt Jones", or "jones"
816 503 bojilova
    String user = args[0];
817
    String password = args[1];
818
819
    AuthLdap authservice = new AuthLdap();
820
821 873 jones
822 503 bojilova
    boolean isValid = false;
823
    try {
824
      isValid = authservice.authenticate(user, password);
825
      if (isValid) {
826 934 tao
        MetaCatUtil.debugMessage("Authentication successful for: " + user );
827 503 bojilova
      } else {
828 934 tao
        MetaCatUtil.debugMessage("Authentication failed for: " + user);
829 503 bojilova
      }
830 725 bojilova
831 871 jones
      // Get attributes for the user
832 503 bojilova
      if (isValid) {
833 934 tao
        MetaCatUtil.debugMessage("\nGetting attributes for user....");
834 871 jones
        HashMap userInfo = authservice.getAttributes(user, password, user);
835 503 bojilova
        // Print all of the attributes
836
        Iterator attList = (Iterator)(((Set)userInfo.keySet()).iterator());
837
        while (attList.hasNext()) {
838
          String att = (String)attList.next();
839
          Vector values = (Vector)userInfo.get(att);
840
          Iterator attvalues = values.iterator();
841
          while (attvalues.hasNext()) {
842
            String value = (String)attvalues.next();
843 934 tao
            MetaCatUtil.debugMessage(att + ": " + value);
844 503 bojilova
          }
845
        }
846 871 jones
      }
847 723 bojilova
848 871 jones
      // get the groups
849
      if (isValid) {
850 934 tao
        MetaCatUtil.debugMessage("\nGetting all groups....");
851 871 jones
        String[] groups = authservice.getGroups(user, password);
852 934 tao
        MetaCatUtil.debugMessage("Groups found: " + groups.length);
853 871 jones
        for (int i=0; i < groups.length; i++) {
854 934 tao
            MetaCatUtil.debugMessage("Group " + i + ": " + groups[i]);
855 871 jones
        }
856 503 bojilova
      }
857 725 bojilova
858 871 jones
      // get the groups for the user
859
      String savedGroup = null;
860
      if (isValid) {
861 934 tao
        MetaCatUtil.debugMessage("\nGetting groups for user....");
862 871 jones
        String[] groups = authservice.getGroups(user, password, user);
863 934 tao
        MetaCatUtil.debugMessage("Groups found: " + groups.length);
864 871 jones
        for (int i=0; i < groups.length; i++) {
865 934 tao
            MetaCatUtil.debugMessage("Group " + i + ": " + groups[i]);
866 871 jones
            savedGroup = groups[i];
867
        }
868
      }
869
870
      // get the users for a group
871
      if (isValid) {
872 934 tao
        MetaCatUtil.debugMessage("\nGetting users for group....");
873
        MetaCatUtil.debugMessage("Group: " + savedGroup);
874 871 jones
        String[] users = authservice.getUsers(user, password, savedGroup);
875 934 tao
        MetaCatUtil.debugMessage("Users found: " + users.length);
876 871 jones
        for (int i=0; i < users.length; i++) {
877 934 tao
            MetaCatUtil.debugMessage("User " + i + ": " + users[i]);
878 871 jones
        }
879
      }
880
881
      // get all users
882
      if (isValid) {
883 934 tao
        MetaCatUtil.debugMessage("\nGetting all users ....");
884 871 jones
        String[] users = authservice.getUsers(user, password);
885 934 tao
        MetaCatUtil.debugMessage("Users found: " + users.length);
886
887 871 jones
      }
888 873 jones
889 726 bojilova
      // get the whole list groups and users in XML format
890 725 bojilova
      if (isValid) {
891 934 tao
        MetaCatUtil.debugMessage("\nTrying principals....");
892 730 bojilova
        authservice = new AuthLdap();
893 725 bojilova
        String out = authservice.getPrincipals(user, password);
894 802 bojilova
        java.io.File f = new java.io.File("principals.xml");
895 725 bojilova
        java.io.FileWriter fw = new java.io.FileWriter(f);
896
        java.io.BufferedWriter buff = new java.io.BufferedWriter(fw);
897
        buff.write(out);
898
        buff.flush();
899
        buff.close();
900
        fw.close();
901 934 tao
        MetaCatUtil.debugMessage("\nFinished getting principals.");
902 725 bojilova
      }
903 873 jones
904 503 bojilova
    } catch (ConnectException ce) {
905 934 tao
      MetaCatUtil.debugMessage(ce.getMessage());
906 725 bojilova
    } catch (java.io.IOException ioe) {
907 934 tao
      MetaCatUtil.debugMessage("I/O Error writing to file principals.txt");
908 503 bojilova
    }
909
  }
910 893 berkley
911 934 tao
  /**
912
   * This method will be called by start a thread.
913
   * It can handle if a referral exception happend.
914
   */
915 893 berkley
  public void run()
916
  {
917
    referralContext = null;
918 934 tao
    DirContext refDirContext=null;
919
    boolean moreReferrals=true;
920
    //set a while loop is because we don't know if a referral excption contains
921
    //another referral exception
922 935 tao
    while (moreReferrals)
923 934 tao
    {
924
      try
925
      {
926
        //revise environment variable
927
        env.put(Context.PROVIDER_URL, refExc.getReferralInfo());
928
        env.put(Context.INITIAL_CONTEXT_FACTORY,
929
                "com.sun.jndi.ldap.LdapCtxFactory");
930
        env.put(Context.SECURITY_PRINCIPAL, userName);
931
        env.put(Context.SECURITY_CREDENTIALS, userPassword);
932
        env.put(Context.REFERRAL, "throw");
933
        //get a context object for referral in the new envriment
934
        rContext = refExc.getReferralContext(env);
935
        //casting the context to dircontext and it will create a
936
        //autherntication or naming exception if DN and password is incorrect
937
        referralContext=rContext;
938
        refDirContext=(DirContext)rContext;
939
        refDirContext.close();
940
        //get context and jump out the while loop
941
        moreReferrals=false;
942
      }//try
943
      //if referral have another referral excption
944 935 tao
      catch (ReferralException re)
945 934 tao
      {
946
        //keep running in while loop
947
        moreReferrals=true;
948
        //assign refExc to new referral exception re
949
        refExc=re;
950
      }
951
      //catch a authentication exception
952 935 tao
      catch (AuthenticationException ae)
953 934 tao
      {
954
        util.debugMessage("Error running referral handler thread: " +
955 928 tao
                          ae.getMessage());
956 934 tao
        //check if has another referral
957
        moreReferrals=refExc.skipReferral();
958
        //don't get the context
959
        referralContext = null;
960
      }
961
      //catch a naming exception
962 935 tao
      catch (NamingException ne)
963 934 tao
      {
964
        util.debugMessage("Error running referral handler thread: " +
965 928 tao
                          ne.getMessage());
966 934 tao
        //check if has another referral
967
        moreReferrals=refExc.skipReferral();
968
        //don't get context
969
        referralContext = null;
970
      }
971
    }//while
972 930 tao
  }//run()
973 503 bojilova
}