Project

General

Profile

« Previous | Next » 

Revision 3156

As a partial fix to http://bugzilla.ecoinformatics.org/show_bug.cgi?id=2747,
I've modified AuthLdap.getGroups() and removed the code that handles LDAP
referral connect and search timeout issues in a separate thread. I've replaced
this code with ReferralException code that uses two JNDI parameter settings:

SearchControls.setTimeLimit() and com.sun.jndi.ldap.connect.timeout. The former
limits how long in milliseconds a search can run without returning, and the
latter limits how long in milliseconds a connection to an LDAP referral can
wait with no successful connection. The previous code opened a new Thread
for each ReferralException, and interrupted the thread after 5 seconds. In
this way, the code is simpler and configurable.

Next, this patch changes how referrals are handled. Previously, the code
would terminate and return the groups array after hitting any NamingException
along the way. The new code iterates through all of the referrals in an outer
loop, handling NamingExceptions within an inner try/catch statement. Once
all referrals are processed, the groups array is finally returned.

Lastly, this patch changes how referred group hits are handled. This should
be open for discussion and testing. As it is, groups that are found at the
top level of the ecoinformatics.org LDAP tree will be returned as a relative
group name, such as cn=marine. However, any referral group hits get returned
as absolute URLs such as
ldap://directory.piscoweb.org:389/cn=data-managers,ou=groups,dc=piscoweb,dc=org??sub.
The above URL needs to be translated into an ecoinformatics.org-relative group.
Therefore, this patch does a second query to the ecoinformatics LDAP and finds
the point of the referral, in this case o=PISCOGROUPS,dc=ecoinformatics,dc=org.
The group is then rebuilt as cn=data-managers,o=PISCOGROUPS,dc=ecoinformatics,dc=org.

The question arises: Is this a good convention to stick to? It assumes (as
other parts of the Metacat code does) that groups are defined by commonName (cn)
attributes, and are located just below the top level of the referral point.
Perhaps there is a more flexible way to implement groups, but this way follows
the conventions thus far in the NCEAS, PISCO, LTER, and UCNRS LDAP servers.

View differences:

src/edu/ucsb/nceas/metacat/AuthLdap.java
670 670
   * @param foruser the user whose group list should be returned
671 671
   * @returns string array of the group names
672 672
   */
673
  public String[][] getGroups(String user, String password, String foruser) throws
