Package org.jasig.portal.groups.ldap

Source Code of org.jasig.portal.groups.ldap.LDAPGroupStore$GroupShadow

/**
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.jasig.portal.groups.ldap;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;

import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.xml.parsers.ParserConfigurationException;

import org.jasig.portal.EntityIdentifier;
import org.jasig.portal.ResourceMissingException;
import org.jasig.portal.groups.EntityGroupImpl;
import org.jasig.portal.groups.EntityImpl;
import org.jasig.portal.groups.GroupsException;
import org.jasig.portal.groups.IEntity;
import org.jasig.portal.groups.IEntityGroup;
import org.jasig.portal.groups.IEntityGroupStore;
import org.jasig.portal.groups.IEntitySearcher;
import org.jasig.portal.groups.IEntityStore;
import org.jasig.portal.groups.IGroupMember;
import org.jasig.portal.groups.ILockableEntityGroup;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jasig.portal.utils.ResourceLoader;
import org.jasig.portal.utils.SmartCache;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;

/**
* LDAPGroupStore.
* @author Alex Vidgor
* @version $Revision: 19776 $
*/
public class LDAPGroupStore implements IEntityGroupStore, IEntityStore, IEntitySearcher {
    private static final Log log = LogFactory.getLog(LDAPGroupStore.class);
  protected String url;
  protected String logonid;
  protected String logonpassword;
  protected String keyfield;
  protected String namefield;
  protected String usercontext="";
  protected HashMap groups;
  protected SmartCache contexts;
  protected SmartCache personkeys;
  protected static Class iperson = org.jasig.portal.security.IPerson.class;
  protected static Class group = org.jasig.portal.groups.IEntityGroup.class;
  protected static short ELEMENT_NODE = Node.ELEMENT_NODE;

  public LDAPGroupStore() {
    Document config = null;
    try{
      config = ResourceLoader.getResourceAsDocument(this.getClass(),"/properties/groups/LDAPGroupStoreConfig.xml", true);
    }
    catch(IOException e){
        throw new RuntimeException("LDAPGroupStore: Unable to find configuration configuration document",e);
    } catch (ResourceMissingException e) {
        throw new RuntimeException("LDAPGroupStore: Unable to find configuration configuration document",e);
    } catch (ParserConfigurationException e) {
        throw new RuntimeException("LDAPGroupStore: Unable to parse configuration configuration document",e);
    } catch (SAXException e) {
        throw new RuntimeException("LDAPGroupStore: Unable to parse configuration configuration document",e);
    }
    init(config);
  }

  public LDAPGroupStore(Document config){
    init(config);
  }

  protected void init(Document config){
    this.groups = new HashMap();
    this.contexts = new SmartCache(120);
    config.normalize();
    int refreshminutes = 120;
    Element root = config.getDocumentElement();
    NodeList nl = root.getElementsByTagName("config");
    if (nl.getLength() == 1){
      Element conf = (Element) nl.item(0);
      Node cc = conf.getFirstChild();
      //NodeList cl= conf.getF.getChildNodes();
      //for(int i=0; i<cl.getLength(); i++){
      while (cc!=null){
        if(cc.getNodeType()==ELEMENT_NODE){
          Element c = (Element) cc;
          c.normalize();
          Node t = c.getFirstChild();
          if(t!=null && t.getNodeType()==Node.TEXT_NODE){
            String name = c.getNodeName();
            String text = ((Text) t).getData();
            //System.out.println(name+" = "+text);
            if (name.equals("url")){
              url = text;
            }
            else if (name.equals("logonid")){
              logonid = text;
            }
            else if (name.equals("logonpassword")){
              logonpassword = text;
            }
            else if (name.equals("keyfield")){
              keyfield = text;
            }
            else if (name.equals("namefield")){
              namefield = text;
            }
            else if (name.equals("usercontext")){
              usercontext = text;
            }
            else if (name.equals("refresh-minutes")){
              try{
                 refreshminutes = Integer.parseInt(text);
              }
              catch(Exception e){}
            }
          }
        }
        cc = cc.getNextSibling();
      }
    }
    else{
      throw new RuntimeException("LDAPGroupStore: config file must contain one config element");
    }

    this.personkeys = new SmartCache(refreshminutes*60);

    NodeList gl = root.getChildNodes();
    for (int j=0; j<gl.getLength(); j++){
      if(gl.item(j).getNodeType() == ELEMENT_NODE){
        Element g = (Element) gl.item(j);
        if (g.getNodeName().equals("group")){
          GroupShadow shadow = processXmlGroupRecursive(g);
          groups.put(shadow.key,shadow);
        }
      }
    }

  }

