Project

General

Profile

1
/**
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: bojilova $'
12
 *     '$Date: 2001-10-02 11:51:18 -0700 (Tue, 02 Oct 2001) $'
13
 * '$Revision: 842 $'
14
 *
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
 */
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
import javax.naming.NamingEnumeration;
37
import javax.naming.NamingException;
38
import javax.naming.InitialContext;
39
import javax.naming.directory.InvalidSearchFilterException;
40
import javax.naming.directory.Attribute;
41
import javax.naming.directory.Attributes;
42
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
import javax.naming.directory.SearchControls;
48
import javax.naming.ldap.*;
49
import java.util.Iterator;
50
import java.util.HashMap;
51
import java.util.Hashtable;
52
import java.util.Enumeration;
53
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
public class AuthLdap implements AuthInterface {
63
  
64
  private MetaCatUtil util = new MetaCatUtil();
65
  private String ldapUrl;
66
  private String ldapsUrl;
67
  private String ldapBase;
68

    
69
  /** 
70
   * Construct an AuthLdap
71
   */
72
  public AuthLdap() {
73

    
74
    // Read LDAP URI for directory service information
75
    this.ldapUrl = MetaCatUtil.getOption("ldapurl");
76
    this.ldapsUrl = MetaCatUtil.getOption("ldapsurl");
77
    this.ldapBase = MetaCatUtil.getOption("ldapbase");
78
    //this.ldapUrl = "ldap://dev.nceas.ucsb.edu:636/";
79
    //this.ldapBase = "o=NCEAS,dc=ecoinformatics,dc=org";
80
  }
81

    
82
  /**
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
    String ldapUrl = this.ldapUrl;
94
    String ldapsUrl = this.ldapsUrl;
95
    String ldapBase = this.ldapBase;
96
    boolean authenticated = false;
97
    String identifier = user;
98
    
99
    // Identify service provider to use
100
    Hashtable env = new Hashtable(11);
101
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
102
            "com.sun.jndi.ldap.LdapCtxFactory");
103

    
104
    try {
105
   
106
      try { 
107
        this.ldapBase = identifier.substring(identifier.indexOf(",")+1);
108
        identifier = identifier.substring(0,identifier.indexOf(","));
109
      } catch (StringIndexOutOfBoundsException e) {}
110

    
111
      /*
112
       * get all subtrees first in the current dir context 
113
       * and then the dn for this uid or cn
114
       */
115
//      Hashtable subtrees = getSubtrees(user,password,ldapUrl,ldapBase);
116
    
117
//      Enumeration enum = subtrees.keys();
118
//      while ( enum.hasMoreElements() ) {
119
//        ldapBase = (String)enum.nextElement();
120
//        ldapUrl = (String)subtrees.get(ldapBase);
121
        identifier = getIdentifyingName(identifier,ldapUrl,ldapBase);
122
System.out.println(ldapsUrl + identifier + "," + ldapBase); 
123

    
124
        if (identifier != null && !password.equals("")) {
125
          // Now that we have the dn, we can authenticate, so
126
          // authenticate this time when opening the DirContext
127
          env.put(Context.PROVIDER_URL, ldapsUrl + ldapBase);
128
          if ( !ldapsUrl.equals(ldapUrl) ) {
129
            // ldap is set on default port 389
130
            // ldaps is set on second port - 636 by default
131
            env.put(Context.SECURITY_PROTOCOL, "ssl");
132
          }
133
          env.put(Context.SECURITY_AUTHENTICATION, "simple");
134
          env.put(Context.SECURITY_PRINCIPAL, identifier + "," + ldapBase);
135
          env.put(Context.SECURITY_CREDENTIALS, password);
136
          // If our auth credentials are invalid, an exception will be thrown
137
          DirContext ctx = null;
138
          try {
139
            double startTime = System.currentTimeMillis();
140
            ctx = new InitialDirContext(env);
141
//            // StartTLS support from LDAPv3 with X.509 cert and with JSDKv1.4+
142
//            LdapContext ctx = new InitialLdapContext(env, null);
143
//            StartTlsResponse tls =
144
//              (StartTlsResponse)ctx.extendedOperation(new StartTlsRequest());
145
//            tls.negotiate();
146

    
147
            double stopTime = System.currentTimeMillis();
148
            System.out.println("Connection time thru " + ldapsUrl + " was: " +
149
                               (stopTime-startTime)/1000 + " seconds.");
150
            authenticated = true;
151
//            tls.close();
152
            ctx.close();
153
            this.ldapUrl = ldapUrl;
154
            this.ldapBase = ldapBase;
155
         //   break;
156
          } catch (AuthenticationException ae) {
157
            authenticated = false;
158
//            if ( tls != null ) {
159
//              tls.close();
160
//            }
161
            if ( ctx != null ) {
162
              ctx.close();
163
            }
164
          }
165
        } else { 
166
          util.debugMessage("User not found");
167
        }
168
//      } /* while ( enum.hasMore() ) */
169

    
170
    } catch (NullPointerException e) {
171
      util.debugMessage("NullPointerException b' password is null");
172
      util.debugMessage("NullPointerException while authenticating in " + 
173
                        "AuthLdap.authenticate: " + e);
174
      throw new ConnectException(
175
      "NullPointerException while authenticating in " + 
176
                        "AuthLdap.authenticate: " + e);
177
    } catch (NamingException e) {
178
      util.debugMessage("Naming exception while authenticating in " + 
179
                        "AuthLdap.authenticate: " + e);
180
      //throw new ConnectException(
181
      //"Naming exception while authenticating in " + 
182
      //                  "AuthLdap.authenticate: " + e);
183
       e.printStackTrace();
184
    } catch (Exception e) {
185
      System.out.println(e.getMessage());
186
    }