674
      ConnectException {
673
  public String[][] getGroups(String user, String password, 
674
    String foruser) throws ConnectException {
675
    
676
    logMetacat.debug("getGroups() called.");
677
    
678
    // create vectors to store group and dscription values returned from the
679
    // ldap servers
675 680
    Vector gvec = new Vector();
676 681
    Vector desc = new Vector();
677 682
    Attributes tempAttr = null;
678

  
679
    //Pass the username and password to run() method
683
    Attributes rsrAttr = null;
684
    
685
    // DURING getGroups(), DO WE NOT BIND USING userName AND userPassword??
686
    // NEED TO FIX THIS ...
680 687
    userName = user;
681 688
    userPassword = password;
682 689
    // Identify service provider to use
683
    env.put(Context.INITIAL_CONTEXT_FACTORY,
684
            "com.sun.jndi.ldap.LdapCtxFactory");
690
    env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
685 691
    env.put(Context.REFERRAL, "throw");
686 692
    env.put(Context.PROVIDER_URL, ldapUrl);
687
    try {
693
    env.put("com.sun.jndi.ldap.connect.timeout", ldapConnectTimeLimit);
694
    
695
        
696
    // Iterate through the referrals, handling NamingExceptions in the 
697
    // outer catch statement, ReferralExceptions in the inner catch statement
698
    try { // outer try
699
       
688 700
      // Create the initial directory context
689 701
      DirContext ctx = new InitialDirContext(env);
690
      // Specify the ids of the attributes to return
691
      String[] attrIDs = {
692
          "cn", "o", "description"};
702
      
693 703
      // Specify the attributes to match.
694 704
      // Groups are objects with attribute objectclass=groupofuniquenames.
695 705
      // and have attribute uniquemember: uid=foruser,ldapbase.
696 706
      SearchControls ctls = new SearchControls();
707
      // Specify the ids of the attributes to return
708
      String[] attrIDs = {"cn", "o", "description"};
697 709
      ctls.setReturningAttributes(attrIDs);
710
      // set the ldap search scope
698 711
      ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
712
      // set a 10 second time limit on searches to limit non-responding servers
713
      ctls.setTimeLimit(ldapSearchTimeLimit);
714
      // return at most 20000 entries
715
      ctls.setCountLimit(ldapSearchCountLimit);
699 716

  
717
      // build the ldap search filter that represents the "group" concept
700 718
      String filter = null;
701 719
      String gfilter = "(objectClass=groupOfUniqueNames)";
702 720
      if (null == foruser) {
703 721
        filter = gfilter;
704
      }
705
      else {
722
      } else {
706 723
        filter = "(& " + gfilter + "(uniqueMember=" + foruser + "))";
707 724
      }
708
      logMetacat.info("searching for groups: " + filter);
709
      NamingEnumeration namingEnum = ctx.search(ldapBase, filter, ctls);
725
      logMetacat.info("group filter is: " + filter);
726
      
727
      // now, search and iterate through the referrals
728
      for (boolean moreReferrals = true; moreReferrals;) {  
729
        try { // inner try
730
      
731
          NamingEnumeration namingEnum = ctx.search(ldapBase, filter, ctls);
710 732

  
711
      // Print the groups
712
      logMetacat.info("getting group results.");
713
      while (namingEnum.hasMore()) {
714
        SearchResult sr = (SearchResult) namingEnum.next();
715
        tempAttr = sr.getAttributes();
733
          // Print the groups
734
          while (namingEnum.hasMore()) {
735
            SearchResult sr = (SearchResult) namingEnum.next();
736
            
737
            tempAttr = sr.getAttributes();
738
            logMetacat.debug("Search result attributes are:" +
739
              "\n\t~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" +
740
              "\n\tcn:          " + tempAttr.get("cn") +
741
              "\n\to:           " + tempAttr.get("o") +
742
              "\n\tdescription: " + tempAttr.get("description") +
743
              "\n\t~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
716 744

  
717
        if ( (tempAttr.get("description") + "").startsWith("description: ")) {
718
          desc.add( (tempAttr.get("description") + "").substring(13));
719
        }
720
        else {
721
          desc.add(tempAttr.get("description") + "");
722
        }
745
            if ( (tempAttr.get("description") + "").startsWith("description: ")) {
746
              desc.add( (tempAttr.get("description") + "").substring(13));
747
            }
748
            else {
749
              desc.add(tempAttr.get("description") + "");
750
            }
751
            
752
            // check for an absolute URL value or an answer value relative 
753
            // to the target context  
754
            if ( !sr.getName().startsWith("ldap") &&
755
                  sr.isRelative()) {
756
              logMetacat.debug("Search result entry is relative ...");
757
              gvec.add(sr.getName() + "," + ldapBase);
758
              logMetacat.info("group " + sr.getName() + "," +ldapBase + 
759
              " added to the group vector"); 
760
            } else {
761
              logMetacat.debug("Search result entry is absolute ...");
762
              
763
              // search the top level directory for referral objects and match
764
              // that of the search result's absolute URL.  This will let us
765
              // rebuild the group name from the search result, referral point
766
              // in the top directory tree, and ldapBase.
767
              
768
              // configure a new directory search first
769
              Hashtable envHash = new Hashtable(11);
770
              // Identify service provider to use
771
              envHash.put(Context.INITIAL_CONTEXT_FACTORY, 
772
                "com.sun.jndi.ldap.LdapCtxFactory");
773
              envHash.put(Context.REFERRAL, "ignore");
774
              envHash.put(Context.PROVIDER_URL, ldapUrl);
775
              envHash.put("com.sun.jndi.ldap.connect.timeout", 
776
                ldapConnectTimeLimit);
777
              
778
              try {
779
                // Create the initial directory context
780
                DirContext DirCtx = new InitialDirContext(envHash);
723 781

  
724
        gvec.add(sr.getName() + "," + ldapBase);
725
        logMetacat.info("group " + sr.getName() +
726
                                 " added to Group vector");
727
      }
728
      // Close the context when we're done
782
                SearchControls searchCtls = new SearchControls();
783
                // Specify the ids of the attributes to return
784
                String[] attrNames = {"o"};
785
                searchCtls.setReturningAttributes(attrNames);
786
                // set the ldap search scope - only look for top level referrals
787
                searchCtls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
788
                // set a time limit on searches to limit non-responding servers
789
                searchCtls.setTimeLimit(ldapSearchTimeLimit);
790
                // return the configured number of entries
791
                searchCtls.setCountLimit(ldapSearchCountLimit);
792
                
793
                // Specify the attributes to match.
794
                // build the ldap search filter to match referral entries that 
795
                // match the search result
796
                String rFilter = "(&(objectClass=referral)(ref=" + 
797
                currentReferralInfo.substring(0, 
798
                currentReferralInfo.indexOf("?")) + "))";
799
                logMetacat.debug("rFilter is: " + rFilter);
800
                
801
                NamingEnumeration rNamingEnum = 
802
                  DirCtx.search(ldapBase, rFilter, searchCtls);
803
                  
804
                while (rNamingEnum.hasMore()) {
805
                  SearchResult rsr = (SearchResult) rNamingEnum.next();
806
                  rsrAttr = rsr.getAttributes();
807
                  logMetacat.debug("referral search result is: " +
808
                    rsr.toString());
809
                    
810
                  // add the returned groups to the group vector.  Test the 
811
                  // syntax of the returned attributes - sometimes they are 
812
                  // preceded with the attribute id and a colon
813
                  if ( (tempAttr.get("cn") + "").startsWith("cn: ")) {
814
                    gvec.add( "cn=" + (tempAttr.get("cn") + "").substring(4) + 
815
                    "," + "o=" + (rsrAttr.get("o") + "").substring(3 ) + "," + 
816
                    ldapBase);
817
                    logMetacat.info("group " + 
818
                    (tempAttr.get("cn") + "").substring(4) + "," + 
819
                    "o=" + (rsrAttr.get("o") + "").substring(3) + "," + 
820
                    ldapBase + " added to the group vector");
821
                  } else {
822
                    gvec.add( "cn=" + tempAttr.get("cn") + "," + 
823
                    "o=" + rsrAttr.get("o") +
824
                    "," + ldapBase);
825
                    logMetacat.info("group " + 
826
                    "cn=" + tempAttr.get("cn") + "," + 
827
                    "o=" + rsrAttr.get("o") + "," + 
828
                    ldapBase + " added to the group vector");
829
                  }  
830
                }
831
                
832
              } catch (NamingException nameEx){
833
                logMetacat.debug("Caught naming exception: ");
834
                nameEx.printStackTrace(System.err);
835
              }
836
            }
837
          }// end while
838
          
839
          moreReferrals = false;
840
          
841
        } catch (ReferralException re) {
842
          
843
          logMetacat.info(
844
            "In AuthLdap.getGroups(), caught referral exception: " +
845
            re.getReferralInfo()
846
          );
847
          this.currentReferralInfo = (String) re.getReferralInfo();
848
          
849
          // set moreReferrals to true and set the referral context
850
          moreReferrals = true;
851
          ctx = (DirContext) re.getReferralContext();
852
           
853
        }// end inner try
854
      }// end for
855
      
856
      // close the context now that all initial and referral 
857
      // searches are processed
729 858
      ctx.close();
730

  
731
    }
732
    catch (ReferralException re) {
733
      refExc = re;
734
      Thread t = new Thread(new GetGroup());
735
      logMetacat.info("Starting thread...");
736
      t.start();
737
      logMetacat.info("sleeping for 5 seconds.");
738
      try {
739
        Thread.sleep(5000);
740
      }
741
      catch (InterruptedException ie) {
742
        logMetacat.error("main thread interrupted: " + ie.getMessage());
743
      }
744
      //this is a manual override of jndi's hideously long time
745
      //out period.
746
      logMetacat.info("Awake after 5 seconds.");
747
      if (referralContext == null) {
748
        logMetacat.info("thread timed out...returning groups: " +
749
                          gvec.toString());
750
        String groups[][] = new String[gvec.size()][2];
751
        for (int i = 0; i < gvec.size(); i++) {
752
          groups[i][0] = (String) gvec.elementAt(i);
753
          groups[i][1] = (String) desc.elementAt(i);
754
        }
755
        t.interrupt();
756
        return groups;
757
      }
758
      DirContext dc = (DirContext) referralContext;
759
      String[] attrIDs = {
760
          "cn", "o", "description"};
761
      // Specify the attributes to match.
762
      // Groups are objects with attribute objectclass=groupofuniquenames.
763
      // and have attribute uniquemember: uid=foruser,ldapbase.
764
      SearchControls ctls = new SearchControls();
765
      ctls.setReturningAttributes(attrIDs);
766
      ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
767

  
768
      String filter = null;
769
      String gfilter = "(objectClass=groupOfUniqueNames)";
770
      if (null == foruser) {
771
        filter = gfilter;
772
      }
773
      else {
774
        filter = "(& " + gfilter + "(uniqueMember=" + foruser + "))";
775
      }
776

  
777
      try {
778
        NamingEnumeration namingEnum = dc.search(ldapBase, filter, ctls);
779
        // Print the groups
780
        while (namingEnum.hasMore()) {
781
          SearchResult sr = (SearchResult) namingEnum.next();
782
          tempAttr = sr.getAttributes();
783

  
784
          if ( (tempAttr.get("description") + "").startsWith("description: ")) {
785
            desc.add( (tempAttr.get("description") + "").substring(13));
786
          }
787
          else {
788
            desc.add(tempAttr.get("description") + "");
789
          }
790

  
791
          gvec.add(sr.getName() + "," + ldapBase);
792
        }
793

  
794
        referralContext.close();
795
        dc.close();
796
      }
797
      catch (NamingException ne) {
798
        logMetacat.info("Naming Exception in AuthLdap.getGroups for referals" +
799
                                 ne.getExplanation() + ne.getMessage());
800
      }
801
    }
802
    catch (NamingException e) {
859
      
860
    } catch (NamingException e) {
861
      
862
      // naming exceptions get logged, groups are returned
863
      logMetacat.info("In AuthLdap.getGroups(), caught naming exception: ");
803 864
      e.printStackTrace(System.err);
865
      
866
    } finally {
867
      // once all referrals are followed, report and return the groups found
868
      logMetacat.warn("The user is in the following groups: " +
869
                               gvec.toString());
870
      //build and return the groups array
804 871
      String groups[][] = new String[gvec.size()][2];
805 872
      for (int i = 0; i < gvec.size(); i++) {
806 873
        groups[i][0] = (String) gvec.elementAt(i);
807 874
        groups[i][1] = (String) desc.elementAt(i);
808 875
      }
809 876
      return groups;
810
      /*throw new ConnectException(
811
             "Problem getting groups for a user in AuthLdap.getGroups:" + e);*/
812
    }
813

  
814
    logMetacat.warn("The user is in the following groups: " +
815
                             gvec.toString());
816
    String groups[][] = new String[gvec.size()][2];
817
    for (int i = 0; i < gvec.size(); i++) {
818
      groups[i][0] = (String) gvec.elementAt(i);
819
      groups[i][1] = (String) desc.elementAt(i);
820
    }
821
    return groups;
877
    }// end outer try
822 878
  }
823 879

  
824 880
  /**
......
839 895
   * @param foruser the user whose attributes should be returned
840 896
   * @returns HashMap a map of attribute name to a Vector of values
841 897
   */
842
  public HashMap getAttributes(String user, String password, String foruser) throws
843
      ConnectException {
898
  public HashMap getAttributes(String user, String password, 
899
    String foruser) throws ConnectException {
844 900
    HashMap attributes = new HashMap();
845 901
    String ldapUrl = this.ldapUrl;
846 902
    String ldapBase = this.ldapBase;
......
894 950
   * starting from the Metacat LDAP root,
895 951
   * i.e. ldap://dev.nceas.ucsb.edu/dc=ecoinformatics,dc=org
896 952
   */
897
  private Hashtable getSubtrees(String user, String password,
898
                                String ldapUrl, String ldapBase) throws
899
      ConnectException {
953
  private Hashtable getSubtrees(String user, String password, 
954
    String ldapUrl, String ldapBase) throws ConnectException {
900 955
    Hashtable trees = new Hashtable();
901 956

  
902 957
    // Identify service provider to use
......
1006 1061
   * @param user the user which requests the information
1007 1062
   * @param password the user's password
1008 1063
   */
1009
  public String getPrincipals(String user, String password) throws
1010
      ConnectException {
1064
  public String getPrincipals(String user, String password) throws 
1065
    ConnectException {
1011 1066
    StringBuffer out = new StringBuffer();
1012 1067
   
1013 1068
    out.append("<?xml version=\"1.0\" encoding=\"US-ASCII\"?>\n");

Also available in: Unified diff