  protected String[] getPersonKeys(String groupKey){
    String[] r= (String[]) personkeys.get(groupKey);
    if(r==null){
      GroupShadow shadow = (GroupShadow) groups.get(groupKey);
      if (shadow.entities!=null){
        r = shadow.entities.getPersonKeys();
      }
      else {
        r = new String[0];
      }
      personkeys.put(groupKey,r);
    }
    return r;
  }

  protected GroupShadow processXmlGroupRecursive(Element groupElem){
    GroupShadow shadow = new GroupShadow();
    shadow.key = groupElem.getAttribute("key");
    shadow.name = groupElem.getAttribute("name");
    //System.out.println("Loading configuration for group "+shadow.name);
    ArrayList subgroups = new ArrayList();
    NodeList nl = groupElem.getChildNodes();
    for(int i = 0; i<nl.getLength(); i++){
      if (nl.item(i).getNodeType()==ELEMENT_NODE){
        Element e = (Element) nl.item(i);
        if(e.getNodeName().equals("group")){
          GroupShadow sub = processXmlGroupRecursive(e);
          subgroups.add(sub);
          groups.put(sub.key,sub);
        }
        else if(e.getNodeName().equals("entity-set")){
          shadow.entities = new EntitySet(e);
        }
        else if(e.getNodeName().equals("description")){
          e.normalize();
          Text t= (Text) e.getFirstChild();
          if (t!=null){
            shadow.description = t.getData();
          }
        }
      }
    }
    shadow.subgroups = (GroupShadow[]) subgroups.toArray(new GroupShadow[0]);
    return shadow;
  }

  protected class GroupShadow{
    protected String key;
    protected String name;
    protected String description;
    protected GroupShadow[] subgroups;
    protected EntitySet entities;
  }

  protected class EntitySet{
    public static final int FILTER=1;
    public static final int UNION=2;
    public static final int DIFFERENCE=3;
    public static final int INTERSECTION=4;
    public static final int SUBTRACT=5;
    public static final int ATTRIBUTES=6;

    protected int type;
    protected String filter;
    protected Attributes attributes;
    protected EntitySet[] subsets;

    protected EntitySet(Element entityset){
      entityset.normalize();
      Node n = entityset.getFirstChild();
      while (n.getNodeType()!=Node.ELEMENT_NODE){
        n = n.getNextSibling();
      }
      Element e = (Element) n;
      String type = e.getNodeName();
      boolean collectSubsets = false;
      if (type.equals("filter")){
        this.type = FILTER;
        filter = e.getAttribute("string");
      }
      else if (type.equals("attributes")){
        this.type = ATTRIBUTES;
        attributes = new BasicAttributes();
        NodeList atts = e.getChildNodes();
        for (int i=0; i< atts.getLength(); i++){
          if (atts.item(i).getNodeType() == ELEMENT_NODE){
            Element a = (Element) atts.item(i);
            attributes.put(a.getAttribute("name"),a.getAttribute("value"));
          }
        }
      }
      else if (type.equals("union")){
        this.type = UNION;
        collectSubsets = true;
      }
      else if (type.equals("intersection")){
        this.type = INTERSECTION;
        collectSubsets = true;
      }
      else if (type.equals("difference")){
        this.type = DIFFERENCE;
        collectSubsets = true;
      }
      else if (type.equals("subtract")){
        this.type = SUBTRACT;
        collectSubsets = true;
      }

      if(collectSubsets){
        ArrayList subs = new ArrayList();
        NodeList nl = e.getChildNodes();
        for (int i=0; i < nl.getLength(); i++){
          if (nl.item(i).getNodeType() == Node.ELEMENT_NODE){
            EntitySet subset = new EntitySet((Element)nl.item(i));
            subs.add(subset);
          }
        }
        subsets = (EntitySet[]) subs.toArray(new EntitySet[0]);
      }
    }

