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