Skip to Content.
Sympa Menu

shibboleth-dev - JNDI data connector and DN-based searches (was: 2 interesting questions)

Subject: Shibboleth Developers

List archive

JNDI data connector and DN-based searches (was: 2 interesting questions)


Chronological Thread 
  • From: Brent Putman <>
  • To:
  • Subject: JNDI data connector and DN-based searches (was: 2 interesting questions)
  • Date: Fri, 23 Sep 2005 17:38:43 -0400

This question over on shib-users brought up again something that I have encountered in the past:  the general need within the resolver to use the value of a user's DN (obtained in an initial LDAP search) in a second search that has the first as a dependency.  I saw that in 1.3 in the JNDIDirectoryDataConnector, the DN value is now nicely being placed in the resolved attribute set (when using Sun's LDAP provider), but it still seems like you would have to write a custom connector to actually make use of it.  If I'm missing something there let me know...

What I want to be able to do is something more declarative in the resolver config example below -  a %DN% macro that gets expanded using the dn value available from a previous search (via a DataConnectorDependency).  And ideally, I want to be able to use it in either the search filter, *or* in the provider URL as the LDAP search base.


    <JNDIDirectoryDataConnector id="directory">
        <Search filter="uid=%PRINCIPAL%">
            <Controls searchScope="SUBTREE_SCOPE" returningObjects="false" />
        </Search>
        <Property name="java.naming.factory.initial" value="com.sun.jndi.ldap.LdapCtxFactory" />
        <Property name="java.naming.provider.url" value="ldap://directory.georgetown.edu/dc=georgetown,dc=edu" />
    </JNDIDirectoryDataConnector>


    <JNDIDirectoryDataConnector id="directory_groups" mergeMultipleResults="true">
        <DataConnectorDependency requires="directory"/>
        <Search filter="uniquemember=%DN%">
            <Controls searchScope="SUBTREE_SCOPE" returningObjects="false" />
        </Search>
        <Property name="java.naming.factory.initial" value="com.sun.jndi.ldap.LdapCtxFactory" />
        <Property name="java.naming.provider.url" value="ldap://directory.georgetown.edu/dc=georgetown,dc=edu" />
    </JNDIDirectoryDataConnector>

   
    <JNDIDirectoryDataConnector id="directory_roles" mergeMultipleResults="true">
        <DataConnectorDependency requires="directory"/>
        <Search filter="objectclass=organizationalRole">
            <Controls searchScope="SUBTREE_SCOPE" returningObjects="false" />
        </Search>
        <Property name="java.naming.factory.initial" value="com.sun.jndi.ldap.LdapCtxFactory" />
        <Property name="java.naming.provider.url" value="ldap://directory.georgetown.edu/%DN%" />
    </JNDIDirectoryDataConnector>