    protected String[] getPersonKeys(){
      ArrayList keys = new ArrayList();
      //System.out.println("Loading keys!!");
      String[] subkeys;
      switch (type){
        case FILTER:
          //System.out.println("Performing ldap query!!");
          DirContext context = getConnection();
          NamingEnumeration userlist = null;
          SearchControls sc = new SearchControls();
          sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
          sc.setReturningAttributes(new String[] {keyfield});
          try {
            userlist = context.search(usercontext,filter,sc);
          } catch (NamingException nex) {
            log.error("LDAPGroupStore: Unable to perform filter "+filter, nex);
          }
          processLdapResults(userlist,keys);
          break;
        case ATTRIBUTES:
          //System.out.println("Performing ldap attribute query!!");
          DirContext context2 = getConnection();
          NamingEnumeration userlist2 = null;
          try {
            userlist2 = context2.search(usercontext,attributes,new String[] {keyfield});
          } catch (NamingException nex) {
            log.error("LDAPGroupStore: Unable to perform attribute search", nex);
          }
          processLdapResults(userlist2,keys);
          break;
        case UNION:
          for(int i=0; i<subsets.length; i++){
            subkeys = subsets[i].getPersonKeys();
            for(int j=0;j<subkeys.length;j++){
              String key =  subkeys[j];
              if(!keys.contains(key)){
                keys.add(key);
              }
            }
          }
          break;
        case INTERSECTION:
          if (subsets.length > 0){
            // load initial keys from first entity set
            String[] interkeys = subsets[0].getPersonKeys();
            // now set non-recurring keys to null
            for(int m=1;m<subsets.length;m++){
              subkeys = subsets[m].getPersonKeys();
              for (int n=0; n < interkeys.length; n++){
                if (interkeys[n] !=null){
                  boolean remove=true;
                  for (int o=0; o<subkeys.length; o++){
                    if (subkeys[o].equals(interkeys[n])){
                      // found a match, so far the intersection for this key is valid
                      remove=false;
                      break;
                    }
                  }
                  if (remove){
                    interkeys[n] = null;
                  }
                }
              }
            }
            for (int p=0; p< interkeys.length; p++){
              if (interkeys[p] != null){
                keys.add(interkeys[p]);
              }
            }
          }
          break;
        case DIFFERENCE:
          if (subsets.length > 0){
            ArrayList discardKeys = new ArrayList();
            subkeys = subsets[0].getPersonKeys();
            // load initial keys from first entity set
            for(int q=0; q<subkeys.length; q++){
              keys.add(subkeys[q]);
            }
            for (int r=1; r<subsets.length; r++){
              subkeys = subsets[r].getPersonKeys();
              for (int s=0; s<subkeys.length; s++){
                String ky = subkeys[s];
                if (keys.contains(ky)){
                  keys.remove(ky);
                  discardKeys.add(ky);
                }
                else{
                  if (!discardKeys.contains(ky)){
                    keys.add(ky);
                  }
                }
              }
            }

          }
          break;
        case SUBTRACT:
          if (subsets.length>0){
            subkeys = subsets[0].getPersonKeys();
            // load initial keys from first entity set
            for(int t=0; t<subkeys.length; t++){
              keys.add(subkeys[t]);
            }
            for(int u=1; u<subsets.length; u++){
              subkeys = subsets[u].getPersonKeys();
              for(int v=0; v<subkeys.length; v++){
                String kyy = subkeys[v];
                if(keys.contains(kyy)){
                  keys.remove(kyy);
                }
              }
            }
          }
          break;
      }
      return (String[]) keys.toArray(new String[0]);
    }
  }

  protected void processLdapResults(NamingEnumeration results, ArrayList keys){
    //long time1 = System.currentTimeMillis();
    //long casting=0;
    //long getting=0;
    //long setting=0;
    //long looping=0;
    //long loop1=System.currentTimeMillis();
    try{
      while(results.hasMore()){
        //long loop2 = System.currentTimeMillis();
        //long cast1=System.currentTimeMillis();
        //looping=looping+loop2-loop1;
        SearchResult result = (SearchResult) results.next();
        //long cast2 = System.currentTimeMillis();
        //long get1 = System.currentTimeMillis();
        Attributes ldapattribs = result.getAttributes();
        //long get2 = System.currentTimeMillis();
        //long set1 = System.currentTimeMillis();
        Attribute attrib = ldapattribs.get(keyfield);
        if (attrib != null) {
            keys.add(String.valueOf(attrib.get()).toLowerCase());
        }
        //long set2 = System.currentTimeMillis();
        //loop1=System.currentTimeMillis();
        //casting=casting+cast2-cast1;
        //setting=setting+set2-set1;
        //getting=getting+get2-get1;
      }
    }
    catch(NamingException nex){
        log.error("LDAPGroupStore: error processing results", nex);
    }
    finally{
      try{results.close();}catch(Exception e){}
    }
    //long time5 = System.currentTimeMillis();
    //System.out.println("Result processing took "+(time5-time1)+": "+getting+" for getting, "
    //  +setting+" for setting, "+casting+" for casting, "+looping+" for looping,"
    //  +(time5-loop1)+" for closing");
  }

