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