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: berkley $'
12
 *     '$Date: 2001-11-14 13:33:27 -0800 (Wed, 14 Nov 2001) $'
13
 * '$Revision: 868 $'
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.ReferralException;
49
import javax.naming.ldap.*;
50
import java.util.Iterator;
51
import java.util.HashMap;
52
import java.util.Hashtable;
53
import java.util.Enumeration;
54
import java.util.Set;
55
import java.util.Vector;
56

    
57
/**
58
 * An implementation of the AuthInterface interface that
59
 * allows Metacat to use the LDAP protocol for directory services.
60
 * The LDAP authentication service is used to determine if a user 
61
 * is authenticated, and whether they are a member of a particular group.
62
 */
63
public class AuthLdap implements AuthInterface {
64
  
65
  private MetaCatUtil util = new MetaCatUtil();
66
  private String ldapUrl;
67
  private String ldapsUrl;
68
  private String ldapBase;
69
  private String referral;
70

    
71
  /** 
72
   * Construct an AuthLdap
73
   */
74
  public AuthLdap() {
75

    
76
    // Read LDAP URI for directory service information
77
    this.ldapUrl = MetaCatUtil.getOption("ldapurl");
78
    this.ldapsUrl = MetaCatUtil.getOption("ldapsurl");
79
    this.ldapBase = MetaCatUtil.getOption("ldapbase");
80
    this.referral = MetaCatUtil.getOption("referral");
81
  }
82

    
83
  /**
84
   * Determine if a user/password are valid according to the authentication
85
   * service.
86
   *
87
   * @param user the name of the principal to authenticate
88
   * @param password the password to use for authentication
89
   * @returns boolean true if authentication successful, false otherwise
90
   */
91
  public boolean authenticate(String user, String password)
92
                    throws ConnectException
93
  {
94
    System.out.println("ldap authenticating");
95
    String ldapUrl = this.ldapUrl;
96
    String ldapsUrl = this.ldapsUrl;
97
    String ldapBase = this.ldapBase;
98
    boolean authenticated = false;
99
    String identifier = user;
100

    
101
    try {
102
   
103
        // Check the usename as passed in
104
        authenticated = ldapAuthenticate(identifier, password);
105

    
106
        // if not found, try looking up a valid DN then auth again
107
        if (!authenticated) {
108
            identifier = getIdentifyingName(identifier,ldapUrl,ldapBase);
109
            authenticated = ldapAuthenticate(identifier+","+ldapBase, password);
110
        }
111

    
112
    } 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
    } catch (NamingException e) {
120
      util.debugMessage("Naming exception while authenticating in " + 
121
                        "AuthLdap.authenticate: " + e);
122
      e.printStackTrace();
123
    } catch (Exception e) {
124
      System.out.println(e.getMessage());
125
    }
126

    
127
    return authenticated;
128
  }
129

    
130
  /**
131
   * 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
    double totStartTime = System.currentTimeMillis();
141
    boolean authenticated = false;
142
    if (identifier != null && !password.equals("")) {
143
    
144
        // Identify service provider to use
145
        Hashtable env = new Hashtable(11);
146
        env.put(Context.INITIAL_CONTEXT_FACTORY, 
147
            "com.sun.jndi.ldap.LdapCtxFactory");
148

    
149
        //System.out.println("referral: " + referral);
150
        // Now that we have the dn, we can authenticate, so
151
        // authenticate this time when opening the DirContext
152
        //System.out.println("referral=throw");
153
        env.put(Context.REFERRAL, "throw");
154
        /*CB:  Note that the above env.put statement does not use the referral 
155
          variable.  it is hard coded to 'throw'.  Matt: Is it ok to do this
156
          only here and not in every method?
157
        */
158
        //System.out.println("ldapsUrl: " + ldapsUrl + " ldapBase: " + ldapBase);
159
        env.put(Context.PROVIDER_URL, ldapsUrl + ldapBase);
160
        if ( !ldapsUrl.equals(ldapUrl) ) {
161
          // ldap is set on default port 389
162
          // ldaps is set on second port - 636 by default
163
          env.put(Context.SECURITY_PROTOCOL, "ssl");
164
        }
165
        env.put(Context.SECURITY_AUTHENTICATION, "simple");
166
        env.put(Context.SECURITY_PRINCIPAL, identifier);
167
        //System.out.println("Trying DN: " + identifier);
168
        env.put(Context.SECURITY_CREDENTIALS, password);
169
        // If our auth credentials are invalid, an exception will be thrown
170
        DirContext ctx = null;
