Merged enterprise features

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2746 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Paul Holmes-Higgin
2006-05-03 18:34:13 +00:00
parent 405c00bd8e
commit c37ff8805c
83 changed files with 15809 additions and 9 deletions

View File

@@ -0,0 +1,125 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Alfresco Network License. You may obtain a
* copy of the License at
*
* http://www.alfrescosoftware.com/legal/
*
* Please view the license relevant to your network subscription.
*
* BY CLICKING THE "I UNDERSTAND AND ACCEPT" BOX, OR INSTALLING,
* READING OR USING ALFRESCO'S Network SOFTWARE (THE "SOFTWARE"),
* YOU ARE AGREEING ON BEHALF OF THE ENTITY LICENSING THE SOFTWARE
* ("COMPANY") THAT COMPANY WILL BE BOUND BY AND IS BECOMING A PARTY TO
* THIS ALFRESCO NETWORK AGREEMENT ("AGREEMENT") AND THAT YOU HAVE THE
* AUTHORITY TO BIND COMPANY. IF COMPANY DOES NOT AGREE TO ALL OF THE
* TERMS OF THIS AGREEMENT, DO NOT SELECT THE "I UNDERSTAND AND AGREE"
* BOX AND DO NOT INSTALL THE SOFTWARE OR VIEW THE SOURCE CODE. COMPANY
* HAS NOT BECOME A LICENSEE OF, AND IS NOT AUTHORIZED TO USE THE
* SOFTWARE UNLESS AND UNTIL IT HAS AGREED TO BE BOUND BY THESE LICENSE
* TERMS. THE "EFFECTIVE DATE" FOR THIS AGREEMENT SHALL BE THE DAY YOU
* CHECK THE "I UNDERSTAND AND ACCEPT" BOX.
*/
package org.alfresco.repo.security.authentication.ldap;
import javax.naming.NamingException;
import javax.naming.directory.InitialDirContext;
import org.alfresco.repo.security.authentication.AbstractAuthenticationComponent;
import org.alfresco.repo.security.authentication.AuthenticationException;
/**
* Currently expects the cn name of the user which is in a fixed location.
*
* @author Andy Hind
*/
public class LDAPAuthenticationComponentImpl extends AbstractAuthenticationComponent
{
private String userNameFormat;
private LDAPInitialDirContextFactory ldapInitialContextFactory;
public LDAPAuthenticationComponentImpl()
{
super();
}
public void setLDAPInitialDirContextFactory(LDAPInitialDirContextFactory ldapInitialDirContextFactory)
{
this.ldapInitialContextFactory = ldapInitialDirContextFactory;
}
public void setUserNameFormat(String userNameFormat)
{
this.userNameFormat = userNameFormat;
}
/**
* Implement the authentication method
*/
public void authenticate(String userName, char[] password) throws AuthenticationException
{
InitialDirContext ctx = null;
try
{
ctx = ldapInitialContextFactory.getInitialDirContext(String.format(userNameFormat, new Object[]{userName}), new String(password));
// Authentication has been successful.
// Set the current user, they are now authenticated.
setCurrentUser(userName);
}
finally
{
if(ctx != null)
{
try
{
ctx.close();
}
catch (NamingException e)
{
clearCurrentSecurityContext();
throw new AuthenticationException("Failed to close connection", e);
}
}
}
}
@Override
protected boolean implementationAllowsGuestLogin()
{
InitialDirContext ctx = null;
try
{
ctx = ldapInitialContextFactory.getDefaultIntialDirContext();
return true;
}
catch(Exception e)
{
return false;
}
finally
{
if(ctx != null)
{
try
{
ctx.close();
}
catch (NamingException e)
{
throw new AuthenticationException("Failed to close connection", e);
}
}
}
}
}

View File