  protected DirContext getConnection(){
     //JNDI boilerplate to connect to an initial context
    DirContext context = (DirContext) contexts.get("context");
    if (context==null){
      Hashtable jndienv = new Hashtable();
      jndienv.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
      jndienv.put(Context.SECURITY_AUTHENTICATION,"simple");
      if (url.startsWith("ldaps")) { // Handle SSL connections
        String newurl = url.substring(0,4) + url.substring(5);
        jndienv.put(Context.SECURITY_PROTOCOL, "ssl");
        jndienv.put(Context.PROVIDER_URL, newurl);
      }
      else {
        jndienv.put(Context.PROVIDER_URL, url);
      }
      if (logonid!=null)
        jndienv.put(Context.SECURITY_PRINCIPAL,logonid);
      if (logonpassword!=null)
        jndienv.put(Context.SECURITY_CREDENTIALS,logonpassword);
      try {
        context = new InitialDirContext(jndienv);
      } catch (NamingException nex) {
        log.error("LDAPGroupStore: unable to get context", nex);
      }
      contexts.put("context",context);
    }
    return context;
  }

  protected IEntityGroup makeGroup(GroupShadow shadow) throws GroupsException{
    IEntityGroup group = null;
    if ( shadow != null )
    {
        group = new EntityGroupImpl(shadow.key,iperson);
        group.setDescription(shadow.description);
        group.setName(shadow.name);
    }
    return group;
  }

  protected GroupShadow getShadow(IEntityGroup group){
    return (GroupShadow) groups.get(group.getLocalKey());
  }

  public void delete(IEntityGroup group) throws GroupsException {
    throw new java.lang.UnsupportedOperationException("LDAPGroupStore: Method delete() not supported.");
  }
  public IEntityGroup find(String key) throws GroupsException {
    return makeGroup((GroupShadow)this.groups.get(key));
  }
  public Iterator findContainingGroups(IGroupMember gm) throws GroupsException {
     ArrayList al = new ArrayList();
     String key;
     GroupShadow[] shadows = getGroupShadows();
     if (gm.isEntity()){
       key = gm.getKey();
       for (int i=0; i < shadows.length; i++){
        String[] keys = getPersonKeys(shadows[i].key);
        for (int j=0; j< keys.length; j++){
          if (keys[j].equals(key)){
            al.add(makeGroup(shadows[i]));
            break;
          }
        }
       }
     }

     if (gm.isGroup()){
        key = ((IEntityGroup)gm).getLocalKey();
        for (int i=0; i < shadows.length; i++){
          for (int j=0; j< shadows[i].subgroups.length; j++){
            if (shadows[i].subgroups[j].key.equals(key)){
              al.add(makeGroup(shadows[i]));
              break;
            }
          }
        }
     }

     return al.iterator();
  }

  public String[] findMemberGroupKeys(IEntityGroup group) throws GroupsException
  {
    List keys = new ArrayList();
    for ( Iterator itr=findMemberGroups(group); itr.hasNext(); )
    {
        IEntityGroup eg = (IEntityGroup) itr.next();
        keys.add(eg.getKey());
    }
    return (String[]) keys.toArray(new String[keys.size()]);
  }
  public Iterator findMemberGroups(IEntityGroup group) throws GroupsException {
    ArrayList al = new ArrayList();
    GroupShadow shadow = getShadow(group);
    for(int i=0; i < shadow.subgroups.length; i++){
      al.add(makeGroup(shadow.subgroups[i]));
    }
    return al.iterator();
  }
  public IEntityGroup newInstance(Class entityType) throws GroupsException {
    throw new java.lang.UnsupportedOperationException("LDAPGroupStore: Method newInstance() not supported");
  }
  public void update(IEntityGroup group) throws GroupsException {
    throw new java.lang.UnsupportedOperationException("LDAPGroupStore: Method update() not supported");
  }
  public void updateMembers(IEntityGroup group) throws GroupsException {
    throw new java.lang.UnsupportedOperationException("LDAPGroupStore: Method updateMembers() not supported");
  }
  public ILockableEntityGroup findLockable(String key) throws GroupsException {
    throw new java.lang.UnsupportedOperationException("LDAPGroupStore: Method findLockable() not supported");
  }