187

    
188
    return authenticated;
189
  }
190

    
191
  /**
192
   * Get the identifying name for a given userid or name.  This is the name
193
   * that is used in conjunction withthe LDAP BaseDN to create a
194
   * distinguished name (dn) for the record
195
   *
196
   * @param user the user for which the identifying name is requested
197
   * @returns String the identifying name for the user, 
198
   *          or null if not found
199
   */
200
  private String getIdentifyingName(String user, String ldapUrl, String ldapBase) 
201
         throws NamingException
202
  {
203
    String identifier = null;
204

    
205
    // Identify service provider to use
206
    Hashtable env = new Hashtable(11);
207
    env.put(Context.INITIAL_CONTEXT_FACTORY,
208
            "com.sun.jndi.ldap.LdapCtxFactory");
209
    env.put(Context.PROVIDER_URL, ldapUrl + ldapBase);
210
//    non-secure LDAP context; dn are publicly readable
211
//    env.put(Context.SECURITY_PROTOCOL, "ssl");
212

    
213
    try {
214

    
215
      // Bind to the LDAP server, in order to search for the right
216
      // distinguished name (dn) based on userid (uid) or common name (cn)
217
      DirContext ctx = new InitialDirContext(env);
218

    
219
      SearchControls ctls = new SearchControls();
220
      ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
221

    
222
      // Search for the user id or name using the uid, then cn and sn attributes
223
      // If we find a record, determine the dn for the record
224
      util.debugMessage("\nStarting search phase...\n");
225

    
226
      String filter = "(" + user + ")";
227
      NamingEnumeration answer;
228
      try {
229
        answer = ctx.search("", filter, ctls);
230
        if (answer.hasMore()) {
231
          SearchResult sr = (SearchResult)answer.next();
232
          identifier = sr.getName();
233
          if ( !sr.isRelative() ) { 
234
            this.ldapUrl = identifier.substring(0,identifier.lastIndexOf("/")+1);
235
            this.ldapBase = identifier.substring(identifier.indexOf(",")+1);
236
            identifier = identifier.substring(identifier.lastIndexOf("/")+1,
237
                                              identifier.indexOf(","));
238
          }
239
          util.debugMessage("Found: " + identifier);
240
          return identifier;
241
        }
242
      } catch (InvalidSearchFilterException e) {}
243

    
244
      filter = "(uid=" + user + ")";
245
      answer = ctx.search("", filter, ctls);
246
      if (answer.hasMore()) {
247
        SearchResult sr = (SearchResult)answer.next();
248
        identifier = sr.getName();
249
        if ( !sr.isRelative() ) { 
250
          this.ldapUrl = identifier.substring(0,identifier.lastIndexOf("/")+1);
251
          this.ldapBase = identifier.substring(identifier.indexOf(",")+1);
252
          identifier = identifier.substring(identifier.lastIndexOf("/")+1,
253
                                            identifier.indexOf(","));
254
        }
255
        util.debugMessage("Found: " + identifier);
256
      } else {
257
        //Attributes matchAttrs2 = new BasicAttributes(true);
258
        //matchAttrs2.put(new BasicAttribute("cn", user));
259
        //NamingEnumeration answer2 = ctx.search("", matchAttrs2);
260
        filter = "(cn=" + user + ")";
261
        NamingEnumeration answer2 = ctx.search("", filter, ctls);
262
        if (answer2.hasMore()) {
263
          SearchResult sr = (SearchResult)answer2.next();
264
          identifier = sr.getName();
265
          if ( !sr.isRelative() ) { 
266
            this.ldapUrl = identifier.substring(0,identifier.lastIndexOf("/")+1);
267
            this.ldapBase = identifier.substring(identifier.indexOf(",")+1);
268
            identifier = identifier.substring(identifier.lastIndexOf("/")+1,
269
                                              identifier.indexOf(","));
270
          }
271
          util.debugMessage("Found: " + identifier);
272
        } else {
273
          //Attributes matchAttrs3 = new BasicAttributes(true);
274
          //matchAttrs3.put(new BasicAttribute("sn", user));
275
          //NamingEnumeration answer3 = ctx.search("", matchAttrs3);
276
          filter = "(sn=" + user + ")";
277
          NamingEnumeration answer3 = ctx.search("", filter, ctls);
278
          if (answer3.hasMore()) {
279
            SearchResult sr = (SearchResult)answer3.next();
280
            identifier = sr.getName();
281
            if ( !sr.isRelative() ) { 
282
              this.ldapUrl = identifier.substring(0,identifier.lastIndexOf("/")+1);
283
              this.ldapBase = identifier.substring(identifier.indexOf(",")+1);
284
              identifier = identifier.substring(identifier.lastIndexOf("/")+1,
285
                                                identifier.indexOf(","));
286
            }
287
            util.debugMessage("Found: " + identifier);
288
          }
289
        }
290
      }
291
      // Close the context when we're done the initial search
292
      ctx.close();
293
    } catch (NamingException e) {
294
      util.debugMessage("Naming exception while getting dn: " + e);
295
      throw new NamingException(
296
      "Naming exception in AuthLdap.getIdentifyingName: " + e);
297
    }
298
    
299
//System.out.println("context: " + identifier);
300
    return identifier;
301
  }
