Improve user management
authorMathieu Baudier <mbaudier@argeo.org>
Thu, 18 Aug 2022 07:26:10 +0000 (09:26 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Thu, 18 Aug 2022 07:26:10 +0000 (09:26 +0200)
22 files changed:
org.argeo.cms/src/org/argeo/cms/CmsUserManager.java
org.argeo.cms/src/org/argeo/cms/auth/CmsRole.java
org.argeo.cms/src/org/argeo/cms/internal/auth/CmsUserManagerImpl.java
org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsUserAdmin.java
org.argeo.util/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java
org.argeo.util/src/org/argeo/osgi/useradmin/DirectoryUserAdmin.java
org.argeo.util/src/org/argeo/osgi/useradmin/LdifGroup.java
org.argeo.util/src/org/argeo/osgi/useradmin/LdifUser.java
org.argeo.util/src/org/argeo/osgi/useradmin/OsUserDirectory.java
org.argeo.util/src/org/argeo/util/directory/Directory.java
org.argeo.util/src/org/argeo/util/directory/HierarchyUnit.java
org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapDirectory.java
org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapDirectoryDao.java
org.argeo.util/src/org/argeo/util/directory/ldap/DefaultLdapEntry.java
org.argeo.util/src/org/argeo/util/directory/ldap/LdapDao.java
org.argeo.util/src/org/argeo/util/directory/ldap/LdapDirectoryDao.java
org.argeo.util/src/org/argeo/util/directory/ldap/LdapEntry.java
org.argeo.util/src/org/argeo/util/directory/ldap/LdapHierarchyUnit.java
org.argeo.util/src/org/argeo/util/directory/ldap/LdapNameUtils.java
org.argeo.util/src/org/argeo/util/directory/ldap/LdifDao.java
org.argeo.util/src/org/argeo/util/transaction/WorkingCopyXaResource.java
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java

index 040138878603fc1f0cade62ef1cd00be7d58a471..4017c7ebf3b56a18f8bffca4ec0888cb9ffa9734 100644 (file)
@@ -7,9 +7,11 @@ import java.util.Set;
 
 import javax.security.auth.Subject;
 
+import org.argeo.cms.auth.SystemRole;
 import org.argeo.osgi.useradmin.UserDirectory;
 import org.argeo.util.directory.HierarchyUnit;
 import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.useradmin.Group;
 import org.osgi.service.useradmin.Role;
 import org.osgi.service.useradmin.User;
 
@@ -55,9 +57,21 @@ public interface CmsUserManager {
        /*
         * EDITION
         */
-       /** Creates a new user.*/
+       /** Creates a new user. */
        User createUser(String username, Map<String, Object> properties, Map<String, Object> credentials);
 
+       /** Creates a group. */
+       Group getOrCreateGroup(HierarchyUnit groups, String commonName);
+
+       /** Creates a new system role. */
+       Group getOrCreateSystemRole(HierarchyUnit roles, SystemRole systemRole);
+
+       /** Add additional object classes to this role. */
+       void addObjectClasses(Role role, Set<String> objectClasses, Map<String, Object> additionalProperties);
+
+       /** Add a member to this group. */
+       void addMember(Group group, Role role);
+
        /* MISCELLANEOUS */
        /** Returns the dn of a role given its local ID */
        String buildDefaultDN(String localId, int type);
@@ -92,5 +106,5 @@ public interface CmsUserManager {
        UserDirectory getDirectory(Role role);
 
        /** Create a new hierarchy unit. Does nothing if it already exists. */
-       HierarchyUnit createHierarchyUnit(UserDirectory directory, String path);
+       HierarchyUnit getOrCreateHierarchyUnit(UserDirectory directory, String path);
 }
\ No newline at end of file
index af64508f4eb6587b9cf6fb6cc13b1c81efa62ce4..6aa1c1c2561b9b12795f39b91a5e45b54fda519b 100644 (file)
@@ -5,6 +5,7 @@ import javax.xml.namespace.QName;
 import org.argeo.api.acr.ContentName;
 import org.argeo.api.acr.CrName;
 
+/** Standard CMS system roles. */
 public enum CmsRole implements SystemRole {
        userAdmin, //
        groupAdmin;
@@ -17,6 +18,7 @@ public enum CmsRole implements SystemRole {
                name = new ContentName(CrName.ROLE_NAMESPACE_URI, QUALIFIER + name());
        }
 
+       @Override
        public QName getName() {
                return name;
        }
index 36ae6e6d6612d4ea4c673e0077a1017626b5f785..0bf9a211bd35ff5bc31de09f7e165cd0d34ed9e5 100644 (file)
@@ -23,16 +23,19 @@ import javax.naming.InvalidNameException;
 import javax.naming.ldap.LdapName;
 import javax.security.auth.Subject;
 
+import org.argeo.api.acr.NamespaceUtils;
 import org.argeo.api.cms.CmsConstants;
 import org.argeo.api.cms.CmsLog;
 import org.argeo.cms.CmsUserManager;
 import org.argeo.cms.auth.CurrentUser;
+import org.argeo.cms.auth.SystemRole;
 import org.argeo.cms.auth.UserAdminUtils;
 import org.argeo.osgi.useradmin.AggregatingUserAdmin;
 import org.argeo.osgi.useradmin.TokenUtils;
 import org.argeo.osgi.useradmin.UserDirectory;
 import org.argeo.util.directory.DirectoryConf;
 import org.argeo.util.directory.HierarchyUnit;
+import org.argeo.util.directory.ldap.LdapEntry;
 import org.argeo.util.directory.ldap.SharedSecret;
 import org.argeo.util.naming.LdapAttrs;
 import org.argeo.util.naming.NamingUtils;
@@ -237,6 +240,116 @@ public class CmsUserManagerImpl implements CmsUserManager {
                }
        }
 
+       @Override
+       public Group getOrCreateGroup(HierarchyUnit groups, String commonName) {
+               try {
+                       String dn = LdapAttrs.cn.name() + "=" + commonName + "," + groups.getBase();
+                       Group group = (Group) getUserAdmin().getRole(dn);
+                       if (group != null)
+                               return group;
+                       userTransaction.begin();
+                       group = (Group) userAdmin.createRole(dn, Role.GROUP);
+                       userTransaction.commit();
+                       return group;
+               } catch (Exception e) {
+                       try {
+                               userTransaction.rollback();
+                       } catch (Exception e1) {
+                               log.error("Could not roll back", e1);
+                       }
+                       if (e instanceof RuntimeException)
+                               throw (RuntimeException) e;
+                       else
+                               throw new RuntimeException("Cannot create group " + commonName + " in " + groups, e);
+               }
+       }
+
+       @Override
+       public Group getOrCreateSystemRole(HierarchyUnit roles, SystemRole systemRole) {
+               try {
+                       String dn = LdapAttrs.cn.name() + "=" + NamespaceUtils.toPrefixedName(systemRole.getName()) + ","
+                                       + roles.getBase();
+                       Group group = (Group) getUserAdmin().getRole(dn);
+                       if (group != null)
+                               return group;
+                       userTransaction.begin();
+                       group = (Group) userAdmin.createRole(dn, Role.GROUP);
+                       userTransaction.commit();
+                       return group;
+               } catch (Exception e) {
+                       try {
+                               userTransaction.rollback();
+                       } catch (Exception e1) {
+                               log.error("Could not roll back", e1);
+                       }
+                       if (e instanceof RuntimeException)
+                               throw (RuntimeException) e;
+                       else
+                               throw new RuntimeException("Cannot create system role " + systemRole + " in " + roles, e);
+               }
+       }
+
+       @Override
+       public HierarchyUnit getOrCreateHierarchyUnit(UserDirectory directory, String path) {
+               HierarchyUnit hi = directory.getHierarchyUnit(path);
+               if (hi != null)
+                       return hi;
+               try {
+                       userTransaction.begin();
+                       HierarchyUnit hierarchyUnit = directory.createHierarchyUnit(path);
+                       userTransaction.commit();
+                       return hierarchyUnit;
+               } catch (Exception e1) {
+                       try {
+                               if (!userTransaction.isNoTransactionStatus())
+                                       userTransaction.rollback();
+                       } catch (Exception e2) {
+                               if (log.isTraceEnabled())
+                                       log.trace("Cannot rollback transaction", e2);
+                       }
+                       throw new RuntimeException("Cannot create hierarchy unit " + path + " in directory " + directory, e1);
+               }
+       }
+
+       @Override
+       public void addObjectClasses(Role role, Set<String> objectClasses, Map<String, Object> additionalProperties) {
+               try {
+                       userTransaction.begin();
+                       LdapEntry.addObjectClasses(role.getProperties(), objectClasses);
+                       for (String key : additionalProperties.keySet()) {
+                               role.getProperties().put(key, additionalProperties.get(key));
+                       }
+                       userTransaction.commit();
+               } catch (Exception e1) {
+                       try {
+                               if (!userTransaction.isNoTransactionStatus())
+                                       userTransaction.rollback();
+                       } catch (Exception e2) {
+                               if (log.isTraceEnabled())
+                                       log.trace("Cannot rollback transaction", e2);
+                       }
+                       throw new RuntimeException("Cannot add object classes " + objectClasses + " to " + role, e1);
+               }
+       }
+
+       @Override
+       public void addMember(Group group, Role role) {
+               try {
+                       userTransaction.begin();
+                       group.addMember(role);
+                       userTransaction.commit();
+               } catch (Exception e1) {
+                       try {
+                               if (!userTransaction.isNoTransactionStatus())
+                                       userTransaction.rollback();
+                       } catch (Exception e2) {
+                               if (log.isTraceEnabled())
+                                       log.trace("Cannot rollback transaction", e2);
+                       }
+                       throw new RuntimeException("Cannot add object classes " + role + " to group " + group, e1);
+               }
+       }
+
        @Override
        public String getDefaultDomainName() {
                Map<String, String> dns = getKnownBaseDns(true);
@@ -251,7 +364,7 @@ public class CmsUserManagerImpl implements CmsUserManager {
                Map<String, String> dns = new HashMap<String, String>();
                for (UserDirectory userDirectory : userDirectories) {
                        Boolean readOnly = userDirectory.isReadOnly();
-                       String baseDn = userDirectory.getContext();
+                       String baseDn = userDirectory.getBase();
 
                        if (onlyWritable && readOnly)
                                continue;
@@ -266,7 +379,7 @@ public class CmsUserManagerImpl implements CmsUserManager {
        }
 
        public Set<UserDirectory> getUserDirectories() {
-               TreeSet<UserDirectory> res = new TreeSet<>((o1, o2) -> o1.getContext().compareTo(o2.getContext()));
+               TreeSet<UserDirectory> res = new TreeSet<>((o1, o2) -> o1.getBase().compareTo(o2.getBase()));
                res.addAll(userDirectories);
                return res;
        }
@@ -464,8 +577,8 @@ public class CmsUserManagerImpl implements CmsUserManager {
                String name = user.getName();
                NavigableMap<String, UserDirectory> possible = new TreeMap<>();
                for (UserDirectory userDirectory : userDirectories) {
-                       if (name.endsWith(userDirectory.getContext())) {
-                               possible.put(userDirectory.getContext(), userDirectory);
+                       if (name.endsWith(userDirectory.getBase())) {
+                               possible.put(userDirectory.getBase(), userDirectory);
                        }
                }
                if (possible.size() == 0)
@@ -473,27 +586,6 @@ public class CmsUserManagerImpl implements CmsUserManager {
                return possible.lastEntry().getValue();
        }
 
-       public HierarchyUnit createHierarchyUnit(UserDirectory directory, String path) {
-               HierarchyUnit hi = directory.getHierarchyUnit(path);
-               if (hi != null)
-                       return hi;
-               try {
-                       userTransaction.begin();
-                       HierarchyUnit hierarchyUnit = directory.createHierarchyUnit(path);
-                       userTransaction.commit();
-                       return hierarchyUnit;
-               } catch (Exception e1) {
-                       try {
-                               if (!userTransaction.isNoTransactionStatus())
-                                       userTransaction.rollback();
-                       } catch (Exception e2) {
-                               if (log.isTraceEnabled())
-                                       log.trace("Cannot rollback transaction", e2);
-                       }
-                       throw new RuntimeException("Cannot create hierarchy unit " + path + " in directory " + directory, e1);
-               }
-       }
-
 //     public User createUserFromPerson(Node person) {
 //             String email = JcrUtils.get(person, LdapAttrs.mail.property());
 //             String dn = buildDefaultDN(email, Role.USER);
index ecb7b8c1c3355afe214ca32b933651b6f863bf42..daec2ea767cb167156bb45bd5578dfd603debfdb 100644 (file)
@@ -219,14 +219,14 @@ public class CmsUserAdmin extends AggregatingUserAdmin {
 //             } else {
 //                     throw new IllegalArgumentException("Unsupported scheme " + u.getScheme());
 //             }
-               String basePath = userDirectory.getContext();
+               String basePath = userDirectory.getBase();
 
                addUserDirectory(userDirectory);
                if (isSystemRolesBaseDn(basePath)) {
                        addStandardSystemRoles();
                }
                if (log.isDebugEnabled()) {
-                       log.debug("User directory " + userDirectory.getContext() + (u != null ? " [" + u.getScheme() + "]" : "")
+                       log.debug("User directory " + userDirectory.getBase() + (u != null ? " [" + u.getScheme() + "]" : "")
                                        + " enabled." + (realm != null ? " " + realm + " realm." : ""));
                }
                return userDirectory;
index ef253800ca304d9b3af6302b1e4df365a65c7af6..179099bad124ebc7cd4c5c049f7723a50177650f 100644 (file)
@@ -174,7 +174,7 @@ public class AggregatingUserAdmin implements UserAdmin {
                if (!(ud instanceof DirectoryUserAdmin))
                        throw new IllegalArgumentException("Only " + DirectoryUserAdmin.class.getName() + " is supported");
                DirectoryUserAdmin userDirectory = (DirectoryUserAdmin) ud;
-               String basePath = userDirectory.getContext();
+               String basePath = userDirectory.getBase();
                if (isSystemRolesBaseDn(basePath)) {
                        this.systemRoles = userDirectory;
                        systemRoles.setExternalRoles(this);
@@ -303,7 +303,7 @@ public class AggregatingUserAdmin implements UserAdmin {
        }
 
        public Set<UserDirectory> getUserDirectories() {
-               TreeSet<UserDirectory> res = new TreeSet<>((o1, o2) -> o1.getContext().compareTo(o2.getContext()));
+               TreeSet<UserDirectory> res = new TreeSet<>((o1, o2) -> o1.getBase().compareTo(o2.getBase()));
                res.addAll(businessRoles.values());
                res.add(systemRoles);
                return res;
index 003aad11d8e1e6ea6e3ab9212b3c38db061755e9..8ed23ad2ec92d747c66b3ba395ddb80844b525fa 100644 (file)
@@ -346,13 +346,13 @@ public class DirectoryUserAdmin extends AbstractLdapDirectory implements UserAdm
                        return getRole(name);
                } else {
                        wc.getModifiedData().put(dn, attrs);
-                       LdapEntry newRole = newRole(dn, type, attrs);
+                       LdapEntry newRole = doCreateRole(dn, type, attrs);
                        wc.getNewData().put(dn, newRole);
                        return (Role) newRole;
                }
        }
 
-       protected LdapEntry newRole(LdapName dn, int type, Attributes attrs) {
+       private LdapEntry doCreateRole(LdapName dn, int type, Attributes attrs) {
                LdapEntry newRole;
                BasicAttribute objClass = new BasicAttribute(objectClass.name());
                if (type == Role.USER) {
@@ -367,14 +367,14 @@ public class DirectoryUserAdmin extends AbstractLdapDirectory implements UserAdm
                        objClass.add(top.name());
                        objClass.add(extensibleObject.name());
                        attrs.put(objClass);
-                       newRole = newUser(dn, attrs);
+                       newRole = newUser(dn);
                } else if (type == Role.GROUP) {
                        String groupObjClass = getGroupObjectClass();
                        objClass.add(groupObjClass);
                        // objClass.add(LdifName.extensibleObject.name());
                        objClass.add(top.name());
                        attrs.put(objClass);
-                       newRole = newGroup(dn, attrs);
+                       newRole = newGroup(dn);
                } else
                        throw new IllegalArgumentException("Unsupported type " + type);
                return newRole;
@@ -416,7 +416,7 @@ public class DirectoryUserAdmin extends AbstractLdapDirectory implements UserAdm
 
        @Override
        public Iterable<? extends Role> getHierarchyUnitRoles(HierarchyUnit hierarchyUnit, String filter, boolean deep) {
-               LdapName dn = LdapNameUtils.toLdapName(hierarchyUnit.getContext());
+               LdapName dn = LdapNameUtils.toLdapName(hierarchyUnit.getBase());
                try {
                        return getRoles(dn, filter, deep);
                } catch (InvalidSyntaxException e) {
@@ -427,13 +427,13 @@ public class DirectoryUserAdmin extends AbstractLdapDirectory implements UserAdm
        /*
         * ROLES CREATION
         */
-       protected LdapEntry newUser(LdapName name, Attributes attrs) {
+       protected LdapEntry newUser(LdapName name) {
                // TODO support devices, applications, etc.
-               return new LdifUser(this, name, attrs);
+               return new LdifUser(this, name);
        }
 
-       protected LdapEntry newGroup(LdapName name, Attributes attrs) {
-               return new LdifGroup(this, name, attrs);
+       protected LdapEntry newGroup(LdapName name) {
+               return new LdifGroup(this, name);
 
        }
 
index 2453fcb23a837d6f4d8d52dec9a404cc810a0bdf..bdf34aa91414ea77bcc7799bef02e7fa7a260011 100644 (file)
@@ -5,7 +5,6 @@ import java.util.List;
 
 import javax.naming.InvalidNameException;
 import javax.naming.directory.Attribute;
-import javax.naming.directory.Attributes;
 import javax.naming.ldap.LdapName;
 
 import org.argeo.util.directory.ldap.AbstractLdapDirectory;
@@ -15,8 +14,8 @@ import org.osgi.service.useradmin.Role;
 class LdifGroup extends LdifUser implements DirectoryGroup {
        private final String memberAttributeId;
 
-       LdifGroup(AbstractLdapDirectory userAdmin, LdapName dn, Attributes attributes) {
-               super(userAdmin, dn, attributes);
+       LdifGroup(AbstractLdapDirectory userAdmin, LdapName dn) {
+               super(userAdmin, dn);
                memberAttributeId = userAdmin.getMemberAttributeId();
        }
 
index a40de1c8360139d98625e936f68b4a88c99decbb..0b07c75656a74601f75ef5704abb2e26d79d4f56 100644 (file)
@@ -1,6 +1,5 @@
 package org.argeo.osgi.useradmin;
 
-import javax.naming.directory.Attributes;
 import javax.naming.ldap.LdapName;
 
 import org.argeo.util.directory.ldap.AbstractLdapDirectory;
@@ -8,8 +7,8 @@ import org.argeo.util.directory.ldap.DefaultLdapEntry;
 
 /** Directory user implementation */
 class LdifUser extends DefaultLdapEntry implements DirectoryUser {
-       LdifUser(AbstractLdapDirectory userAdmin, LdapName dn, Attributes attributes) {
-               super(userAdmin, dn, attributes);
+       LdifUser(AbstractLdapDirectory userAdmin, LdapName dn) {
+               super(userAdmin, dn);
        }
 
        @Override
index 466563a4d1a17bdad5a0ef2651bd944cae05b7dc..e1ad5edf5372a7ce5eb17996f6e7faffa4de194c 100644 (file)
@@ -6,7 +6,6 @@ import java.util.List;
 import javax.naming.NameNotFoundException;
 import javax.naming.NamingException;
 import javax.naming.directory.Attributes;
-import javax.naming.directory.BasicAttributes;
 import javax.naming.ldap.LdapName;
 
 import org.argeo.util.directory.HierarchyUnit;
@@ -27,9 +26,9 @@ public class OsUserDirectory extends AbstractLdapDirectoryDao {
                try {
                        osUserDn = new LdapName(LdapAttrs.uid.name() + "=" + osUsername + "," + directory.getUserBaseRdn() + ","
                                        + directory.getBaseDn());
-                       Attributes attributes = new BasicAttributes();
-                       attributes.put(LdapAttrs.uid.name(), osUsername);
-                       osUser = newUser(osUserDn, attributes);
+//                     Attributes attributes = new BasicAttributes();
+//                     attributes.put(LdapAttrs.uid.name(), osUsername);
+                       osUser = newUser(osUserDn);
                } catch (NamingException e) {
                        throw new IllegalStateException("Cannot create system user", e);
                }
index 11e8e6285e64bcb0bf809e5adc22e2c56a76b953..9e564ced9d339ba1255f23ce3bf389782831454d 100644 (file)
@@ -7,12 +7,6 @@ import org.argeo.util.transaction.WorkControl;
 
 /** An information directory (typicylly LDAP). */
 public interface Directory extends HierarchyUnit {
-       /**
-        * The base of the hierarchy defined by this directory. This could typically be
-        * an LDAP base DN.
-        */
-       String getContext();
-
        String getName();
 
        /** Whether this directory is read only. */
index 2673f16f0760cf04ad2f75ab6929d1418d5862ed..d35557784dd1f2e00ef5f1873c68b45a764f1721 100644 (file)
@@ -12,7 +12,11 @@ public interface HierarchyUnit {
 
        boolean isFunctional();
 
-       String getContext();
+       /**
+        * The base of this organisational unit within the hierarchy. This would
+        * typically be an LDAP base DN.
+        */
+       String getBase();
 
        Directory getDirectory();
 
index 27c2b9531d6465b3ade47fce8dafcf69cfbc243b..54d9776b5fd15106bf6de8b560ef81f863f1d470 100644 (file)
@@ -161,9 +161,9 @@ public abstract class AbstractLdapDirectory implements Directory, XAResourceProv
        /*
         * CREATION
         */
-       protected abstract LdapEntry newUser(LdapName name, Attributes attrs);
+       protected abstract LdapEntry newUser(LdapName name);
 
-       protected abstract LdapEntry newGroup(LdapName name, Attributes attrs);
+       protected abstract LdapEntry newGroup(LdapName name);
 
        /*
         * EDITION
@@ -255,11 +255,11 @@ public abstract class AbstractLdapDirectory implements Directory, XAResourceProv
                                        } else {
                                                // user doesn't have the right to retrieve role, but we know it exists
                                                // otherwise memberOf would not work
-                                               Attributes a = new BasicAttributes();
-                                               a.put(LdapNameUtils.getLastRdn(groupDn).getType(),
-                                                               LdapNameUtils.getLastRdn(groupDn).getValue());
-                                               a.put(LdapAttrs.objectClass.name(), LdapObjs.groupOfNames.name());
-                                               group = newGroup(groupDn, a);
+//                                             Attributes a = new BasicAttributes();
+//                                             a.put(LdapNameUtils.getLastRdn(groupDn).getType(),
+//                                                             LdapNameUtils.getLastRdn(groupDn).getValue());
+//                                             a.put(LdapAttrs.objectClass.name(), LdapObjs.groupOfNames.name());
+                                               group = newGroup(groupDn);
                                                allRoles.add(group);
                                        }
                                }
@@ -267,10 +267,13 @@ public abstract class AbstractLdapDirectory implements Directory, XAResourceProv
                                throw new IllegalStateException("Cannot get memberOf groups for " + user, e);
                        }
                } else {
-                       for (LdapName groupDn : getDirectoryDao().getDirectGroups(user.getDn())) {
-                               // TODO check for loops
+                       directGroups: for (LdapName groupDn : getDirectoryDao().getDirectGroups(user.getDn())) {
                                LdapEntry group = doGetRole(groupDn);
                                if (group != null) {
+                                       if (allRoles.contains(group)) {
+                                               // important in order to avoi loops
+                                               continue directGroups;
+                                       }
                                        allRoles.add(group);
                                        collectGroups(group, allRoles);
                                }
@@ -326,7 +329,7 @@ public abstract class AbstractLdapDirectory implements Directory, XAResourceProv
                // TODO deal with multiple attr RDN
                attrs.put(nameRdn.getType(), nameRdn.getValue());
                wc.getModifiedData().put(dn, attrs);
-               LdapHierarchyUnit newHierarchyUnit = new LdapHierarchyUnit(this, dn, attrs);
+               LdapHierarchyUnit newHierarchyUnit = new LdapHierarchyUnit(this, dn);
                wc.getNewData().put(dn, newHierarchyUnit);
                return newHierarchyUnit;
        }
@@ -336,7 +339,7 @@ public abstract class AbstractLdapDirectory implements Directory, XAResourceProv
         */
 
        @Override
-       public String getContext() {
+       public String getBase() {
                return getBaseDn().toString();
        }
 
index 8c87170fe5dcc788962b9b9ca7581bdc4b4b5b3d..8e132887dec377ac8ebab4cb14f8d380ccd7b5d7 100644 (file)
@@ -22,13 +22,13 @@ public abstract class AbstractLdapDirectoryDao implements LdapDirectoryDao {
        }
 
        @Override
-       public LdapEntry newUser(LdapName name, Attributes attrs) {
-               return getDirectory().newUser(name, attrs);
+       public LdapEntry newUser(LdapName name) {
+               return getDirectory().newUser(name);
        }
 
        @Override
-       public LdapEntry newGroup(LdapName name, Attributes attrs) {
-               return getDirectory().newGroup(name, attrs);
+       public LdapEntry newGroup(LdapName name) {
+               return getDirectory().newGroup(name);
        }
 
 }
index 14a7eb87cc2f5b78dc931ab0e5dd2eb47750550b..c4d20feba922576dc181129ebcd75dae523fbde5 100644 (file)
@@ -32,18 +32,18 @@ public class DefaultLdapEntry implements LdapEntry {
 
        private final LdapName dn;
 
-       private Attributes publishedAttributes;
+//     private Attributes publishedAttributes;
 
        // Temporarily expose the fields
        protected AttributeDictionary properties;
        protected AttributeDictionary credentials;
 
-       protected DefaultLdapEntry(AbstractLdapDirectory directory, LdapName dn, Attributes attributes) {
+       protected DefaultLdapEntry(AbstractLdapDirectory directory, LdapName dn) {
                Objects.requireNonNull(directory);
                Objects.requireNonNull(dn);
                this.directory = directory;
                this.dn = dn;
-               this.publishedAttributes = attributes;
+//             this.publishedAttributes = attributes;
 //             properties = new AttributeDictionary(false);
 //             credentials = new AttributeDictionary(true);
        }
@@ -54,10 +54,10 @@ public class DefaultLdapEntry implements LdapEntry {
        }
 
        public synchronized Attributes getAttributes() {
-               // lazy loading
-               if (publishedAttributes == null)
-                       publishedAttributes = getDirectory().getDirectoryDao().doGetAttributes(dn);
-               return isEditing() ? getModifiedAttributes() : publishedAttributes;
+//             // lazy loading
+//             if (publishedAttributes == null)
+//                     publishedAttributes = getDirectory().getDirectoryDao().doGetAttributes(dn);
+               return isEditing() ? getModifiedAttributes() : getDirectory().getDirectoryDao().doGetAttributes(dn);
        }
 
        @Override
@@ -104,7 +104,7 @@ public class DefaultLdapEntry implements LdapEntry {
        }
 
        public synchronized void publishAttributes(Attributes modifiedAttributes) {
-               publishedAttributes = modifiedAttributes;
+//             publishedAttributes = modifiedAttributes;
        }
 
        /*
@@ -376,6 +376,7 @@ public class DefaultLdapEntry implements LdapEntry {
                public Object put(String key, Object value) {
                        try {
                                if (key == null) {
+                                       // FIXME remove this "feature", a key should be specified
                                        // TODO persist to other sources (like PKCS12)
                                        char[] password = DirectoryDigestUtils.bytesToChars(value);
                                        byte[] hashedPassword = sha1hash(password);
@@ -384,6 +385,13 @@ public class DefaultLdapEntry implements LdapEntry {
                                if (key.startsWith("X-")) {
                                        return put(LdapAttrs.authPassword.name(), value);
                                }
+
+                               // start editing
+                               getDirectory().checkEdit();
+                               if (!isEditing())
+                                       startEditing();
+
+                               // object classes special case.
                                if (key.equals(LdapAttrs.objectClasses.name())) {
                                        Attribute attribute = new BasicAttribute(LdapAttrs.objectClass.name());
                                        String[] objectClasses = value.toString().split("\n");
@@ -399,10 +407,6 @@ public class DefaultLdapEntry implements LdapEntry {
                                                return null;
                                }
 
-                               getDirectory().checkEdit();
-                               if (!isEditing())
-                                       startEditing();
-
                                if (!(value instanceof String || value instanceof byte[]))
                                        throw new IllegalArgumentException("Value must be String or byte[]");
 
index b1c0c9849a14cb6abdd5214ad239912e591d887f..e5ce0a4c16fafd7428ed69111026e5568e5fdf36 100644 (file)
@@ -93,23 +93,23 @@ public class LdapDao extends AbstractLdapDirectoryDao {
                                        attrs.put(LdapAttrs.objectClass.name(), LdapObjs.top.name());
                                        attrs.put(LdapAttrs.objectClass.name(), getDirectory().getGroupObjectClass());
                                }
-                               res = newGroup(name, attrs);
+                               res = newGroup(name);
                        } else if (getDirectory().getSystemRoleBaseRdn().equals(technicalRdn)) {
                                if (attrs.size() == 0) {// exists but not accessible
                                        attrs = new BasicAttributes();
                                        attrs.put(LdapAttrs.objectClass.name(), LdapObjs.top.name());
                                        attrs.put(LdapAttrs.objectClass.name(), getDirectory().getGroupObjectClass());
                                }
-                               res = newGroup(name, attrs);
+                               res = newGroup(name);
                        } else if (getDirectory().getUserBaseRdn().equals(technicalRdn)) {
                                if (attrs.size() == 0) {// exists but not accessible
                                        attrs = new BasicAttributes();
                                        attrs.put(LdapAttrs.objectClass.name(), LdapObjs.top.name());
                                        attrs.put(LdapAttrs.objectClass.name(), getDirectory().getUserObjectClass());
                                }
-                               res = newUser(name, attrs);
+                               res = newUser(name);
                        } else {
-                               res = new DefaultLdapEntry(getDirectory(), name, attrs);
+                               res = new DefaultLdapEntry(getDirectory(), name);
                        }
                        return res;
                } catch (NameNotFoundException e) {
@@ -146,9 +146,11 @@ public class LdapDao extends AbstractLdapDirectoryDao {
                ArrayList<LdapEntry> res = new ArrayList<>();
                try {
                        String searchFilter = f != null ? f.toString()
-                                       : "(|(" + objectClass + "=" + getDirectory().getUserObjectClass() + ")(" + objectClass + "="
-                                                       + getDirectory().getGroupObjectClass() + "))";
+                                       : "(|(" + objectClass.name() + "=" + getDirectory().getUserObjectClass() + ")(" + objectClass.name()
+                                                       + "=" + getDirectory().getGroupObjectClass() + "))";
                        SearchControls searchControls = new SearchControls();
+                       // only attribute needed is objectClass
+                       searchControls.setReturningAttributes(new String[] { objectClass.name() });
                        // FIXME make one level consistent with deep
                        searchControls.setSearchScope(deep ? SearchControls.SUBTREE_SCOPE : SearchControls.ONELEVEL_SCOPE);
 
@@ -163,10 +165,10 @@ public class LdapDao extends AbstractLdapDirectoryDao {
                                LdapEntry role;
                                if (objectClassAttr.contains(getDirectory().getGroupObjectClass())
                                                || objectClassAttr.contains(getDirectory().getGroupObjectClass().toLowerCase()))
-                                       role = newGroup(dn, attrs);
+                                       role = newGroup(dn);
                                else if (objectClassAttr.contains(getDirectory().getUserObjectClass())
                                                || objectClassAttr.contains(getDirectory().getUserObjectClass().toLowerCase()))
-                                       role = newUser(dn, attrs);
+                                       role = newUser(dn);
                                else {
 //                                     log.warn("Unsupported LDAP type for " + searchResult.getName());
                                        continue results;
@@ -248,14 +250,16 @@ public class LdapDao extends AbstractLdapDirectoryDao {
 
                        SearchControls searchControls = new SearchControls();
                        searchControls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
+                       // no attributes needed
+                       searchControls.setReturningAttributes(new String[0]);
 
                        NamingEnumeration<SearchResult> results = ldapConnection.search(searchBase, searchFilter, searchControls);
 
                        while (results.hasMoreElements()) {
                                SearchResult searchResult = (SearchResult) results.nextElement();
                                LdapName dn = toDn(searchBase, searchResult);
-                               Attributes attrs = searchResult.getAttributes();
-                               LdapHierarchyUnit hierarchyUnit = new LdapHierarchyUnit(getDirectory(), dn, attrs);
+//                             Attributes attrs = searchResult.getAttributes();
+                               LdapHierarchyUnit hierarchyUnit = new LdapHierarchyUnit(getDirectory(), dn);
                                if (functionalOnly) {
                                        if (hierarchyUnit.isFunctional())
                                                res.add(hierarchyUnit);
@@ -276,8 +280,9 @@ public class LdapDao extends AbstractLdapDirectoryDao {
                                return getDirectory();
                        if (!dn.startsWith(getDirectory().getBaseDn()))
                                throw new IllegalArgumentException(dn + " does not start with base DN " + getDirectory().getBaseDn());
-                       Attributes attrs = ldapConnection.getAttributes(dn);
-                       return new LdapHierarchyUnit(getDirectory(), dn, attrs);
+                       if (!ldapConnection.entryExists(dn))
+                               return null;
+                       return new LdapHierarchyUnit(getDirectory(), dn);
                } catch (NameNotFoundException e) {
                        return null;
                } catch (NamingException e) {
index 81a86fd05f48a8412422e81755d8859725d7c058..a4e65998c5df1e9eb10b370aaae71066467ec6f0 100644 (file)
@@ -24,9 +24,9 @@ public interface LdapDirectoryDao extends WorkingCopyProcessor<LdapEntryWorkingC
 
        HierarchyUnit doGetHierarchyUnit(LdapName dn);
 
-       LdapEntry newUser(LdapName name, Attributes attrs);
+       LdapEntry newUser(LdapName name);
 
-       LdapEntry newGroup(LdapName name, Attributes attrs);
+       LdapEntry newGroup(LdapName name);
 
        void init();
 
index 45788b93576ac0757643252aef9cd3080ca295a0..cf293cee2e94aa2ec6eda2312ae4aef2209a19ab 100644 (file)
@@ -1,11 +1,18 @@
 package org.argeo.util.directory.ldap;
 
+import java.util.Arrays;
 import java.util.Dictionary;
 import java.util.List;
+import java.util.Set;
+import java.util.StringJoiner;
+import java.util.TreeSet;
 
 import javax.naming.directory.Attributes;
 import javax.naming.ldap.LdapName;
 
+import org.argeo.util.naming.LdapAttrs;
+
+/** An LDAP entry. */
 public interface LdapEntry {
        LdapName getDn();
 
@@ -13,10 +20,21 @@ public interface LdapEntry {
 
        void publishAttributes(Attributes modifiedAttributes);
 
-       public List<LdapName> getReferences(String attributeId);
-       
-       public Dictionary<String, Object> getProperties();
+       List<LdapName> getReferences(String attributeId);
+
+       Dictionary<String, Object> getProperties();
 
-       public boolean hasCredential(String key, Object value) ;
+       boolean hasCredential(String key, Object value);
 
+       /*
+        * UTILITIES
+        */
+       public static void addObjectClasses(Dictionary<String, Object> properties, Set<String> objectClasses) {
+               String value = properties.get(LdapAttrs.objectClasses.name()).toString();
+               Set<String> currentObjectClasses = new TreeSet<>(Arrays.asList(value.toString().split("\n")));
+               currentObjectClasses.addAll(objectClasses);
+               StringJoiner values = new StringJoiner("\n");
+               currentObjectClasses.forEach((s) -> values.add(s));
+               properties.put(LdapAttrs.objectClasses.name(), values.toString());
+       }
 }
index 8579bc5fdc5697367999da21f919790943e9af7b..bd12244eafb84d9a1daa2f6623698bc4bf917054 100644 (file)
@@ -1,6 +1,5 @@
 package org.argeo.util.directory.ldap;
 
-import javax.naming.directory.Attributes;
 import javax.naming.ldap.LdapName;
 import javax.naming.ldap.Rdn;
 
@@ -10,12 +9,12 @@ import org.argeo.util.directory.HierarchyUnit;
 public class LdapHierarchyUnit extends DefaultLdapEntry implements HierarchyUnit {
        private final boolean functional;
 
-       public LdapHierarchyUnit(AbstractLdapDirectory directory, LdapName dn, Attributes attributes) {
-               super(directory, dn, attributes);
+       public LdapHierarchyUnit(AbstractLdapDirectory directory, LdapName dn) {
+               super(directory, dn);
 
                Rdn rdn = LdapNameUtils.getLastRdn(dn);
                functional = !(directory.getUserBaseRdn().equals(rdn) || directory.getGroupBaseRdn().equals(rdn)
-                               || directory.getSystemRoleBaseRdn().equals(rdn) );
+                               || directory.getSystemRoleBaseRdn().equals(rdn));
        }
 
        @Override
@@ -41,7 +40,7 @@ public class LdapHierarchyUnit extends DefaultLdapEntry implements HierarchyUnit
        }
 
        @Override
-       public String getContext() {
+       public String getBase() {
                return getDn().toString();
        }
 
index 689ef23295851b133b6fe9a0c3748f186e819dd1..88d317542321d88dcab2588239792a4d191f19dd 100644 (file)
@@ -54,6 +54,10 @@ public class LdapNameUtils {
                return getLastRdn(dn).toString();
        }
 
+       public static String getLastRdnValue(String dn) {
+               return getLastRdnValue(toLdapName(dn));
+       }
+
        public static String getLastRdnValue(LdapName dn) {
                return getLastRdn(dn).getValue().toString();
        }
index d74ac166fc72075adb08008885344d4ac565e240..27a934377b48b0277fecef7046174b7ea3e0c972 100644 (file)
@@ -36,9 +36,10 @@ import org.osgi.service.useradmin.Role;
 /** A user admin based on a LDIF files. */
 public class LdifDao extends AbstractLdapDirectoryDao {
        private NavigableMap<LdapName, LdapEntry> entries = new TreeMap<>();
-
        private NavigableMap<LdapName, LdapHierarchyUnit> hierarchy = new TreeMap<>();
 
+       private NavigableMap<LdapName, Attributes> values = new TreeMap<>();
+
        public LdifDao(AbstractLdapDirectory directory) {
                super(directory);
        }
@@ -104,6 +105,8 @@ public class LdifDao extends AbstractLdapDirectoryDao {
                                        lowerCase.add(id);
                                }
 
+                               values.put(key, attributes);
+
                                // analyse object classes
                                NamingEnumeration<?> objectClasses = attributes.get(objectClass.name()).getAll();
                                // System.out.println(key);
@@ -111,14 +114,14 @@ public class LdifDao extends AbstractLdapDirectoryDao {
                                        String objectClass = objectClasses.next().toString();
                                        // System.out.println(" " + objectClass);
                                        if (objectClass.toLowerCase().equals(inetOrgPerson.name().toLowerCase())) {
-                                               entries.put(key, newUser(key, attributes));
+                                               entries.put(key, newUser(key));
                                                break objectClasses;
                                        } else if (objectClass.toLowerCase().equals(getDirectory().getGroupObjectClass().toLowerCase())) {
-                                               entries.put(key, newGroup(key, attributes));
+                                               entries.put(key, newGroup(key));
                                                break objectClasses;
                                        } else if (objectClass.equalsIgnoreCase(LdapObjs.organizationalUnit.name())) {
                                                // TODO skip if it does not contain groups or users
-                                               hierarchy.put(key, new LdapHierarchyUnit(getDirectory(), key, attributes));
+                                               hierarchy.put(key, new LdapHierarchyUnit(getDirectory(), key));
                                                break objectClasses;
                                        }
                                }
@@ -151,11 +154,9 @@ public class LdifDao extends AbstractLdapDirectoryDao {
 
        @Override
        public Attributes doGetAttributes(LdapName name) {
-               try {
-                       return doGetEntry(name).getAttributes();
-               } catch (NameNotFoundException e) {
-                       throw new IllegalStateException(name + " doe not exist in " + getDirectory().getBaseDn(), e);
-               }
+               if (!values.containsKey(name))
+                       throw new IllegalStateException(name + " doe not exist in " + getDirectory().getBaseDn());
+               return values.get(name);
        }
 
        @Override
index ebafd267fb04cca13ab5ba0ecbcfeda336d8a0b7..ddb605a19fd49e06db3e43ec7b043af8d3afab1d 100644 (file)
@@ -48,8 +48,11 @@ public class WorkingCopyXaResource<WC extends WorkingCopy<?, ?, ?>> implements X
        }
 
        private synchronized void cleanUp(Xid xid) {
-               wc(xid).cleanUp();
-               workingCopies.remove(xid);
+               WC wc = workingCopies.get(xid);
+               if (wc != null) {
+                       wc.cleanUp();
+                       workingCopies.remove(xid);
+               }
                editingXid = null;
        }
 
index d120ae9a289b8494e62372e28a5f607edc08f8d1..00b519d5cc08fafc8e3cd82ccf0cc3b5b3b6644f 100644 (file)
@@ -91,7 +91,7 @@ public class UserAdminWrapper {
                Map<String, String> dns = new HashMap<String, String>();
                for (UserDirectory userDirectory : userDirectories.keySet()) {
                        Boolean readOnly = userDirectory.isReadOnly();
-                       String baseDn = userDirectory.getContext();
+                       String baseDn = userDirectory.getBase();
 
                        if (onlyWritable && readOnly)
                                continue;