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