302

    
303
  /**
304
   * Get all users from the authentication service
305
   */
306
  public String[] getUsers(String user, String password) 
307
         throws ConnectException
308
  {
309
    String[] users = null;
310

    
311
    // Identify service provider to use
312
    Hashtable env = new Hashtable(11);
313
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
314
            "com.sun.jndi.ldap.LdapCtxFactory");
315
    env.put(Context.PROVIDER_URL, ldapUrl + ldapBase);
316

    
317
    try {
318

    
319
        // Create the initial directory context
320
        DirContext ctx = new InitialDirContext(env);
321

    
322
        // Specify the ids of the attributes to return
323
        String[] attrIDs = {"uid"};
324

    
325
        // Specify the attributes to match.
326
        // Users are objects that have the attribute objectclass=InetOrgPerson.
327
        Attributes matchAttrs = new BasicAttributes(true); // ignore case
328
        matchAttrs.put(new BasicAttribute("objectclass", "inetOrgPerson"));
329

    
330
        // Search for objects in the current context
331
        NamingEnumeration enum = ctx.search("", matchAttrs, attrIDs);
332

    
333
        // Print the users
334
        Vector uvec = new Vector();
335
        while (enum.hasMore()) {
336
          SearchResult sr = (SearchResult)enum.next();
337
          Attributes attrs = sr.getAttributes();
338
          NamingEnumeration enum1 = attrs.getAll(); // only "uid" attr
339
          while (enum1.hasMore()) {
340
            Attribute attr = (Attribute)enum1.next();
341
            uvec.add(attr.get());
342
          }
343
        }
344

    
345
        // initialize users[]; fill users[]
346
        users = new String[uvec.size()];
347
        for (int i=0; i < uvec.size(); i++) {
348
          users[i] = (String)uvec.elementAt(i); 
349
        }
350

    
351
        // Close the context when we're done
352
        ctx.close();
353

    
354
    } catch (NamingException e) {
355
      System.err.println("Problem getting users in AuthLdap.getUsers:" + e);
356
      throw new ConnectException(
357
      "Problem getting users in AuthLdap.getUsers:" + e);
358
    }
359

    
360
    return users;
361
  }
362

    
363
  /**
364
   * Get the users for a particular group from the authentication service
365
   */
366
  public String[] getUsers(String user, String password, String group) 
367
         throws ConnectException