171
        try {
172
          double startTime = System.currentTimeMillis();
173
          ctx = new InitialDirContext(env);
174
//          // StartTLS support from LDAPv3 with X.509 cert and with JSDKv1.4+
175
//          LdapContext ctx = new InitialLdapContext(env, null);
176
//          StartTlsResponse tls =
177
//            (StartTlsResponse)ctx.extendedOperation(new StartTlsRequest());
178
//          tls.negotiate();
179
          double stopTime = System.currentTimeMillis();
180
          System.out.println("Connection time thru " + ldapsUrl + " was: " +
181
                             (stopTime-startTime)/1000 + " seconds.");
182
          authenticated = true;
183
          //tls.close();
184
          ctx.close();
185
          this.ldapUrl = ldapUrl;
186
          this.ldapBase = ldapBase;
187
          //break;
188
        } catch (AuthenticationException ae) {
189
          authenticated = false;
190
//          if ( tls != null ) {
191
//            tls.close();
192
//          }
193
          if ( ctx != null ) {
194
            ctx.close();
195
          }
196
        } 
197
        catch (javax.naming.InvalidNameException ine) 
198
        {
199
            System.out.println("An invalid DN was provided!");
200
        } 
201
        catch(javax.naming.ReferralException re) 
202
        {
203
	        try
204
          {
205
            Context c = handleReferral(env, re);
206
            authenticated = true;
207
          }
208
          catch(Exception e)
209
          {
210
            authenticated = false;
211
          }
212
        }
213
    } else { 
214
        util.debugMessage("User not found");
215
    }
216
    double totStopTime = System.currentTimeMillis();
217
    System.out.println("total ldap authentication time: " + 
218
                      (totStopTime - totStartTime)/1000 + " seconds");
219
    return authenticated;
220
  }
221
  
222
  /**
223
   * handles a referral exception.  this method should be called from
224
   * within the catch statement of a ReferralException 
225
   */
226
  private Context handleReferral(Hashtable env, ReferralException re)
227
                  throws Exception 
228
  {
229
    System.out.println("referral to : " + re.getReferralInfo().toString());
230
    boolean referralSuccess = false;
231
    while(referralSuccess != true)
232
    {
233
      try
234
      {
235
        /*
236
         Matt, I think this is right but I'm not sure...please check me to make
237
         sure I didn't do something wrong here.
238
        */
239
        double refStartTime = System.currentTimeMillis();
240
        Context refctx = re.getReferralContext(env);
241
        referralSuccess = true;
242
        refctx.close();
243
        this.ldapUrl = ldapUrl;
244
        this.ldapBase = ldapBase;
245
        double refStopTime = System.currentTimeMillis();
246
        System.out.println("total referral time: " + 
247
                          (refStopTime - refStartTime)/1000 + " seconds");
248
        return refctx;
249
      }
250
      catch(ReferralException e)
251
      {
252
          System.out.println("Referring to: " + 
253
                             re.getReferralInfo().toString());
254
      }
255
      catch(Exception e)
256
      {
257
        throw e;
258
      }
259
    }
260
    return null; //this should never get called
261
  }
262

    
263
  /**
264
   * Get the identifying name for a given userid or name.  This is the name
265
   * that is used in conjunction withthe LDAP BaseDN to create a
266
   * distinguished name (dn) for the record
267
   *
268
   * @param user the user for which the identifying name is requested
269
   * @returns String the identifying name for the user, 
270
   *          or null if not found
271
   */
272
  private String getIdentifyingName(String user, String ldapUrl, String ldapBase) 
273
         throws NamingException
