Hey everyone,

I have a patch ready for UP-2133 ("Add the SmartLdapGroupStore to 
uPortal").  I'm attaching it to this email and to the JIRA.

SmartLdapGroupStore allows uPortal to see and use groups of people that 
are maintained in an LDAP directory.  It was developed for JohnsHopkins, 
and I know I've spoken to a handful of others who have expressed 
interest, particularly University of Illinois.

drew wills

-- 
You are currently subscribed to [email protected] as: [EMAIL 
PROTECTED]
To unsubscribe, change settings or access archives, see 
http://www.ja-sig.org/wiki/display/JSG/uportal-dev
Index: 
uportal-impl/src/main/java/org/jasig/portal/groups/smartldap/LdapRecord.java
===================================================================
--- 
uportal-impl/src/main/java/org/jasig/portal/groups/smartldap/LdapRecord.java    
    (revision 0)
+++ 
uportal-impl/src/main/java/org/jasig/portal/groups/smartldap/LdapRecord.java    
    (revision 0)
@@ -0,0 +1,50 @@
+/**
+ * Copyright 2008 The JA-SIG Collaborative.  All rights reserved.
+ * See license distributed with this file and
+ * available online at http://www.uportal.org/license.html
+ */
+
+package org.jasig.portal.groups.smartldap;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.jasig.portal.groups.IEntityGroup;
+
+public final class LdapRecord {
+
+       // Instance Members.
+       private final IEntityGroup group;
+       private final List<String> keysOfChildren;
+
+       /*
+        * Public API.
+        */
+               
+       public LdapRecord(IEntityGroup group, List<String> keysOfChildren) {
+
+               // Assertions.
+               if (group == null) {
+                       String msg = "Argument 'group' cannot be null.";
+                       throw new IllegalArgumentException(msg);
+               }
+               if (keysOfChildren == null) {
+                       String msg = "Argument 'keysOfChildren' cannot be 
null.";
+                       throw new IllegalArgumentException(msg);
+               }
+               
+               // Instance Members.
+               this.group = group;
+               this.keysOfChildren = 
Collections.unmodifiableList(keysOfChildren);
+               
+       }
+       
+       public IEntityGroup getGroup() {
+               return group;
+       }
+       
+       public List<String> getKeysOfChildren() {
+               return keysOfChildren;
+       }
+       
+}
Index: 
uportal-impl/src/main/java/org/jasig/portal/groups/smartldap/SmartLdapEntitySearcher.java
===================================================================
--- 
uportal-impl/src/main/java/org/jasig/portal/groups/smartldap/SmartLdapEntitySearcher.java
   (revision 0)
+++ 
uportal-impl/src/main/java/org/jasig/portal/groups/smartldap/SmartLdapEntitySearcher.java
   (revision 0)
@@ -0,0 +1,50 @@
+/**
+ * Copyright 2008 The JA-SIG Collaborative.  All rights reserved.
+ * See license distributed with this file and
+ * available online at http://www.uportal.org/license.html
+ */
+
+package org.jasig.portal.groups.smartldap;
+
+import org.jasig.portal.EntityIdentifier;
+import org.jasig.portal.groups.GroupsException;
+import org.jasig.portal.groups.IEntityGroupStore;
+import org.jasig.portal.groups.IEntitySearcher;
+import org.jasig.portal.groups.IEntitySearcherFactory;
+
+public class SmartLdapEntitySearcher implements IEntitySearcher {
+
+       // Instance Members.
+       private final IEntityGroupStore store;
+       
+    /*
+     * Public API.
+     */
+
+    public static final class Factory implements IEntitySearcherFactory {
+        
+        /*
+         * Public API.
+         */
+
+        public IEntitySearcher newEntitySearcher() throws GroupsException {
+            return new SmartLdapEntitySearcher(new 
SmartLdapGroupStore.Factory().newGroupStore());
+        }
+    
+    }
+
+    public EntityIdentifier[] searchForEntities(String query, int method, 
Class type) throws GroupsException {
+       return store.searchForGroups(query, method, type);
+    }
+
+    /*
+     * Implementation.
+     */
+
+    private SmartLdapEntitySearcher(IEntityGroupStore store) {
+
+       // Instance Members.
+       this.store = store;
+    }
+
+}
Index: 
uportal-impl/src/main/java/org/jasig/portal/groups/smartldap/SmartLdapEntityStore.java
===================================================================
--- 
uportal-impl/src/main/java/org/jasig/portal/groups/smartldap/SmartLdapEntityStore.java
      (revision 0)
+++ 
uportal-impl/src/main/java/org/jasig/portal/groups/smartldap/SmartLdapEntityStore.java
      (revision 0)
@@ -0,0 +1,47 @@
+/**
+ * Copyright 2008 The JA-SIG Collaborative.  All rights reserved.
+ * See license distributed with this file and
+ * available online at http://www.uportal.org/license.html
+ */
+
+package org.jasig.portal.groups.smartldap;
+
+import org.jasig.portal.groups.EntityImpl;
+import org.jasig.portal.groups.GroupsException;
+import org.jasig.portal.groups.IEntity;
+import org.jasig.portal.groups.IEntityStore;
+import org.jasig.portal.groups.IEntityStoreFactory;
+
+public class SmartLdapEntityStore implements IEntityStore {
+
+    /*
+     * Public API.
+     */
+
+    public static final class Factory implements IEntityStoreFactory {
+        
+        /*
+         * Public API.
+         */
+
+        public IEntityStore newEntityStore() throws GroupsException {
+            return new SmartLdapEntityStore();
+        }
+    
+    }
+    
+    public IEntity newInstance(String key) throws GroupsException {
+       return newInstance(key, null);
+    }
+
+    public IEntity newInstance(String key, Class type) throws GroupsException {
+       return new EntityImpl(key, type);
+    }
+    
+    /*
+     * Implementation.
+     */
+
+    private SmartLdapEntityStore() {}
+    
+}
Index: 
uportal-impl/src/main/java/org/jasig/portal/groups/smartldap/SmartLdapGroupStore.java
===================================================================
--- 
uportal-impl/src/main/java/org/jasig/portal/groups/smartldap/SmartLdapGroupStore.java
       (revision 0)
+++ 
uportal-impl/src/main/java/org/jasig/portal/groups/smartldap/SmartLdapGroupStore.java
       (revision 0)
@@ -0,0 +1,578 @@
+/**
+ * Copyright 2008 The JA-SIG Collaborative.  All rights reserved.
+ * See license distributed with this file and
+ * available online at http://www.uportal.org/license.html
+ */
+
+package org.jasig.portal.groups.smartldap;
+
+import java.net.URL;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.danann.cernunnos.Task;
+import org.danann.cernunnos.runtime.RuntimeRequestResponse;
+import org.danann.cernunnos.runtime.ScriptRunner;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.support.FileSystemXmlApplicationContext;
+
+import org.jasig.portal.EntityIdentifier;
+import org.jasig.portal.groups.ComponentGroupServiceDescriptor;
+import org.jasig.portal.groups.EntityTestingGroupImpl;
+import org.jasig.portal.groups.GroupsException;
+import org.jasig.portal.groups.IEntityGroup;
+import org.jasig.portal.groups.IEntityGroupStore;
+import org.jasig.portal.groups.IEntityGroupStoreFactory;
+import org.jasig.portal.groups.IGroupConstants;
+import org.jasig.portal.groups.IGroupMember;
+import org.jasig.portal.groups.ILockableEntityGroup;
+import org.jasig.portal.properties.PropertiesManager;
+import org.jasig.portal.security.IPerson;
+import org.jasig.portal.security.PersonFactory;
+import org.jasig.portal.services.PersonDirectory;
+
+public class SmartLdapGroupStore implements IEntityGroupStore {
+               
+    // Instance Members.
+    private ApplicationContext spring_context = null;
+    private final ScriptRunner runner;
+    private final Task initTask;
+    private final Log log = LogFactory.getLog(getClass());
+    private boolean initialized;
+    
+    /*
+     * Indexed Collections.
+     */
+    
+    /**
+     * Map of all groups keyed by 'key' (DN).  Includes ROOT_GROUP.
+     */
+    private Map<String,IEntityGroup> groups;
+    
+    /**
+     * Map of all parent relationships keyed by the 'key' (DN) of the child;  
+     * the values are lists of the 'keys' (DNs) of its parents.  
+     * Includes ROOT_GROUP.
+     */
+    private Map<String,List<String>> parents;
+    
+    /**
+     * Map of all child relationships keyed by the 'key' (DN) of the parent;  
+     * the values are lists of the 'keys' (DNs) of its children.  
+     * Includes ROOT_GROUP.
+     */
+    private Map<String,List<String>> children;
+    
+    /**
+     * Map of all 'keys' (DNs) of SmartLdap managed groups indexed by group 
+     * name in upper case.  Includes ROOT_GROUP.
+     */
+    private Map<String,List<String>> keysByUpperCaseName;
+    
+    /*
+     * Public API.
+     */
+
+    public static final String UNSUPPORTED_MESSAGE = 
+            "The SmartLdap implementation of JA-SIG Groups and Permissions 
(GaP) " +
+            "does not support this operation.";
+
+    public static final String ROOT_KEY = "SmartLdap ROOT";
+    public static final String ROOT_DESC = "A root group provided for the 
SmartLdapGroupStore.";
+       
+    public static final EntityIdentifier ENTITY_IDENTIFIER = new 
EntityIdentifier(ROOT_KEY, 
+                                                                               
        org.jasig.portal.groups.IEntityGroup.class);
+       
+    public static final IEntityGroup ROOT_GROUP;
+    static {
+           
+           try {
+               ROOT_GROUP = new EntityTestingGroupImpl(ROOT_KEY, 
IPerson.class);
+               ROOT_GROUP.setCreatorID("System");
+               ROOT_GROUP.setName(ROOT_KEY);
+               ROOT_GROUP.setDescription(ROOT_DESC);
+           } catch (Throwable t) {
+            throw new RuntimeException(t);
+           }
+
+    }
+
+       public static final class Factory implements IEntityGroupStoreFactory {
+        
+               private static final IEntityGroupStore INSTANCE = new 
SmartLdapGroupStore();
+               
+        /*
+         * Public API.
+         */
+
+        public IEntityGroupStore newGroupStore() throws GroupsException {
+            return INSTANCE;
+        }
+    
+        public IEntityGroupStore newGroupStore(ComponentGroupServiceDescriptor 
svcDescriptor) throws GroupsException {
+            return INSTANCE;
+        }
+    
+    }
+
+    public boolean contains(IEntityGroup group, IGroupMember member) throws 
GroupsException {
+        log.warn("Unsupported method accessed:  SmartLdapGroupStore.contains");
+        throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+    }
+
+    public void delete(IEntityGroup group) throws GroupsException {
+        log.warn("Unsupported method accessed:  SmartLdapGroupStore.delete");
+        throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+    }
+
+    /**
+     * Returns an instance of the <code>IEntityGroup</code> from the data 
store.
+     * @return org.jasig.portal.groups.IEntityGroup
+     * @param key java.lang.String
+     */
+    public IEntityGroup find(String key) throws GroupsException {
+       
+       if (!initialized) {
+               init();
+       }
+
+       if (log.isDebugEnabled()) {
+               log.debug("Invoking find() for key:  " + key);
+       }
+       
+       // All of our groups (incl. ROOT_GROUP) 
+       // are indexed in the 'groups' map by key...
+       return groups.get(key);
+    
+    }
+
+    /**
+     * Returns an <code>Iterator</code> over the <code>Collection</code> of
+     * <code>IEntityGroups</code> that the <code>IGroupMember</code> belongs 
to.
+     * @return java.util.Iterator
+     * @param gm org.jasig.portal.groups.IEntityGroup
+     */
+    public Iterator findContainingGroups(IGroupMember gm) throws 
GroupsException {
+       
+       if (!initialized) {
+               init();
+       }
+
+       List<IEntityGroup> rslt = new LinkedList<IEntityGroup>();
+       if (gm.isGroup()) {             // Check the local indeces...
+               IEntityGroup group = (IEntityGroup) gm;
+               List<String> list = parents.get(group.getLocalKey());
+               if (list != null) {
+                       // should only reach this code if its a SmartLdap 
managed group...
+                       for (String s : list) {
+                               rslt.add(groups.get(s));
+                       }
+               }
+       } else if (gm.isEntity()) {     // Ask the individual...
+
+               // Build an IPerson...
+               EntityIdentifier ei = gm.getUnderlyingEntityIdentifier();
+               Map<String,List<Object>> seed = new 
HashMap<String,List<Object>>();
+               List<Object> seedValue = new LinkedList<Object>();
+               seedValue.add(ei.getKey());
+               seed.put(IPerson.USERNAME, seedValue);
+               Map<String,List<Object>> attr = 
PersonDirectory.getPersonAttributeDao().getMultivaluedUserAttributes(seed);
+               IPerson p = PersonFactory.createPerson();
+               p.setAttributes(attr);
+
+               // Analyze its memberships...
+               String attrName = (String) 
spring_context.getBean("memberOfAttributeName");
+               Object groupKeys = p.getAttributeValues(attrName);
+               // IPerson returns null if no value is defined for this 
attribute...
+               if (groupKeys != null) {
+
+                       List<String> list = new LinkedList<String>();
+                       if (groupKeys instanceof String) {
+                               list.add((String) groupKeys);
+                       } else if (groupKeys instanceof Object[]) {
+                               Object[] objs = (Object[]) groupKeys;
+                               for (Object o : objs) {
+                                       list.add((String) o);
+                               }
+                       } else if (groupKeys instanceof List) {
+                               List<?> objs = (List<?>) groupKeys;
+                               for (Object o : objs) {
+                                       list.add((String) o);
+                               }
+                       }
+
+                       for (String s : list) {
+                               if (groups.containsKey(s)) {
+                               rslt.add(groups.get(s));
+                               }
+                       }
+               }
+               
+       } else {
+               // WTF...
+           log.warn("The specified IGroupMember is neither a group nor an 
entity:  " + gm.getKey());
+       }
+       
+       return rslt.iterator();
+       
+    }
+
+    /**
+     * Returns an <code>Iterator</code> over the <code>Collection</code> of
+     * <code>IEntities</code> that are members of this 
<code>IEntityGroup</code>.
+     * @return java.util.Iterator
+     * @param group org.jasig.portal.groups.IEntityGroup
+     */
+    public Iterator findEntitiesForGroup(IEntityGroup group) throws 
GroupsException {
+        
+
+       if (!initialized) {
+               init();
+       }
+
+       if (log.isDebugEnabled()) {
+               log.debug("Invoking findEntitiesForGroup() for group:  " + 
group.getLocalKey());
+       }
+       
+       // We only deal w/ group-group relationships here...
+       return findMemberGroups(group);
+        
+    }
+
+    public ILockableEntityGroup findLockable(String key) throws 
GroupsException {
+        log.warn("Unsupported method accessed:  
SmartLdapGroupStore.findLockable");
+        throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+    }
+
+    /**
+     * Returns a <code>String[]</code> containing the keys of  
<code>IEntityGroups</code>
+     * that are members of this <code>IEntityGroup</code>.  In a composite 
group
+     * system, a group may contain a member group from a different service.  
This is
+     * called a foreign membership, and is only possible in an 
internally-managed
+     * service.  A group store in such a service can return the key of a 
foreign member
+     * group, but not the group itself, which can only be returned by its 
local store.
+     *
+     * @return String[]
+     * @param group org.jasig.portal.groups.IEntityGroup
+     */
+    public String[] findMemberGroupKeys(IEntityGroup group) throws 
GroupsException {
+
+       if (!initialized) {
+               init();
+       }
+
+       if (log.isDebugEnabled()) {
+               log.debug("Invoking findMemberGroupKeys() for group:  " + 
group.getLocalKey());
+       }
+
+       List<String> rslt = new LinkedList<String>();
+       for (Iterator it=findMemberGroups(group); it.hasNext();) {
+               IEntityGroup g = (IEntityGroup) it.next();
+               // Return composite keys here...
+               rslt.add(g.getKey());
+       }
+       
+       return rslt.toArray(new String[0]);
+       
+    }
+
+    /**
+     * Returns an <code>Iterator</code> over the <code>Collection</code> of
+     * <code>IEntityGroups</code> that are members of this 
<code>IEntityGroup</code>.
+     * @return java.util.Iterator
+     * @param group org.jasig.portal.groups.IEntityGroup
+     */
+    public Iterator findMemberGroups(IEntityGroup group) throws 
GroupsException {
+
+       if (!initialized) {
+               init();
+       }
+
+       if (log.isDebugEnabled()) {
+               log.debug("Invoking findMemberGroups() for group:  " + 
group.getLocalKey());
+       }
+
+       List<IEntityGroup> rslt = new LinkedList<IEntityGroup>();
+       
+       List<String> list = children.get(group.getLocalKey());
+       if (list != null) {
+                       // should only reach this code if its a SmartLdap 
managed group...
+               for (String s : list) {
+                       rslt.add(groups.get(s));
+               }
+       }
+       
+       return rslt.iterator();
+
+    }
+
+    public IEntityGroup newInstance(Class entityType) throws GroupsException {
+        log.warn("Unsupported method accessed:  
SmartLdapGroupStore.newInstance");
+        throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+    }
+
+    public EntityIdentifier[] searchForGroups(String query, int method, Class 
leaftype) throws GroupsException {
+
+       if (!initialized) {
+               init();
+       }
+
+       if (log.isDebugEnabled()) {
+               log.debug("Invoking searchForGroups():  query=" + query + ", 
method=" 
+                               + method + ", leaftype=" + 
leaftype.getClass().getName());
+       }
+
+       // We only match the IPerson leaf type...
+       if (!leaftype.equals(IPerson.class)) {
+               return new EntityIdentifier[0];
+       }
+       
+       // Establish the regex pattern to match on...
+       String regex = null;
+       switch (method) {
+               case IGroupConstants.IS:
+                       regex = query.toUpperCase();
+                       break;
+               case IGroupConstants.STARTS_WITH:
+                       regex = query.toUpperCase() + ".*";
+                       break;
+               case IGroupConstants.ENDS_WITH: 
+                       regex = ".*" + query.toUpperCase();
+                       break;
+               case IGroupConstants.CONTAINS: 
+                       regex = ".*" + query.toUpperCase() + ".*";
+                       break;
+               default:
+                       String msg = "Unsupported search method:  " + method;
+                       throw new GroupsException(msg);
+       }
+       
+       List<EntityIdentifier> rslt = new LinkedList<EntityIdentifier>(); 
+       for (Map.Entry<String,List<String>> y : keysByUpperCaseName.entrySet()) 
{
+               if (y.getKey().matches(regex)) {
+                       List<String> keys = y.getValue();
+                       for (String k : keys) {
+                               rslt.add(new EntityIdentifier(k, 
IEntityGroup.class));
+                       }
+               }
+       }
+       
+       return rslt.toArray(new EntityIdentifier[0]);
+
+    }
+
+    public void update(IEntityGroup group) throws GroupsException {
+        log.warn("Unsupported method accessed:  SmartLdapGroupStore.update");
+        throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+    }
+
+    public void updateMembers(IEntityGroup group) throws GroupsException {
+        log.warn("Unsupported method accessed:  
SmartLdapGroupStore.updateMembers");
+        throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+    }
+    
+    public synchronized void init() {
+       
+       if (initialized) {
+               return;
+       }
+       
+       // Prepare the new local indeces...
+        Map<String,IEntityGroup> new_groups = Collections.synchronizedMap(new 
HashMap<String,IEntityGroup>());
+        Map<String,List<String>> new_parents = Collections.synchronizedMap(new 
HashMap<String,List<String>>());
+        Map<String,List<String>> new_children = 
Collections.synchronizedMap(new HashMap<String,List<String>>());
+        Map<String,List<String>> new_keysByUpperCaseName = 
Collections.synchronizedMap(new HashMap<String,List<String>>());
+
+        // Gather IEntityGroup objects from LDAP...
+       RuntimeRequestResponse req = new RuntimeRequestResponse();
+       List<LdapRecord> list = new LinkedList<LdapRecord>();
+       req.setAttribute("GROUPS", list);
+       for (String name : spring_context.getBeanDefinitionNames()) {
+               req.setAttribute(name, spring_context.getBean(name));
+       }
+       runner.run(initTask, req);
+       
+       if (log.isInfoEnabled()) {
+               String msg = "init() found " + list.size() + " records.";
+               log.info(msg);
+       }
+       
+       // Do a first loop to build the main catalog (new_groups)...
+       for (LdapRecord r : list) {
+               
+               // new_groups (me)...
+               IEntityGroup g = r.getGroup();
+               new_groups.put(g.getLocalKey(), g);
+
+       }
+       
+       // Do a second loop to build local indeces...
+       for (LdapRecord r : list) {
+
+               IEntityGroup g = r.getGroup();
+
+               // new_parents (I am a parent for all my children)...
+               for (String childKey : r.getKeysOfChildren()) {
+                       
+                       // NB:  We're only interested in relationships between 
+                       // objects in the main catalog (i.e. new_groups);  
+                       // discard everything else...
+                       if (!new_groups.containsKey(childKey)) {
+                               break;
+                       }
+
+                       List<String> parentsList = new_parents.get(childKey);
+                       if (parentsList == null) {
+                               // first parent for this child...
+                               parentsList = Collections.synchronizedList(new 
LinkedList<String>());
+                               new_parents.put(childKey, parentsList);
+                       }
+                       parentsList.add(g.getLocalKey());
+
+               }
+               
+               // new_children...
+               List<String> childrenList = Collections.synchronizedList(new 
LinkedList<String>());
+               for (String childKey : r.getKeysOfChildren()) {
+                       // NB:  We're only interested in relationships between 
+                       // objects in the main catalog (i.e. new_groups);  
+                       // discard everything else...
+                       if (new_groups.containsKey(childKey)) {
+                               childrenList.add(childKey);
+                       }
+               }
+               new_children.put(g.getLocalKey(), childrenList);
+               
+               // new_keysByUpperCaseName...
+               List<String> groupsWithMyName = 
new_keysByUpperCaseName.get(g.getName().toUpperCase());
+               if (groupsWithMyName == null) {
+                       // I am the first group with my name (pretty likely)...
+                       groupsWithMyName = Collections.synchronizedList(new 
LinkedList<String>());
+                       new_keysByUpperCaseName.put(g.getName().toUpperCase(), 
groupsWithMyName);
+               }
+               groupsWithMyName.add(g.getLocalKey());
+               
+       }       
+       
+       /*
+        * Now load the ROOT_GROUP into the collections...
+        */
+
+       // new_groups (me)...
+               new_groups.put(ROOT_GROUP.getLocalKey(), ROOT_GROUP);
+
+               // new_parents (I am a parent for all groups that have no other 
parent)...
+               List<String> childrenOfRoot = Collections.synchronizedList(new 
LinkedList<String>());   // for later...
+               for (String possibleChildKey : new_groups.keySet()) {
+                       if (!possibleChildKey.equals(ROOT_GROUP.getLocalKey()) 
&& !new_parents.containsKey(possibleChildKey)) {
+                               List<String> p = 
Collections.synchronizedList(new LinkedList<String>());
+                               p.add(ROOT_GROUP.getLocalKey());
+                               new_parents.put(possibleChildKey, p);
+                               childrenOfRoot.add(possibleChildKey);   // for 
later...
+                       }
+               }
+               
+               // new_children...
+               new_children.put(ROOT_GROUP.getLocalKey(), childrenOfRoot);
+               
+               // new_keysByUpperCaseName...
+               List<String> groupsWithMyName = 
new_keysByUpperCaseName.get(ROOT_GROUP.getName().toUpperCase());
+               if (groupsWithMyName == null) {
+                       // I am the first group with my name (pretty likely)...
+                       groupsWithMyName = Collections.synchronizedList(new 
LinkedList<String>());
+                       
new_keysByUpperCaseName.put(ROOT_GROUP.getName().toUpperCase(), 
groupsWithMyName);
+               }
+               groupsWithMyName.add(ROOT_GROUP.getLocalKey());
+
+       if (log.isInfoEnabled()) {
+               String msg = "init() :: final size of each collection is as 
follows..."
+                                               + "\n\tgroups=" + 
new_groups.size()
+                                               + "\n\tparents=" + 
new_parents.size()
+                                               + "\n\tchildren=" + 
new_children.size()
+                                               + "\n\tkeysByUpperCaseName=" + 
new_keysByUpperCaseName.size();
+               log.info(msg);
+       }
+       
+       if (log.isTraceEnabled()) {
+               
+               StringBuilder msg = new StringBuilder();
+
+               // new_groups...
+               msg.setLength(0);
+               msg.append("Here are the keys of the new_groups collection:");
+               for (String s : new_groups.keySet()) {
+                       msg.append("\n\t").append(s);
+               }
+               log.trace(msg.toString());
+               
+               // new_parents...
+               msg.setLength(0);
+               msg.append("Here are the parents of each child in the 
new_parents collection:");
+               for (Map.Entry<String,List<String>> y : new_parents.entrySet()) 
{
+                       msg.append("\n\tchild=").append(y.getKey());
+                       for (String s : y.getValue()) {
+                               msg.append("\n\t\tparent=").append(s);
+                       }
+               }
+               log.trace(msg.toString());
+               
+               // new_children...
+               msg.setLength(0);
+               msg.append("Here are the children of each parent in the 
new_children collection:");
+               for (Map.Entry<String,List<String>> y : 
new_children.entrySet()) {
+                       msg.append("\n\tparent=").append(y.getKey());
+                       for (String s : y.getValue()) {
+                               msg.append("\n\t\tchild=").append(s);
+                       }
+               }
+               log.trace(msg.toString());
+               
+               // new_keysByUpperCaseName...
+               msg.append("Here are the groups that have each name in the 
new_keysByUpperCaseName collection:");
+               for (Map.Entry<String,List<String>> y : 
new_keysByUpperCaseName.entrySet()) {
+                       msg.append("\n\tname=").append(y.getKey());
+                       for (String s : y.getValue()) {
+                               msg.append("\n\t\tgroup=").append(s);
+                       }
+               }
+               log.trace(msg.toString());
+               
+       }
+
+       // Replace the old with the new...
+        this.groups = new_groups;
+        this.parents = new_parents;
+        this.children = new_children;
+        this.keysByUpperCaseName = new_keysByUpperCaseName;
+        
+        // Set the 'initialized' flag...
+        this.initialized = true;
+
+    }
+
+    /*
+     * Implementation.
+     */
+
+    private SmartLdapGroupStore() {
+        
+       // Spring tech...
+       URL u = 
getClass().getResource("/properties/groups/SmartLdapGroupStoreConfig.xml");
+               spring_context = new 
FileSystemXmlApplicationContext(u.toExternalForm());
+
+               // Cernunnos tech...
+               runner = new ScriptRunner();
+        initTask = 
runner.compileTask(getClass().getResource("init.crn").toExternalForm());
+        
+        // SmartLdapGroupStore will be initialized on first use...
+        initialized = false;
+
+    }
+
+}
Index: 
uportal-impl/src/main/java/org/jasig/portal/groups/smartldap/SimpleAttributesMapper.java
===================================================================
--- 
uportal-impl/src/main/java/org/jasig/portal/groups/smartldap/SimpleAttributesMapper.java
    (revision 0)
+++ 
uportal-impl/src/main/java/org/jasig/portal/groups/smartldap/SimpleAttributesMapper.java
    (revision 0)
@@ -0,0 +1,130 @@
+/**
+ * Copyright 2008 The JA-SIG Collaborative.  All rights reserved.
+ * See license distributed with this file and
+ * available online at http://www.uportal.org/license.html
+ */
+
+package org.jasig.portal.groups.smartldap;
+
+import java.util.Enumeration;
+import java.util.LinkedList;
+import java.util.List;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.jasig.portal.groups.EntityTestingGroupImpl;
+import org.jasig.portal.groups.IEntityGroup;
+import org.jasig.portal.security.IPerson;
+import org.springframework.ldap.core.AttributesMapper;
+
+public final class SimpleAttributesMapper implements AttributesMapper {
+       
+       private static final String GROUP_DESCRIPTION = 
+                                       "This group was pulled from the 
directory server.";
+       
+       /**
+        * Name of the LDAP attribute on a group that tells you 
+        * its key (normally 'dn').
+        */
+       private String keyAttributeName = null; 
+       
+       /**
+        * Name of the LDAP attribute on a group that tells you 
+        * the name of the group.
+        */
+       private String groupNameAttributeName = null; 
+       
+       /**
+        * Name of the LDAP attribute on a group that tells you 
+        * who its children are.
+        */
+       private String membershipAttributeName = null; 
+       
+    private final Log log = LogFactory.getLog(getClass());
+
+    /*
+        * Public API.
+        */     
+       
+       public Object mapFromAttributes(Attributes attr) {
+               
+               // Assertions.
+               if (keyAttributeName == null) {
+                       String msg = "The property 'keyAttributeName' must be 
set.";
+                       throw new IllegalStateException(msg);
+               }
+               if (groupNameAttributeName == null) {
+                       String msg = "The property 'groupNameAttributeName' 
must be set.";
+                       throw new IllegalStateException(msg);
+               }
+               if (membershipAttributeName == null) {
+                       String msg = "The property 'membershipAttributeName' 
must be set.";
+                       throw new IllegalStateException(msg);
+               }
+               
+               if (log.isInfoEnabled()) {
+                       String msg = 
"SimpleAttributesMapper.mapFromAttributes() :: settings:  keyAttributeName='" 
+                                                       + keyAttributeName + 
"', groupNameAttributeName='" 
+                                                       + 
groupNameAttributeName + "', groupNameAttributeName='" 
+                                                       + 
groupNameAttributeName + "'";
+                       log.info(msg);
+               }
+               
+               LdapRecord rslt;
+               
+               try {
+                       
+                       String key = (String) attr.get(keyAttributeName).get();
+                       String groupName = (String) 
attr.get(groupNameAttributeName).get();
+                                               
+                       IEntityGroup g = new EntityTestingGroupImpl(key, 
IPerson.class);
+               g.setCreatorID("System");
+               g.setName(groupName);
+               g.setDescription(GROUP_DESCRIPTION);
+                       List<String> membership = new LinkedList<String>();
+                       Attribute m = attr.get(membershipAttributeName);
+                       if (m != null) {
+                               for (Enumeration<?> en=m.getAll(); 
en.hasMoreElements();) {
+                                       membership.add((String) 
en.nextElement());
+                               }
+                       }
+                       rslt = new LdapRecord(g, membership);
+                       
+                       if (log.isDebugEnabled()) {
+                               StringBuilder msg = new StringBuilder();
+                               msg.append("Record Details:")
+                                                       
.append("\n\tkey=").append(key)
+                                                       
.append("\n\tgroupName=").append(groupName)
+                                                       .append("\n\tmembers:");
+                               for (String s : membership) {
+                                       msg.append("\n\t\t").append(s);
+                               }
+                               log.debug(msg.toString());
+                       }
+
+               } catch (Throwable t) {
+                       log.error("Error in SimpleAttributesMapper", t);
+                       String msg = "SimpleAttributesMapper failed to create a 
LdapRecord "
+                                                                       + "from 
the specified Attributes:  " + attr;
+                       throw new RuntimeException(msg, t);
+               }
+               
+               return rslt;            
+               
+       }
+       
+       public void setKeyAttributeName(String keyAttributeName) {
+               this.keyAttributeName = keyAttributeName;
+       }
+
+       public void setGroupNameAttributeName(String groupNameAttributeName) {
+               this.groupNameAttributeName = groupNameAttributeName;
+       }
+
+       public void setMembershipAttributeName(String membershipAttributeName) {
+               this.membershipAttributeName = membershipAttributeName;
+       }
+
+}
Index: 
uportal-impl/src/main/resources/org/jasig/portal/groups/smartldap/init.crn
===================================================================
--- uportal-impl/src/main/resources/org/jasig/portal/groups/smartldap/init.crn  
(revision 0)
+++ uportal-impl/src/main/resources/org/jasig/portal/groups/smartldap/init.crn  
(revision 0)
@@ -0,0 +1,17 @@
+<ldap-search 
+               context-source="${ldapContext}" 
+               base-dn="${baseDn}" 
+               filter="${filter}" 
+               
scope="${groovy(javax.naming.directory.SearchControls.SUBTREE_SCOPE)}" 
+               attributes-mapper="${attributesMapper}" 
+               attribute-name="record"
+       >
+       <subtasks>
+               <groovy>
+                       <script>GROUPS.add(record);</script>
+                       <subtasks>
+                <log>SmartLdap adding record for group:  
${groovy(record.getGroup().getName())}</log>
+                       </subtasks>
+               </groovy>
+       </subtasks>
+</ldap-search>
\ No newline at end of file
Index: 
uportal-impl/src/main/resources/properties/groups/compositeGroupServices.xml
===================================================================
--- 
uportal-impl/src/main/resources/properties/groups/compositeGroupServices.xml    
    (revision 44174)
+++ 
uportal-impl/src/main/resources/properties/groups/compositeGroupServices.xml    
    (working copy)
@@ -54,5 +54,24 @@
   </service>
 -->
 
+<!--  
+ | Uncomment to configure a SmartLdap group service component.  Be sure to 
edit 
+ | SmartLdapGroupStoreConfig.xml to apply your settings.
+ |
+ | SmartLdapGroupStore is a GaP implementation that recognizes groups defined 
in 
+ | an LDAP directory and makes them available in the portal.  You can read 
about 
+ | this technology here:  http://www.ja-sig.org/wiki/x/3QPI   
+ +-->
+<!--
+  <service>
+    <name>smartldap</name>
+    
<service_factory>org.jasig.portal.groups.ReferenceIndividualGroupServiceFactory</service_factory>
+    
<entity_store_factory>org.jasig.portal.groups.smartldap.SmartLdapEntityStore$Factory</entity_store_factory>
+    
<group_store_factory>org.jasig.portal.groups.smartldap.SmartLdapGroupStore$Factory</group_store_factory>
+    
<entity_searcher_factory>org.jasig.portal.groups.smartldap.SmartLdapEntitySearcher$Factory</entity_searcher_factory>
+    <internally_managed>false</internally_managed>
+    <caching_enabled>true</caching_enabled>
+  </service>
+-->
 
 </servicelist>
Index: 
uportal-impl/src/main/resources/properties/groups/SmartLdapGroupStoreConfig.xml
===================================================================
--- 
uportal-impl/src/main/resources/properties/groups/SmartLdapGroupStoreConfig.xml 
    (revision 0)
+++ 
uportal-impl/src/main/resources/properties/groups/SmartLdapGroupStoreConfig.xml 
    (revision 0)
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" 
"http://www.springframework.org/dtd/spring-beans.dtd";>
+
+<beans>
+
+    <!--
+     | This bean is the ContextSource instance that will be used to connect to 
LDAP.
+     +-->
+    <bean id="ldapContext" 
class="org.springframework.ldap.core.support.LdapContextSource">
+        <property name="url" value="changeme"/>
+        <property name="userDn" value="changeme"/>
+        <property name="password" value="changeme"/>
+    </bean>
+
+    <!--
+     | BaseDn that will be passed to the search (not to the context).
+     |
+     | WARNING:  If you get an error like this...
+     |   ...PartialResultException: [LDAP: error code 10...
+     | it probably means your baseDn isn't correct!
+     +-->
+    <bean id="baseDn" class="java.lang.String">
+        <constructor-arg><value>changeme</value></constructor-arg>
+    </bean>
+
+    <!--
+     | NOTE:  The remaining examples in this file are configured correctly for 
+     | Active Directory servers.
+     +-->
+
+    <!--
+     | LDAP query string that will be passed to the search.
+     +-->
+    <bean id="filter" class="java.lang.String">
+        
<constructor-arg><value>(objectCategory=group)</value></constructor-arg>
+    </bean>
+
+    <!--
+     | This bean identifies the name of the Person Attribute that
+     | lists the SmartLdap groups each person is a member of.
+     +-->
+    <bean id="memberOfAttributeName" class="java.lang.String">
+        <constructor-arg><value>memberOf</value></constructor-arg>
+    </bean>
+
+    <!--
+     | This bean identifies the org.springframework.ldap.core.AttributesMapper
+     | implementation used in reading the groups records from LDAP.
+     +-->
+    <bean id="attributesMapper" 
class="org.jasig.portal.groups.smartldap.SimpleAttributesMapper">
+        <!--
+         | Name of the group attribute that tells you its key.
+         +-->
+        <property name="keyAttributeName">
+            <value>distinguishedName</value>
+        </property>
+        <!--
+         | Name of the group attribute that tells you its name.
+         +-->
+        <property name="groupNameAttributeName">
+            <value>cn</value>
+        </property>
+        <!--
+         | Name of the group attribute that lists its members.
+         +-->
+        <property name="membershipAttributeName">
+            <value>member</value>
+        </property>
+    </bean>
+
+</beans>

Reply via email to