368
  {
369
    String[] users = null;
370

    
371
    // Identify service provider to use
372
    Hashtable env = new Hashtable(11);
373
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
374
            "com.sun.jndi.ldap.LdapCtxFactory");
375
    env.put(Context.PROVIDER_URL, ldapUrl + ldapBase);
376

    
377
    try {
378

    
379
        // Create the initial directory context
380
        DirContext ctx = new InitialDirContext(env);
381

    
382
        // Specify the ids of the attributes to return
383
        String[] attrIDs = {"uniquemember"};
384

    
385
        // Specify the attributes to match.
386
        // Groups are objects with attribute objectclass=groupofuniquenames.
387
        Attributes matchAttrs = new BasicAttributes(true); // ignore case
388
        matchAttrs.put(new BasicAttribute("objectclass", "groupofuniquenames"));
389
        matchAttrs.put(new BasicAttribute("cn", group));
390

    
391
        // Search for objects in the current context
392
        NamingEnumeration enum = ctx.search("", matchAttrs, attrIDs);
393

    
394
        // Print the users
395
        Vector uvec = new Vector();
396
        while (enum.hasMore()) {
397
          SearchResult sr = (SearchResult)enum.next();
398
          Attributes attrs = sr.getAttributes();
399
          // return all attributes (only "uniquemember" attr)
400
          NamingEnumeration enum1 = attrs.getAll();
401
          while (enum1.hasMore()) {
402
            Attribute attr = (Attribute)enum1.next();
403
            // return all values of that attribute
404
            NamingEnumeration enum2 = attr.getAll();
405
            while (enum2.hasMore()) {
406
              // get DN of a member
407
              String memberDN = (String)enum2.next();
408
              try {
409
                // we actually need RDN of the member
410
                // try to get RDN (UID) of the member in case of a user
411
                String memberID = getUserID(memberDN);
412
                if ( memberID != null ) {
413
                  uvec.add(memberID);
414
                // CURRENTLY WE DON'T SUPPORT SUBGROUPING, THUS
415
                // IGNORE SUBGROUPS AS MEMBERS OF THE GROUP
416
                // // this is a group, not user
417
                // // try to get RDN (CN) of the group
418
                // } else {
419
                //   memberID = getGroupID(memberDN);
420
                //   uvec.add(memberID);
421
                }
422
              } catch (NamingException ne) {}
423
            }
424
          }
425
        }
426

    
427
        // initialize users[]; fill users[]
428
        users = new String[uvec.size()];
429
        for (int i=0; i < uvec.size(); i++) {
430
          users[i] = (String)uvec.elementAt(i); 
431
        }
432

    
433
        // Close the context when we're done
434
        ctx.close();
435

    
436
    } catch (NamingException e) {
437
      System.err.println("Problem getting users for a group in AuthLdap.getUsers:" + e);
438
      throw new ConnectException(
439
      "Problem getting users for a group in AuthLdap.getUsers:" + e);
440
    }
441

    
442
    return users;
443
  }
444

    
445
  /**
446
   * Get UID by DN of a member
447
   */
448
  private String getUserID(String dn) 
449
         throws NamingException
450
  {
451
    String[] users = null;
452

    
453
    // Identify service provider to use
454
    Hashtable env = new Hashtable(11);
455
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
456
            "com.sun.jndi.ldap.LdapCtxFactory");
457
    env.put(Context.PROVIDER_URL, ldapUrl); // + ldapBase);
458

    
459
    try {
460

    
461
        // Create the initial directory context
462
        DirContext ctx = new InitialDirContext(env);
463

    
464
        // Specify the ids of the attributes to return
465
        String[] attrIDs = {"uid"};
466

    
467
        // Ask for "uid" attributes of the user 
468
        Attributes attrs = ctx.getAttributes(dn, attrIDs);
469

    
470
        // Print all of the attributes (only "uid" attr)
471
        Vector uvec = new Vector();
472
        NamingEnumeration en = attrs.getAll();
473
        while (en.hasMore()) {
474
          Attribute att = (Attribute)en.next();
475
          Vector values = new Vector();
476
          String attName = att.getID();
477
          NamingEnumeration attvalues = att.getAll();
478
          while (attvalues.hasMore()) {
479
            String value = (String)attvalues.next();
480
            values.add(value);
481
          }
482
          uvec.add(values.elementAt(0));
483
        }
484

    
485
        // initialize users[]; fill users[]
486
        users = new String[uvec.size()];
487
        for (int i=0; i < uvec.size(); i++) {
488
          users[i] = (String)uvec.elementAt(i); 
489
        }
490

    
491
        // Close the context when we're done
492
        ctx.close();
493

    
494
    } catch (NamingException ne) {
495
      System.err.println("Problem getting userID by \"dn\" in AuthLdap.getUserID:" + ne);
496
      throw ne;
497
    }