The "directory" connector is the traditional basic search using the user's authenticated principal name, and returns the user's "dn" attribute, among others.  The "directory_groups" example shows the typical case where the user's DN needs to be used as the input to the search filter, for example when the directory contains GroupofNames or GroupofUniqueNames objects, containing member or uniqueMember attributes with DN values.  I think this is a very common use case.  The "directory_roles" example is the less common (but nonetheless extant and valid) case where the user's DN is not a leaf, but is a container holding subordinate objects specific to that user (ala Roman's example).  Here you need to be able to insert the  user's DN into the LDAP search base.

So, with that in mind, I have written the attached patch for JNDIDirectoryDataConnector which implements the above basic functionality.  The search filter case is pretty easy.  The provider url case is a little more complicated, and means having to skip the fail-fast test on connector initialization (or maybe using a known fixed search base to test with, but that might not be portable across directory servers).  In order for the %DN% macro expansion to work, you must have a DataConnectorDependency declared which has resolved a "dn" attribute.  I have tested it and it does work for both of the above configs, but would consider it a work-in-progress... I'm sure I've missed some edge cases, etc.

So I throw this out there for consideration, or at least for discussion.  Perhaps others won't consider the use cases to be common enough to warrant adding this functionality to the base connector.  If that is the case, then I could perhaps rework this into a custom connector that extends the base JNDI connector.  Mainly I just wanted to see what others think.

Thanks,
Brent


Roman Sozinov wrote:
The inner structure of my ldap server is:

dn: ou=People,c=LT
objectClass: organizationalUnit
ou: People

dn: uid=user3,ou=People,c=lt
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
uid: user3
cn: Sam
telephoneNumber: 55544

dn: ou=sites,uid=user3,ou=People,c=lt
ou: sites
objectClass: top
objectClass: organizationalUnit

dn: ou=www.site1.lt,ou=sites,uid=user3,ou=People,c=lt
cn: admin
ou: cnt.e-space.lt
objectClass: organizationalRole
objectClass: top

dn: ou=www.site2.lt,ou=sites,uid=user3,ou=People,c=lt
cn: manager
ou: cnt.e-space.lt
objectClass: organizationalRole
objectClass: top

As you can see, my user uid=user3,ou=People,c=lt has different roles for
different sites(www.site1.lt, www.site2.lt).
But i don't know how to say to IdP to get roles(admin, manager) from my ldap
server, because the user is authenticated on IdP using JNDI-Connector:
   <JNDIDirectoryDataConnector id="directory">
                <Search filter="uid=%PRINCIPAL%">
                 <Controls searchScope="SUBTREE_SCOPE"
returningObjects="false" />
                </Search>
                <Property name="java.naming.factory.initial"
value="com.sun.jndi.ldap.LdapCtxFactory" />
                <Property name="java.naming.provider.url"
value="ldap://myldap/ou=People,c=lt
                <Property name="java.naming.security.principal"
value="cn=admin,c=lt" />
                <Property name="java.naming.security.credentials"
value="password" />
        </JNDIDirectoryDataConnector>

and i don't know how to get attributes ...ou=sites,uid=user3,ou=People,c=lt
with a help of arps-files.
If i need to use the second JNDI-connector, what the sintax should be?

-- Roman Sozinov


----- Original Message ----- 
From: "Walter Hoehn" 
To: 
Sent: Thursday, September 22, 2005 10:45 PM
Subject: Re: 2 interesting questions


I'm not certain that I understand your DIT layout, but I'm pretty sure
you can accomplish what you want by having two JNDI connectors in your
configuration.

-Walter


On Sep 22, 2005, at 4:14 AM, Roman Sozinov wrote:
  
and there is another question about accessing LDAP-atributes from our
IdP:
When we give access with JNDIDirectoryDataConnector to the user, we
can operate only with that his attributes, which are in the same level
of directory as his uid(cn). But how can we get access to attributes
which are inside user's tree.
For example,

1st level:
(key attribute) uid
uid=user1
cn=bob
sn=smith
ou=roles

2nd level
(key attribute) ou
ou=roles
cn=manager

We have access to attributes: uid, cn,sn,ou in the first level of
user's tree, but can we have access to attribute cn at the 2-nd level?
If "yes" - how?

    
__________
http://www.newhost.ru - ÃîâÝëÙ ÔÞÜ ÔÛï ²ÐèÕÓÞ áÐÙâÐ!


  
Index: JNDIDirectoryDataConnector.java
===================================================================
RCS file:
/home/cvs/shibboleth/shibboleth/java/src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/JNDIDirectoryDataConnector.java,v
retrieving revision 1.18
diff -u -r1.18 JNDIDirectoryDataConnector.java
--- JNDIDirectoryDataConnector.java 21 Aug 2005 11:42:03 -0000 1.18
+++ JNDIDirectoryDataConnector.java 23 Sep 2005 20:53:21 -0000
@@ -148,8 +148,12 @@
try {
if (!startTls) {
try {
- log.debug("Attempting to connect to
JNDI directory source as a sanity check.");
- context = initConnection();
+ if ( !
properties.getProperty("java.naming.provider.url").contains("%DN%") ) {
+ log.debug("Attempting to
connect to JNDI directory source as a sanity check.");
+ context = initConnection();
+ } else {
+ log.debug("Bypassing JNDI
fail-fast test because provider URL contains '%DN%'");
+ }
} catch (IOException ioe) {
log.error("Failed to startup
directory context: " + ioe);
throw new
ResolutionPlugInException("Failed to startup directory context.");
@@ -185,8 +189,12 @@
sslc.init(new
KeyManager[]{keyManager}, null, new SecureRandom());
sslsf = sslc.getSocketFactory();

- log.debug("Attempting to connect to
JNDI directory source as a sanity check.");
- initConnection();
+ if (!
properties.getProperty("java.naming.provider.url").contains("%DN%") ) {
+ log.debug("Attempting to
connect to JNDI directory source as a sanity check.");
+ initConnection();
+ } else {
+ log.debug("Bypassing JNDI
fail-fast test because provider URL contains '%DN%'");
+ }
} catch (GeneralSecurityException gse) {
log.error("Failed to startup
directory context. Error creating SSL socket: " + gse);
throw new
ResolutionPlugInException("Failed to startup directory context.");
@@ -299,6 +307,54 @@
InitialDirContext context = null;
NamingEnumeration nEnumeration = null;
String populatedSearch =
searchFilter.replaceAll("%PRINCIPAL%", principal.getName());
+
+ if (populatedSearch.contains("%DN%")
+ ||
properties.getProperty("java.naming.provider.url").contains("%DN%") ) {
+ Iterator connectorDependIt =
connectorDependencyIds.iterator();
+ Attribute attr = null;
+ while (connectorDependIt.hasNext()) {
+ Attributes attrs =
depends.getConnectorResolution((String) connectorDependIt.next());
+ if (attrs != null) {
+ attr = attrs.get("dn");
+ // TODO: Break out when find the
first dn attribute - there can be only one...
+ // or maybe should loop over
them all.. or throw an error if multi ???
+ if (attr != null)
+ break;
+ }
+ }
+ try {
+ if (attr != null) {
+ // There should only be one value for
dn
+ String dn = (String) attr.get();
+ if (!dn.equals("")) {
+ populatedSearch =
populatedSearch.replaceAll("%DN%", dn);
+ // Also allow interpolation
into the LDAP search base
+ String providerURL =
properties.getProperty("java.naming.provider.url");
+ if
(providerURL.contains("%DN%")) {
+ providerURL =
providerURL.replaceAll("%DN%", dn);
+ // TODO may need to
encode/escape this string further, if it has spaces, etc
+
properties.setProperty("java.naming.provider.url",
+
providerURL);
+ }
+ } else {
+ log.error("Found an empty
'dn' value for '%DN%' for principal ("
+ +
principal.getName() + ")");
+ throw new
ResolutionPlugInException("Found an empty 'dn' value for '%DN%'");
+ }
+ } else {
+ log.error("Search filter contained
%DN% macro, but was unable to resolve a 'dn' attribute value for principal ("
+ + principal.getName()
+ ")" );
+ throw new
ResolutionPlugInException("Unable to resolve 'dn' value for %DN%.");
+ }
+ }
+ catch (NamingException e) {
+ log.error("An error occurred while obtaining
DN attribute dependency data for principal (" + principal.getName() + ") :"
+ + e.getMessage());
+ throw new ResolutionPlugInException("Problem
obtaining 'dn' attribute value from resolved data connector dependency");
+ }
+ }
+
+
try {
try {
context = initConnection();



Archive powered by MHonArc 2.6.16.

Top of Page