From 99bca5f6b71c24837c23cf2a9bb944e09dca3dea Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Mon, 20 Jun 2022 10:32:09 +0200 Subject: [PATCH] Simplify hierarchy units --- .../argeo/cms/e4/users/UserAdminWrapper.java | 2 +- .../src/org/argeo/cms/acr/ContentUtils.java | 8 + .../cms/acr/directory/DirectoryContent.java | 51 +++++ .../directory/DirectoryContentProvider.java | 39 ++-- .../acr/directory/HierarchyUnitContent.java | 26 ++- .../argeo/cms/acr/directory/RoleContent.java | 7 +- .../cms/internal/auth/CmsUserManagerImpl.java | 8 +- .../cms/internal/runtime/CmsUserAdmin.java | 4 +- .../osgi/useradmin/AbstractUserDirectory.java | 195 ++++++++++++------ .../osgi/useradmin/AggregatingUserAdmin.java | 4 +- .../argeo/osgi/useradmin/HierarchyUnit.java | 11 +- .../argeo/osgi/useradmin/LdapNameUtils.java | 36 ++-- .../osgi/useradmin/LdifHierarchyUnit.java | 18 +- .../org/argeo/osgi/useradmin/LdifUser.java | 40 ++-- .../argeo/osgi/useradmin/LdifUserAdmin.java | 44 ++-- .../argeo/osgi/useradmin/UserDirectory.java | 14 +- .../src/org/argeo/util/LangUtils.java | 31 ++- 17 files changed, 362 insertions(+), 176 deletions(-) create mode 100644 org.argeo.cms/src/org/argeo/cms/acr/directory/DirectoryContent.java diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java index 465c15a17..dbbd970a7 100644 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java +++ b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java @@ -91,7 +91,7 @@ public class UserAdminWrapper { Map dns = new HashMap(); for (UserDirectory userDirectory : userDirectories.keySet()) { Boolean readOnly = userDirectory.isReadOnly(); - String baseDn = userDirectory.getBasePath(); + String baseDn = userDirectory.getGlobalId(); if (onlyWritable && readOnly) continue; diff --git a/org.argeo.cms/src/org/argeo/cms/acr/ContentUtils.java b/org.argeo.cms/src/org/argeo/cms/acr/ContentUtils.java index 272a5c5a1..eeec4fcfc 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/ContentUtils.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/ContentUtils.java @@ -3,6 +3,7 @@ package org.argeo.cms.acr; import java.io.PrintStream; import java.util.ArrayList; import java.util.List; +import java.util.StringJoiner; import java.util.function.BiConsumer; import javax.xml.namespace.QName; @@ -78,6 +79,13 @@ public class ContentUtils { path.substring(parentIndex + 1) }; } + public static String toPath(List segments) { + // TODO checks + StringJoiner sj = new StringJoiner("/"); + segments.forEach((s) -> sj.add(s)); + return sj.toString(); + } + public static List toPathSegments(String path) { List res = new ArrayList<>(); if ("".equals(path) || ROOT_SLASH.equals(path)) diff --git a/org.argeo.cms/src/org/argeo/cms/acr/directory/DirectoryContent.java b/org.argeo.cms/src/org/argeo/cms/acr/directory/DirectoryContent.java new file mode 100644 index 000000000..b0c1b6bd1 --- /dev/null +++ b/org.argeo.cms/src/org/argeo/cms/acr/directory/DirectoryContent.java @@ -0,0 +1,51 @@ +package org.argeo.cms.acr.directory; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.xml.namespace.QName; + +import org.argeo.api.acr.Content; +import org.argeo.api.acr.ContentName; +import org.argeo.api.acr.spi.ContentProvider; +import org.argeo.api.acr.spi.ProvidedSession; +import org.argeo.cms.acr.AbstractContent; +import org.argeo.osgi.useradmin.HierarchyUnit; +import org.argeo.osgi.useradmin.UserDirectory; + +class DirectoryContent extends AbstractContent { + private UserDirectory directory; + private DirectoryContentProvider provider; + + public DirectoryContent(ProvidedSession session, DirectoryContentProvider provider, UserDirectory directory) { + super(session); + this.provider = provider; + this.directory = directory; + } + + @Override + public ContentProvider getProvider() { + return provider; + } + + @Override + public Iterator iterator() { + List res = new ArrayList<>(); + for (Iterator it = directory.getRootHierarchyUnits().iterator(); it.hasNext();) { + res.add(new HierarchyUnitContent(getSession(), provider, it.next())); + } + return res.iterator(); + } + + @Override + public QName getName() { + return new ContentName(directory.getName()); + } + + @Override + public Content getParent() { + return provider.getRootContent(getSession()); + } + +} diff --git a/org.argeo.cms/src/org/argeo/cms/acr/directory/DirectoryContentProvider.java b/org.argeo.cms/src/org/argeo/cms/acr/directory/DirectoryContentProvider.java index 60ef67551..c1ded945a 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/directory/DirectoryContentProvider.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/directory/DirectoryContentProvider.java @@ -5,8 +5,6 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; -import javax.naming.InvalidNameException; -import javax.naming.ldap.LdapName; import javax.xml.namespace.QName; import org.argeo.api.acr.Content; @@ -20,7 +18,6 @@ import org.argeo.cms.CmsUserManager; import org.argeo.cms.acr.AbstractContent; import org.argeo.cms.acr.ContentUtils; import org.argeo.osgi.useradmin.HierarchyUnit; -import org.argeo.osgi.useradmin.LdapNameUtils; import org.argeo.osgi.useradmin.UserDirectory; import org.osgi.service.useradmin.User; @@ -45,7 +42,7 @@ public class DirectoryContentProvider implements ContentProvider { String userDirectoryDn = segments.get(0); UserDirectory userDirectory = null; userDirectories: for (UserDirectory ud : userManager.getUserDirectories()) { - if (userDirectoryDn.equals(ud.getBasePath())) { + if (userDirectoryDn.equals(ud.getGlobalId())) { userDirectory = ud; break userDirectories; } @@ -53,25 +50,29 @@ public class DirectoryContentProvider implements ContentProvider { if (userDirectory == null) throw new ContentNotFoundException("Cannot find user directory " + userDirectoryDn); if (segments.size() == 1) { - return new HierarchyUnitContent(session, this, userDirectory); + return new DirectoryContent(session, this, userDirectory); } else { - LdapName dn; - try { - dn = LdapNameUtils.toLdapName(userDirectoryDn); - for (int i = 1; i < segments.size(); i++) { - dn.add(segments.get(i)); - } - } catch (InvalidNameException e) { - throw new IllegalStateException("Cannot interpret " + segments + " as DN", e); - } - User user = userManager.getUser(dn.toString()); + List relSegments = new ArrayList<>(segments); + relSegments.remove(0); + String pathWithinUserDirectory = ContentUtils.toPath(relSegments); +// LdapName dn; +// try { +// dn = LdapNameUtils.toLdapName(userDirectoryDn); +// for (int i = 1; i < segments.size(); i++) { +// dn.add(segments.get(i)); +// } +// } catch (InvalidNameException e) { +// throw new IllegalStateException("Cannot interpret " + segments + " as DN", e); +// } + User user = (User) userDirectory.getRoleByPath(pathWithinUserDirectory); if (user != null) { HierarchyUnit parent = userDirectory.getHierarchyUnit(user); return new RoleContent(session, this, new HierarchyUnitContent(session, this, parent), user); } - HierarchyUnit hierarchyUnit = userDirectory.getHierarchyUnit(dn.toString()); + HierarchyUnit hierarchyUnit = userDirectory.getHierarchyUnit(pathWithinUserDirectory); if (hierarchyUnit == null) - throw new ContentNotFoundException("Cannot find " + dn); + throw new ContentNotFoundException( + "Cannot find " + pathWithinUserDirectory + " within " + userDirectoryDn); return new HierarchyUnitContent(session, this, hierarchyUnit); } } @@ -104,7 +105,7 @@ public class DirectoryContentProvider implements ContentProvider { public void setUserManager(CmsUserManager userManager) { this.userManager = userManager; } - + UserManagerContent getRootContent(ProvidedSession session) { return new UserManagerContent(session); } @@ -134,7 +135,7 @@ public class DirectoryContentProvider implements ContentProvider { public Iterator iterator() { List res = new ArrayList<>(); for (UserDirectory userDirectory : userManager.getUserDirectories()) { - HierarchyUnitContent content = new HierarchyUnitContent(getSession(), DirectoryContentProvider.this, + DirectoryContent content = new DirectoryContent(getSession(), DirectoryContentProvider.this, userDirectory); res.add(content); } diff --git a/org.argeo.cms/src/org/argeo/cms/acr/directory/HierarchyUnitContent.java b/org.argeo.cms/src/org/argeo/cms/acr/directory/HierarchyUnitContent.java index 9184c63af..5ec57c51f 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/directory/HierarchyUnitContent.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/directory/HierarchyUnitContent.java @@ -16,7 +16,7 @@ import org.argeo.cms.acr.AbstractContent; import org.argeo.osgi.useradmin.HierarchyUnit; import org.osgi.service.useradmin.Role; -public class HierarchyUnitContent extends AbstractContent { +class HierarchyUnitContent extends AbstractContent { private HierarchyUnit hierarchyUnit; private DirectoryContentProvider provider; @@ -36,10 +36,10 @@ public class HierarchyUnitContent extends AbstractContent { @Override public QName getName() { - if (hierarchyUnit.getParent() == null) {// base DN - String baseDn = hierarchyUnit.getBasePath(); - return new ContentName(baseDn); - } +// if (hierarchyUnit.getParent() == null) {// base DN +// String baseDn = hierarchyUnit.getBasePath(); +// return new ContentName(baseDn); +// } String name = hierarchyUnit.getHierarchyUnitName(); return new ContentName(name); } @@ -48,7 +48,7 @@ public class HierarchyUnitContent extends AbstractContent { public Content getParent() { HierarchyUnit parentHu = hierarchyUnit.getParent(); if (parentHu == null) { - return provider.getRootContent(getSession()); + return new DirectoryContent(getSession(), provider, hierarchyUnit.getDirectory()); } return new HierarchyUnitContent(getSession(), provider, parentHu); } @@ -56,10 +56,10 @@ public class HierarchyUnitContent extends AbstractContent { @Override public Iterator iterator() { List lst = new ArrayList<>(); - for (int i = 0; i < hierarchyUnit.getHierarchyChildCount(); i++) - lst.add(new HierarchyUnitContent(getSession(), provider, hierarchyUnit.getHierarchyChild(i))); + for (HierarchyUnit hu : hierarchyUnit.getDirectHierachyUnits()) + lst.add(new HierarchyUnitContent(getSession(), provider, hu)); - for (Role role : hierarchyUnit.getRoles(null, false)) + for (Role role : hierarchyUnit.getHierarchyUnitRoles(null, false)) lst.add(new RoleContent(getSession(), provider, this, role)); return lst.iterator(); } @@ -75,4 +75,12 @@ public class HierarchyUnitContent extends AbstractContent { return res; } + /* + * ACCESSOR + */ + HierarchyUnit getHierarchyUnit() { + return hierarchyUnit; + } + + } diff --git a/org.argeo.cms/src/org/argeo/cms/acr/directory/RoleContent.java b/org.argeo.cms/src/org/argeo/cms/acr/directory/RoleContent.java index 8043205ec..344dede76 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/directory/RoleContent.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/directory/RoleContent.java @@ -7,7 +7,6 @@ import java.util.Optional; import java.util.Set; import java.util.TreeSet; -import javax.naming.ldap.LdapName; import javax.swing.GroupLayout.Group; import javax.xml.namespace.QName; @@ -18,13 +17,12 @@ import org.argeo.api.acr.NamespaceUtils; import org.argeo.api.acr.spi.ContentProvider; import org.argeo.api.acr.spi.ProvidedSession; import org.argeo.cms.acr.AbstractContent; -import org.argeo.osgi.useradmin.LdapNameUtils; import org.argeo.util.naming.LdapAttrs; import org.argeo.util.naming.LdapObjs; import org.osgi.service.useradmin.Role; import org.osgi.service.useradmin.User; -public class RoleContent extends AbstractContent { +class RoleContent extends AbstractContent { private DirectoryContentProvider provider; private HierarchyUnitContent parent; @@ -45,8 +43,7 @@ public class RoleContent extends AbstractContent { @Override public QName getName() { - LdapName dn = LdapNameUtils.toLdapName(role.getName()); - String name = LdapNameUtils.getLastRdnAsString(dn); + String name = parent.getHierarchyUnit().getDirectory().getRoleSimpleName(role); return new ContentName(name); } diff --git a/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsUserManagerImpl.java b/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsUserManagerImpl.java index 135bf50c8..e6a305685 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsUserManagerImpl.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsUserManagerImpl.java @@ -232,7 +232,7 @@ public class CmsUserManagerImpl implements CmsUserManager { Map dns = new HashMap(); for (UserDirectory userDirectory : userDirectories.keySet()) { Boolean readOnly = userDirectory.isReadOnly(); - String baseDn = userDirectory.getBasePath(); + String baseDn = userDirectory.getGlobalId(); if (onlyWritable && readOnly) continue; @@ -247,7 +247,7 @@ public class CmsUserManagerImpl implements CmsUserManager { } public Set getUserDirectories() { - TreeSet res = new TreeSet<>((o1, o2) -> o1.getBasePath().compareTo(o2.getBasePath())); + TreeSet res = new TreeSet<>((o1, o2) -> o1.getGlobalId().compareTo(o2.getGlobalId())); res.addAll(userDirectories.keySet()); return res; } @@ -445,8 +445,8 @@ public class CmsUserManagerImpl implements CmsUserManager { String name = user.getName(); NavigableMap possible = new TreeMap<>(); for (UserDirectory userDirectory : userDirectories.keySet()) { - if (name.endsWith(userDirectory.getBasePath())) { - possible.put(userDirectory.getBasePath(), userDirectory); + if (name.endsWith(userDirectory.getGlobalId())) { + possible.put(userDirectory.getGlobalId(), userDirectory); } } if (possible.size() == 0) diff --git a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsUserAdmin.java b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsUserAdmin.java index 391c94a3d..49e55f16c 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsUserAdmin.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsUserAdmin.java @@ -107,14 +107,14 @@ public class CmsUserAdmin extends AggregatingUserAdmin { } else { throw new IllegalArgumentException("Unsupported scheme " + u.getScheme()); } - String basePath = userDirectory.getBasePath(); + String basePath = userDirectory.getGlobalId(); addUserDirectory(userDirectory); if (isSystemRolesBaseDn(basePath)) { addStandardSystemRoles(); } if (log.isDebugEnabled()) { - log.debug("User directory " + userDirectory.getBasePath() + (u != null ? " [" + u.getScheme() + "]" : "") + log.debug("User directory " + userDirectory.getGlobalId() + (u != null ? " [" + u.getScheme() + "]" : "") + " enabled." + (realm != null ? " " + realm + " realm." : "")); } return userDirectory; diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java b/org.argeo.util/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java index 889f9cfa7..27662938f 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java @@ -18,6 +18,7 @@ import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Optional; +import java.util.StringJoiner; import javax.naming.InvalidNameException; import javax.naming.NameNotFoundException; @@ -104,12 +105,16 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { throw new IllegalArgumentException("Badly formated base DN " + UserAdminConf.baseDn.getValue(properties), e); } + + // read only String readOnlyStr = UserAdminConf.readOnly.getValue(properties); if (readOnlyStr == null) { readOnly = readOnlyDefault(uri); properties.put(UserAdminConf.readOnly.name(), Boolean.toString(readOnly)); } else readOnly = Boolean.parseBoolean(readOnlyStr); + + // disabled String disabledStr = UserAdminConf.disabled.getValue(properties); if (disabledStr != null) disabled = Boolean.parseBoolean(disabledStr); @@ -136,11 +141,77 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { } + /* + * PATHS + */ + @Override - public String getBasePath() { + public String getGlobalId() { return getBaseDn().toString(); } + @Override + public String getName() { + return nameToSimple(getBaseDn(), "."); + } + + @Override + public String getRolePath(Role role) { + return nameToRelativePath(((DirectoryUser) role).getDn()); + } + + @Override + public String getRoleSimpleName(Role role) { + LdapName dn = LdapNameUtils.toLdapName(role.getName()); + String name = LdapNameUtils.getLastRdnValue(dn); + return name; + } + + protected String nameToRelativePath(LdapName dn) { + LdapName name = LdapNameUtils.relativeName(getBaseDn(), dn); + return nameToSimple(name, "/"); + } + + protected String nameToSimple(LdapName name, String separator) { + StringJoiner path = new StringJoiner(separator); + for (int i = 0; i < name.size(); i++) { + path.add(name.getRdn(i).getValue().toString()); + } + return path.toString(); + + } + + protected LdapName pathToName(String path) { + try { + LdapName name = (LdapName) getBaseDn().clone(); + String[] segments = path.split("/"); + String parentSegment = null; + for (String segment : segments) { + String attr = "ou"; + if (parentSegment != null) { + if (getUserBase().equals(parentSegment)) + attr = "uid"; + else if (getGroupBase().equals(parentSegment)) + attr = "cn"; + } + Rdn rdn = new Rdn(attr, segment); + name.add(rdn); + + // TODO make it more robust using RDNs + parentSegment = rdn.toString(); + } + return name; + } catch (InvalidNameException e) { + throw new IllegalStateException("Cannot get role " + path, e); + } + + } + + @Override + public Role getRoleByPath(String path) { + return doGetRole(pathToName(path)); + } + @Override public Optional getRealm() { Object realm = getProperties().get(UserAdminConf.realm.name()); @@ -149,6 +220,10 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { return Optional.of(realm.toString()); } + /* + * EDITION + */ + protected boolean isEditing() { return xaResource.wc() != null; } @@ -240,23 +315,6 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { @Override public Role[] getRoles(String filter) throws InvalidSyntaxException { -// UserDirectoryWorkingCopy wc = getWorkingCopy(); -// Filter f = filter != null ? FrameworkUtil.createFilter(filter) : null; -// List res = doGetRoles(getBaseDn(), f, true); -// if (wc != null) { -// for (Iterator it = res.iterator(); it.hasNext();) { -// DirectoryUser user = it.next(); -// LdapName dn = user.getDn(); -// if (wc.getDeletedUsers().containsKey(dn)) -// it.remove(); -// } -// for (DirectoryUser user : wc.getNewUsers().values()) { -// if (f == null || f.match(user.getProperties())) -// res.add(user); -// } -// // no need to check modified users, -// // since doGetRoles was already based on the modified attributes -// } List res = getRoles(getBaseDn(), filter, true); return res.toArray(new Role[res.size()]); } @@ -281,19 +339,19 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { } // if non deep we also search users and groups - if (!deep) { - try { - if (!(searchBase.endsWith(new LdapName(getUserBase())) - || searchBase.endsWith(new LdapName(getGroupBase())))) { - LdapName usersBase = (LdapName) ((LdapName) searchBase.clone()).add(getUserBase()); - res.addAll(getRoles(usersBase, filter, false)); - LdapName groupsBase = (LdapName) ((LdapName) searchBase.clone()).add(getGroupBase()); - res.addAll(getRoles(groupsBase, filter, false)); - } - } catch (InvalidNameException e) { - throw new IllegalStateException("Cannot search users and groups", e); - } - } +// if (!deep) { +// try { +// if (!(searchBase.endsWith(new LdapName(getUserBase())) +// || searchBase.endsWith(new LdapName(getGroupBase())))) { +// LdapName usersBase = (LdapName) ((LdapName) searchBase.clone()).add(getUserBase()); +// res.addAll(getRoles(usersBase, filter, false)); +// LdapName groupsBase = (LdapName) ((LdapName) searchBase.clone()).add(getGroupBase()); +// res.addAll(getRoles(groupsBase, filter, false)); +// } +// } catch (InvalidNameException e) { +// throw new IllegalStateException("Cannot search users and groups", e); +// } +// } return res; } @@ -434,50 +492,55 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { /* * HIERARCHY */ - @Override - public int getHierarchyChildCount() { - return 0; - } - - @Override - public HierarchyUnit getHierarchyChild(int i) { - throw new IllegalArgumentException("No child hierarchy unit available"); - } - - @Override - public HierarchyUnit getParent() { - return null; - } - - @Override - public int getHierarchyUnitType() { - return 0; - } - - @Override - public String getHierarchyUnitName() { - String name = LdapNameUtils.getLastRdnAsString(baseDn); - // TODO check ou, o, etc. - return name; - } +// @Override +// public int getHierarchyChildCount() { +// return 0; +// } +// +// @Override +// public HierarchyUnit getHierarchyChild(int i) { +// throw new IllegalArgumentException("No child hierarchy unit available"); +// } +// +// @Override +// public HierarchyUnit getParent() { +// return null; +// } +// +// @Override +// public int getHierarchyUnitType() { +// return 0; +// } +// +// @Override +// public String getHierarchyUnitName() { +// String name = LdapNameUtils.getLastRdnValue(baseDn); +// // TODO check ou, o, etc. +// return name; +// } @Override public HierarchyUnit getHierarchyUnit(String path) { - return null; + throw new UnsupportedOperationException(); } @Override public HierarchyUnit getHierarchyUnit(Role role) { - return null; + throw new UnsupportedOperationException(); } +// @Override +// public List getHierarchyUnitRoles(String filter, boolean deep) { +// try { +// return getRoles(getBaseDn(), filter, deep); +// } catch (InvalidSyntaxException e) { +// throw new IllegalArgumentException("Cannot filter " + filter + " " + getBaseDn(), e); +// } +// } + @Override - public List getRoles(String filter, boolean deep) { - try { - return getRoles(getBaseDn(), filter, deep); - } catch (InvalidSyntaxException e) { - throw new IllegalArgumentException("Cannot filter " + filter + " " + getBaseDn(), e); - } + public Iterable getRootHierarchyUnits() { + throw new UnsupportedOperationException(); } // GETTERS @@ -562,7 +625,7 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { return groupBase; } - public LdapName getBaseDn() { + LdapName getBaseDn() { return (LdapName) baseDn.clone(); } diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java b/org.argeo.util/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java index ad6a83fb5..54a6d9e31 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java @@ -160,7 +160,7 @@ public class AggregatingUserAdmin implements UserAdmin { if (!(ud instanceof AbstractUserDirectory)) throw new IllegalArgumentException("Only " + AbstractUserDirectory.class.getName() + " is supported"); AbstractUserDirectory userDirectory = (AbstractUserDirectory) ud; - String basePath = userDirectory.getBasePath(); + String basePath = userDirectory.getGlobalId(); if (isSystemRolesBaseDn(basePath)) { this.systemRoles = userDirectory; systemRoles.setExternalRoles(this); @@ -272,7 +272,7 @@ public class AggregatingUserAdmin implements UserAdmin { } public Set getUserDirectories() { - TreeSet res = new TreeSet<>((o1, o2) -> o1.getBasePath().compareTo(o2.getBasePath())); + TreeSet res = new TreeSet<>((o1, o2) -> o1.getGlobalId().compareTo(o2.getGlobalId())); res.addAll(businessRoles.values()); return res; } diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/HierarchyUnit.java b/org.argeo.util/src/org/argeo/osgi/useradmin/HierarchyUnit.java index 249434631..9783a2786 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/HierarchyUnit.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/HierarchyUnit.java @@ -1,7 +1,6 @@ package org.argeo.osgi.useradmin; import java.util.List; -import java.util.Map; import org.osgi.service.useradmin.Role; @@ -13,17 +12,17 @@ public interface HierarchyUnit { String getHierarchyUnitName(); - int getHierarchyChildCount(); - HierarchyUnit getParent(); - HierarchyUnit getHierarchyChild(int i); + Iterable getDirectHierachyUnits(); int getHierarchyUnitType(); String getBasePath(); - List getRoles(String filter, boolean deep); - + List getHierarchyUnitRoles(String filter, boolean deep); + + UserDirectory getDirectory(); + // Map getHierarchyProperties(); } diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/LdapNameUtils.java b/org.argeo.util/src/org/argeo/osgi/useradmin/LdapNameUtils.java index b98c8bff7..f76d9441b 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/LdapNameUtils.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/LdapNameUtils.java @@ -1,27 +1,27 @@ package org.argeo.osgi.useradmin; -import java.util.StringJoiner; - import javax.naming.InvalidNameException; import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; /** Utilities to simplify using {@link LdapName}. */ -public class LdapNameUtils { - - public static String toRevertPath(String dn, String prefix) { - if (!dn.endsWith(prefix)) - throw new IllegalArgumentException("Prefix " + prefix + " not consistent with " + dn); - String relativeName = dn.substring(0, dn.length() - prefix.length() - 1); - LdapName name = toLdapName(relativeName); - StringJoiner path = new StringJoiner("/"); - for (int i = 0; i < name.size(); i++) { - path.add(name.get(i)); +class LdapNameUtils { + + static LdapName relativeName(LdapName prefix, LdapName dn) { + try { + if (!dn.startsWith(prefix)) + throw new IllegalArgumentException("Prefix " + prefix + " not consistent with " + dn); + LdapName res = (LdapName) dn.clone(); + for (int i = 0; i < prefix.size(); i++) { + res.remove(0); + } + return res; + } catch (InvalidNameException e) { + throw new IllegalStateException("Cannot find realtive name", e); } - return path.toString(); } - public static LdapName getParent(LdapName dn) { + static LdapName getParent(LdapName dn) { try { LdapName parent = (LdapName) dn.clone(); parent.remove(parent.size() - 1); @@ -31,7 +31,7 @@ public class LdapNameUtils { } } - public static LdapName toLdapName(String distinguishedName) { + static LdapName toLdapName(String distinguishedName) { try { return new LdapName(distinguishedName); } catch (InvalidNameException e) { @@ -39,15 +39,15 @@ public class LdapNameUtils { } } - public static Rdn getLastRdn(LdapName dn) { + static Rdn getLastRdn(LdapName dn) { return dn.getRdn(dn.size() - 1); } - public static String getLastRdnAsString(LdapName dn) { + static String getLastRdnAsString(LdapName dn) { return getLastRdn(dn).toString(); } - public static String getLastRdnValue(LdapName dn) { + static String getLastRdnValue(LdapName dn) { return getLastRdn(dn).getValue().toString(); } diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/LdifHierarchyUnit.java b/org.argeo.util/src/org/argeo/osgi/useradmin/LdifHierarchyUnit.java index 593416259..271f23611 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/LdifHierarchyUnit.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/LdifHierarchyUnit.java @@ -32,19 +32,14 @@ class LdifHierarchyUnit implements HierarchyUnit { this.attributes = attributes; } - @Override - public int getHierarchyChildCount() { - return children.size(); - } - @Override public HierarchyUnit getParent() { return parent; } @Override - public HierarchyUnit getHierarchyChild(int i) { - return children.get(i); + public Iterable getDirectHierachyUnits() { + return children; } @Override @@ -54,7 +49,7 @@ class LdifHierarchyUnit implements HierarchyUnit { @Override public String getHierarchyUnitName() { - String name = LdapNameUtils.getLastRdnAsString(dn); + String name = LdapNameUtils.getLastRdnValue(dn); // TODO check ou, o, etc. return name; } @@ -69,7 +64,7 @@ class LdifHierarchyUnit implements HierarchyUnit { } @Override - public List getRoles(String filter, boolean deep) { + public List getHierarchyUnitRoles(String filter, boolean deep) { try { return directory.getRoles(dn, filter, deep); } catch (InvalidSyntaxException e) { @@ -77,6 +72,11 @@ class LdifHierarchyUnit implements HierarchyUnit { } } + @Override + public UserDirectory getDirectory() { + return directory; + } + @Override public int hashCode() { return dn.hashCode(); diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/LdifUser.java b/org.argeo.util/src/org/argeo/osgi/useradmin/LdifUser.java index c03465b70..552bfdc8d 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/LdifUser.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/LdifUser.java @@ -25,6 +25,7 @@ import javax.naming.ldap.LdapName; import org.argeo.util.naming.AuthPassword; import org.argeo.util.naming.LdapAttrs; +import org.argeo.util.naming.LdapObjs; import org.argeo.util.naming.SharedSecret; /** Directory user implementation */ @@ -308,21 +309,34 @@ class LdifUser implements DirectoryUser { } if (attr.size() == 1) return value; -// if (!attr.getID().equals(LdapAttrs.objectClass.name())) -// return value; // special case for object class - NamingEnumeration en = attr.getAll(); - StringJoiner values = new StringJoiner("\n"); - // Set values = new HashSet(); - while (en.hasMore()) { - String v = en.next().toString(); - values.add(v); + if (attr.getID().equals(LdapAttrs.objectClass.name())) { + // TODO support multiple object classes + NamingEnumeration en = attr.getAll(); + String first = null; + attrs: while (en.hasMore()) { + String v = en.next().toString(); + if (v.equalsIgnoreCase(LdapObjs.top.name())) + continue attrs; + if (first == null) + first = v; + if (v.equalsIgnoreCase(userAdmin.getUserObjectClass())) + return userAdmin.getUserObjectClass(); + else if (v.equalsIgnoreCase(userAdmin.getGroupObjectClass())) + return userAdmin.getGroupObjectClass(); + } + if (first != null) + return first; + throw new IllegalStateException("Cannot find objectClass in " + value); + } else { + NamingEnumeration en = attr.getAll(); + StringJoiner values = new StringJoiner("\n"); + while (en.hasMore()) { + String v = en.next().toString(); + values.add(v); + } + return values.toString(); } - return values.toString(); -// if (objectClasses.contains(userAdmin.getUserObjectClass())) -// return userAdmin.getUserObjectClass(); -// else if (objectClasses.contains(userAdmin.getGroupObjectClass())) -// return userAdmin.getGroupObjectClass(); // else // return value; } catch (NamingException e) { diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/LdifUserAdmin.java b/org.argeo.util/src/org/argeo/osgi/useradmin/LdifUserAdmin.java index 6d9305a17..90b64b257 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/LdifUserAdmin.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/LdifUserAdmin.java @@ -15,6 +15,7 @@ import java.util.Collections; import java.util.Dictionary; import java.util.HashSet; import java.util.Hashtable; +import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.Set; @@ -161,14 +162,14 @@ public class LdifUserAdmin extends AbstractUserDirectory { } else if (objectClass.toLowerCase().equals(getGroupObjectClass().toLowerCase())) { groups.put(key, new LdifGroup(this, key, attributes)); break objectClasses; - } else if (objectClass.equalsIgnoreCase(LdapObjs.organization.name())) { - // we only consider organizations which are not groups - hierarchy.put(key, new LdifHierarchyUnit(this, key, HierarchyUnit.ORGANIZATION, attributes)); - break objectClasses; +// } else if (objectClass.equalsIgnoreCase(LdapObjs.organization.name())) { +// // we only consider organizations which are not groups +// hierarchy.put(key, new LdifHierarchyUnit(this, key, HierarchyUnit.ORGANIZATION, attributes)); +// break objectClasses; } else if (objectClass.equalsIgnoreCase(LdapObjs.organizationalUnit.name())) { - String name = key.getRdn(key.size() - 1).toString(); - if (getUserBase().equalsIgnoreCase(name) || getGroupBase().equalsIgnoreCase(name)) - break objectClasses; // skip +// String name = key.getRdn(key.size() - 1).toString(); +// if (getUserBase().equalsIgnoreCase(name) || getGroupBase().equalsIgnoreCase(name)) +// break objectClasses; // skip // TODO skip if it does not contain groups or users hierarchy.put(key, new LdifHierarchyUnit(this, key, HierarchyUnit.OU, attributes)); break objectClasses; @@ -183,7 +184,7 @@ public class LdifUserAdmin extends AbstractUserDirectory { LdifHierarchyUnit parent = hierarchy.get(parentDn); if (parent == null) { rootHierarchyUnits.add(unit); - unit.parent = this; + unit.parent = null; continue hierachyUnits; } parent.children.add(unit); @@ -311,27 +312,32 @@ public class LdifUserAdmin extends AbstractUserDirectory { /* * HIERARCHY */ - @Override - public int getHierarchyChildCount() { - return rootHierarchyUnits.size(); - } - @Override - public HierarchyUnit getHierarchyChild(int i) { - return rootHierarchyUnits.get(i); - } +// @Override +// public int getHierarchyChildCount() { +// return rootHierarchyUnits.size(); +// } +// +// @Override +// public HierarchyUnit getHierarchyChild(int i) { +// return rootHierarchyUnits.get(i); +// } @Override public HierarchyUnit getHierarchyUnit(String path) { - LdapName dn = LdapNameUtils.toLdapName(path); + LdapName dn = pathToName(path); return hierarchy.get(dn); } + @Override + public Iterable getRootHierarchyUnits() { + return rootHierarchyUnits; + } + @Override public HierarchyUnit getHierarchyUnit(Role role) { LdapName dn = LdapNameUtils.toLdapName(role.getName()); - // 2 levels - LdapName huDn = LdapNameUtils.getParent(LdapNameUtils.getParent(dn)); + LdapName huDn = LdapNameUtils.getParent(dn); HierarchyUnit hierarchyUnit = hierarchy.get(huDn); if (hierarchyUnit == null) throw new IllegalStateException("No hierarchy unit found for " + role); diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/UserDirectory.java b/org.argeo.util/src/org/argeo/osgi/useradmin/UserDirectory.java index 781b7855a..3800b7548 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/UserDirectory.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/UserDirectory.java @@ -6,12 +6,14 @@ import org.argeo.osgi.transaction.WorkControl; import org.osgi.service.useradmin.Role; /** Information about a user directory. */ -public interface UserDirectory extends HierarchyUnit { +public interface UserDirectory { /** * The base of the hierarchy defined by this directory. This could typically be * an LDAP base DN. */ - String getBasePath(); + String getGlobalId(); + + String getName(); // /** The base DN of all entries in this user directory */ // LdapName getBaseDn(); @@ -33,10 +35,18 @@ public interface UserDirectory extends HierarchyUnit { Optional getRealm(); + Iterable getRootHierarchyUnits(); + HierarchyUnit getHierarchyUnit(String path); HierarchyUnit getHierarchyUnit(Role role); + String getRolePath(Role role); + + String getRoleSimpleName(Role role); + + Role getRoleByPath(String path); + @Deprecated void setTransactionControl(WorkControl transactionControl); } diff --git a/org.argeo.util/src/org/argeo/util/LangUtils.java b/org.argeo.util/src/org/argeo/util/LangUtils.java index 36bec240e..1177d4f53 100644 --- a/org.argeo.util/src/org/argeo/util/LangUtils.java +++ b/org.argeo.util/src/org/argeo/util/LangUtils.java @@ -11,11 +11,12 @@ import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; import java.time.temporal.Temporal; import java.util.ArrayList; -import java.util.Collections; +import java.util.Collection; import java.util.Dictionary; import java.util.Enumeration; import java.util.HashMap; import java.util.Hashtable; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; @@ -236,6 +237,34 @@ public class LangUtils { return values; } + /** Size of an {@link Iterable}, optimised if it is a {@link Collection}. */ + public static int size(Iterable iterable) { + if (iterable instanceof Collection) + return ((Collection) iterable).size(); + + int size = 0; + for (Iterator it = iterable.iterator(); it.hasNext(); size++) + it.next(); + return size; + } + + public static T getAt(Iterable iterable, int index) { + if (iterable instanceof List) { + List lst = ((List) iterable); + if (index >= lst.size()) + throw new IllegalArgumentException("Index " + index + " is not available (size is " + lst.size() + ")"); + return lst.get(index); + } + int i = 0; + for (Iterator it = iterable.iterator(); it.hasNext(); i++) { + if (i == index) + return it.next(); + else + it.next(); + } + throw new IllegalArgumentException("Index " + index + " is not available (size is " + i + ")"); + } + /* * EXCEPTIONS */ -- 2.30.2