498

    
499
    if ( users.length > 0 ) {
500
      return users[0];
501
    }
502
    return null;
503
  }
504

    
505
  /**
506
   * Get CN by DN of a member
507
   */
508
  private String getGroupID(String dn) 
509
         throws NamingException
510
  {
511
    String[] groups = null;
512

    
513
    // Identify service provider to use
514
    Hashtable env = new Hashtable(11);
515
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
516
            "com.sun.jndi.ldap.LdapCtxFactory");
517
    env.put(Context.PROVIDER_URL, ldapUrl); // + ldapBase);
518

    
519
    try {
520

    
521
        // Create the initial directory context
522
        DirContext ctx = new InitialDirContext(env);
523

    
524
        // Specify the ids of the attributes to return
525
        String[] attrIDs = {"cn"};
526

    
527
        // Ask for "uid" attributes of the user 
528
        Attributes attrs = ctx.getAttributes(dn, attrIDs);
529

    
530
        // Print all of the attributes (only "cn" attr)
531
        Vector uvec = new Vector();
532
        NamingEnumeration en = attrs.getAll();
533
        while (en.hasMore()) {
534
          Attribute att = (Attribute)en.next();
535
          Vector values = new Vector();
536
          String attName = att.getID();
537
          NamingEnumeration attvalues = att.getAll();
538
          while (attvalues.hasMore()) {
539
            String value = (String)attvalues.next();
540
            values.add(value);
541
          }
542
          uvec.add(values.elementAt(0));
543
        }
544

    
545
        // initialize users[]; fill users[]
546
        groups = new String[uvec.size()];
547
        for (int i=0; i < uvec.size(); i++) {
548
          groups[i] = (String)uvec.elementAt(i); 
549
        }
550

    
551
        // Close the context when we're done
552
        ctx.close();
553

    
554
    } catch (NamingException ne) {
555
      System.err.println("Problem getting groupID by \"dn\" in AuthLdap.getGroupID:" + ne);
556
      throw ne;
557
    }
558

    
559
    if ( groups.length > 0 ) {
560
      return groups[0];
561
    }
562
    return null;
563
  }
564

    
565
  /**
566
   * Get all groups from the authentication service
567
   */
568
  public String[] getGroups(String user, String password) 
569
         throws ConnectException
570
  {
571
    String[] groups = null;
572

    
573
    // Identify service provider to use
574
    Hashtable env = new Hashtable(11);
575
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
576
            "com.sun.jndi.ldap.LdapCtxFactory");
577
    env.put(Context.PROVIDER_URL, ldapUrl + ldapBase);
578

    
579
    try {
580

    
581
        // Create the initial directory context
582
        DirContext ctx = new InitialDirContext(env);
583

    
584
        // Specify the ids of the attributes to return
585
        String[] attrIDs = {"cn"};
586

    
587
        // Specify the attributes to match.
588
        // Groups are objects with attribute objectclass=groupofuniquenames.
589
        Attributes matchAttrs = new BasicAttributes(true); // ignore case
590
        matchAttrs.put(new BasicAttribute("objectclass", "groupofuniquenames"));
591

    
592
        // Search for objects in the current context
593
        NamingEnumeration enum = ctx.search("", matchAttrs, attrIDs);
594

    
595
        // Print the users
596
        Vector uvec = new Vector();
597
        while (enum.hasMore()) {
598
          SearchResult sr = (SearchResult)enum.next();
599
          Attributes attrs = sr.getAttributes();
600
          NamingEnumeration enum1 = attrs.getAll(); // only "cn" attr
601
          while (enum1.hasMore()) {
602
            Attribute attr = (Attribute)enum1.next();
603
            uvec.add(attr.get());
604
          }
605
        }
606

    
607
        // initialize groups[] and fill it
608
        groups = new String[uvec.size()];
609
        for (int i=0; i < uvec.size(); i++) {
610
          groups[i] = (String)uvec.elementAt(i); 
611
        }
612

    
613
        // Close the context when we're done
614
        ctx.close();
615

    
616
    } catch (NamingException e) {
617
      System.err.println("Problem getting groups in AuthLdap.getGroups:" + e);
618
      throw new ConnectException(
619
      "Problem getting groups in AuthLdap.getGroups:" + e);
620
    }
