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