274
  {
275
    String identifier = null;
276

    
277
    // Identify service provider to use
278
    Hashtable env = new Hashtable(11);
279
    env.put(Context.INITIAL_CONTEXT_FACTORY,
280
            "com.sun.jndi.ldap.LdapCtxFactory");
281
    util.debugMessage("setting referrals to: " + referral);
282
    env.put(Context.REFERRAL, referral);
283
    env.put(Context.PROVIDER_URL, ldapUrl + ldapBase);
284
    //    non-secure LDAP context; dn are publicly readable
285
    //    env.put(Context.SECURITY_PROTOCOL, "ssl");
286
    try {
287
      
288
      // Bind to the LDAP server, in order to search for the right
289
      // distinguished name (dn) based on userid (uid) or common name (cn)
290
      DirContext ctx = new InitialDirContext(env);
291
      SearchControls ctls = new SearchControls();
292
      ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
293
      // Search for the user id or name using the uid, then cn and sn attributes
294
      // If we find a record, determine the dn for the record
295
      //System.out.println("Starting search phase...");
296

    
297
      String filter = "(" + user + ")";
298
      NamingEnumeration answer;
299
      try {
300
        answer = ctx.search("", filter, ctls);
301
        if (answer.hasMore()) {
302
          SearchResult sr = (SearchResult)answer.next();
303
          identifier = sr.getName();
304
          if ( !sr.isRelative() ) { 
305
            this.ldapUrl = identifier.substring(0,identifier.lastIndexOf("/")+1);
306
            this.ldapBase = identifier.substring(identifier.indexOf(",")+1);
307
            identifier = identifier.substring(identifier.lastIndexOf("/")+1,
308
                                              identifier.indexOf(","));
309
          }
310
          util.debugMessage("Found: " + identifier);
311
          return identifier;
312
        }
313
      } catch (InvalidSearchFilterException e) {}
314
      filter = "(uid=" + user + ")";
315
      answer = ctx.search("", filter, ctls);
316
      if (answer.hasMore()) {
317
        SearchResult sr = (SearchResult)answer.next();
318
        identifier = sr.getName();
319
        if ( !sr.isRelative() ) { 
320
          this.ldapUrl = identifier.substring(0,identifier.lastIndexOf("/")+1);
321
          this.ldapBase = identifier.substring(identifier.indexOf(",")+1);
322
          identifier = identifier.substring(identifier.lastIndexOf("/")+1,
323
                                            identifier.indexOf(","));
324
        }
325
        util.debugMessage("Found: " + identifier);
326
      } else {
327
        //Attributes matchAttrs2 = new BasicAttributes(true);
328
        //matchAttrs2.put(new BasicAttribute("cn", user));
329
        //NamingEnumeration answer2 = ctx.search("", matchAttrs2);
330
        filter = "(cn=" + user + ")";
331
        NamingEnumeration answer2 = ctx.search("", filter, ctls);
332
        if (answer2.hasMore()) {
333
          SearchResult sr = (SearchResult)answer2.next();
334
          identifier = sr.getName();
335
          if ( !sr.isRelative() ) { 
336
            this.ldapUrl = identifier.substring(0,identifier.lastIndexOf("/")+1);
337
            this.ldapBase = identifier.substring(identifier.indexOf(",")+1);
338
            identifier = identifier.substring(identifier.lastIndexOf("/")+1,
339
                                              identifier.indexOf(","));
340
          }
341
          util.debugMessage("Found: " + identifier);
342
        } else {
343
          //Attributes matchAttrs3 = new BasicAttributes(true);
344
          //matchAttrs3.put(new BasicAttribute("sn", user));
345
          //NamingEnumeration answer3 = ctx.search("", matchAttrs3);
346
          filter = "(sn=" + user + ")";
347
          NamingEnumeration answer3 = ctx.search("", filter, ctls);
348
          if (answer3.hasMore()) {
349
            SearchResult sr = (SearchResult)answer3.next();
350
            identifier = sr.getName();
351
            if ( !sr.isRelative() ) { 
352
              this.ldapUrl = identifier.substring(0,identifier.lastIndexOf("/")+1);
353
              this.ldapBase = identifier.substring(identifier.indexOf(",")+1);
354
              identifier = identifier.substring(identifier.lastIndexOf("/")+1,
355
                                                identifier.indexOf(","));
356
            }
357
            util.debugMessage("Found: " + identifier);
358
          }
359
        }
360
      }
361
      // Close the context when we're done the initial search
362
      ctx.close();
363
    } catch (NamingException e) {
364
      util.debugMessage("Naming exception while getting dn: " + e);
365
      throw new NamingException(
366
      "Naming exception in AuthLdap.getIdentifyingName: " + e);
367
    }
368
    return identifier;
369
  }
370

    
371
  /**
372
   * Get all users from the authentication service
373
   */
374
  public String[] getUsers(String user, String password) 
375
         throws ConnectException
376
  {
377
    String[] users = null;
378

    
379
    // Identify service provider to use
380
    Hashtable env = new Hashtable(11);
381
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
382
            "com.sun.jndi.ldap.LdapCtxFactory");
383
    env.put(Context.REFERRAL, referral);
384
    env.put(Context.PROVIDER_URL, ldapUrl + ldapBase);
385

    
386
    try {
387

    
388
        // Create the initial directory context
389
        DirContext ctx = new InitialDirContext(env);
390

    
391
        // Specify the ids of the attributes to return
392
        String[] attrIDs = {"uid"};
393

    
394
        // Specify the attributes to match.
395
        // Users are objects that have the attribute objectclass=InetOrgPerson.
396
        Attributes matchAttrs = new BasicAttributes(true); // ignore case
397
        matchAttrs.put(new BasicAttribute("objectclass", "inetOrgPerson"));
398

    
399
        // Search for objects in the current context
400
        NamingEnumeration enum = ctx.search("", matchAttrs, attrIDs);
401

    
402
        // Print the users
403
        Vector uvec = new Vector();
404
        while (enum.hasMore()) {
405
          SearchResult sr = (SearchResult)enum.next();
406
          Attributes attrs = sr.getAttributes();
407
          NamingEnumeration enum1 = attrs.getAll(); // only "uid" attr
408
          while (enum1.hasMore()) {
409
            Attribute attr = (Attribute)enum1.next();
410
            uvec.add(attr.get());
411
          }
412
        }
413

    
414
        // initialize users[]; fill users[]
415
        users = new String[uvec.size()];
416
        for (int i=0; i < uvec.size(); i++) {
417
          users[i] = (String)uvec.elementAt(i); 
418
        }
419

    
420
        // Close the context when we're done
421
        ctx.close();
422

    
423
    } catch (NamingException e) {
424
      System.err.println("Problem getting users in AuthLdap.getUsers:" + e);
425
      throw new ConnectException(
426
      "Problem getting users in AuthLdap.getUsers:" + e);
427
    }