621

    
622
    return groups;
623
  }
624

    
625
  /**
626
   * Get the groups for a particular user from the authentication service
627
   */
628
  public String[] getGroups(String user, String password, String foruser) 
629
         throws ConnectException
630
  {
631
    String[] groups = null;
632

    
633
    // Identify service provider to use
634
    Hashtable env = new Hashtable(11);
635
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
636
            "com.sun.jndi.ldap.LdapCtxFactory");
637
    env.put(Context.PROVIDER_URL, ldapUrl + ldapBase);
638

    
639
    try {
640

    
641
        // Create the initial directory context
642
        DirContext ctx = new InitialDirContext(env);
643

    
644
        // Specify the ids of the attributes to return
645
        String[] attrIDs = {"cn"};
646

    
647
        // Specify the attributes to match.
648
        // Groups are objects with attribute objectclass=groupofuniquenames.
649
        // and have attribute uniquemember: uid=foruser,ldapbase.
650
        Attributes matchAttrs = new BasicAttributes(true); // ignore case
651
        matchAttrs.put(new BasicAttribute("objectclass", "groupofuniquenames"));
652
        String dn = getIdentifyingName(foruser, ldapUrl, ldapBase);
653
// System.out.println("Identifying Name: " + dn);
654
        matchAttrs.put(new BasicAttribute("uniquemember",dn+","+ldapBase));
655
        // Search for objects in the current context
656
        NamingEnumeration enum = ctx.search("", matchAttrs, attrIDs);
657

    
658
        // Print the users
659
        Vector uvec = new Vector();
660
        while (enum.hasMore()) {
661
          SearchResult sr = (SearchResult)enum.next();
662
          Attributes attrs = sr.getAttributes();
663
          NamingEnumeration enum1 = attrs.getAll(); // only "cn" attr
664
          while (enum1.hasMore()) {
665
            Attribute attr = (Attribute)enum1.next();
666
            uvec.add(attr.get());
667
          }
668
        }
669

    
670
        // initialize groups[] and fill it
671
        groups = new String[uvec.size()];
672
        for (int i=0; i < uvec.size(); i++) {
673
          groups[i] = (String)uvec.elementAt(i); 
674
        }
675

    
676
        // Close the context when we're done
677
        ctx.close();
678

    
679
    } catch (NamingException e) {
680
      System.err.println("Problem getting groups in AuthLdap.getGroups:" + e);
681
      throw new ConnectException(
682
      "Problem getting groups for a user in AuthLdap.getGroups:" + e);
683
    }
684

    
685
    return groups;
686
  }
687

    
688
  /**
689
   * Get attributes describing a user or group
690
   *
691
   * @param user the user for which the attribute list is requested
692
   * @returns HashMap a map of attribute name to a Vector of values
693
   */
694
  public HashMap getAttributes(String foruser) 
695
         throws ConnectException
696
  {
697
    return getAttributes(null, null, foruser);
698
  }
699

    
700
  /**
701
   * Get attributes describing a user or group
702
   *
703
   * @param user the user for which the attribute list is requested
704
   * @param authuser the user for authenticating against the service
705
   * @param password the password for authenticating against the service
706
   * @returns HashMap a map of attribute name to a Vector of values
707
   */
708
  public HashMap getAttributes(String user, String password, String foruser) 
709
         throws ConnectException
710
  {
711
    HashMap attributes = new HashMap();
712
    String ldapUrl = this.ldapUrl;
713
    String ldapBase = this.ldapBase;
714
    String userident = foruser;
715
    try { 
716
      this.ldapBase = userident.substring(userident.indexOf(",")+1);
717
      userident = userident.substring(0,userident.indexOf(","));
718
    } catch (StringIndexOutOfBoundsException e) {}
719

    
720
    // Identify service provider to use
721
    Hashtable env = new Hashtable(11);
722
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
723
        "com.sun.jndi.ldap.LdapCtxFactory");
724
    env.put(Context.PROVIDER_URL, ldapUrl + ldapBase);
