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