428

    
429
    return users;
430
  }
431

    
432
  /**
433
   * Get the users for a particular group from the authentication service
434
   */
435
  public String[] getUsers(String user, String password, String group) 
436
         throws ConnectException
437
  {
438
    String[] users = null;
439

    
440
    // Identify service provider to use
441
    Hashtable env = new Hashtable(11);
442
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
443
            "com.sun.jndi.ldap.LdapCtxFactory");
444
    env.put(Context.REFERRAL, referral);
445
    env.put(Context.PROVIDER_URL, ldapUrl + ldapBase);
446

    
447
    try {
448

    
449
        // Create the initial directory context
450
        DirContext ctx = new InitialDirContext(env);
451

    
452
        // Specify the ids of the attributes to return
453
        String[] attrIDs = {"uniquemember"};
454

    
455
        // Specify the attributes to match.
456
        // Groups are objects with attribute objectclass=groupofuniquenames.
457
        Attributes matchAttrs = new BasicAttributes(true); // ignore case
458
        matchAttrs.put(new BasicAttribute("objectclass", "groupofuniquenames"));
459
        matchAttrs.put(new BasicAttribute("cn", group));
460

    
461
        // Search for objects in the current context
462
        NamingEnumeration enum = ctx.search("", matchAttrs, attrIDs);
463

    
464
        // Print the users
465
        Vector uvec = new Vector();
466
        while (enum.hasMore()) {
467
          SearchResult sr = (SearchResult)enum.next();
468
          Attributes attrs = sr.getAttributes();
469
          // return all attributes (only "uniquemember" attr)
470
          NamingEnumeration enum1 = attrs.getAll();
471
          while (enum1.hasMore()) {
472
            Attribute attr = (Attribute)enum1.next();
473
            // return all values of that attribute
474
            NamingEnumeration enum2 = attr.getAll();
475
            while (enum2.hasMore()) {
476
              // get DN of a member
477
              String memberDN = (String)enum2.next();
478
              try {
479
                // we actually need RDN of the member
480
                // try to get RDN (UID) of the member in case of a user
481
                String memberID = getUserID(memberDN);
482
                if ( memberID != null ) {
483
                  uvec.add(memberID);
484
                // CURRENTLY WE DON'T SUPPORT SUBGROUPING, THUS
485
                // IGNORE SUBGROUPS AS MEMBERS OF THE GROUP
486
                // // this is a group, not user
487
                // // try to get RDN (CN) of the group
488
                // } else {
489
                //   memberID = getGroupID(memberDN);
490
                //   uvec.add(memberID);
491
                }
492
              } catch (NamingException ne) {}
493
            }
494
          }
495
        }
496

    
497
        // initialize users[]; fill users[]
498
        users = new String[uvec.size()];
499
        for (int i=0; i < uvec.size(); i++) {
500
          users[i] = (String)uvec.elementAt(i); 
501
        }
502

    
503
        // Close the context when we're done
504
        ctx.close();
505

    
506
    } catch (NamingException e) {
507
      System.err.println("Problem getting users for a group in AuthLdap.getUsers:" + e);
508
      throw new ConnectException(
509
      "Problem getting users for a group in AuthLdap.getUsers:" + e);
510
    }
511

    
512
    return users;
513
  }
514

    
515
  /**
516
   * Get UID by DN of a member
517
   */
518
  private String getUserID(String dn) 
519
         throws NamingException
520
  {
521
    String[] users = null;
522

    
523
    // Identify service provider to use
524
    Hashtable env = new Hashtable(11);
525
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
526
            "com.sun.jndi.ldap.LdapCtxFactory");
527
    env.put(Context.REFERRAL, referral);
528
    env.put(Context.PROVIDER_URL, ldapUrl); // + ldapBase);
