Revision 3156
Added by Chris Jones almost 18 years ago
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
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.