725

    
726
    try {
727
      
728
      // Create the initial directory context
729
      DirContext ctx = new InitialDirContext(env);
730
        
731
      // Find out the identifying attribute for the user
732
      userident = getIdentifyingName(userident,ldapUrl,ldapBase);
733

    
734
      // Ask for all attributes of the user 
735
      Attributes attrs = ctx.getAttributes(userident);
736
 
737
      // Print all of the attributes
738
      NamingEnumeration en = attrs.getAll();
739
      while (en.hasMore()) {
740
        Attribute att = (Attribute)en.next();
741
        Vector values = new Vector();
742
        String attName = att.getID();
743
        NamingEnumeration attvalues = att.getAll();
744
        while (attvalues.hasMore()) {
745
          String value = (String)attvalues.next();
746
          values.add(value);
747
        }
748
        attributes.put(attName, values);
749
      }
750
  
751
      // Close the context when we're done
752
      ctx.close();
753
    } catch (NamingException e) {
754
      System.err.println("Problem getting attributes in AuthLdap.getAttributes:" 
755
                          + e);
756
      throw new ConnectException(
757
      "Problem getting attributes in AuthLdap.getAttributes:" + e);
758
    }
759

    
760
    return attributes;
761
  }
762

    
763
  /**
764
   * Get list of all subtrees holding Metacat's groups and users
765
   * starting from the Metacat LDAP root, 
766
   * i.e. ldap://dev.nceas.ucsb.edu/dc=ecoinformatics,dc=org
767
   */
768
  private Hashtable getSubtrees(String user, String password,
769
                                String ldapUrl, String ldapBase)
770
                throws ConnectException
771
  {
772
    Hashtable trees = new Hashtable();
773

    
774
    // Identify service provider to use
775
    Hashtable env = new Hashtable(11);
776
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
777
            "com.sun.jndi.ldap.LdapCtxFactory");
778
    env.put(Context.PROVIDER_URL, ldapUrl + ldapBase);
779

    
780
    try {
781

    
782
        // Create the initial directory context
783
        DirContext ctx = new InitialDirContext(env);
784

    
785
        // Specify the ids of the attributes to return
786
        String[] attrIDs = {"o","ref"};
787
        SearchControls ctls = new SearchControls();
788
        ctls.setReturningAttributes(attrIDs);
789
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
790
          
791
        // Specify the attributes to match.
792
        // Subtrees from the main server are found as objects with attribute
793
        // objectclass=organization or objectclass=referral to the subtree
794
        // resided on other server.
795
        String filter = "(|(objectclass=organization)(objectclass=referral))";
796

    
797
        // Search for objects in the current context
798
        NamingEnumeration enum = ctx.search("", filter, ctls);
799

    
800
        // Print the subtrees' <ldapURL, baseDN>
801
        while (enum.hasMore()) {
802
          SearchResult sr = (SearchResult)enum.next();
803
          Attributes attrs = sr.getAttributes();
804
          NamingEnumeration enum1 = attrs.getAll(); // "dc" and "ref" attrs
805
          if (enum1.hasMore()) {
806
            Attribute attr = (Attribute)enum1.next();
807
            String attrValue = (String)attr.get();
808
            String attrName = (String)attr.getID();
809
 //System.out.println(attrName + "=" + attrValue);
810
            if ( enum1.hasMore() ) {
811
              attr = (Attribute)enum1.next();
812
              String refValue = (String)attr.get();
813
              String refName = (String)attr.getID();
814
 //System.out.println(refName + "=" + refValue);
815
              if ( ldapBase.startsWith(refName + "=" + refValue) ) {
816
                trees.put(ldapBase,
817
                          attrValue.substring(0,attrValue.lastIndexOf("/")+1) );
818
              } else {
819
                trees.put(refName + "=" + refValue + "," + ldapBase,
820
                          attrValue.substring(0,attrValue.lastIndexOf("/")+1) );
821
              }
822
 //System.out.println("REFERRAL:" + attrValue);
823
            } else if ( ldapBase.startsWith(attrName + "=" + attrValue) ) {
824
                trees.put(ldapBase, ldapUrl);
825
            } else {              
826
                trees.put(attrName + "=" + attrValue + "," + ldapBase, ldapUrl);
827
 //System.out.println(ldapUrl + attrName + "=" + attrValue + "," + ldapBase);
828
            }
829
          }
830
        }
831

    
832
        // Close the context when we're done
833
        ctx.close();
834

    
835
    } catch (NamingException e) {
836
      System.err.println("Problem getting subtrees in AuthLdap.getSubtrees:" + e);
837
      throw new ConnectException(
838
      "Problem getting subtrees in AuthLdap.getSubtrees:" + e);
839
    }
840

    
841
//System.out.println("number of subtrees:" + trees.size());
842
    return trees;
843
  }
844

    
845
  /**
846
   * Get all groups and users from authentication scheme.
847
   * The output is formatted in XML.
848
   * @param user the user which requests the information
849
   * @param password the user's password
850
   */