529

    
530
    try {
531

    
532
        // Create the initial directory context
533
        DirContext ctx = new InitialDirContext(env);
534

    
535
        // Specify the ids of the attributes to return
536
        String[] attrIDs = {"uid"};
537

    
538
        // Ask for "uid" attributes of the user 
539
        Attributes attrs = ctx.getAttributes(dn, attrIDs);
540

    
541
        // Print all of the attributes (only "uid" attr)
542
        Vector uvec = new Vector();
543
        NamingEnumeration en = attrs.getAll();
544
        while (en.hasMore()) {
545
          Attribute att = (Attribute)en.next();
546
          Vector values = new Vector();
547
          String attName = att.getID();
548
          NamingEnumeration attvalues = att.getAll();
549
          while (attvalues.hasMore()) {
550
            String value = (String)attvalues.next();
551
            values.add(value);
552
          }
553
          uvec.add(values.elementAt(0));
554
        }
555

    
556
        // initialize users[]; fill users[]
557
        users = new String[uvec.size()];
558
        for (int i=0; i < uvec.size(); i++) {
559
          users[i] = (String)uvec.elementAt(i); 
560
        }
561

    
562
        // Close the context when we're done
563
        ctx.close();
564

    
565
    } catch (NamingException ne) {
566
      System.err.println("Problem getting userID by \"dn\" in AuthLdap.getUserID:" + ne);
567
      throw ne;
568
    }
569

    
570
    if ( users.length > 0 ) {
571
      return users[0];
572
    }
573
    return null;
574
  }
575

    
576
  /**
577
   * Get CN by DN of a member
578
   */
579
  private String getGroupID(String dn) 
580
         throws NamingException
581
  {
582
    String[] groups = null;
583

    
584
    // Identify service provider to use
585
    Hashtable env = new Hashtable(11);
586
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
587
            "com.sun.jndi.ldap.LdapCtxFactory");
588
    env.put(Context.REFERRAL, referral);
589
    env.put(Context.PROVIDER_URL, ldapUrl); // + ldapBase);
590

    
591
    try {
592

    
593
        // Create the initial directory context
594
        DirContext ctx = new InitialDirContext(env);
595

    
596
        // Specify the ids of the attributes to return
597
        String[] attrIDs = {"cn"};
598

    
599
        // Ask for "uid" attributes of the user 
600
        Attributes attrs = ctx.getAttributes(dn, attrIDs);
601

    
602
        // Print all of the attributes (only "cn" attr)
603
        Vector uvec = new Vector();
604
        NamingEnumeration en = attrs.getAll();
605
        while (en.hasMore()) {
606
          Attribute att = (Attribute)en.next();
607
          Vector values = new Vector();
608
          String attName = att.getID();
609
          NamingEnumeration attvalues = att.getAll();
610
          while (attvalues.hasMore()) {
611
            String value = (String)attvalues.next();
612
            values.add(value);
613
          }
614
          uvec.add(values.elementAt(0));
615
        }
616

    
617
        // initialize users[]; fill users[]
618
        groups = new String[uvec.size()];
619
        for (int i=0; i < uvec.size(); i++) {
620
          groups[i] = (String)uvec.elementAt(i); 
621
        }
622

    
623
        // Close the context when we're done
624
        ctx.close();
625

    
626
    } catch (NamingException ne) {
627
      System.err.println("Problem getting groupID by \"dn\" in AuthLdap.getGroupID:" + ne);
628
      throw ne;
629
    }
630

    
631
    if ( groups.length > 0 ) {
632
      return groups[0];
633
    }
634
    return null;
635
  }
636

    
637
  /**
638
   * Get all groups from the authentication service
639
   */
640
  public String[] getGroups(String user, String password) 
641
         throws ConnectException
642
  {
643
    String[] groups = null;
644

    
645
    // Identify service provider to use
646
    Hashtable env = new Hashtable(11);
647
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
648
            "com.sun.jndi.ldap.LdapCtxFactory");
649
    env.put(Context.REFERRAL, referral);
650
    env.put(Context.PROVIDER_URL, ldapUrl + ldapBase);
651

    
652
    try {
653
        // Create the initial directory context
654
        DirContext ctx = new InitialDirContext(env);
655
        // Specify the ids of the attributes to return
656
        String[] attrIDs = {"cn"};
657

    
658
        // Specify the attributes to match.
659
        // Groups are objects with attribute objectclass=groupofuniquenames.
660
        Attributes matchAttrs = new BasicAttributes(true); // ignore case
661
        matchAttrs.put(new BasicAttribute("objectclass", "groupofuniquenames"));
662
        // Search for objects in the current context
663
        NamingEnumeration enum = ctx.search("", matchAttrs, attrIDs);
664

    
665
        // Print the users
666
        Vector uvec = new Vector();
667
        while (enum.hasMore()) {
668
          SearchResult sr = (SearchResult)enum.next();
669
          Attributes attrs = sr.getAttributes();
670
          NamingEnumeration enum1 = attrs.getAll(); // only "cn" attr
671
          while (enum1.hasMore()) {
672
            Attribute attr = (Attribute)enum1.next();
673
            uvec.add(attr.get());
674
          }
675
        }
676

    
677
        // initialize groups[] and fill it
678
        groups = new String[uvec.size()];
679
        for (int i=0; i < uvec.size(); i++) {
680
          groups[i] = (String)uvec.elementAt(i); 
681
        }
682

    
683
        // Close the context when we're done
684
        ctx.close();
685

    
686
    } catch (NamingException e) {
687
      System.err.println("Problem getting groups in AuthLdap.getGroups 1:" + e);
688
      throw new ConnectException(
689
      "Problem getting groups in AuthLdap.getGroups:" + e);
690
    }