  protected GroupShadow[] getGroupShadows(){
     return (GroupShadow[]) groups.values().toArray(new GroupShadow[0]);
  }

  public EntityIdentifier[] searchForGroups(String query, int method, Class leaftype) throws GroupsException {
    ArrayList ids = new ArrayList();
    GroupShadow[] g = getGroupShadows();
    int i;
    switch (method){
      case IS:
        for (i=0; i<g.length;i++){
          if(g[i].name.equalsIgnoreCase(query)){
            ids.add(new EntityIdentifier(g[i].key,group));
          }
        }
        break;
      case STARTS_WITH:
        for (i=0; i<g.length;i++){
          if(g[i].name.toUpperCase().startsWith(query.toUpperCase())){
            ids.add(new EntityIdentifier(g[i].key,group));
          }
        }
        break;
      case ENDS_WITH:
        for (i=0; i<g.length;i++){
          if(g[i].name.toUpperCase().endsWith(query.toUpperCase())){
            ids.add(new EntityIdentifier(g[i].key,group));
          }
        }
        break;
      case CONTAINS:
        for (i=0; i<g.length;i++){
          if(g[i].name.toUpperCase().indexOf(query.toUpperCase()) > -1){
            ids.add(new EntityIdentifier(g[i].key,group));
          }
        }
        break;
    }
    return (EntityIdentifier[]) ids.toArray(new EntityIdentifier[0]);
  }
  public Iterator findEntitiesForGroup(IEntityGroup group) throws GroupsException {
    GroupShadow shadow = getShadow(group);
    ArrayList al = new ArrayList();
    String[] keys = getPersonKeys(shadow.key);
    for (int i=0; i < keys.length; i++){
      al.add(new EntityImpl(keys[i],iperson));
    }
    return al.iterator();
  }
  public IEntity newInstance(String key) throws GroupsException {
    return new EntityImpl(key, null);
  }
  public IEntity newInstance(String key, Class type) throws GroupsException {
    if ( org.jasig.portal.EntityTypes.getEntityTypeID(type) == null )
        { throw new GroupsException("Invalid group type: " + type); }
    return new EntityImpl(key, type);
  }
  public EntityIdentifier[] searchForEntities(String query, int method, Class type)
  throws GroupsException {
    if (type != group && type != iperson)
      return new EntityIdentifier[0];
    ArrayList ids = new ArrayList();
    switch (method){
      case STARTS_WITH:
          query = query+"*";
        break;
      case ENDS_WITH:
          query="*"+query;
        break;
      case CONTAINS:
          query="*"+query+"*";
        break;
    }
    query = namefield+"="+query;
    DirContext context = getConnection();
    NamingEnumeration userlist = null;
    SearchControls sc = new SearchControls();
    sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
    sc.setReturningAttributes(new String[] {keyfield});
    try {
      userlist = context.search(usercontext,query,sc);
    } catch (NamingException nex) {
      log.error("LDAPGroupStore: Unable to perform filter "+query, nex);
    }
    ArrayList keys = new ArrayList();
    processLdapResults(userlist,keys);
    String[] k = (String[]) keys.toArray(new String[0]);
    for (int i=0; i<k.length; i++){
      ids.add(new EntityIdentifier(k[i],iperson));
    }
    return (EntityIdentifier[]) ids.toArray(new EntityIdentifier[0]);
  }

/**
* Answers if <code>group</code> contains <code>member</code>.
* @return boolean
* @param group org.jasig.portal.groups.IEntityGroup
* @param member org.jasig.portal.groups.IGroupMember
*/
public boolean contains(IEntityGroup group, IGroupMember member)
throws GroupsException
{
    boolean found = false;
    Iterator itr = ( member.isGroup() )
      ? findMemberGroups(group)
      : findEntitiesForGroup(group);
    while ( itr.hasNext() && ! found )
        { found = member.equals(itr.next()); }
    return found;
}

/**
* Answers if <code>group</code> contains a member group named
* <code>name</code>.
* @return boolean
* @param group org.jasig.portal.groups.IEntityGroup
* @param name java.lang.String
*/
public boolean containsGroupNamed(IEntityGroup group, String name)
throws GroupsException
{
    boolean found = false;
    Iterator itr = findMemberGroups(group);
    while ( itr.hasNext() && ! found )
    {
        String otherName = ((IEntityGroup)itr.next()).getName();
        found = otherName != null && otherName.equals(name);
    }
    return found;
}
}
TOP

Related Classes of org.jasig.portal.groups.ldap.LDAPGroupStore$GroupShadow

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.