Index: src/main/java/com/xpn/xwiki/user/impl/LDAP/XWikiLDAPAuthServiceImpl.java
===================================================================
--- src/main/java/com/xpn/xwiki/user/impl/LDAP/XWikiLDAPAuthServiceImpl.java	(révision 0)
+++ src/main/java/com/xpn/xwiki/user/impl/LDAP/XWikiLDAPAuthServiceImpl.java	(révision 0)
@@ -0,0 +1,851 @@
+/*
+ * See the NOTICE file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ *
+ */
+
+package com.xpn.xwiki.user.impl.LDAP;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.securityfilter.realm.SimplePrincipal;
+
+import com.novell.ldap.LDAPConnection;
+import com.xpn.xwiki.XWikiContext;
+import com.xpn.xwiki.XWikiException;
+import com.xpn.xwiki.cache.api.XWikiCache;
+import com.xpn.xwiki.doc.XWikiDocument;
+import com.xpn.xwiki.objects.BaseObject;
+import com.xpn.xwiki.objects.classes.BaseClass;
+import com.xpn.xwiki.user.api.XWikiGroupService;
+import com.xpn.xwiki.user.impl.xwiki.XWikiAuthServiceImpl;
+import java.security.Principal;
+import java.text.MessageFormat;
+import java.util.HashMap;
+
+/**
+ * This class provides an authentication method that validates a user trough LDAP against a
+ * directory. It just gives LDAP users access if they belong to a particular group and creates XWiki
+ * users if they have never logged in before Furthermore, it synchronizes membership to XWiki groups
+ * based on membership to LDAP groups.
+ * 
+ * @version $Id: $
+ */
+public class XWikiLDAPAuthServiceImpl extends XWikiAuthServiceImpl
+{
+    /**
+     * simple cache for LDAP groups and their members - cache entries are to expire after 6h and
+     * there is no size limit.
+     */
+    protected static XWikiCache cache;
+
+    /**
+     * Logging tool.
+     */
+    private static final Log LOG = LogFactory.getLog(LDAPAuthenticaterTest.class);
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see com.xpn.xwiki.user.impl.xwiki.XWikiAuthServiceImpl#authenticate(java.lang.String,
+     *      java.lang.String, com.xpn.xwiki.XWikiContext)
+     */
+    public Principal authenticate(String userName, String password, XWikiContext context)
+        throws XWikiException
+    {
+        // 1. check Superadmin
+        // 2. check if ldap authentication is off => authenticate against db
+        // 3. bind to LDAP => if failed try db
+        // 4. if group param, verify group membership (& get DN)
+        // 4. if no group locate dn_ldap attribute on user
+        // 5. if no dn search for user
+        // 6. compare pwd
+        // 7. if flag check if !user exists => create user
+        // 8. if flag update user attributes
+        // 9. if flag update Xwiki groupmembership
+        // on Error try local DB login
+
+        Principal result = null;
+
+        // message to the user: nousername, nopassword, wrongpassword, wronguser, loginfailed
+        // context.put("message", "nopassword");
+
+        if ((userName == null) || (userName.length() == 0)) {
+            return null;
+        }
+        String ldapUserName = userName.replaceAll(" ", "");
+        if ((password == null) || (password.trim().length() == 0)) {
+            return null;
+        }
+
+        // strip possible "XWiki."
+        // ATTENTION: Possible incompatibility to before now user is NEVER located with
+        // "XWiki.username" in LDAP
+        int i = ldapUserName.indexOf(".");
+        if (i != -1) {
+            ldapUserName = ldapUserName.substring(i + 1);
+        }
+
+        // If we have the context then we are using direct mode
+        // then we should specify the database
+        // This is needed for virtual mode to work
+
+        XWikiLDAPConnection connector = new XWikiLDAPConnection();
+
+        try {
+            result = checkSuperadmin(ldapUserName, password, context);
+            if (result != null) {
+                LOG.debug("Superadmin logged in.");
+                return result;
+            }
+
+            // check if LDAP is activated in xwiki.cfg
+            String ldapActiv = getParam("ldap", context);
+            if (ldapActiv.length() == 0 || !ldapActiv.equalsIgnoreCase("1")) {
+                LOG.debug("LDAP authentication failed: LDAP not activ");
+                return null;
+            }
+
+            // open LDAP
+            int ldapPort = getLDAPPort(context);
+            String ldapHost = getParam("ldap_server", context);
+            String bindDNFormat = getParam("ldap_bind_DN", context);
+            String bindPasswordFormat = getParam("ldap_bind_pass", context);
+            String uidattr = getParam("ldap_UID_attr", context);
+            if (uidattr == null || uidattr.length() == 0) {
+                uidattr = "cn";
+            }
+
+            Object[] arguments = {ldapUserName, password};
+
+            // allow to use the given user and password also as the LDAP bind user and password
+            String bindDN = MessageFormat.format(bindDNFormat, arguments);
+            String bindPassword = MessageFormat.format(bindPasswordFormat, arguments);
+
+            boolean bind;
+            if ("1".equals(getParam("ldap_ssl", context))) {
+                String keyStore = getParam("ldap_ssl.keystore", context);
+                LOG.debug("Connecting to LDAP using SSL");
+                bind = connector.open(ldapHost, ldapPort, bindDN, bindPassword, keyStore, true);
+            } else {
+                bind = connector.open(ldapHost, ldapPort, bindDN, bindPassword);
+            }
+
+            if (!bind) {
+                LOG.error("Bind to LDAP server failed.");
+                return null;
+            }
+
+            // update cache parameter
+            int expire = getCacheExpiration(context);
+
+            // verify group
+            String userDn = null;
+            String userGroup = null;
+            userGroup = getParam("ldap_user_group", context);
+            LOG.debug("Checking if the user belongs to the user group: " + userGroup);
+            if (userGroup.length() > 0) {
+                // get members
+                Map list = null;
+
+                synchronized (cache) {
+                    list = (Map) cache.getFromCache(userGroup, expire);
+                }
+                if (list == null) {
+                    list = new HashMap();
+
+                    LOG.debug("Retrieving Members of the group: " + userGroup);
+                    connector.getGroupMembers(userGroup, uidattr, list, new ArrayList());
+                    synchronized (cache) {
+                        cache.putInCache(userGroup, list);
+                    }
+                }
+
+                // check if user is in the list
+                userDn = findInGroup(ldapUserName, list, "cn");
+                LOG.debug("Found user dn in user group:" + userDn);
+
+                // if a usergroup is specified THEN the user MUST be in the group to validate in
+                // LDAP
+                if (userDn == null) {
+                    if (LOG.isDebugEnabled()) {
+                        LOG.debug("LDAP authentication failed: user not in LDAP user group");
+                    }
+
+                    // no valid LDAP user from the group
+                    return null;
+                }
+            }
+
+            if (userDn == null) {
+                // get DN from existing XWiki user
+                userDn = getLDAPDN(ldapUserName, context);
+                LOG.debug("Found user dn with the user object: " + userDn);
+            }
+
+            // parse field mappings
+            List attributes = null;
+            Map mappings = new HashMap();
+            List attrList = new ArrayList();
+
+            String ldapFieldMapping = getParam("ldap_fields_mapping", context);
+
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Ready to create user from LDAP with fields " + ldapFieldMapping);
+            }
+
+            if (ldapFieldMapping != null && ldapFieldMapping.length() > 0) {
+                String[] fields = ldapFieldMapping.split(",");
+
+                for (int j = 0; j < fields.length; j++) {
+                    String[] field = fields[j].split("=");
+                    if (2 == field.length) {
+                        String xwikiattr = field[0].replace(" ", "");
+                        String ldapattr = field[1].replace(" ", "");
+                        mappings.put(ldapattr, xwikiattr);
+                        attrList.add(ldapattr);
+                        LOG.debug("Found field mapping: " + xwikiattr + "=" + ldapattr);
+                    } else {
+                        LOG.error("Error parsing ldap_fields_mapping attribute in xwiki.cfg: "
+                            + fields[j]);
+                    }
+                }
+            }
+
+            // if we still don't have a dn, search for it. Also get the attributes, we might need
+            // them
+            if (userDn == null) {
+                // search for the user in LDAP
+                String query = "(" + uidattr + "=" + ldapUserName + ")";
+                String baseDN = getParam("ldap_base_DN", context);
+
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("Searching for the user in LDAP: user:" + ldapUserName + " base:"
+                        + baseDN + " query:" + query + " uid:" + uidattr);
+                }
+
+                String[] attrl = null;
+                int lsize = attrList.size();
+                if (lsize > 0) {
+                    attrl = (String[]) attrList.toArray(new String[lsize]);
+                }
+
+                attributes = connector.searchLDAP(baseDN, query, attrl, LDAPConnection.SCOPE_SUB);
+
+                for (Iterator it = attributes.iterator(); it.hasNext();) {
+                    XWikiLDAPSearchAttributes sa = (XWikiLDAPSearchAttributes) it.next();
+
+                    if ("dn".equals(sa.attrName)) {
+                        userDn = sa.attrValue;
+                        break;
+                    }
+                }
+
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("Found Attributes in LDAP " + attributes.size());
+                    LOG.debug("Found user dn=" + userDn);
+                }
+            }
+
+            // NOW verify password
+            if ("1".equals(getParam("ldap_validate_password", context))) {
+                if (!connector.verifyPWD(userDn, password)) {
+                    if (LOG.isDebugEnabled()) {
+                        LOG
+                            .debug("LDAP authentication failed: could not validate the password: wrong password for "
+                                + userDn);
+                    }
+
+                    return null;
+                }
+            } else {
+                LOG.debug("Password is already supposed to be verified when bound to LDAP");
+            }
+
+            // default, don't update the user with attributes
+            boolean updateUser = false;
+
+            // check if we have to create the user
+            String xwikiuser = findUser(ldapUserName, context);
+
+            String upduser = getParam("ldap_update_user", context);
+            if (upduser.equals("1")) {
+                updateUser = true;
+            }
+
+            boolean createuser = false;
+            if (xwikiuser == null || updateUser) {
+                LOG.debug("LDAP attributes will be used to update XWiki attributes.");
+
+                // get attributes from LDAP if we don't already have them
+                if (attributes == null) {
+
+                    String[] attrl = null;
+                    int lsize = attrList.size();
+                    if (lsize > 0) {
+                        attrl = (String[]) attrList.toArray(new String[lsize]);
+                    }
+
+                    // didn't get attributes before, so do it now
+                    attributes =
+                        connector.searchLDAP(userDn, null, attrl, LDAPConnection.SCOPE_BASE);
+
+                    if (LOG.isDebugEnabled()) {
+                        LOG.debug("Found Attributes in LDAP " + attributes.size());
+                    }
+                }
+
+                // create user
+                if (xwikiuser == null) {
+                    LOG.debug("Creating new XWiki user based on LDAP attribues located at "
+                        + userDn);
+                    createUserFromLDAP(ldapUserName, mappings, attributes, context);
+                    LOG.debug("New XWiki user created: " + ldapUserName);
+                    createuser = true;
+                } else {
+                    LOG.debug("Updating existing user with LDAP attribues located at " + userDn);
+                    updateUserFromLDAP(ldapUserName, mappings, attributes, context);
+                }
+            }
+
+            // from now on we can enter the application
+            result = getUserPrincipal(ldapUserName, context);
+            if (result == null) {
+                LOG.debug("Could not create authenticated principal.");
+                return null;
+            }
+
+            Map groupmappings = new HashMap();
+            int pos = 1;
+            String grouplistmapping = getParam("ldap_group_mapping_" + pos, context);
+            while (grouplistmapping != null && grouplistmapping.length() > 0) {
+                // first =
+                int splitt = grouplistmapping.indexOf('=');
+                if (splitt < 1) {
+                    LOG.error("Error parsing ldap_group_mapping attribute in xwiki.cfg: "
+                        + grouplistmapping);
+                } else {
+                    String xwikigroup = grouplistmapping.substring(0, splitt);
+                    String ldapgroup = grouplistmapping.substring(splitt + 1);
+                    groupmappings.put(ldapgroup, xwikigroup);
+                    LOG.debug("Groupmapping found: " + xwikigroup + "=" + ldapgroup);
+                }
+                pos++;
+                grouplistmapping = getParam("ldap_group_mapping_" + pos, context);
+            }
+
+            if (groupmappings.size() > 0) {
+                // got valid group mappings
+                // update group membership, join and remove from given groups
+                // sync group membership for this user
+
+                // flag if always sync or just on create of the user
+                String syncmode = getParam("ldap_mode_group_sync", context);
+                if ((syncmode.equalsIgnoreCase("create") && createuser)
+                    || syncmode.equalsIgnoreCase("always")) {
+
+                    LOG.debug("Updating group membership for the user: " + ldapUserName);
+
+                    // ASSUMING the implementation still returns the actual list. In this case
+                    // manipulations to the list are for real.
+                    // get the list of groups the user already belongs to
+                    java.util.Collection c =
+                        context.getWiki().getGroupService(context).getAllGroupsNamesForMember(
+                            ldapUserName, 0, 0, context);
+
+                    if (LOG.isDebugEnabled()) {
+                        LOG.debug("The user belongs to following XWiki groups: ");
+                        for (Iterator it = c.iterator(); it.hasNext();) {
+                            LOG.debug(it.next().toString());
+                        }
+                    }
+
+                    // retrieve list of all groups
+                    List allxwikigroups =
+                        context.getWiki().getGroupService(context).getAllMatchedGroups(null,
+                            false, 0, 0, null, context);
+
+                    if (LOG.isDebugEnabled()) {
+                        LOG.debug("All defined XWiki groups: ");
+                        for (Iterator it = allxwikigroups.iterator(); it.hasNext();) {
+                            LOG.debug(it.next().toString());
+                        }
+                    }
+
+                    // go through mapped groups to locate the user
+                    for (Iterator itGroupMap = groupmappings.entrySet().iterator(); itGroupMap
+                        .hasNext();) {
+                        Map.Entry entry = (Map.Entry) itGroupMap.next();
+                        String groupname = (String) entry.getKey();
+                        String xwikigroupname = (String) entry.getValue();
+
+                        // check if group is in list of all groups
+                        if (!allxwikigroups.contains(xwikigroupname)) {
+                            LOG.error("XWiki group not found:" + xwikigroupname);
+                            continue;
+                        }
+                        Map map = null;
+                        synchronized (cache) {
+                            map = (Map) cache.getFromCache(groupname, expire);
+                        }
+                        if (map == null) {
+                            map = new HashMap();
+                            if (connector.getGroupMembers(groupname, uidattr, map,
+                                new ArrayList())) {
+                                synchronized (cache) {
+                                    cache.putInCache(groupname, map);
+                                }
+                            }
+                        }
+
+                        if (map.containsKey(userDn)) {
+                            // add to group if not there
+                            if (!c.contains(xwikigroupname)) {
+                                if (LOG.isDebugEnabled()) {
+                                    LOG.debug("Adding user " + ldapUserName + " to xwiki group "
+                                        + xwikigroupname);
+                                }
+
+                                addUserToGroup(ldapUserName, xwikigroupname, context);
+                                // c.add(xwikigroupname);
+                                LOG.debug("Finished adding user " + ldapUserName
+                                    + " to xwiki group " + xwikigroupname);
+                                // is supposed to equivalent to
+                                // context.getWiki().getGroupService(context).addUserToGroup(user,
+                                // context.getDatabase(), xwikigroupname, context);
+                            }
+                        } else {
+                            // remove from group if there
+                            if (c.contains(xwikigroupname)) {
+                                removeUserFromGroup(ldapUserName, xwikigroupname, context);
+                                // c.remove(xwikigroupname);
+                                LOG.debug("Finished removing xwiki group " + xwikigroupname
+                                    + " from user " + ldapUserName);
+                            }
+                        }
+                    }
+                    /*
+                     * This may not be necessary, but the group service does have a cache, and I've
+                     * found that adding or removing entries sometimes don't take effect if I don't
+                     * do this.
+                     */
+                    context.getWiki().getGroupService(context).flushCache();
+                }
+            }
+
+        } catch (Exception e) {
+            LOG.error("LDAP authentication failed.", e);
+        } finally {
+            connector.close();
+
+            // one last option
+            if (result == null && context != null) {
+
+                String trylocal = getParam("ldap_trylocal", context);
+                if (trylocal.length() > 0 && "1".equals(trylocal)) {
+
+                    LOG.debug("Trying authentication against XWiki DB");
+                    // try local user
+
+                    Principal principal = null;
+                    // Use XWiki password
+                    if (checkPassword(findUser(ldapUserName, context), password, context)) {
+                        principal = getUserPrincipal(ldapUserName, context);
+                        LOG.debug("XWiki DB login succeeded");
+                    } else {
+                        context.put("message", "loginfailed");
+                        LOG.debug("XWiki DB login failed");
+                    }
+                    // overwrite the result!
+                    return principal;
+                }
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Retrieve the time in ms until a entry in the cache is to expire - helper.
+     */
+    protected int getCacheExpiration(XWikiContext context)
+    {
+        try {
+            return context.getWiki().getXWikiPreferenceAsInt("ldap_groupcache_expiration",
+                context);
+        } catch (Exception e) {
+            return (int) context.getWiki().ParamAsLong(
+                "xwiki.authentication.ldap.groupcache_expiration", 21800);
+        }
+    }
+
+    protected void addUserToGroup(String userName, String groupName, XWikiContext context)
+    {
+        try {
+            String fullWikiUserName = "XWiki." + userName;
+
+            String groupClassName = context.getWiki().getGroupClass(context).getName();
+
+            // Get document representing group
+            XWikiDocument groupDoc = context.getWiki().getDocument(groupName, context);
+
+            // Add a member object to document
+            BaseObject memberobj = groupDoc.newObject(groupClassName, context);
+            memberobj.setStringValue("member", fullWikiUserName);
+
+            // Save modifications
+            context.getWiki().saveDocument(groupDoc, context);
+
+            // Update group cache
+            XWikiGroupService gservice = context.getWiki().getGroupService(context);
+            gservice.addUserToGroup(fullWikiUserName, context.getDatabase(), groupName, context);
+
+        } catch (Exception e) {
+            LOG.error(String.format("Failed to add a user [{0}] to a group [{1}]", new String[] {
+            userName, groupName}), e);
+        }
+    }
+
+    protected void removeUserFromGroup(String userName, String groupName, XWikiContext context)
+    {
+        try {
+            String fullWikiUserName = "XWiki." + userName;
+
+            String groupClassName = context.getWiki().getGroupClass(context).getName();
+
+            // Get the XWiki document holding the objects comprising the group membership list
+            XWikiDocument groupDoc = context.getWiki().getDocument(groupName, context);
+
+            // Get and remove the specific group membership object for the user
+            BaseObject groupObj = groupDoc.getObject(groupClassName, "member", fullWikiUserName);
+            groupDoc.removeObject(groupObj);
+
+            // Save modifications
+            context.getWiki().saveDocument(groupDoc, context);
+
+            // XWikiGroupService gservice = context.getWiki().getGroupService(context);
+            // gservice.addUserToGroup(fullwikiname, context.getDatabase(), groupname, context);
+
+        } catch (Exception e) {
+            LOG.error(
+                "Failed to remove a user from a group " + userName + " group: " + groupName, e);
+        }
+    }
+
+    protected SimplePrincipal checkSuperadmin(String username, String password,
+        XWikiContext context)
+    {
+        String superadmin = "superadmin";
+        if (username.equals(superadmin)) {
+            String superadminpassword = context.getWiki().Param("xwiki.superadminpassword");
+            if ((superadminpassword != null) && (superadminpassword.equals(password))) {
+                SimplePrincipal principal = new SimplePrincipal("XWiki.superadmin");
+                return principal;
+            } else {
+                return null;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * First try to retrieve value from XWiki Preferences and then from xwiki.cfg Syntax ldap_<name>
+     * (for XWiki Preferences) will be changed to ldap.<name> for xwiki.cfg
+     */
+    protected String getParam(String name, XWikiContext context)
+    {
+        String param = "";
+        try {
+            param = context.getWiki().getXWikiPreference(name, context);
+        } catch (Exception e) {
+            // ignore
+        }
+        if (param == null || "".equals(param)) {
+            try {
+                param =
+                    context.getWiki().Param(
+                        "xwiki.authentication." + StringUtils.replace(name, "ldap_", "ldap."));
+            } catch (Exception e) {
+                // ignore
+            }
+        }
+
+        if (param == null) {
+            param = "";
+        }
+
+        return param;
+    }
+
+    /**
+     * Retrieve the LDAP port - helper.
+     * 
+     * @param context
+     * @return
+     */
+    protected int getLDAPPort(XWikiContext context)
+    {
+        try {
+            return context.getWiki().getXWikiPreferenceAsInt("ldap_port", context);
+        } catch (Exception e) {
+            return (int) context.getWiki().ParamAsLong("xwiki.authentication.ldap.port", 0);
+        }
+    }
+
+    protected Principal getUserPrincipal(String susername, XWikiContext context)
+    {
+        Principal principal = null;
+
+        // First we check in the local database
+        String user = null;
+        try {
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Finding user " + susername);
+            }
+            user = findUser(susername, context);
+            if (user != null) {
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("Found user " + susername);
+                }
+                principal = new SimplePrincipal(user);
+            }
+        } catch (Exception e) {
+            LOG.debug("Failed creating a Principal for user " + user, e);
+        }
+
+        if (context.isVirtual()) {
+            if (principal == null) {
+                // Then we check in the main database
+                String db = context.getDatabase();
+                try {
+                    context.setDatabase(context.getWiki().getDatabase());
+                    try {
+                        user = findUser(susername, context);
+                        if (user != null) {
+                            principal = new SimplePrincipal(context.getDatabase() + ":" + user);
+                        }
+                    } catch (Exception e) {
+                        LOG.debug("Failed creating a Principal for user " + user, e);
+                    }
+                } finally {
+                    context.setDatabase(db);
+                }
+            }
+        }
+        return principal;
+    }
+
+    /**
+     * Sets attributes on the user object based on attribute values provided by the LDAP.
+     * 
+     * @param user
+     * @param mappings
+     * @param attributes
+     * @param context
+     * @throws XWikiException
+     */
+    protected void updateUserFromLDAP(String user, Map mappings, List attributes,
+        XWikiContext context) throws XWikiException
+    {
+        BaseClass bclass = context.getWiki().getUserClass(context);
+
+        String fullwikiname = "XWiki." + user;
+        XWikiDocument doc = context.getWiki().getDocument(fullwikiname, context);
+        BaseObject bobj = doc.getObject(bclass.getName());
+
+        for (Iterator it = attributes.iterator(); it.hasNext();) {
+            XWikiLDAPSearchAttributes lattr = (XWikiLDAPSearchAttributes) it.next();
+
+            String lval = lattr.attrValue;
+            String xattr = (String) mappings.get(lattr.attrName);
+
+            if (xattr == null) {
+                continue;
+            }
+
+            if (xattr.equals("name")) {
+                // ignore
+                LOG.debug("Ignoring a mapping for name since the name is already set to " + user);
+            } else {
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("Setting XWiki field " + xattr + " (from LDAP attribute: "
+                        + lattr.attrName + ") Value:" + lval);
+                }
+                bobj.setStringValue(xattr, lval);
+            }
+        }
+
+        context.setDoc(doc);
+        if (doc.isNew()) {
+            context.getWiki().saveDocument(doc, context);
+        } else {
+            context.getWiki().getHibernateStore().saveXWikiObject(bobj, context, true);
+        }
+    }
+
+    /**
+     * Create an XWiki user and set all mapped attributes from LDAP to XWiki attributes.
+     * 
+     * @param mappings
+     * @param attributes
+     * @param context
+     * @throws XWikiException
+     */
+    protected void createUserFromLDAP(String user, Map mappings, List attributes,
+        XWikiContext context) throws XWikiException
+    {
+
+        Map map = new HashMap();
+        for (Iterator it = attributes.iterator(); it.hasNext();) {
+            XWikiLDAPSearchAttributes lattr = (XWikiLDAPSearchAttributes) it.next();
+
+            String lval = lattr.attrValue;
+            String xattr = (String) mappings.get(lattr.attrName);
+            if (xattr == null) {
+                continue;
+            }
+            map.put(xattr, lval);
+            LOG.debug("Mapping " + xattr + " to " + lval);
+        }
+        // Mark user active
+        map.put("active", "1");
+
+        LOG.debug("Creating User: " + user);
+        context.getWiki().createUser(user, map, "XWiki.XWikiUsers",
+            "#includeForm(\"XWiki.XWikiUserTemplate\")", "edit", context);
+        /*
+         * BaseClass bclass = context.getWiki().getUserClass(context); BaseObject bobj = new
+         * BaseObject(); log.debug("Userclass objekt name: " + bclass.getName());
+         * bobj.setClassName(bclass.getName()); String fullwikiname = "XWiki." + user;
+         * bobj.setName(fullwikiname); for(SearchAttributes lattr : attributes){ String lval =
+         * lattr.attrValue; String xattr = mappings.get(lattr.attrName); if(xattr == null) continue;
+         * if (xattr.equals("name")) { // ignore log.debug("Ignoring a mapping for name since the
+         * name is already set to " + user); } else { if(log.isDebugEnabled()) log.debug("Setting
+         * XWiki field " + xattr + " (from LDAP attribute: " + lattr.attrName + ") Value:" + lval);
+         * bobj.setStringValue(xattr, lval); } } XWikiDocument doc =
+         * context.getWiki().getDocument(fullwikiname, context); doc.setParent("");
+         * doc.addObject(bclass.getName(), bobj);
+         * doc.setContent("#includeForm(\"XWiki.XWikiUserTemplate\")");
+         * context.getWiki().protectUserPage(fullwikiname, "edit", doc, context);
+         * context.setDoc(doc); context.getWiki().saveDocument(doc, context);
+         * context.getWiki().setUserDefaultGroup(fullwikiname, context); log.debug("XWiki User
+         * created:" + fullwikiname);
+         */
+    }
+
+    /**
+     * Locates the user in the Map: either the user is a value or the key starts with the LDAP
+     * syntax.
+     * 
+     * @param user
+     * @param map
+     * @param uid
+     * @return
+     */
+    protected String findInGroup(String user, Map map, String uid)
+    {
+        String result = null;
+        String ldapuser = null;
+        if (uid == null || uid.length() == 0) {
+            uid = "cn";
+        }
+        ldapuser = uid + "=" + user.toLowerCase();
+
+        for (Iterator it = map.keySet().iterator(); it.hasNext();) {
+            String u = (String) it.next();
+            // implementing it case-insensitive for now
+            if (user.equalsIgnoreCase((String) map.get(u))
+                || u.toLowerCase().startsWith(ldapuser)) {
+                return u;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Tries to retrieve the DN from the users object.
+     * 
+     * @param susername
+     * @param context
+     * @return
+     */
+    protected String getLDAPDN(String susername, XWikiContext context)
+    {
+        String dn = null;
+
+        if (context != null) {
+            // First we check in the local database
+            try {
+                String user = findUser(susername, context);
+                if (user != null && user.length() != 0) {
+                    dn = readLDAPDN(user, context);
+                }
+            } catch (Exception e) {
+                LOG.debug("Faild finding LDAP DN stored in the user object.", e);
+                // ignore
+            }
+
+            if (context.isVirtual()) {
+                if (dn == null || dn.length() == 0) {
+                    // Then we check in the main database
+                    String db = context.getDatabase();
+                    try {
+                        context.setDatabase(context.getWiki().getDatabase());
+                        try {
+                            String user = findUser(susername, context);
+                            if (user != null && user.length() != 0) {
+                                dn = readLDAPDN(user, context);
+                            }
+                        } catch (Exception e) {
+                            LOG.debug(
+                                "Faild finding LDAP DN stored in the user object (virtual).", e);
+                            // ignore
+                        }
+                    } finally {
+                        context.setDatabase(db);
+                    }
+                }
+            }
+        }
+        return dn;
+    }
+
+    protected String readLDAPDN(String username, XWikiContext context)
+    {
+        String dn = null;
+
+        try {
+            XWikiDocument doc = context.getWiki().getDocument(username, context);
+            // We only allow empty password from users having a XWikiUsers object.
+            if (doc.getObject("XWiki.XWikiUsers") != null) {
+                dn = doc.getStringValue("XWiki.XWikiUsers", "ldap_dn");
+            }
+
+        } catch (Throwable e) {
+            LOG.debug("Retrieving the ldap_dn attribute from the XWikiUsers object failed.", e);
+        }
+
+        return dn;
+    }
+}

Modification de propriétés sur src/main/java/com/xpn/xwiki/user/impl/LDAP/XWikiLDAPAuthServiceImpl.java
___________________________________________________________________
Nom : svn:eol-style
   + native

Index: src/main/java/com/xpn/xwiki/user/impl/LDAP/XWikiLDAPSearchAttributes.java
===================================================================
--- src/main/java/com/xpn/xwiki/user/impl/LDAP/XWikiLDAPSearchAttributes.java	(révision 0)
+++ src/main/java/com/xpn/xwiki/user/impl/LDAP/XWikiLDAPSearchAttributes.java	(révision 0)
@@ -0,0 +1,31 @@
+package com.xpn.xwiki.user.impl.LDAP;
+
+/**
+ * Represent an LDAP attribute.
+ * 
+ * @version $Id: $
+ */
+public class XWikiLDAPSearchAttributes
+{
+    /**
+     * Attribute name.
+     */
+    public String attrName;
+
+    /**
+     * Attribute value.
+     */
+    public String attrValue;
+
+    /**
+     * Create attribute instance.
+     * 
+     * @param name attribute name.
+     * @param value attribute value.
+     */
+    public XWikiLDAPSearchAttributes(String name, String value)
+    {
+        attrName = name;
+        attrValue = value;
+    }
+}

Modification de propriétés sur src/main/java/com/xpn/xwiki/user/impl/LDAP/XWikiLDAPSearchAttributes.java
___________________________________________________________________
Nom : svn:eol-style
   + native

Index: src/main/java/com/xpn/xwiki/user/impl/LDAP/XWikiLDAPConnection.java
===================================================================
--- src/main/java/com/xpn/xwiki/user/impl/LDAP/XWikiLDAPConnection.java	(révision 0)
+++ src/main/java/com/xpn/xwiki/user/impl/LDAP/XWikiLDAPConnection.java	(révision 0)
@@ -0,0 +1,306 @@
+/*
+ * See the NOTICE file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ *
+ */
+
+package com.xpn.xwiki.user.impl.LDAP;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.io.UnsupportedEncodingException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.novell.ldap.LDAPAttribute;
+import com.novell.ldap.LDAPAttributeSet;
+import com.novell.ldap.LDAPConnection;
+import com.novell.ldap.LDAPEntry;
+import com.novell.ldap.LDAPException;
+import com.novell.ldap.LDAPJSSESecureSocketFactory;
+import com.novell.ldap.LDAPSearchConstraints;
+import com.novell.ldap.LDAPSearchResults;
+import com.novell.ldap.LDAPSocketFactory;
+import java.security.Security;
+
+/**
+ * LDAP communication tool.
+ * 
+ * @version $Id: $
+ */
+public class XWikiLDAPConnection
+{
+    /**
+     * Logging tool.
+     */
+    private static final Log LOG = LogFactory.getLog(LDAPAuthenticaterTest.class);
+
+    private LDAPConnection connection;
+    
+    public boolean open(String ldapHost, int ldapPort, String loginDN, String password)
+    {
+        return open(ldapHost, ldapPort, loginDN, password, null, false);
+    }
+
+    public boolean open(String ldapHost, int ldapPort, String loginDN,
+        String password, String pathToKeys, boolean ssl)
+    {
+        int port = ldapPort;
+
+        if (port <= 0) {
+            port = ssl ? LDAPConnection.DEFAULT_SSL_PORT : LDAPConnection.DEFAULT_PORT;
+        }
+
+        int ldapVersion = LDAPConnection.LDAP_V3;
+
+        try {
+            if (ssl) {
+                // Dynamically set JSSE as a security provider
+                Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
+
+                if (pathToKeys != null && pathToKeys.length() > 0) {
+                    // Dynamically set the property that JSSE uses to identify
+                    // the keystore that holds trusted root certificates
+
+                    System.setProperty("javax.net.ssl.trustStore", pathToKeys);
+                    // obviously unnecessary: sun default pwd = "changeit"
+                    // System.setProperty("javax.net.ssl.trustStorePassword", sslpwd);
+                }
+
+                LDAPSocketFactory ssf = new LDAPJSSESecureSocketFactory();
+
+                // Set the socket factory as the default for all future connections
+                // LDAPConnection.setSocketFactory(ssf);
+
+                // Note: the socket factory can also be passed in as a parameter
+                // to the constructor to set it for this connection only.
+                this.connection = new LDAPConnection(ssf);
+            } else {
+                this.connection = new LDAPConnection();
+            }
+
+            // connect to the server
+            this.connection.connect(ldapHost, port);
+
+            // authenticate to the server
+            this.connection.bind(ldapVersion, loginDN, password.getBytes("UTF8"));
+
+            if (!this.connection.isConnected() || !this.connection.isConnectionAlive()) {
+                if (ssl) {
+                    LOG
+                        .error("Connection to LDAP through SSL failed. Beyond other causes, verify that the SSL certificate is found in the keystore.");
+                } else {
+                    LOG.error("Connection to LDAP failed.");
+                }
+
+                return false;
+            }
+
+            if (!this.connection.isBound()) {
+                LOG.debug("Binding to LDAP failed, - ok if Annonymous Access");
+            }
+
+            return true;
+        } catch (UnsupportedEncodingException e) {
+            LOG.error("LDAP bind failed.", e);
+        } catch (LDAPException e) {
+            LOG.error("LDAP bind failed.", e);
+        }
+
+        return false;
+    }
+
+    /**
+     * Close LDAP connection.
+     */
+    public void close()
+    {
+        try {
+            if (this.connection != null) {
+                this.connection.disconnect();
+            }
+        } catch (LDAPException e) {
+            LOG.debug("LDAP close failed.", e);
+        }
+    }
+
+    public boolean verifyPWD(String user_dn, String pwd)
+    {
+        try {
+            LDAPAttribute attr = new LDAPAttribute("userPassword", pwd);
+            return this.connection.compare(user_dn, attr);
+        } catch (LDAPException e) {
+            if (e.getResultCode() == LDAPException.NO_SUCH_OBJECT) {
+                LOG.error("Unable to locate user_dn:" + user_dn, e);
+            } else if (e.getResultCode() == LDAPException.NO_SUCH_ATTRIBUTE) {
+                LOG.error("Unable to verify password because userPassword attribute not found.",
+                    e);
+            } else {
+                LOG.error("Unable to verify password", e);
+            }
+        }
+        return false;
+    }
+
+    // LDAPConnection.SCOPE_SUB oder LDAPConnection.SCOPE_BASE
+    protected List searchLDAP(String baseDN, String query, String[] attr,
+        int ldap_scope)
+    {
+        List attributes = new ArrayList();
+
+        try {
+            LDAPSearchConstraints cons = new LDAPSearchConstraints();
+            cons.setTimeLimit(1000);
+
+            // filter return all attributes return attrs and values time out value
+            LDAPSearchResults searchResults =
+                this.connection.search(baseDN, ldap_scope, query, attr, false, cons);
+
+            if (!searchResults.hasMore()) {
+                return null;
+            }
+
+            LDAPEntry nextEntry = searchResults.next();
+            String foundDN = nextEntry.getDN();
+            attributes.add(new XWikiLDAPSearchAttributes("dn", foundDN));
+
+            LDAPAttributeSet attributeSet = nextEntry.getAttributeSet();
+            Iterator allAttributes = attributeSet.iterator();
+
+            while (allAttributes.hasNext()) {
+                LDAPAttribute attribute = (LDAPAttribute) allAttributes.next();
+                String attributeName = attribute.getName();
+
+                Enumeration allValues = attribute.getStringValues();
+
+                if (allValues != null) {
+                    while (allValues.hasMoreElements()) {
+
+                        String value = (String) allValues.nextElement();
+                        attributes.add(new XWikiLDAPSearchAttributes(attributeName, value));
+                    }
+                }
+            }
+        } catch (LDAPException e) {
+            LOG.debug("LDAP Search failed", e);
+            return null;
+        }
+        
+        return attributes;
+    }
+
+    /**
+     * Get all members of a given group based on the groupDN. If the group contains subgroups get
+     * these members as well. Retrieve an identifier for each member.
+     * 
+     * @param groupDN The group to retrieve the members of and scan for subgroups.
+     * @param uidattr The attribute containing the identifier for a user (default=cn).
+     * @param members is the result: maps dn to member id.
+     * @param subgroups returns all the subgroubs identified.
+     * @return whether the groupDN is actually a group.
+     */
+    public boolean getGroupMembers(String groupDN, String uidattr,
+        Map members, List subgroups)
+    {
+        // break out if there is a look of groups
+        if (subgroups != null && subgroups.contains(groupDN)) {
+            return true;
+        }
+
+        if (uidattr == null || uidattr.length() == 0) {
+            uidattr = "cn";
+        }
+
+        String[] attrs = new String[] {"objectClass", uidattr, "member"};
+
+        List list = searchLDAP(groupDN, null, attrs, LDAPConnection.SCOPE_BASE);
+
+        if (list == null) {
+            LOG.debug("Failed locating group " + groupDN);
+            return false;
+        }
+
+        boolean isGroup = false;
+        String id = null;
+
+        for (Iterator it = list.iterator(); it.hasNext();) {
+            XWikiLDAPSearchAttributes sa = (XWikiLDAPSearchAttributes) it.next();
+
+            String key = sa.attrName;
+            if (key.equalsIgnoreCase("objectClass")) {
+
+                String objectName = sa.attrValue;
+                if (objectName.equalsIgnoreCase("group")
+                    || objectName.equalsIgnoreCase("groupOfNames")
+                    || objectName.equalsIgnoreCase("groupOfUniqueNames")
+                    || objectName.equalsIgnoreCase("dynamicGroup")
+                    || objectName.equalsIgnoreCase("dynamicGroupAux")
+                    || objectName.equalsIgnoreCase("groupWiseDistributionList")) {
+                    isGroup = true;
+                }
+            }
+
+            if (key.equalsIgnoreCase(uidattr)) {
+                id = sa.attrValue;
+            }
+        }
+
+        if (!isGroup) {
+            // not a group but actually a member
+            LOG.debug("This object is NOT a group object: " + groupDN);
+
+            // add all new members
+            if (id == null) {
+                LOG.error("Could not find attribute " + uidattr + " for LDAP dn " + groupDN);
+                id = "";
+            }
+
+            if (!members.containsKey(groupDN)) {
+                members.put(groupDN, id);
+            }
+
+            return false;
+        }
+
+        // remember this group
+        if (subgroups != null) {
+            subgroups.add(groupDN);
+        }
+
+        for (Iterator it = list.iterator(); it.hasNext();) {
+            XWikiLDAPSearchAttributes sa = (XWikiLDAPSearchAttributes) it.next();
+
+            String key = sa.attrName;
+            if (key.equalsIgnoreCase("member") || key.equalsIgnoreCase("uniqueMember")) {
+
+                // or subgroup
+                String member = sa.attrValue;
+
+                // we check for subgroups recursive call to scan all subgroups and identify members
+                // and their uid
+                getGroupMembers(member, uidattr, members, subgroups);
+            }
+        }
+
+        return true;
+    }
+}

Modification de propriétés sur src/main/java/com/xpn/xwiki/user/impl/LDAP/XWikiLDAPConnection.java
___________________________________________________________________
Nom : svn:eol-style
   + native