691

    
692
    return groups;
693
  }
694

    
695
  /**
696
   * Get the groups for a particular user from the authentication service
697
   */
698
  public String[] getGroups(String user, String password, String foruser) 
699
         throws ConnectException
700
  {
701
    //System.err.println("GG in get groups 2");
702
    String[] groups = null;
703

    
704
    // Identify service provider to use
705
    Hashtable env = new Hashtable(11);
706
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
707
            "com.sun.jndi.ldap.LdapCtxFactory");
708
    env.put(Context.REFERRAL, referral);
709
    //System.out.println("GG server: " + ldapUrl + ldapBase); 
710
    env.put(Context.PROVIDER_URL, ldapUrl + ldapBase);
711
    try {
712

    
713
        // Create the initial directory context
714
        DirContext ctx = new InitialDirContext(env);
715
        // Specify the ids of the attributes to return
716
        String[] attrIDs = {"cn"};
717
        // Specify the attributes to match.
718
        // Groups are objects with attribute objectclass=groupofuniquenames.
719
        // and have attribute uniquemember: uid=foruser,ldapbase.
720
        Attributes matchAttrs = new BasicAttributes(); // ignore case
721
        matchAttrs.put(new BasicAttribute("objectClass", "groupOfUniqueNames"));
722
        //System.out.println("GG user: " + user);
723
        //System.out.println("GG foruser: " + foruser);
724
        String dn = user;/*getIdentifyingName(foruser, ldapUrl, ldapBase);*/ 
725
        //System.out.println("GG dn: " + dn);
726
        matchAttrs.put(new BasicAttribute("uniqueMember", dn));
727
        // Search for objects in the current context
728
        //System.out.println("GG matchAttrs: " + matchAttrs.toString());
729
        NamingEnumeration enum = ctx.search("", matchAttrs, attrIDs);
730
        // Print the users
731
        Vector uvec = new Vector();
732
        while (enum.hasMore()) {
733
          //System.out.println("GG search result found");
734
          SearchResult sr = (SearchResult)enum.next();
735
          Attributes attrs = sr.getAttributes();
736
          NamingEnumeration enum1 = attrs.getAll(); // only "cn" attr
737
          while (enum1.hasMore()) {
738
            Attribute attr = (Attribute)enum1.next();
739
            uvec.add(attr.get());
740
          }
741
        }
742

    
743
        // initialize groups[] and fill it
744
        //System.out.println("GG getting groups: " + uvec.size());
745
        groups = new String[uvec.size()];
746
        for (int i=0; i < uvec.size(); i++) {
747
          //System.out.println("GG group: " + groups[i]);
748
          groups[i] = (String)uvec.elementAt(i); 
749
        }
750

    
751
        // Close the context when we're done
752
        ctx.close();
753

    
754
    } catch (NamingException e) {
755
      System.err.println("Problem getting groups in AuthLdap.getGroups 2:" + e);
756
      e.printStackTrace(System.err);
757
      throw new ConnectException(
758
      "Problem getting groups for a user in AuthLdap.getGroups:" + e);
759
    }
760
    return groups;
761
  }
762

    
763
  /**
764
   * Get attributes describing a user or group
765
   *
766
   * @param user the user for which the attribute list is requested
767
   * @returns HashMap a map of attribute name to a Vector of values
768
   */
769
  public HashMap getAttributes(String foruser) 
770
         throws ConnectException
771
  {
772
    return getAttributes(null, null, foruser);
773
  }
774

    
775
  /**
776
   * Get attributes describing a user or group
777
   *
778
   * @param user the user for which the attribute list is requested
779
   * @param authuser the user for authenticating against the service
780
   * @param password the password for authenticating against the service
781
   * @returns HashMap a map of attribute name to a Vector of values
782
   */
783
  public HashMap getAttributes(String user, String password, String foruser) 
784
         throws ConnectException
785
  {
786
    HashMap attributes = new HashMap();
787
    String ldapUrl = this.ldapUrl;
788
    String ldapBase = this.ldapBase;
789
    String userident = foruser;
790
    try { 
791
      this.ldapBase = userident.substring(userident.indexOf(",")+1);
792
      userident = userident.substring(0,userident.indexOf(","));
793
    } catch (StringIndexOutOfBoundsException e) {}
794

    
795
    // Identify service provider to use
796
    Hashtable env = new Hashtable(11);
797
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
798
        "com.sun.jndi.ldap.LdapCtxFactory");
799
    env.put(Context.REFERRAL, referral);