851
  public String getPrincipals(String user, String password)
852
                throws ConnectException
853
  {
854
    StringBuffer out = new StringBuffer();
855
    Vector usersIn = new Vector();
856
    
857
    out.append("<?xml version=\"1.0\"?>\n");
858
    out.append("<principals>\n");
859
    
860
    /*
861
     * get all subtrees first in the current dir context 
862
     * and then the Metacat users under them
863
     */
864
    Hashtable subtrees = getSubtrees(user,password,this.ldapUrl,this.ldapBase);
865
    
866
    Enumeration enum = subtrees.keys();
867
    while ( enum.hasMoreElements() ) {
868
      this.ldapBase = (String)enum.nextElement();
869
      this.ldapUrl = (String)subtrees.get(ldapBase);
870
      
871
      out.append("  <authSystem URI=\"" + 
872
                 this.ldapUrl + this.ldapBase + "\">\n");
873

    
874
      // get all groups for directory context
875
      String[] groups = getGroups(user, password);
876

    
877
      // for the groups and users that belong to them
878
      if ( groups.length > 0 ) {
879
        for (int i=0; i < groups.length; i++ ) {
880
          out.append("    <group>\n");
881
          out.append("      <groupname>" + groups[i] + "</groupname>\n");
882
          String[] usersForGroup = getUsers(user,password,groups[i]);
883
          for (int j=0; j < usersForGroup.length; j++ ) {
884
            usersIn.addElement(usersForGroup[j]);
885
            out.append("      <user>\n");
886
            out.append("        <username>" + usersForGroup[j] + "</username>\n");
887
            out.append("      </user>\n");
888
          }
889
          out.append("    </group>\n");
890
        }
891
      }
892
      // for the users not belonging to any group
893
      String[] users = getUsers(user, password);
894
      for (int j=0; j < users.length; j++ ) {
895
        if ( !usersIn.contains(users[j]) ) {
896
          out.append("    <user>\n");
897
          out.append("      <username>" + users[j] + "</username>\n");
898
          out.append("    </user>\n");
899
        }
900
      }
901
    
902
      out.append("  </authSystem>\n");
903
      if ( !usersIn.isEmpty() ) {
904
        usersIn.removeAllElements();
905
        usersIn.trimToSize();
906
      }
907
    
908
    }
909
    out.append("</principals>");
910
    return out.toString();
911
  }
912

    
913
  /**
914
   * Test method for the class
915
   */
916
  public static void main(String[] args) {
917

    
918
    // Provide a user, such as: "Matt Jones", or "jones"
919
    String user = args[0];
920
    String password = args[1];
921

    
922
    AuthLdap authservice = new AuthLdap();
923

    
924
    boolean isValid = false;
925
    try {
926
      isValid = authservice.authenticate(user, password);
927
      if (isValid) {
928
        System.out.println("Authentication successful for: " + user );
929
        System.out.println(" ");
930
        
931
      } else {
932
        System.out.println("Authentication failed for: " + user);
933
      }
934

    
935
      if (isValid) {
936
        //String group = args[2];
937
        HashMap userInfo = authservice.getAttributes(user, password, "knb");
938
        // Print all of the attributes
939
        Iterator attList = (Iterator)(((Set)userInfo.keySet()).iterator());
940
        while (attList.hasNext()) {
941
          String att = (String)attList.next();
942
          Vector values = (Vector)userInfo.get(att);
943
          Iterator attvalues = values.iterator();
944
          while (attvalues.hasNext()) {
945
            String value = (String)attvalues.next();
946
            System.out.println(att + ": " + value);
947
          }
948
        }
949

    
950
      }
951

    
952
      // get the whole list groups and users in XML format
953
      if (isValid) {
954
        authservice = new AuthLdap();
955
        String out = authservice.getPrincipals(user, password);
956
        java.io.File f = new java.io.File("principals.xml");
957
        java.io.FileWriter fw = new java.io.FileWriter(f);
958
        java.io.BufferedWriter buff = new java.io.BufferedWriter(fw);
959
        buff.write(out);
960
        buff.flush();
961
        buff.close();
962
        fw.close();
963
      }
964

    
965
    } catch (ConnectException ce) {
966
      System.err.println(ce.getMessage());
967
    } catch (java.io.IOException ioe) {
968
      System.err.println("I/O Error writing to file principals.txt");
969
    }
970
  }
971
}
(6-6/40)