@@ -0,0 +1,695 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Alfresco Network License. You may obtain a
* copy of the License at
*
* http://www.alfrescosoftware.com/legal/
*
* Please view the license relevant to your network subscription.
*
* BY CLICKING THE "I UNDERSTAND AND ACCEPT" BOX, OR INSTALLING,
* READING OR USING ALFRESCO'S Network SOFTWARE (THE "SOFTWARE"),
* YOU ARE AGREEING ON BEHALF OF THE ENTITY LICENSING THE SOFTWARE
* ("COMPANY") THAT COMPANY WILL BE BOUND BY AND IS BECOMING A PARTY TO
* THIS ALFRESCO NETWORK AGREEMENT ("AGREEMENT") AND THAT YOU HAVE THE
* AUTHORITY TO BIND COMPANY. IF COMPANY DOES NOT AGREE TO ALL OF THE
* TERMS OF THIS AGREEMENT, DO NOT SELECT THE "I UNDERSTAND AND AGREE"
* BOX AND DO NOT INSTALL THE SOFTWARE OR VIEW THE SOURCE CODE. COMPANY
* HAS NOT BECOME A LICENSEE OF, AND IS NOT AUTHORIZED TO USE THE
* SOFTWARE UNLESS AND UNTIL IT HAS AGREED TO BE BOUND BY THESE LICENSE
* TERMS. THE "EFFECTIVE DATE" FOR THIS AGREEMENT SHALL BE THE DAY YOU
* CHECK THE "I UNDERSTAND AND ACCEPT" BOX.
*/
package org.alfresco.repo.security.authentication.ldap;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.importer.ExportSource;
import org.alfresco.repo.importer.ExportSourceImporterException;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.EqualsHelper;
import org.alfresco.util.GUID;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
public class LDAPGroupExportSource implements ExportSource, InitializingBean
{
private static Log s_logger = LogFactory.getLog(LDAPGroupExportSource.class);
private String groupQuery = "(objectclass=groupOfNames)";
private String searchBase;
private String groupIdAttributeName = "cn";
private String userIdAttributeName = "uid";
private String groupType = "groupOfNames";
private String personType = "inetOrgPerson";
private LDAPInitialDirContextFactory ldapInitialContextFactory;
private NamespaceService namespaceService;
private String memberAttribute = "member";
private boolean errorOnMissingMembers = false;
private QName viewRef;
private QName viewId;
private QName viewAssociations;
private QName childQName;
private QName viewValueQName;
private QName viewIdRef;
public LDAPGroupExportSource()
{
super();
}
public void setGroupIdAttributeName(String groupIdAttributeName)
{
this.groupIdAttributeName = groupIdAttributeName;
}
public void setGroupQuery(String groupQuery)
{
this.groupQuery = groupQuery;
}
public void setGroupType(String groupType)
{
this.groupType = groupType;
}
public void setLDAPInitialDirContextFactory(LDAPInitialDirContextFactory ldapInitialDirContextFactory)
{
this.ldapInitialContextFactory = ldapInitialDirContextFactory;
}
public void setMemberAttribute(String memberAttribute)
{
this.memberAttribute = memberAttribute;
}
public void setNamespaceService(NamespaceService namespaceService)
{
this.namespaceService = namespaceService;
}
public void setPersonType(String personType)
{
this.personType = personType;
}
public void setSearchBase(String searchBase)
{
this.searchBase = searchBase;
}
public void setUserIdAttributeName(String userIdAttributeName)
{
this.userIdAttributeName = userIdAttributeName;
}
public void setErrorOnMissingMembers(boolean errorOnMissingMembers)
{
this.errorOnMissingMembers = errorOnMissingMembers;
}
public void generateExport(XMLWriter writer)
{
HashSet<Group> rootGroups = new HashSet<Group>();
HashMap<String, Group> lookup = new HashMap<String, Group>();
HashSet<SecondaryLink> secondaryLinks = new HashSet<SecondaryLink>();
buildGroupsAndRoots(rootGroups, lookup, secondaryLinks);
buildXML(rootGroups, lookup, secondaryLinks, writer);
}
private void buildXML(HashSet<Group> rootGroups, HashMap<String, Group> lookup,
HashSet<SecondaryLink> secondaryLinks, XMLWriter writer)
{
Collection<String> prefixes = namespaceService.getPrefixes();
QName childQName = QName.createQName(NamespaceService.REPOSITORY_VIEW_PREFIX, "childName", namespaceService);
try
{
AttributesImpl attrs = new AttributesImpl();
attrs.addAttribute(NamespaceService.REPOSITORY_VIEW_1_0_URI, childQName.getLocalName(), childQName
.toPrefixString(), null, ContentModel.TYPE_PERSON.toPrefixString(namespaceService));
writer.startDocument();
for (String prefix : prefixes)
{
if (!prefix.equals("xml"))
{
String uri = namespaceService.getNamespaceURI(prefix);
writer.startPrefixMapping(prefix, uri);
}
}
writer.startElement(NamespaceService.REPOSITORY_VIEW_PREFIX, "view",
NamespaceService.REPOSITORY_VIEW_PREFIX + ":" + "view", new AttributesImpl());
// Create group structure
for (Group group : rootGroups)
{
addRootGroup(lookup, group, writer);
}
// Create secondary links.
for (SecondaryLink sl : secondaryLinks)
{
addSecondarylink(lookup, sl, writer);
}
for (String prefix : prefixes)
{
if (!prefix.equals("xml"))
{
writer.endPrefixMapping(prefix);
}
}
writer.endElement(NamespaceService.REPOSITORY_VIEW_PREFIX, "view", NamespaceService.REPOSITORY_VIEW_PREFIX
+ ":" + "view");
writer.endDocument();
}
catch (SAXException e)
{
throw new ExportSourceImporterException("Failed to create file for import.", e);
}
}
private void addSecondarylink(HashMap<String, Group> lookup, SecondaryLink sl, XMLWriter writer)
throws SAXException
{
String fromId = lookup.get(sl.from).guid;
String toId = lookup.get(sl.to).guid;
AttributesImpl attrs = new AttributesImpl();
attrs.addAttribute(viewIdRef.getNamespaceURI(), viewIdRef.getLocalName(), viewIdRef.toPrefixString(), null, fromId);
writer.startElement(viewRef.getNamespaceURI(), viewRef.getLocalName(),
viewRef.toPrefixString(namespaceService), attrs);
writer.startElement(viewAssociations.getNamespaceURI(), viewAssociations.getLocalName(), viewAssociations
.toPrefixString(namespaceService), new AttributesImpl());
writer.startElement(ContentModel.ASSOC_MEMBER.getNamespaceURI(), ContentModel.ASSOC_MEMBER.getLocalName(),
ContentModel.ASSOC_MEMBER.toPrefixString(namespaceService), new AttributesImpl());
AttributesImpl attrsRef = new AttributesImpl();
attrsRef.addAttribute(viewIdRef.getNamespaceURI(), viewIdRef.getLocalName(), viewIdRef.toPrefixString(), null, toId);
attrsRef.addAttribute(childQName.getNamespaceURI(), childQName.getLocalName(), childQName.toPrefixString(),
null, QName.createQName(ContentModel.USER_MODEL_URI, sl.to).toPrefixString(namespaceService));
writer.startElement(viewRef.getNamespaceURI(), viewRef.getLocalName(),
viewRef.toPrefixString(namespaceService), attrsRef);
writer.endElement(viewRef.getNamespaceURI(), viewRef.getLocalName(), viewRef.toPrefixString(namespaceService));
writer.endElement(ContentModel.ASSOC_MEMBER.getNamespaceURI(), ContentModel.ASSOC_MEMBER.getLocalName(),
ContentModel.ASSOC_MEMBER.toPrefixString(namespaceService));
writer.endElement(viewAssociations.getNamespaceURI(), viewAssociations.getLocalName(), viewAssociations
.toPrefixString(namespaceService));
writer.endElement(viewRef.getNamespaceURI(), viewRef.getLocalName(), viewRef.toPrefixString(namespaceService));
}
private void addRootGroup(HashMap<String, Group> lookup, Group group, XMLWriter writer) throws SAXException
{
AttributesImpl attrs = new AttributesImpl();
attrs.addAttribute(NamespaceService.REPOSITORY_VIEW_1_0_URI, childQName.getLocalName(), childQName
.toPrefixString(), null, QName.createQName(ContentModel.USER_MODEL_URI, group.gid).toPrefixString(
namespaceService));
attrs.addAttribute(viewId.getNamespaceURI(), viewId.getLocalName(), viewId
.toPrefixString(), null, group.guid);
writer.startElement(ContentModel.TYPE_AUTHORITY_CONTAINER.getNamespaceURI(),
ContentModel.TYPE_AUTHORITY_CONTAINER.getLocalName(), ContentModel.TYPE_AUTHORITY_CONTAINER
.toPrefixString(namespaceService), attrs);
writer.startElement(ContentModel.PROP_AUTHORITY_NAME.getNamespaceURI(), ContentModel.PROP_AUTHORITY_NAME
.getLocalName(), ContentModel.PROP_AUTHORITY_NAME.toPrefixString(namespaceService),
new AttributesImpl());
writer.characters(group.gid.toCharArray(), 0, group.gid.length());
writer.endElement(ContentModel.PROP_AUTHORITY_NAME.getNamespaceURI(), ContentModel.PROP_AUTHORITY_NAME
.getLocalName(), ContentModel.PROP_AUTHORITY_NAME.toPrefixString(namespaceService));
if (group.members.size() > 0)
{
writer.startElement(ContentModel.PROP_MEMBERS.getNamespaceURI(), ContentModel.PROP_MEMBERS.getLocalName(),
ContentModel.PROP_MEMBERS.toPrefixString(namespaceService), new AttributesImpl());
for (String member : group.members)
{
writer.startElement(viewValueQName.getNamespaceURI(), viewValueQName.getLocalName(), viewValueQName
.toPrefixString(namespaceService), new AttributesImpl());
writer.characters(member.toCharArray(), 0, member.length());
writer.endElement(viewValueQName.getNamespaceURI(), viewValueQName.getLocalName(), viewValueQName
.toPrefixString(namespaceService));
}
writer.endElement(ContentModel.PROP_MEMBERS.getNamespaceURI(), ContentModel.PROP_MEMBERS.getLocalName(),
ContentModel.PROP_MEMBERS.toPrefixString(namespaceService));
}
for (Group child : group.children)
{
addgroup(lookup, child, writer);
}
writer.endElement(ContentModel.TYPE_AUTHORITY_CONTAINER.getNamespaceURI(),
ContentModel.TYPE_AUTHORITY_CONTAINER.getLocalName(), ContentModel.TYPE_AUTHORITY_CONTAINER
.toPrefixString(namespaceService));
}
private void addgroup(HashMap<String, Group> lookup, Group group, XMLWriter writer) throws SAXException
{
AttributesImpl attrs = new AttributesImpl();
writer.startElement(ContentModel.ASSOC_MEMBER.getNamespaceURI(), ContentModel.ASSOC_MEMBER.getLocalName(),
ContentModel.ASSOC_MEMBER.toPrefixString(namespaceService), attrs);
addRootGroup(lookup, group, writer);
writer.endElement(ContentModel.ASSOC_MEMBER.getNamespaceURI(), ContentModel.ASSOC_MEMBER.getLocalName(),
ContentModel.ASSOC_MEMBER.toPrefixString(namespaceService));
}
private void buildGroupsAndRoots(HashSet<Group> rootGroups, HashMap<String, Group> lookup,
HashSet<SecondaryLink> secondaryLinks)
{
InitialDirContext ctx = null;
try
{
ctx = ldapInitialContextFactory.getDefaultIntialDirContext();
SearchControls userSearchCtls = new SearchControls();
userSearchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration searchResults = ctx.search(searchBase, groupQuery, userSearchCtls);
while (searchResults.hasMoreElements())
{
SearchResult result = (SearchResult) searchResults.next();
Attributes attributes = result.getAttributes();
Attribute gidAttribute = attributes.get(groupIdAttributeName);
if(gidAttribute == null)
{
throw new ExportSourceImporterException("Group returned by group search does not have mandatory group id attribute "+attributes);
}
String gid = (String) gidAttribute.get(0);
Group group = lookup.get(gid);
if (group == null)
{
group = new Group(gid);
lookup.put(group.gid, group);
rootGroups.add(group);
}
Attribute memAttribute = attributes.get(memberAttribute);
// check for null
if (memAttribute != null)
{
for (int i = 0; i < memAttribute.size(); i++)
{
String attribute = (String) memAttribute.get(i);
if (attribute != null)
{
group.distinguishedNames.add(attribute);
}
}
}
}
if (s_logger.isDebugEnabled())
{
s_logger.debug("Found " + lookup.size());
}
for (Group group : lookup.values())
{
if (s_logger.isDebugEnabled())
{
s_logger.debug("Linking " + group.gid);
}
for (String dn : group.distinguishedNames)
{
if (s_logger.isDebugEnabled())
{
s_logger.debug("... " + dn);
}
String id;
Boolean isGroup = null;
SearchControls memberSearchCtls = new SearchControls();
memberSearchCtls.setSearchScope(SearchControls.OBJECT_SCOPE);
NamingEnumeration memberSearchResults;
try
{
memberSearchResults = ctx.search(dn, "(objectClass=*)", memberSearchCtls);
}
catch (NamingException e)
{
if (errorOnMissingMembers)
{
throw e;
}
s_logger.warn("Failed to resolve distinguished name: " + dn);
continue;
}
while (memberSearchResults.hasMoreElements())
{
id = null;
SearchResult result;
try
{
result = (SearchResult) memberSearchResults.next();
}
catch (NamingException e)
{
if (errorOnMissingMembers)
{
throw e;
}
s_logger.warn("Failed to resolve distinguished name: " + dn);
continue;
}
Attributes attributes = result.getAttributes();
Attribute objectclass = attributes.get("objectclass");
if(objectclass == null)
{
throw new ExportSourceImporterException("Failed to find attribute objectclass for DN "+dn);
}
for (int i = 0; i < objectclass.size(); i++)
{
String testType;
try
{
testType = (String) objectclass.get(i);
}
catch (NamingException e)
{
if (errorOnMissingMembers)
{
throw e;
}
s_logger.warn("Failed to resolve object class attribute for distinguished name: " + dn);
continue;
}
if (testType.equals(groupType))
{
isGroup = true;
try
{
Attribute groupIdAttribute = attributes.get(groupIdAttributeName);
if(groupIdAttribute == null)
{
throw new ExportSourceImporterException("Group missing group id attribute DN ="+dn + " att = "+groupIdAttributeName);
}
id = (String) groupIdAttribute.get(0);
}
catch (NamingException e)
{
if (errorOnMissingMembers)
{
throw e;
}
s_logger.warn("Failed to resolve group identifier "
+ groupIdAttributeName + " for distinguished name: " + dn);
id = "Unknown sub group";
}
break;
}
else if (testType.equals(personType))
{
isGroup = false;
try
{
Attribute userIdAttribute = attributes.get(userIdAttributeName);
if(userIdAttribute == null)
{
throw new ExportSourceImporterException("User missing user id attribute DN ="+dn + " att = "+userIdAttributeName);
}
id = (String) userIdAttribute.get(0);
}
catch (NamingException e)
{
if (errorOnMissingMembers)
{
throw e;
}
s_logger.warn("Failed to resolve group identifier "
+ userIdAttributeName + " for distinguished name: " + dn);
id = "Unknown member";
}
break;
}
}
if (id != null)
{
if (isGroup == null)
{
throw new ExportSourceImporterException("Type not recognised for DN"+dn);
}
else if (isGroup)
{
if (s_logger.isDebugEnabled())
{
s_logger.debug("... is sub group");
}
Group child = lookup.get("GROUP_" + id);
if (child == null)
{
throw new ExportSourceImporterException("Failed to find child group " + id);
}
if (rootGroups.contains(child))
{
if (s_logger.isDebugEnabled())
{
s_logger.debug("... Primary created from "
+ group.gid + " to " + child.gid);
}
group.children.add(child);
rootGroups.remove(child);
}
else
{
if (s_logger.isDebugEnabled())
{
s_logger.debug("... Secondary created from "
+ group.gid + " to " + child.gid);
}
secondaryLinks.add(new SecondaryLink(group.gid, child.gid));
}
}
else
{
if (s_logger.isDebugEnabled())
{
s_logger.debug("... is member");
}
group.members.add(id);
}
}
}
}
}
if (s_logger.isDebugEnabled())
{
s_logger.debug("Top " + rootGroups.size());
s_logger.debug("Secondary " + secondaryLinks.size());
}
}
catch (NamingException e)
{
throw new ExportSourceImporterException("Failed to import people.", e);
}
finally
{
if (ctx != null)
{
try
{
ctx.close();
}
catch (NamingException e)
{
throw new ExportSourceImporterException("Failed to import people.", e);
}
}
}
}
private static class Group
{
String gid;
String guid = GUID.generate();
HashSet<Group> children = new HashSet<Group>();
HashSet<String> members = new HashSet<String>();
HashSet<String> distinguishedNames = new HashSet<String>();
private Group(String gid)
{
this.gid = "GROUP_" + gid;
}
@Override
public boolean equals(Object o)
{
if (this == o)
{
return true;
}
if (!(o instanceof Group))
{
return false;
}
Group g = (Group) o;
return this.gid.equals(g.gid);
}
@Override
public int hashCode()
{
return gid.hashCode();
}
}
private static class SecondaryLink
{
String from;
String to;
private SecondaryLink(String from, String to)
{
this.from = from;
this.to = to;
}
@Override
public boolean equals(Object o)
{
if (this == o)
{
return true;
}
if (!(o instanceof Group))
{
return false;
}
SecondaryLink l = (SecondaryLink) o;
return EqualsHelper.nullSafeEquals(this.from, l.from) && EqualsHelper.nullSafeEquals(this.to, l.to);
}
@Override
public int hashCode()
{
int hashCode = 0;
if (from != null)
{
hashCode = hashCode * 37 + from.hashCode();
}
if (to != null)
{
hashCode = hashCode * 37 + to.hashCode();
}
return hashCode;
}
}
public static void main(String[] args) throws IOException
{
ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
ExportSource source = (ExportSource) ctx.getBean("ldapGroupExportSource");
File file = new File(args[0]);
Writer writer = new BufferedWriter(new FileWriter(file));
XMLWriter xmlWriter = createXMLExporter(writer);
source.generateExport(xmlWriter);
xmlWriter.close();
}
private static XMLWriter createXMLExporter(Writer writer)
{
// Define output format
OutputFormat format = OutputFormat.createPrettyPrint();
format.setNewLineAfterDeclaration(false);
format.setIndentSize(3);
format.setEncoding("UTF-8");
// Construct an XML Exporter
XMLWriter xmlWriter = new XMLWriter(writer, format);
return xmlWriter;
}
public void afterPropertiesSet() throws Exception
{
viewRef = QName.createQName(NamespaceService.REPOSITORY_VIEW_PREFIX, "reference", namespaceService);
viewId = QName.createQName(NamespaceService.REPOSITORY_VIEW_PREFIX, "id", namespaceService);
viewIdRef = QName.createQName(NamespaceService.REPOSITORY_VIEW_PREFIX, "idref", namespaceService);
viewAssociations = QName.createQName(NamespaceService.REPOSITORY_VIEW_PREFIX, "associations", namespaceService);
childQName = QName.createQName(NamespaceService.REPOSITORY_VIEW_PREFIX, "childName", namespaceService);
viewValueQName = QName.createQName(NamespaceService.REPOSITORY_VIEW_PREFIX, "value", namespaceService);
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Alfresco Network License. You may obtain a
* copy of the License at
*
* http://www.alfrescosoftware.com/legal/
*
* Please view the license relevant to your network subscription.
*
* BY CLICKING THE "I UNDERSTAND AND ACCEPT" BOX, OR INSTALLING,
* READING OR USING ALFRESCO'S Network SOFTWARE (THE "SOFTWARE"),
* YOU ARE AGREEING ON BEHALF OF THE ENTITY LICENSING THE SOFTWARE
* ("COMPANY") THAT COMPANY WILL BE BOUND BY AND IS BECOMING A PARTY TO
* THIS ALFRESCO NETWORK AGREEMENT ("AGREEMENT") AND THAT YOU HAVE THE
* AUTHORITY TO BIND COMPANY. IF COMPANY DOES NOT AGREE TO ALL OF THE
* TERMS OF THIS AGREEMENT, DO NOT SELECT THE "I UNDERSTAND AND AGREE"
* BOX AND DO NOT INSTALL THE SOFTWARE OR VIEW THE SOURCE CODE. COMPANY
* HAS NOT BECOME A LICENSEE OF, AND IS NOT AUTHORIZED TO USE THE
* SOFTWARE UNLESS AND UNTIL IT HAS AGREED TO BE BOUND BY THESE LICENSE
* TERMS. THE "EFFECTIVE DATE" FOR THIS AGREEMENT SHALL BE THE DAY YOU
* CHECK THE "I UNDERSTAND AND ACCEPT" BOX.
*/
package org.alfresco.repo.security.authentication.ldap;
import java.util.Map;
import javax.naming.directory.InitialDirContext;
import org.alfresco.repo.security.authentication.AuthenticationException;
/**
* Interface that defines a factory for obtaining ldap directory contexts.
*
* @author Andy Hind
*/
public interface LDAPInitialDirContextFactory
{
/**
* Set the LDAP environment Hashtable properties used ot initialise the LDAP connection.
*
* @param environment
*/
public void setInitialDirContextEnvironment(Map<String, String> environment);
/**
* Use the environment properties and connect to the LDAP server.
* Used to obtain read only access to the LDAP server.
*
* @return
* @throws AuthenticationException
*/
public InitialDirContext getDefaultIntialDirContext() throws AuthenticationException;
/**
* Augment the connection environment with the identity and credentials and bind to the ldap server.
* Mainly used to validate a user's credentials during authentication.
*
* @param principal
* @param credentials
* @return
* @throws AuthenticationException
*/
public InitialDirContext getInitialDirContext(String principal, String credentials) throws AuthenticationException;
}

View File

@@ -0,0 +1,197 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Alfresco Network License. You may obtain a
* copy of the License at
*
* http://www.alfrescosoftware.com/legal/
*
* Please view the license relevant to your network subscription.
*
* BY CLICKING THE "I UNDERSTAND AND ACCEPT" BOX, OR INSTALLING,
* READING OR USING ALFRESCO'S Network SOFTWARE (THE "SOFTWARE"),
* YOU ARE AGREEING ON BEHALF OF THE ENTITY LICENSING THE SOFTWARE
* ("COMPANY") THAT COMPANY WILL BE BOUND BY AND IS BECOMING A PARTY TO
* THIS ALFRESCO NETWORK AGREEMENT ("AGREEMENT") AND THAT YOU HAVE THE
* AUTHORITY TO BIND COMPANY. IF COMPANY DOES NOT AGREE TO ALL OF THE
* TERMS OF THIS AGREEMENT, DO NOT SELECT THE "I UNDERSTAND AND AGREE"
* BOX AND DO NOT INSTALL THE SOFTWARE OR VIEW THE SOURCE CODE. COMPANY
* HAS NOT BECOME A LICENSEE OF, AND IS NOT AUTHORIZED TO USE THE
* SOFTWARE UNLESS AND UNTIL IT HAS AGREED TO BE BOUND BY THESE LICENSE
* TERMS. THE "EFFECTIVE DATE" FOR THIS AGREEMENT SHALL BE THE DAY YOU
* CHECK THE "I UNDERSTAND AND ACCEPT" BOX.
*/
package org.alfresco.repo.security.authentication.ldap;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Map;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.InitialDirContext;
import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.util.ApplicationContextHelper;
import org.springframework.context.ApplicationContext;
public class LDAPInitialDirContextFactoryImpl implements LDAPInitialDirContextFactory
{
private Map<String, String> initialDirContextEnvironment = Collections.<String, String> emptyMap();
static
{
System.setProperty("javax.security.auth.useSubjectCredentialsOnly", "false");
}
public LDAPInitialDirContextFactoryImpl()
{
super();
}
public void setInitialDirContextEnvironment(Map<String, String> initialDirContextEnvironment)
{
this.initialDirContextEnvironment = initialDirContextEnvironment;
}
public Map<String, String> getInitialDirContextEnvironment()
{
return initialDirContextEnvironment;
}
public InitialDirContext getDefaultIntialDirContext() throws AuthenticationException
{
Hashtable<String, String> env = new Hashtable<String, String>(initialDirContextEnvironment.size());
env.putAll(initialDirContextEnvironment);
env.put("javax.security.auth.useSubjectCredsOnly", "false");
return buildInitialDirContext(env);
}
private InitialDirContext buildInitialDirContext(Hashtable<String, String> env) throws AuthenticationException
{
try
{
return new InitialDirContext(env);
}
catch (javax.naming.AuthenticationException ax)
{
throw new AuthenticationException("LDAP authentication failed.", ax);
}
catch (NamingException nx)
{
throw new AuthenticationException("Unable to connect to LDAP Server; check LDAP configuration", nx);
}
}
public InitialDirContext getInitialDirContext(String principal, String credentials) throws AuthenticationException
{
if (principal == null)
{
throw new AuthenticationException("Null user name provided.");
}
if (credentials == null)
{
throw new AuthenticationException("No credentials provided.");
}
Hashtable<String, String> env = new Hashtable<String, String>(initialDirContextEnvironment.size());
env.putAll(initialDirContextEnvironment);
env.put(Context.SECURITY_PRINCIPAL, principal);
env.put(Context.SECURITY_CREDENTIALS, credentials);
return buildInitialDirContext(env);
}
public static void main(String[] args)
{
// ....build a pyramid selling scheme .....
// A group has three user members and 2 group members .... and off we go ....
// We make the people and groups to represent this and stick them into LDAP ...used to populate a test data base for user and groups
int userMembers = Integer.parseInt(args[3]);
ApplicationContext applicationContext = ApplicationContextHelper.getApplicationContext();
LDAPInitialDirContextFactory factory = (LDAPInitialDirContextFactory) applicationContext
.getBean("ldapInitialDirContextFactory");
InitialDirContext ctx = null;
try
{
ctx = factory.getInitialDirContext("cn=" + args[0] + "," + args[2], args[1]);
/* Values we'll use in creating the entry */
Attribute objClasses = new BasicAttribute("objectclass");
objClasses.add("top");
objClasses.add("person");
objClasses.add("organizationalPerson");
objClasses.add("inetOrgPerson");
for (int i = 0; i < userMembers; i++)
{
Attribute cn = new BasicAttribute("cn", "User" + i + " TestUser");
Attribute sn = new BasicAttribute("sn", "TestUser");
Attribute givenNames = new BasicAttribute("givenName", "User" + i);
Attribute telephoneNumber = new BasicAttribute("telephoneNumber", "123");
Attribute uid = new BasicAttribute("uid", "User" + i);
Attribute mail = new BasicAttribute("mail", "woof@woof");
Attribute o = new BasicAttribute("o", "Alfresco");
Attribute userPassword = new BasicAttribute("userPassword", "bobbins");
/* Specify the DN we're adding */
String dn = "cn=User" + i + " TestUser," + args[2];
Attributes orig = new BasicAttributes();
orig.put(objClasses);
orig.put(cn);
orig.put(sn);
orig.put(givenNames);
orig.put(telephoneNumber);
orig.put(uid);
orig.put(mail);
orig.put(o);
orig.put(userPassword);
try
{
ctx.destroySubcontext(dn);
}
catch (NamingException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
ctx.createSubcontext(dn, orig);
}
}
catch (NamingException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
finally
{
if (ctx != null)
{
try
{
ctx.close();
}
catch (NamingException e)
{
e.printStackTrace();
}
}
}
}
}

View File

@@ -0,0 +1,328 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Alfresco Network License. You may obtain a
* copy of the License at
*
* http://www.alfrescosoftware.com/legal/
*
* Please view the license relevant to your network subscription.
*
* BY CLICKING THE "I UNDERSTAND AND ACCEPT" BOX, OR INSTALLING,
* READING OR USING ALFRESCO'S Network SOFTWARE (THE "SOFTWARE"),
* YOU ARE AGREEING ON BEHALF OF THE ENTITY LICENSING THE SOFTWARE
* ("COMPANY") THAT COMPANY WILL BE BOUND BY AND IS BECOMING A PARTY TO
* THIS ALFRESCO NETWORK AGREEMENT ("AGREEMENT") AND THAT YOU HAVE THE
* AUTHORITY TO BIND COMPANY. IF COMPANY DOES NOT AGREE TO ALL OF THE
* TERMS OF THIS AGREEMENT, DO NOT SELECT THE "I UNDERSTAND AND AGREE"
* BOX AND DO NOT INSTALL THE SOFTWARE OR VIEW THE SOURCE CODE. COMPANY
* HAS NOT BECOME A LICENSEE OF, AND IS NOT AUTHORIZED TO USE THE
* SOFTWARE UNLESS AND UNTIL IT HAS AGREED TO BE BOUND BY THESE LICENSE
* TERMS. THE "EFFECTIVE DATE" FOR THIS AGREEMENT SHALL BE THE DAY YOU
* CHECK THE "I UNDERSTAND AND ACCEPT" BOX.
*/
package org.alfresco.repo.security.authentication.ldap;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.Collection;
import java.util.Map;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.importer.ExportSource;
import org.alfresco.repo.importer.ExportSourceImporterException;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.ApplicationContextHelper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.springframework.context.ApplicationContext;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
public class LDAPPersonExportSource implements ExportSource
{
private static Log s_logger = LogFactory.getLog(LDAPPersonExportSource.class);
private String personQuery = "(objectclass=inetOrgPerson)";
private String searchBase;
private String userIdAttributeName;
private LDAPInitialDirContextFactory ldapInitialContextFactory;
private PersonService personService;
private Map<String, String> attributeMapping;
private NamespaceService namespaceService;
private String defaultHomeFolder;
public LDAPPersonExportSource()
{
super();
}
public void setPersonQuery(String personQuery)
{
this.personQuery = personQuery;
}
public void setSearchBase(String searchBase)
{
this.searchBase = searchBase;
}
public void setUserIdAttributeName(String userIdAttributeName)
{
this.userIdAttributeName = userIdAttributeName;
}
public void setLDAPInitialDirContextFactory(LDAPInitialDirContextFactory ldapInitialDirContextFactory)
{
this.ldapInitialContextFactory = ldapInitialDirContextFactory;
}
public void setPersonService(PersonService personService)
{
this.personService = personService;
}
public void setDefaultHomeFolder(String defaultHomeFolder)
{
this.defaultHomeFolder = defaultHomeFolder;
}
public void setNamespaceService(NamespaceService namespaceService)
{
this.namespaceService = namespaceService;
}
public void setAttributeMapping(Map<String, String> attributeMapping)
{
this.attributeMapping = attributeMapping;
}
public void generateExport(XMLWriter writer)
{
QName nodeUUID = QName.createQName("sys:node-uuid", namespaceService);
Collection<String> prefixes = namespaceService.getPrefixes();
QName childQName = QName.createQName(NamespaceService.REPOSITORY_VIEW_PREFIX, "childName", namespaceService);
try
{
AttributesImpl attrs = new AttributesImpl();
attrs.addAttribute(NamespaceService.REPOSITORY_VIEW_1_0_URI, childQName.getLocalName(), childQName
.toPrefixString(), null, ContentModel.TYPE_PERSON.toPrefixString(namespaceService));
writer.startDocument();
for (String prefix : prefixes)
{
if (!prefix.equals("xml"))
{
String uri = namespaceService.getNamespaceURI(prefix);
writer.startPrefixMapping(prefix, uri);
}
}
writer.startElement(NamespaceService.REPOSITORY_VIEW_PREFIX, "view",
NamespaceService.REPOSITORY_VIEW_PREFIX + ":" + "view", new AttributesImpl());
InitialDirContext ctx = null;
try
{
ctx = ldapInitialContextFactory.getDefaultIntialDirContext();
// Authentication has been successful.
// Set the current user, they are now authenticated.
SearchControls userSearchCtls = new SearchControls();
userSearchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
System.out.println("COUNT "+userSearchCtls.getCountLimit());
System.out.println("TIME "+userSearchCtls.getTimeLimit());
userSearchCtls.setCountLimit(Integer.MAX_VALUE);
NamingEnumeration searchResults = ctx.search(searchBase, personQuery, userSearchCtls);
while (searchResults.hasMoreElements())
{
SearchResult result = (SearchResult) searchResults.next();
Attributes attributes = result.getAttributes();
Attribute uidAttribute = attributes.get(userIdAttributeName);
String uid = (String) uidAttribute.get(0);
if(s_logger.isDebugEnabled())
{
s_logger.debug("Adding user for "+uid);
}
System.out.println("User "+uid);
writer.startElement(ContentModel.TYPE_PERSON.getNamespaceURI(), ContentModel.TYPE_PERSON
.getLocalName(), ContentModel.TYPE_PERSON.toPrefixString(namespaceService), attrs);
// permissions
// owner
writer.startElement(ContentModel.ASPECT_OWNABLE.getNamespaceURI(), ContentModel.ASPECT_OWNABLE
.getLocalName(), ContentModel.ASPECT_OWNABLE.toPrefixString(namespaceService),
new AttributesImpl());
writer.endElement(ContentModel.ASPECT_OWNABLE.getNamespaceURI(), ContentModel.ASPECT_OWNABLE
.getLocalName(), ContentModel.ASPECT_OWNABLE.toPrefixString(namespaceService));
writer.startElement(ContentModel.PROP_OWNER.getNamespaceURI(), ContentModel.PROP_OWNER
.getLocalName(), ContentModel.PROP_OWNER.toPrefixString(namespaceService),
new AttributesImpl());
writer.characters(uid.toCharArray(), 0, uid.length());
writer.endElement(ContentModel.PROP_OWNER.getNamespaceURI(),
ContentModel.PROP_OWNER.getLocalName(), ContentModel.PROP_OWNER
.toPrefixString(namespaceService));
for (String key : attributeMapping.keySet())
{
QName keyQName = QName.createQName(key, namespaceService);
writer.startElement(keyQName.getNamespaceURI(), keyQName.getLocalName(), keyQName
.toPrefixString(namespaceService), new AttributesImpl());
// cater for null
String attribute = attributeMapping.get(key);
if (attribute != null)
{
String value = (String) attributes.get(attribute).get(0);
if (value != null)
{
writer.characters(value.toCharArray(), 0, value.length());
}
}
writer.endElement(keyQName.getNamespaceURI(), keyQName.getLocalName(), keyQName
.toPrefixString(namespaceService));
}
// Default home folder
if (!(attributeMapping.keySet().contains(ContentModel.PROP_HOMEFOLDER.toString()) || attributeMapping
.keySet().contains(ContentModel.PROP_HOMEFOLDER.toPrefixString(namespaceService))))
{
// Only if we are creating the person for the first time
if (!personService.personExists(uid))
{
writer.startElement(ContentModel.PROP_HOMEFOLDER.getNamespaceURI(),
ContentModel.PROP_HOMEFOLDER.getLocalName(), ContentModel.PROP_HOMEFOLDER
.toPrefixString(namespaceService), new AttributesImpl());
if (defaultHomeFolder != null)
{
writer.characters(defaultHomeFolder.toCharArray(), 0, defaultHomeFolder.length());
}
writer.endElement(ContentModel.PROP_HOMEFOLDER.getNamespaceURI(),
ContentModel.PROP_HOMEFOLDER.getLocalName(), ContentModel.PROP_HOMEFOLDER
.toPrefixString(namespaceService));
}
}
if (personService.personExists(uid))
{
String uguid = personService.getPerson(uid).getId();
writer.startElement(nodeUUID.getNamespaceURI(), nodeUUID.getLocalName(), nodeUUID
.toPrefixString(namespaceService), new AttributesImpl());
writer.characters(uguid.toCharArray(), 0, uguid.length());
writer.endElement(nodeUUID.getNamespaceURI(), nodeUUID.getLocalName(), nodeUUID
.toPrefixString(namespaceService));
}
writer.endElement(ContentModel.TYPE_PERSON.getNamespaceURI(), ContentModel.TYPE_PERSON
.getLocalName(), ContentModel.TYPE_PERSON.toPrefixString(namespaceService));
}
}
catch (NamingException e)
{
throw new ExportSourceImporterException("Failed to import people.", e);
}
finally
{
if (ctx != null)
{
try
{
ctx.close();
}
catch (NamingException e)
{
throw new ExportSourceImporterException("Failed to import people.", e);
}
}
}
for (String prefix : prefixes)
{
if (!prefix.equals("xml"))
{
writer.endPrefixMapping(prefix);
}
}
writer.endElement(NamespaceService.REPOSITORY_VIEW_PREFIX, "view", NamespaceService.REPOSITORY_VIEW_PREFIX
+ ":" + "view");
writer.endDocument();
}
catch (SAXException e)
{
throw new ExportSourceImporterException("Failed to create file for import.", e);
}
}
public static void main(String[] args) throws IOException
{
ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
ExportSource source = (ExportSource) ctx.getBean("ldapPeopleExportSource");
File file = new File(args[0]);
Writer writer = new BufferedWriter(new FileWriter(file));
XMLWriter xmlWriter = createXMLExporter(writer);
source.generateExport(xmlWriter);
xmlWriter.close();
}
private static XMLWriter createXMLExporter(Writer writer)
{
// Define output format
OutputFormat format = OutputFormat.createPrettyPrint();
format.setNewLineAfterDeclaration(false);
format.setIndentSize(3);
format.setEncoding("UTF-8");
// Construct an XML Exporter
XMLWriter xmlWriter = new XMLWriter(writer, format);
return xmlWriter;
}
}