800
    env.put(Context.PROVIDER_URL, ldapUrl + ldapBase);
801

    
802
    try {
803
      
804
      // Create the initial directory context
805
      DirContext ctx = new InitialDirContext(env);
806
        
807
      // Find out the identifying attribute for the user
808
      userident = getIdentifyingName(userident,ldapUrl,ldapBase);
809

    
810
      // Ask for all attributes of the user 
811
      Attributes attrs = ctx.getAttributes(userident);
812
 
813
      // Print all of the attributes
814
      NamingEnumeration en = attrs.getAll();
815
      while (en.hasMore()) {
816
        Attribute att = (Attribute)en.next();
817
        Vector values = new Vector();
818
        String attName = att.getID();
819
        NamingEnumeration attvalues = att.getAll();
820
        while (attvalues.hasMore()) {
821
          String value = (String)attvalues.next();
822
          values.add(value);
823
        }
824
        attributes.put(attName, values);
825
      }
826
  
827
      // Close the context when we're done
828
      ctx.close();
829
    } catch (NamingException e) {
830
      System.err.println("Problem getting attributes in AuthLdap.getAttributes:" 
831
                          + e);
832
      throw new ConnectException(
833
      "Problem getting attributes in AuthLdap.getAttributes:" + e);
834
    }
835

    
836
    return attributes;
837
  }
838

    
839
  /**
840
   * Get list of all subtrees holding Metacat's groups and users
841
   * starting from the Metacat LDAP root, 
842
   * i.e. ldap://dev.nceas.ucsb.edu/dc=ecoinformatics,dc=org
843
   */
844
  private Hashtable getSubtrees(String user, String password,
845
                                String ldapUrl, String ldapBase)
846
                throws ConnectException
847
  {
848
    Hashtable trees = new Hashtable();
849

    
850
    // Identify service provider to use
851
    Hashtable env = new Hashtable(11);
852
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
853
            "com.sun.jndi.ldap.LdapCtxFactory");
854
    env.put(Context.REFERRAL, referral);
855
    env.put(Context.PROVIDER_URL, ldapUrl + ldapBase);
856

    
857
    try {
858

    
859
        // Create the initial directory context
860
        DirContext ctx = new InitialDirContext(env);
861

    
862
        // Specify the ids of the attributes to return
863
        String[] attrIDs = {"o","ref"};
864
        SearchControls ctls = new SearchControls();
865
        ctls.setReturningAttributes(attrIDs);
866
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
867
          
868
        // Specify the attributes to match.
869
        // Subtrees from the main server are found as objects with attribute
870
        // objectclass=organization or objectclass=referral to the subtree
871
        // resided on other server.
872
        String filter = "(|(objectclass=organization)(objectclass=referral))";
873

    
874
        // Search for objects in the current context
875
        NamingEnumeration enum = ctx.search("", filter, ctls);
876

    
877
        // Print the subtrees' <ldapURL, baseDN>
878
        while (enum.hasMore()) {
879
          SearchResult sr = (SearchResult)enum.next();
880
          Attributes attrs = sr.getAttributes();
881
          NamingEnumeration enum1 = attrs.getAll(); // "dc" and "ref" attrs
882
          if (enum1.hasMore()) {
883
            Attribute attr = (Attribute)enum1.next();
884
            String attrValue = (String)attr.get();
885
            String attrName = (String)attr.getID();
886
            //System.out.println(attrName + "=" + attrValue);
887
            if ( enum1.hasMore() ) {
888
              attr = (Attribute)enum1.next();
889
              String refValue = (String)attr.get();
890
              String refName = (String)attr.getID();
891
              //System.out.println(refName + "=" + refValue);
892
              if ( ldapBase.startsWith(refName + "=" + refValue) ) {
893
                trees.put(ldapBase,
894
                          attrValue.substring(0,attrValue.lastIndexOf("/")+1) );
895
              } else {
896
                trees.put(refName + "=" + refValue + "," + ldapBase,
897
                          attrValue.substring(0,attrValue.lastIndexOf("/")+1) );
898
              }
899
              //System.out.println("REFERRAL:" + attrValue);
900
            } else if ( ldapBase.startsWith(attrName + "=" + attrValue) ) {
901
                trees.put(ldapBase, ldapUrl);
902
            } else {              
903
                trees.put(attrName + "=" + attrValue + "," + ldapBase, ldapUrl);
904
                //System.out.println(ldapUrl + attrName + "=" + attrValue + "," + ldapBase);
905
            }
906
          }
907
        }
908

    
909
        // Close the context when we're done
910
        ctx.close();
911

    
912
    } catch (NamingException e) {
913
      System.err.println("Problem getting subtrees in AuthLdap.getSubtrees:" + e);
914
      throw new ConnectException(
915
      "Problem getting subtrees in AuthLdap.getSubtrees:" + e);
916
    }
917

    
918
    //System.out.println("number of subtrees:" + trees.size());
919
    return trees;
920
  }
921

    
922
  /**
923
   * Get all groups and users from authentication scheme.
924
   * The output is formatted in XML.
925
   * @param user the user which requests the information
926
   * @param password the user's password
927
   */
928
  public String getPrincipals(String user, String password)
929
                throws ConnectException
930
  {
931
    StringBuffer out = new StringBuffer();
932
    Vector usersIn = new Vector();
933
    
934
    out.append("<?xml version=\"1.0\"?>\n");
935
    out.append("<principals>\n");
936
    
937
    /*
938
     * get all subtrees first in the current dir context 
939
     * and then the Metacat users under them
940
     */
941
    Hashtable subtrees = getSubtrees(user,password,this.ldapUrl,this.ldapBase);
942
    
943
    Enumeration enum = subtrees.keys();
944
    while ( enum.hasMoreElements() ) {
945
      this.ldapBase = (String)enum.nextElement();
946
      this.ldapUrl = (String)subtrees.get(ldapBase);
947
      
948
      out.append("  <authSystem URI=\"" + 
949
                 this.ldapUrl + this.ldapBase + "\">\n");
950

    
951
      // get all groups for directory context
952
      String[] groups = getGroups(user, password);
953

    
954
      // for the groups and users that belong to them
955
      if ( groups.length > 0 ) {
956
        for (int i=0; i < groups.length; i++ ) {
957
          out.append("    <group>\n");
958
          out.append("      <groupname>" + groups[i] + "</groupname>\n");
959
          String[] usersForGroup = getUsers(user,password,groups[i]);
960
          for (int j=0; j < usersForGroup.length; j++ ) {
961
            usersIn.addElement(usersForGroup[j]);
962
            out.append("      <user>\n");
963
            out.append("        <username>" + usersForGroup[j] + "</username>\n");
964
            out.append("      </user>\n");
965
          }
966
          out.append("    </group>\n");
967
        }
968
      }
969
      // for the users not belonging to any group
970
      String[] users = getUsers(user, password);
971
      for (int j=0; j < users.length; j++ ) {
972
        if ( !usersIn.contains(users[j]) ) {
973
          out.append("    <user>\n");
974
          out.append("      <username>" + users[j] + "</username>\n");
975
          out.append("    </user>\n");
976
        }
977
      }
978
    
979
      out.append("  </authSystem>\n");
980
      if ( !usersIn.isEmpty() ) {
981
        usersIn.removeAllElements();
982
        usersIn.trimToSize();
983
      }
984
    
985
    }
986
    out.append("</principals>");
987
    return out.toString();
988
  }
989

    
990
  /**
991
   * Test method for the class
992
   */
993
  public static void main(String[] args) {
994

    
995
    // Provide a user, such as: "Matt Jones", or "jones"
996
    String user = args[0];
997
    String password = args[1];
998

    
999
    AuthLdap authservice = new AuthLdap();
1000

    
1001
    boolean isValid = false;
1002
    try {
1003
      isValid = authservice.authenticate(user, password);
1004
      if (isValid) {
1005
        System.out.println("Authentication successful for: " + user );
1006
        System.out.println(" ");
1007
        
1008
      } else {
1009
        System.out.println("Authentication failed for: " + user);
1010
      }
1011

    
1012
      if (isValid) {
1013
        //String group = args[2];
1014
        HashMap userInfo = authservice.getAttributes(user, password, "knb");
1015
        // Print all of the attributes
1016
        Iterator attList = (Iterator)(((Set)userInfo.keySet()).iterator());
1017
        while (attList.hasNext()) {
1018
          String att = (String)attList.next();
1019
          Vector values = (Vector)userInfo.get(att);
1020
          Iterator attvalues = values.iterator();
1021
          while (attvalues.hasNext()) {
1022
            String value = (String)attvalues.next();
1023
            System.out.println(att + ": " + value);
1024
          }
1025
        }
1026

    
1027
      }
1028

    
1029
      // get the whole list groups and users in XML format
1030
      if (isValid) {
1031
        authservice = new AuthLdap();
1032
        String out = authservice.getPrincipals(user, password);
1033
        java.io.File f = new java.io.File("principals.xml");
1034
        java.io.FileWriter fw = new java.io.FileWriter(f);
1035
        java.io.BufferedWriter buff = new java.io.BufferedWriter(fw);
1036
        buff.write(out);
1037
        buff.flush();
1038
        buff.close();
1039
        fw.close();
1040
      }
1041

    
1042
    } catch (ConnectException ce) {
1043
      System.err.println(ce.getMessage());
1044
    } catch (java.io.IOException ioe) {
1045
      System.err.println("I/O Error writing to file principals.txt");
1046
    }
1047
  }
1048
}
(6-6/40)