From dc27b57704278684e72efcaf72b01c5b91df39f8 Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Wed, 22 Jun 2022 13:13:19 +0200 Subject: [PATCH] Separate LDIF and LDAP DAOs --- .../cms/internal/runtime/CmsUserAdmin.java | 28 +- .../osgi/useradmin/AggregatingUserAdmin.java | 34 +-- .../argeo/osgi/useradmin/DirectoryGroup.java | 6 +- .../argeo/osgi/useradmin/DirectoryUser.java | 3 +- ...Directory.java => DirectoryUserAdmin.java} | 241 +++++++++-------- .../org/argeo/osgi/useradmin/LdifGroup.java | 47 ++-- .../org/argeo/osgi/useradmin/LdifUser.java | 9 +- .../argeo/osgi/useradmin/OsUserDirectory.java | 48 ++-- .../org/argeo/util/directory/Directory.java | 4 - .../directory/ldap/AbstractLdapDirectory.java | 181 ++++++++++--- .../ldap/AbstractLdapDirectoryDao.java | 34 +++ .../directory/ldap/AbstractLdapEntry.java | 39 ++- .../directory/ldap/LdapDao.java} | 121 +++++---- .../util/directory/ldap/LdapDirectoryDao.java | 32 +++ .../argeo/util/directory/ldap/LdapEntry.java | 3 + .../directory/ldap/LdapHierarchyUnit.java | 59 +---- .../directory/ldap/LdifDao.java} | 249 ++++++++++-------- 17 files changed, 672 insertions(+), 466 deletions(-) rename org.argeo.util/src/org/argeo/osgi/useradmin/{AbstractUserDirectory.java => DirectoryUserAdmin.java} (60%) create mode 100644 org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapDirectoryDao.java rename org.argeo.util/src/org/argeo/{osgi/useradmin/LdapUserAdmin.java => util/directory/ldap/LdapDao.java} (63%) create mode 100644 org.argeo.util/src/org/argeo/util/directory/ldap/LdapDirectoryDao.java rename org.argeo.util/src/org/argeo/{osgi/useradmin/LdifUserAdmin.java => util/directory/ldap/LdifDao.java} (54%) 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 64b25c99e..64e32b16a 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 @@ -33,10 +33,8 @@ import org.argeo.api.cms.CmsConstants; import org.argeo.api.cms.CmsLog; import org.argeo.cms.internal.http.client.HttpCredentialProvider; import org.argeo.cms.internal.http.client.SpnegoAuthScheme; +import org.argeo.osgi.useradmin.DirectoryUserAdmin; import org.argeo.osgi.useradmin.AggregatingUserAdmin; -import org.argeo.osgi.useradmin.LdapUserAdmin; -import org.argeo.osgi.useradmin.LdifUserAdmin; -import org.argeo.osgi.useradmin.OsUserDirectory; import org.argeo.osgi.useradmin.UserDirectory; import org.argeo.util.directory.DirectoryConf; import org.argeo.util.naming.dns.DnsBrowser; @@ -95,18 +93,18 @@ public class CmsUserAdmin extends AggregatingUserAdmin { } // Create - UserDirectory userDirectory; - if (realm != null || DirectoryConf.SCHEME_LDAP.equals(u.getScheme()) - || DirectoryConf.SCHEME_LDAPS.equals(u.getScheme())) { - userDirectory = new LdapUserAdmin(properties); - } else if (DirectoryConf.SCHEME_FILE.equals(u.getScheme())) { - userDirectory = new LdifUserAdmin(u, properties); - } else if (DirectoryConf.SCHEME_OS.equals(u.getScheme())) { - userDirectory = new OsUserDirectory(u, properties); - singleUser = true; - } else { - throw new IllegalArgumentException("Unsupported scheme " + u.getScheme()); - } + UserDirectory userDirectory = new DirectoryUserAdmin(u, properties); +// if (realm != null || DirectoryConf.SCHEME_LDAP.equals(u.getScheme()) +// || DirectoryConf.SCHEME_LDAPS.equals(u.getScheme())) { +// userDirectory = new LdapUserAdmin(properties); +// } else if (DirectoryConf.SCHEME_FILE.equals(u.getScheme())) { +// userDirectory = new LdifUserAdmin(u, properties); +// } else if (DirectoryConf.SCHEME_OS.equals(u.getScheme())) { +// userDirectory = new OsUserDirectory(u, properties); +// singleUser = true; +// } else { +// throw new IllegalArgumentException("Unsupported scheme " + u.getScheme()); +// } String basePath = userDirectory.getContext(); addUserDirectory(userDirectory); 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 955178ce4..3857b08d0 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java @@ -1,6 +1,6 @@ package org.argeo.osgi.useradmin; -import static org.argeo.osgi.useradmin.AbstractUserDirectory.toLdapName; +import static org.argeo.osgi.useradmin.DirectoryUserAdmin.toLdapName; import java.util.ArrayList; import java.util.Arrays; @@ -30,9 +30,9 @@ public class AggregatingUserAdmin implements UserAdmin { private final LdapName tokensBaseDn; // DAOs - private AbstractUserDirectory systemRoles = null; - private AbstractUserDirectory tokens = null; - private Map businessRoles = new HashMap(); + private DirectoryUserAdmin systemRoles = null; + private DirectoryUserAdmin tokens = null; + private Map businessRoles = new HashMap(); // TODO rather use an empty constructor and an init method public AggregatingUserAdmin(String systemRolesBaseDn, String tokensBaseDn) { @@ -91,7 +91,7 @@ public class AggregatingUserAdmin implements UserAdmin { if (user == null) {// anonymous return systemRoles.getAuthorization(null); } - AbstractUserDirectory userReferentialOfThisUser = findUserAdmin(user.getName()); + DirectoryUserAdmin userReferentialOfThisUser = findUserAdmin(user.getName()); Authorization rawAuthorization = userReferentialOfThisUser.getAuthorization(user); String usernameToUse; String displayNameToUse; @@ -113,11 +113,11 @@ public class AggregatingUserAdmin implements UserAdmin { } // gather roles from other referentials - final AbstractUserDirectory userAdminToUse;// possibly scoped when authenticating + final DirectoryUserAdmin userAdminToUse;// possibly scoped when authenticating if (user instanceof DirectoryUser) { userAdminToUse = userReferentialOfThisUser; } else if (user instanceof AuthenticatingUser) { - userAdminToUse = (AbstractUserDirectory) userReferentialOfThisUser.scope(user); + userAdminToUse = (DirectoryUserAdmin) userReferentialOfThisUser.scope(user); } else { throw new IllegalArgumentException("Unsupported user type " + user.getClass()); } @@ -157,9 +157,9 @@ public class AggregatingUserAdmin implements UserAdmin { // USER ADMIN AGGREGATOR // protected void addUserDirectory(UserDirectory ud) { - if (!(ud instanceof AbstractUserDirectory)) - throw new IllegalArgumentException("Only " + AbstractUserDirectory.class.getName() + " is supported"); - AbstractUserDirectory userDirectory = (AbstractUserDirectory) ud; + if (!(ud instanceof DirectoryUserAdmin)) + throw new IllegalArgumentException("Only " + DirectoryUserAdmin.class.getName() + " is supported"); + DirectoryUserAdmin userDirectory = (DirectoryUserAdmin) ud; String basePath = userDirectory.getContext(); if (isSystemRolesBaseDn(basePath)) { this.systemRoles = userDirectory; @@ -181,7 +181,7 @@ public class AggregatingUserAdmin implements UserAdmin { protected void postAdd(UserDirectory userDirectory) { } - private AbstractUserDirectory findUserAdmin(String name) { + private DirectoryUserAdmin findUserAdmin(String name) { try { return findUserAdmin(new LdapName(name)); } catch (InvalidNameException e) { @@ -189,14 +189,14 @@ public class AggregatingUserAdmin implements UserAdmin { } } - private AbstractUserDirectory findUserAdmin(LdapName name) { + private DirectoryUserAdmin findUserAdmin(LdapName name) { if (name.startsWith(systemRolesBaseDn)) return systemRoles; if (tokensBaseDn != null && name.startsWith(tokensBaseDn)) return tokens; - List res = new ArrayList<>(1); + List res = new ArrayList<>(1); userDirectories: for (LdapName baseDn : businessRoles.keySet()) { - AbstractUserDirectory userDirectory = businessRoles.get(baseDn); + DirectoryUserAdmin userDirectory = businessRoles.get(baseDn); if (name.startsWith(baseDn)) { if (userDirectory.isDisabled()) continue userDirectories; @@ -240,7 +240,7 @@ public class AggregatingUserAdmin implements UserAdmin { public void destroy() { for (LdapName name : businessRoles.keySet()) { - AbstractUserDirectory userDirectory = businessRoles.get(name); + DirectoryUserAdmin userDirectory = businessRoles.get(name); destroy(userDirectory); } businessRoles.clear(); @@ -249,7 +249,7 @@ public class AggregatingUserAdmin implements UserAdmin { systemRoles = null; } - private void destroy(AbstractUserDirectory userDirectory) { + private void destroy(DirectoryUserAdmin userDirectory) { preDestroy(userDirectory); userDirectory.destroy(); } @@ -260,7 +260,7 @@ public class AggregatingUserAdmin implements UserAdmin { LdapName baseDn = toLdapName(basePath); if (!businessRoles.containsKey(baseDn)) throw new IllegalStateException("No user directory registered for " + baseDn); - AbstractUserDirectory userDirectory = businessRoles.remove(baseDn); + DirectoryUserAdmin userDirectory = businessRoles.remove(baseDn); destroy(userDirectory); } diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/DirectoryGroup.java b/org.argeo.util/src/org/argeo/osgi/useradmin/DirectoryGroup.java index 7f8046313..1d58a2dae 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/DirectoryGroup.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/DirectoryGroup.java @@ -1,12 +1,8 @@ package org.argeo.osgi.useradmin; -import java.util.List; - -import javax.naming.ldap.LdapName; - import org.osgi.service.useradmin.Group; /** A group in a user directroy. */ interface DirectoryGroup extends Group, DirectoryUser { - List getMemberNames(); +// List getMemberNames(); } diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/DirectoryUser.java b/org.argeo.util/src/org/argeo/osgi/useradmin/DirectoryUser.java index c82c5a01d..18b28a288 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/DirectoryUser.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/DirectoryUser.java @@ -1,8 +1,7 @@ package org.argeo.osgi.useradmin; -import org.argeo.util.directory.ldap.LdapEntry; import org.osgi.service.useradmin.User; /** A user in a user directory. */ -interface DirectoryUser extends User, LdapEntry { +interface DirectoryUser extends User { } diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java b/org.argeo.util/src/org/argeo/osgi/useradmin/DirectoryUserAdmin.java similarity index 60% rename from org.argeo.util/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java rename to org.argeo.util/src/org/argeo/osgi/useradmin/DirectoryUserAdmin.java index 4b13728af..9f6d62d7a 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/DirectoryUserAdmin.java @@ -8,28 +8,28 @@ import static org.argeo.util.naming.LdapObjs.person; import static org.argeo.util.naming.LdapObjs.top; import java.net.URI; +import java.nio.channels.UnsupportedAddressTypeException; import java.util.ArrayList; import java.util.Dictionary; import java.util.Iterator; import java.util.List; +import javax.naming.Context; import javax.naming.InvalidNameException; -import javax.naming.NameNotFoundException; -import javax.naming.NamingEnumeration; -import javax.naming.NamingException; -import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; import javax.naming.directory.BasicAttribute; import javax.naming.directory.BasicAttributes; import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; +import org.argeo.util.directory.DirectoryConf; +import org.argeo.util.directory.DirectoryDigestUtils; import org.argeo.util.directory.HierarchyUnit; import org.argeo.util.directory.ldap.AbstractLdapDirectory; import org.argeo.util.directory.ldap.LdapEntry; import org.argeo.util.directory.ldap.LdapEntryWorkingCopy; import org.argeo.util.directory.ldap.LdapNameUtils; -import org.argeo.util.naming.LdapAttrs; +import org.argeo.util.directory.ldap.LdifDao; import org.argeo.util.naming.LdapObjs; import org.osgi.framework.Filter; import org.osgi.framework.FrameworkUtil; @@ -40,7 +40,7 @@ import org.osgi.service.useradmin.User; import org.osgi.service.useradmin.UserAdmin; /** Base class for a {@link UserDirectory}. */ -abstract class AbstractUserDirectory extends AbstractLdapDirectory implements UserAdmin, UserDirectory { +public class DirectoryUserAdmin extends AbstractLdapDirectory implements UserAdmin, UserDirectory { private UserAdmin externalRoles; // private List indexedUserProperties = Arrays @@ -49,34 +49,72 @@ abstract class AbstractUserDirectory extends AbstractLdapDirectory implements Us // Transaction // private TransactionManager transactionManager; - AbstractUserDirectory(URI uriArg, Dictionary props, boolean scoped) { - super(uriArg, props, scoped); + public DirectoryUserAdmin(URI uriArg, Dictionary props) { + this(uriArg, props, false); } - /* - * ABSTRACT METHODS - */ - - protected abstract AbstractLdapDirectory scope(User user); + public DirectoryUserAdmin(URI uriArg, Dictionary props, boolean scoped) { + super(uriArg, props, scoped); + } - /** Returns the groups this user is a direct member of. */ - protected abstract List getDirectGroups(LdapName dn); + public DirectoryUserAdmin(Dictionary props) { + this(null, props); + } /* - * INITIALIZATION + * ABSTRACT METHODS */ - public void init() { - + protected AbstractLdapDirectory scope(User user) { + throw new UnsupportedAddressTypeException(); } - public void destroy() { + protected DirectoryUserAdmin scopeLdap(User user) { + Dictionary credentials = user.getCredentials(); + String username = (String) credentials.get(SHARED_STATE_USERNAME); + if (username == null) + username = user.getName(); + Dictionary properties = cloneProperties(); + properties.put(Context.SECURITY_PRINCIPAL, username.toString()); + Object pwdCred = credentials.get(SHARED_STATE_PASSWORD); + byte[] pwd = (byte[]) pwdCred; + if (pwd != null) { + char[] password = DirectoryDigestUtils.bytesToChars(pwd); + properties.put(Context.SECURITY_CREDENTIALS, new String(password)); + } else { + properties.put(Context.SECURITY_AUTHENTICATION, "GSSAPI"); + } + return new DirectoryUserAdmin(null, properties, true); + } + protected DirectoryUserAdmin scopeLdif(User user) { + Dictionary credentials = user.getCredentials(); + String username = (String) credentials.get(SHARED_STATE_USERNAME); + if (username == null) + username = user.getName(); + Object pwdCred = credentials.get(SHARED_STATE_PASSWORD); + byte[] pwd = (byte[]) pwdCred; + if (pwd != null) { + char[] password = DirectoryDigestUtils.bytesToChars(pwd); + User directoryUser = (User) getRole(username); + if (!directoryUser.hasCredential(null, password)) + throw new IllegalStateException("Invalid credentials"); + } else { + throw new IllegalStateException("Password is required"); + } + Dictionary properties = cloneProperties(); + properties.put(DirectoryConf.readOnly.name(), "true"); + DirectoryUserAdmin scopedUserAdmin = new DirectoryUserAdmin(null, properties, true); +// scopedUserAdmin.groups = Collections.unmodifiableNavigableMap(groups); +// scopedUserAdmin.users = Collections.unmodifiableNavigableMap(users); + // FIXME do it better + ((LdifDao) getDirectoryDao()).scope((LdifDao) scopedUserAdmin.getDirectoryDao()); + return scopedUserAdmin; } @Override public String getRolePath(Role role) { - return nameToRelativePath(((DirectoryUser) role).getDn()); + return nameToRelativePath(LdapNameUtils.toLdapName(role.getName())); } @Override @@ -88,7 +126,7 @@ abstract class AbstractUserDirectory extends AbstractLdapDirectory implements Us @Override public Role getRoleByPath(String path) { - return doGetRole(pathToName(path)); + return (Role) doGetRole(pathToName(path)); } protected List getAllRoles(DirectoryUser user) { @@ -102,33 +140,39 @@ abstract class AbstractUserDirectory extends AbstractLdapDirectory implements Us } private void collectRoles(DirectoryUser user, List allRoles) { - Attributes attrs = user.getAttributes(); - // TODO centralize attribute name - Attribute memberOf = attrs.get(LdapAttrs.memberOf.name()); - // if user belongs to this directory, we only check meberOf - if (memberOf != null && user.getDn().startsWith(getBaseDn())) { - try { - NamingEnumeration values = memberOf.getAll(); - while (values.hasMore()) { - Object value = values.next(); - LdapName groupDn = new LdapName(value.toString()); - DirectoryUser group = doGetRole(groupDn); - if (group != null) - allRoles.add(group); - } - } catch (NamingException e) { - throw new IllegalStateException("Cannot get memberOf groups for " + user, e); - } - } else { - for (LdapName groupDn : getDirectGroups(user.getDn())) { - // TODO check for loops - DirectoryUser group = doGetRole(groupDn); - if (group != null) { - allRoles.add(group); - collectRoles(group, allRoles); - } - } + List allEntries = new ArrayList<>(); + LdapEntry entry = (LdapEntry) user; + collectGroups(entry, allEntries); + for (LdapEntry e : allEntries) { + allRoles.add((Role) e); } +// Attributes attrs = user.getAttributes(); +// // TODO centralize attribute name +// Attribute memberOf = attrs.get(LdapAttrs.memberOf.name()); +// // if user belongs to this directory, we only check memberOf +// if (memberOf != null && user.getDn().startsWith(getBaseDn())) { +// try { +// NamingEnumeration values = memberOf.getAll(); +// while (values.hasMore()) { +// Object value = values.next(); +// LdapName groupDn = new LdapName(value.toString()); +// DirectoryUser group = doGetRole(groupDn); +// if (group != null) +// allRoles.add(group); +// } +// } catch (NamingException e) { +// throw new IllegalStateException("Cannot get memberOf groups for " + user, e); +// } +// } else { +// for (LdapName groupDn : getDirectoryDao().getDirectGroups(user.getDn())) { +// // TODO check for loops +// DirectoryUser group = doGetRole(groupDn); +// if (group != null) { +// allRoles.add(group); +// collectRoles(group, allRoles); +// } +// } +// } } private void collectAnonymousRoles(List allRoles) { @@ -138,24 +182,7 @@ abstract class AbstractUserDirectory extends AbstractLdapDirectory implements Us // USER ADMIN @Override public Role getRole(String name) { - return doGetRole(toLdapName(name)); - } - - protected DirectoryUser doGetRole(LdapName dn) { - LdapEntryWorkingCopy wc = getWorkingCopy(); - DirectoryUser user; - try { - user = (DirectoryUser) daoGetEntry(dn); - } catch (NameNotFoundException e) { - user = null; - } - if (wc != null) { - if (user == null && wc.getNewData().containsKey(dn)) - user = (DirectoryUser) wc.getNewData().get(dn); - else if (wc.getDeletedData().containsKey(dn)) - user = null; - } - return user; + return (Role) doGetRole(toLdapName(name)); } @Override @@ -166,18 +193,19 @@ abstract class AbstractUserDirectory extends AbstractLdapDirectory implements Us List getRoles(LdapName searchBase, String filter, boolean deep) throws InvalidSyntaxException { LdapEntryWorkingCopy wc = getWorkingCopy(); - Filter f = filter != null ? FrameworkUtil.createFilter(filter) : null; - List searchRes = doGetEntries(searchBase, f, deep); +// Filter f = filter != null ? FrameworkUtil.createFilter(filter) : null; + List searchRes = getDirectoryDao().doGetEntries(searchBase, filter, deep); List res = new ArrayList<>(); for (LdapEntry entry : searchRes) res.add((DirectoryUser) entry); if (wc != null) { for (Iterator it = res.iterator(); it.hasNext();) { DirectoryUser user = (DirectoryUser) it.next(); - LdapName dn = user.getDn(); + LdapName dn = LdapNameUtils.toLdapName(user.getName()); if (wc.getDeletedData().containsKey(dn)) it.remove(); } + Filter f = filter != null ? FrameworkUtil.createFilter(filter) : null; for (LdapEntry ldapEntry : wc.getNewData().values()) { DirectoryUser user = (DirectoryUser) ldapEntry; if (f == null || f.match(user.getProperties())) @@ -224,14 +252,10 @@ abstract class AbstractUserDirectory extends AbstractLdapDirectory implements Us } protected void doGetUser(String key, String value, List collectedUsers) { - try { - Filter f = FrameworkUtil.createFilter("(" + key + "=" + value + ")"); - List users = doGetEntries(getBaseDn(), f, true); - for (LdapEntry entry : users) - collectedUsers.add((DirectoryUser) entry); - } catch (InvalidSyntaxException e) { - throw new IllegalArgumentException("Cannot get user with " + key + "=" + value, e); - } + String f = "(" + key + "=" + value + ")"; + List users = getDirectoryDao().doGetEntries(getBaseDn(), f, true); + for (LdapEntry entry : users) + collectedUsers.add((DirectoryUser) entry); } @Override @@ -240,7 +264,7 @@ abstract class AbstractUserDirectory extends AbstractLdapDirectory implements Us return new LdifAuthorization(user, getAllRoles((DirectoryUser) user)); } else { // bind - AbstractUserDirectory scopedUserAdmin = (AbstractUserDirectory) scope(user); + DirectoryUserAdmin scopedUserAdmin = (DirectoryUserAdmin) scope(user); try { DirectoryUser directoryUser = (DirectoryUser) scopedUserAdmin.getRole(user.getName()); if (directoryUser == null) @@ -259,7 +283,8 @@ abstract class AbstractUserDirectory extends AbstractLdapDirectory implements Us checkEdit(); LdapEntryWorkingCopy wc = getWorkingCopy(); LdapName dn = toLdapName(name); - if ((daoHasEntry(dn) && !wc.getDeletedData().containsKey(dn)) || wc.getNewData().containsKey(dn)) + if ((getDirectoryDao().daoHasEntry(dn) && !wc.getDeletedData().containsKey(dn)) + || wc.getNewData().containsKey(dn)) throw new IllegalArgumentException("Already a role " + name); BasicAttributes attrs = new BasicAttributes(true); // attrs.put(LdifName.dn.name(), dn.toString()); @@ -272,17 +297,17 @@ abstract class AbstractUserDirectory extends AbstractLdapDirectory implements Us return getRole(name); } else { wc.getModifiedData().put(dn, attrs); - DirectoryUser newRole = newRole(dn, type, attrs); + LdapEntry newRole = newRole(dn, type, attrs); wc.getNewData().put(dn, newRole); - return newRole; + return (Role) newRole; } } - protected DirectoryUser newRole(LdapName dn, int type, Attributes attrs) { - DirectoryUser newRole; + protected LdapEntry newRole(LdapName dn, int type, Attributes attrs) { + LdapEntry newRole; BasicAttribute objClass = new BasicAttribute(objectClass.name()); if (type == Role.USER) { - String userObjClass = newUserObjectClass(dn); + String userObjClass = getUserObjectClass(); objClass.add(userObjClass); if (inetOrgPerson.name().equals(userObjClass)) { objClass.add(organizationalPerson.name()); @@ -308,22 +333,23 @@ abstract class AbstractUserDirectory extends AbstractLdapDirectory implements Us @Override public boolean removeRole(String name) { - checkEdit(); - LdapEntryWorkingCopy wc = getWorkingCopy(); - LdapName dn = toLdapName(name); - boolean actuallyDeleted; - if (daoHasEntry(dn) || wc.getNewData().containsKey(dn)) { - DirectoryUser user = (DirectoryUser) getRole(name); - wc.getDeletedData().put(dn, user); - actuallyDeleted = true; - } else {// just removing from groups (e.g. system roles) - actuallyDeleted = false; - } - for (LdapName groupDn : getDirectGroups(dn)) { - DirectoryUser group = doGetRole(groupDn); - group.getAttributes().get(getMemberAttributeId()).remove(dn.toString()); - } - return actuallyDeleted; + return removeEntry(LdapNameUtils.toLdapName(name)); +// checkEdit(); +// LdapEntryWorkingCopy wc = getWorkingCopy(); +// LdapName dn = toLdapName(name); +// boolean actuallyDeleted; +// if (getDirectoryDao().daoHasEntry(dn) || wc.getNewData().containsKey(dn)) { +// DirectoryUser user = (DirectoryUser) getRole(name); +// wc.getDeletedData().put(dn, user); +// actuallyDeleted = true; +// } else {// just removing from groups (e.g. system roles) +// actuallyDeleted = false; +// } +// for (LdapName groupDn : getDirectoryDao().getDirectGroups(dn)) { +// LdapEntry group = doGetRole(groupDn); +// group.getAttributes().get(getMemberAttributeId()).remove(dn.toString()); +// } +// return actuallyDeleted; } /* @@ -333,7 +359,7 @@ abstract class AbstractUserDirectory extends AbstractLdapDirectory implements Us public HierarchyUnit getHierarchyUnit(Role role) { LdapName dn = LdapNameUtils.toLdapName(role.getName()); LdapName huDn = LdapNameUtils.getParent(dn); - HierarchyUnit hierarchyUnit = doGetHierarchyUnit(huDn); + HierarchyUnit hierarchyUnit = getDirectoryDao().doGetHierarchyUnit(huDn); if (hierarchyUnit == null) throw new IllegalStateException("No hierarchy unit found for " + role); return hierarchyUnit; @@ -352,12 +378,12 @@ abstract class AbstractUserDirectory extends AbstractLdapDirectory implements Us /* * ROLES CREATION */ - protected DirectoryUser newUser(LdapName name, Attributes attrs) { + protected LdapEntry newUser(LdapName name, Attributes attrs) { // TODO support devices, applications, etc. return new LdifUser.LdifPerson(this, name, attrs); } - protected DirectoryGroup newGroup(LdapName name, Attributes attrs) { + protected LdapEntry newGroup(LdapName name, Attributes attrs) { if (LdapNameUtils.getParentRdn(name).equals(getSystemRoleBaseRdn())) return new LdifGroup.LdifSystemPermissions(this, name, attrs); @@ -373,17 +399,6 @@ abstract class AbstractUserDirectory extends AbstractLdapDirectory implements Us return externalRoles; } - protected int roleType(LdapName dn) { - Rdn technicalRdn = LdapNameUtils.getParentRdn(dn); - if (getGroupBaseRdn().equals(technicalRdn) || getSystemRoleBaseRdn().equals(technicalRdn)) - return Role.GROUP; - else if (getUserBaseRdn().equals(technicalRdn)) - return Role.USER; - else - throw new IllegalArgumentException( - "Cannot dind role type, " + technicalRdn + " is not a technical RDN for " + dn); - } - public void setExternalRoles(UserAdmin externalRoles) { this.externalRoles = externalRoles; } diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/LdifGroup.java b/org.argeo.util/src/org/argeo/osgi/useradmin/LdifGroup.java index 72b08a8c3..7aad15a8c 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/LdifGroup.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/LdifGroup.java @@ -4,8 +4,6 @@ import java.util.ArrayList; import java.util.List; import javax.naming.InvalidNameException; -import javax.naming.NamingEnumeration; -import javax.naming.NamingException; import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; import javax.naming.ldap.LdapName; @@ -13,13 +11,14 @@ import javax.naming.ldap.LdapName; import org.argeo.util.directory.FunctionalGroup; import org.argeo.util.directory.Organization; import org.argeo.util.directory.SystemPermissions; +import org.argeo.util.directory.ldap.AbstractLdapDirectory; import org.osgi.service.useradmin.Role; /** Directory group implementation */ abstract class LdifGroup extends LdifUser implements DirectoryGroup { private final String memberAttributeId; - LdifGroup(AbstractUserDirectory userAdmin, LdapName dn, Attributes attributes) { + LdifGroup(AbstractLdapDirectory userAdmin, LdapName dn, Attributes attributes) { super(userAdmin, dn, attributes); memberAttributeId = userAdmin.getMemberAttributeId(); } @@ -74,7 +73,7 @@ abstract class LdifGroup extends LdifUser implements DirectoryGroup { @Override public Role[] getMembers() { List directMembers = new ArrayList(); - for (LdapName ldapName : getMemberNames()) { + for (LdapName ldapName : getReferences(memberAttributeId)) { Role role = findRole(ldapName); if (role == null) { throw new IllegalStateException("Role " + ldapName + " not found."); @@ -98,23 +97,23 @@ abstract class LdifGroup extends LdifUser implements DirectoryGroup { return role; } - @Override - public List getMemberNames() { - Attribute memberAttribute = getAttributes().get(memberAttributeId); - if (memberAttribute == null) - return new ArrayList(); - try { - List roles = new ArrayList(); - NamingEnumeration values = memberAttribute.getAll(); - while (values.hasMore()) { - LdapName dn = new LdapName(values.next().toString()); - roles.add(dn); - } - return roles; - } catch (NamingException e) { - throw new IllegalStateException("Cannot get members", e); - } - } +// @Override +// public List getMemberNames() { +// Attribute memberAttribute = getAttributes().get(memberAttributeId); +// if (memberAttribute == null) +// return new ArrayList(); +// try { +// List roles = new ArrayList(); +// NamingEnumeration values = memberAttribute.getAll(); +// while (values.hasMore()) { +// LdapName dn = new LdapName(values.next().toString()); +// roles.add(dn); +// } +// return roles; +// } catch (NamingException e) { +// throw new IllegalStateException("Cannot get members", e); +// } +// } @Override public Role[] getRequiredMembers() { @@ -131,7 +130,7 @@ abstract class LdifGroup extends LdifUser implements DirectoryGroup { */ static class LdifFunctionalGroup extends LdifGroup implements FunctionalGroup { - public LdifFunctionalGroup(AbstractUserDirectory userAdmin, LdapName dn, Attributes attributes) { + public LdifFunctionalGroup(DirectoryUserAdmin userAdmin, LdapName dn, Attributes attributes) { super(userAdmin, dn, attributes); } @@ -139,7 +138,7 @@ abstract class LdifGroup extends LdifUser implements DirectoryGroup { static class LdifOrganization extends LdifGroup implements Organization { - public LdifOrganization(AbstractUserDirectory userAdmin, LdapName dn, Attributes attributes) { + public LdifOrganization(DirectoryUserAdmin userAdmin, LdapName dn, Attributes attributes) { super(userAdmin, dn, attributes); } @@ -147,7 +146,7 @@ abstract class LdifGroup extends LdifUser implements DirectoryGroup { static class LdifSystemPermissions extends LdifGroup implements SystemPermissions { - public LdifSystemPermissions(AbstractUserDirectory userAdmin, LdapName dn, Attributes attributes) { + public LdifSystemPermissions(DirectoryUserAdmin userAdmin, LdapName dn, Attributes attributes) { super(userAdmin, dn, attributes); } 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 6cf6725cc..cceb6e461 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/LdifUser.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/LdifUser.java @@ -23,6 +23,7 @@ import javax.naming.ldap.LdapName; import org.argeo.util.directory.DirectoryDigestUtils; import org.argeo.util.directory.Person; +import org.argeo.util.directory.ldap.AbstractLdapDirectory; import org.argeo.util.directory.ldap.AbstractLdapEntry; import org.argeo.util.directory.ldap.AuthPassword; import org.argeo.util.naming.LdapAttrs; @@ -34,7 +35,7 @@ abstract class LdifUser extends AbstractLdapEntry implements DirectoryUser { private final AttributeDictionary properties; private final AttributeDictionary credentials; - LdifUser(AbstractUserDirectory userAdmin, LdapName dn, Attributes attributes) { + LdifUser(AbstractLdapDirectory userAdmin, LdapName dn, Attributes attributes) { super(userAdmin, dn, attributes); properties = new AttributeDictionary(false); credentials = new AttributeDictionary(true); @@ -160,8 +161,8 @@ abstract class LdifUser extends AbstractLdapEntry implements DirectoryUser { // return hashedPassword; // } - protected AbstractUserDirectory getUserAdmin() { - return (AbstractUserDirectory) getDirectory(); + protected DirectoryUserAdmin getUserAdmin() { + return (DirectoryUserAdmin) getDirectory(); } private class AttributeDictionary extends Dictionary { @@ -357,7 +358,7 @@ abstract class LdifUser extends AbstractLdapEntry implements DirectoryUser { static class LdifPerson extends LdifUser implements Person { - public LdifPerson(AbstractUserDirectory userAdmin, LdapName dn, Attributes attributes) { + public LdifPerson(DirectoryUserAdmin userAdmin, LdapName dn, Attributes attributes) { super(userAdmin, dn, attributes); } diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/OsUserDirectory.java b/org.argeo.util/src/org/argeo/osgi/useradmin/OsUserDirectory.java index 1f428ecbd..1adc7e0df 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/OsUserDirectory.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/OsUserDirectory.java @@ -1,8 +1,6 @@ package org.argeo.osgi.useradmin; -import java.net.URI; import java.util.ArrayList; -import java.util.Dictionary; import java.util.List; import javax.naming.NameNotFoundException; @@ -12,23 +10,23 @@ import javax.naming.directory.BasicAttributes; import javax.naming.ldap.LdapName; import org.argeo.util.directory.HierarchyUnit; +import org.argeo.util.directory.ldap.AbstractLdapDirectory; +import org.argeo.util.directory.ldap.AbstractLdapDirectoryDao; import org.argeo.util.directory.ldap.LdapEntry; import org.argeo.util.directory.ldap.LdapEntryWorkingCopy; import org.argeo.util.naming.LdapAttrs; -import org.osgi.framework.Filter; -import org.osgi.service.useradmin.User; /** Pseudo user directory to be used when logging in as OS user. */ -public class OsUserDirectory extends AbstractUserDirectory { +public class OsUserDirectory extends AbstractLdapDirectoryDao { private final String osUsername = System.getProperty("user.name"); private final LdapName osUserDn; - private final DirectoryUser osUser; + private final LdapEntry osUser; - public OsUserDirectory(URI uriArg, Dictionary props) { - super(uriArg, props, false); + public OsUserDirectory(AbstractLdapDirectory directory) { + super(directory); try { - osUserDn = new LdapName( - LdapAttrs.uid.name() + "=" + osUsername + "," + getUserBaseRdn() + "," + getBaseDn()); + 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); @@ -38,17 +36,17 @@ public class OsUserDirectory extends AbstractUserDirectory { } @Override - protected List getDirectGroups(LdapName dn) { + public List getDirectGroups(LdapName dn) { return new ArrayList<>(); } @Override - protected Boolean daoHasEntry(LdapName dn) { + public Boolean daoHasEntry(LdapName dn) { return osUserDn.equals(dn); } @Override - protected DirectoryUser daoGetEntry(LdapName key) throws NameNotFoundException { + public LdapEntry daoGetEntry(LdapName key) throws NameNotFoundException { if (osUserDn.equals(key)) return osUser; else @@ -56,18 +54,13 @@ public class OsUserDirectory extends AbstractUserDirectory { } @Override - protected List doGetEntries(LdapName searchBase, Filter f, boolean deep) { + public List doGetEntries(LdapName searchBase, String f, boolean deep) { List res = new ArrayList<>(); - if (f == null || f.match(osUser.getProperties())) - res.add(osUser); +// if (f == null || f.match(osUser.getProperties())) + res.add(osUser); return res; } - @Override - protected AbstractUserDirectory scope(User user) { - throw new UnsupportedOperationException(); - } - @Override public HierarchyUnit doGetHierarchyUnit(LdapName dn) { return null; @@ -90,4 +83,17 @@ public class OsUserDirectory extends AbstractUserDirectory { } + @Override + public void init() { + // TODO Auto-generated method stub + + } + + @Override + public void destroy() { + // TODO Auto-generated method stub + + } + + } diff --git a/org.argeo.util/src/org/argeo/util/directory/Directory.java b/org.argeo.util/src/org/argeo/util/directory/Directory.java index b3dfa8b05..05808908d 100644 --- a/org.argeo.util/src/org/argeo/util/directory/Directory.java +++ b/org.argeo.util/src/org/argeo/util/directory/Directory.java @@ -17,10 +17,6 @@ public interface Directory { boolean isDisabled(); - String getUserObjectClass(); - - String getGroupObjectClass(); - Optional getRealm(); void setTransactionControl(WorkControl transactionControl); diff --git a/org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapDirectory.java b/org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapDirectory.java index 27f9c55e3..d8e8e7d21 100644 --- a/org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapDirectory.java +++ b/org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapDirectory.java @@ -23,19 +23,17 @@ import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; import javax.transaction.xa.XAResource; +import org.argeo.osgi.useradmin.OsUserDirectory; import org.argeo.util.directory.Directory; import org.argeo.util.directory.DirectoryConf; import org.argeo.util.directory.HierarchyUnit; import org.argeo.util.naming.LdapAttrs; import org.argeo.util.naming.LdapObjs; import org.argeo.util.transaction.WorkControl; -import org.argeo.util.transaction.WorkingCopyProcessor; import org.argeo.util.transaction.WorkingCopyXaResource; import org.argeo.util.transaction.XAResourceProvider; -import org.osgi.framework.Filter; -public abstract class AbstractLdapDirectory - implements Directory, WorkingCopyProcessor, XAResourceProvider { +public abstract class AbstractLdapDirectory implements Directory, XAResourceProvider { protected static final String SHARED_STATE_USERNAME = "javax.security.auth.login.name"; protected static final String SHARED_STATE_PASSWORD = "javax.security.auth.login.password"; @@ -43,6 +41,7 @@ public abstract class AbstractLdapDirectory protected final Hashtable properties; private final Rdn userBaseRdn, groupBaseRdn, systemRoleBaseRdn; private final String userObjectClass, groupObjectClass; + private String memberAttributeId = "member"; private final boolean readOnly; private final boolean disabled; @@ -52,12 +51,13 @@ public abstract class AbstractLdapDirectory private final boolean scoped; - private String memberAttributeId = "member"; private List credentialAttributeIds = Arrays .asList(new String[] { LdapAttrs.userPassword.name(), LdapAttrs.authPassword.name() }); private WorkControl transactionControl; - private WorkingCopyXaResource xaResource = new WorkingCopyXaResource<>(this); + private WorkingCopyXaResource xaResource; + + private LdapDirectoryDao directoryDao; public AbstractLdapDirectory(URI uriArg, Dictionary props, boolean scoped) { this.properties = new Hashtable(); @@ -82,8 +82,9 @@ public abstract class AbstractLdapDirectory forcedPassword = DirectoryConf.forcedPassword.getValue(properties); userObjectClass = DirectoryConf.userObjectClass.getValue(properties); - String userBase = DirectoryConf.userBase.getValue(properties); groupObjectClass = DirectoryConf.groupObjectClass.getValue(properties); + + String userBase = DirectoryConf.userBase.getValue(properties); String groupBase = DirectoryConf.groupBase.getValue(properties); String systemRoleBase = DirectoryConf.systemRoleBase.getValue(properties); try { @@ -112,21 +113,56 @@ public abstract class AbstractLdapDirectory disabled = Boolean.parseBoolean(disabledStr); else disabled = false; + + URI u = URI.create(uri); + if (!getRealm().isEmpty() || DirectoryConf.SCHEME_LDAP.equals(u.getScheme()) + || DirectoryConf.SCHEME_LDAPS.equals(u.getScheme())) { + directoryDao = new LdapDao(this); + } else if (DirectoryConf.SCHEME_FILE.equals(u.getScheme())) { + directoryDao = new LdifDao(this); + } else if (DirectoryConf.SCHEME_OS.equals(u.getScheme())) { + directoryDao = new OsUserDirectory(this); + // singleUser = true; + } else { + throw new IllegalArgumentException("Unsupported scheme " + u.getScheme()); + } + xaResource = new WorkingCopyXaResource<>(directoryDao); } /* * ABSTRACT METHODS */ - public abstract HierarchyUnit doGetHierarchyUnit(LdapName dn); +// public abstract HierarchyUnit doGetHierarchyUnit(LdapName dn); +// +// public abstract Iterable doGetDirectHierarchyUnits(LdapName searchBase, boolean functionalOnly); +// +// protected abstract Boolean daoHasEntry(LdapName dn); +// +// protected abstract LdapEntry daoGetEntry(LdapName key) throws NameNotFoundException; +// +// protected abstract List doGetEntries(LdapName searchBase, Filter f, boolean deep); +// +// /** Returns the groups this user is a direct member of. */ +// protected abstract List getDirectGroups(LdapName dn); + /* + * INITIALIZATION + */ - public abstract Iterable doGetDirectHierarchyUnits(LdapName searchBase, boolean functionalOnly); + public void init() { + getDirectoryDao().init(); + } - protected abstract Boolean daoHasEntry(LdapName dn); + public void destroy() { + getDirectoryDao().destroy(); + } - protected abstract LdapEntry daoGetEntry(LdapName key) throws NameNotFoundException; + /* + * CREATION + */ + protected abstract LdapEntry newUser(LdapName name, Attributes attrs); - protected abstract List doGetEntries(LdapName searchBase, Filter f, boolean deep); + protected abstract LdapEntry newGroup(LdapName name, Attributes attrs); /* * EDITION @@ -162,9 +198,73 @@ public abstract class AbstractLdapDirectory return xaResource; } - @Override - public LdapEntryWorkingCopy newWorkingCopy() { - return new LdapEntryWorkingCopy(); + public boolean removeEntry(LdapName dn) { + checkEdit(); + LdapEntryWorkingCopy wc = getWorkingCopy(); + boolean actuallyDeleted; + if (getDirectoryDao().daoHasEntry(dn) || wc.getNewData().containsKey(dn)) { + LdapEntry user = doGetRole(dn); + wc.getDeletedData().put(dn, user); + actuallyDeleted = true; + } else {// just removing from groups (e.g. system roles) + actuallyDeleted = false; + } + for (LdapName groupDn : getDirectoryDao().getDirectGroups(dn)) { + LdapEntry group = doGetRole(groupDn); + group.getAttributes().get(getMemberAttributeId()).remove(dn.toString()); + } + return actuallyDeleted; + } + + /* + * RETRIEVAL + */ + + protected LdapEntry doGetRole(LdapName dn) { + LdapEntryWorkingCopy wc = getWorkingCopy(); + LdapEntry user; + try { + user = getDirectoryDao().daoGetEntry(dn); + } catch (NameNotFoundException e) { + user = null; + } + if (wc != null) { + if (user == null && wc.getNewData().containsKey(dn)) + user = wc.getNewData().get(dn); + else if (wc.getDeletedData().containsKey(dn)) + user = null; + } + return user; + } + + protected void collectGroups(LdapEntry user, List allRoles) { + Attributes attrs = user.getAttributes(); + // TODO centralize attribute name + Attribute memberOf = attrs.get(LdapAttrs.memberOf.name()); + // if user belongs to this directory, we only check memberOf + if (memberOf != null && user.getDn().startsWith(getBaseDn())) { + try { + NamingEnumeration values = memberOf.getAll(); + while (values.hasMore()) { + Object value = values.next(); + LdapName groupDn = new LdapName(value.toString()); + LdapEntry group = doGetRole(groupDn); + if (group != null) + allRoles.add(group); + } + } catch (NamingException e) { + throw new IllegalStateException("Cannot get memberOf groups for " + user, e); + } + } else { + for (LdapName groupDn : getDirectoryDao().getDirectGroups(user.getDn())) { + // TODO check for loops + LdapEntry group = doGetRole(groupDn); + if (group != null) { + allRoles.add(group); + collectGroups(group, allRoles); + } + } + } } /* @@ -173,12 +273,12 @@ public abstract class AbstractLdapDirectory @Override public HierarchyUnit getHierarchyUnit(String path) { LdapName dn = pathToName(path); - return doGetHierarchyUnit(dn); + return directoryDao.doGetHierarchyUnit(dn); } @Override public Iterable getDirectHierarchyUnits(boolean functionalOnly) { - return doGetDirectHierarchyUnits(baseDn, functionalOnly); + return directoryDao.doGetDirectHierarchyUnits(baseDn, functionalOnly); } /* @@ -239,20 +339,23 @@ public abstract class AbstractLdapDirectory /* * UTILITIES */ - protected static boolean hasObjectClass(Attributes attrs, LdapObjs objectClass) { + return hasObjectClass(attrs, objectClass.name()); + } + + protected static boolean hasObjectClass(Attributes attrs, String objectClass) { try { Attribute attr = attrs.get(LdapAttrs.objectClass.name()); NamingEnumeration en = attr.getAll(); while (en.hasMore()) { String v = en.next().toString(); - if (v.equalsIgnoreCase(objectClass.name())) + if (v.equalsIgnoreCase(objectClass)) return true; } return false; } catch (NamingException e) { - throw new IllegalStateException("Cannot search for objectClass " + objectClass.name(), e); + throw new IllegalStateException("Cannot search for objectClass " + objectClass, e); } } @@ -294,7 +397,7 @@ public abstract class AbstractLdapDirectory return Optional.of(realm.toString()); } - protected LdapName getBaseDn() { + public LdapName getBaseDn() { return (LdapName) baseDn.clone(); } @@ -306,23 +409,10 @@ public abstract class AbstractLdapDirectory return disabled; } - /** dn can be null, in that case a default should be returned. */ - public String getUserObjectClass() { - return userObjectClass; - } - public Rdn getUserBaseRdn() { return userBaseRdn; } - protected String newUserObjectClass(LdapName dn) { - return getUserObjectClass(); - } - - public String getGroupObjectClass() { - return groupObjectClass; - } - public Rdn getGroupBaseRdn() { return groupBaseRdn; } @@ -347,18 +437,31 @@ public abstract class AbstractLdapDirectory return scoped; } - public String getMemberAttributeId() { - return memberAttributeId; - } - public List getCredentialAttributeIds() { return credentialAttributeIds; } - protected String getUri() { + public String getUri() { return uri; } + public LdapDirectoryDao getDirectoryDao() { + return directoryDao; + } + + /** dn can be null, in that case a default should be returned. */ + public String getUserObjectClass() { + return userObjectClass; + } + + public String getGroupObjectClass() { + return groupObjectClass; + } + + public String getMemberAttributeId() { + return memberAttributeId; + } + /* * OBJECT METHODS */ diff --git a/org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapDirectoryDao.java b/org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapDirectoryDao.java new file mode 100644 index 000000000..8c87170fe --- /dev/null +++ b/org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapDirectoryDao.java @@ -0,0 +1,34 @@ +package org.argeo.util.directory.ldap; + +import javax.naming.directory.Attributes; +import javax.naming.ldap.LdapName; + +public abstract class AbstractLdapDirectoryDao implements LdapDirectoryDao { + + private AbstractLdapDirectory directory; + + public AbstractLdapDirectoryDao(AbstractLdapDirectory directory) { + this.directory = directory; + + } + + public AbstractLdapDirectory getDirectory() { + return directory; + } + + @Override + public LdapEntryWorkingCopy newWorkingCopy() { + return new LdapEntryWorkingCopy(); + } + + @Override + public LdapEntry newUser(LdapName name, Attributes attrs) { + return getDirectory().newUser(name, attrs); + } + + @Override + public LdapEntry newGroup(LdapName name, Attributes attrs) { + return getDirectory().newGroup(name, attrs); + } + +} diff --git a/org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapEntry.java b/org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapEntry.java index be919c020..25f233a01 100644 --- a/org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapEntry.java +++ b/org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapEntry.java @@ -1,8 +1,16 @@ package org.argeo.util.directory.ldap; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; import javax.naming.ldap.LdapName; +/** An entry in an LDAP (or LDIF) directory. */ public abstract class AbstractLdapEntry implements LdapEntry { private final AbstractLdapDirectory directory; @@ -10,8 +18,10 @@ public abstract class AbstractLdapEntry implements LdapEntry { private Attributes publishedAttributes; - protected AbstractLdapEntry(AbstractLdapDirectory userAdmin, LdapName dn, Attributes attributes) { - this.directory = userAdmin; + protected AbstractLdapEntry(AbstractLdapDirectory directory, LdapName dn, Attributes attributes) { + Objects.requireNonNull(directory); + Objects.requireNonNull(dn); + this.directory = directory; this.dn = dn; this.publishedAttributes = attributes; } @@ -24,6 +34,25 @@ public abstract class AbstractLdapEntry implements LdapEntry { public synchronized Attributes getAttributes() { return isEditing() ? getModifiedAttributes() : publishedAttributes; } + + @Override + public List getReferences(String attributeId){ + Attribute memberAttribute = getAttributes().get(attributeId); + if (memberAttribute == null) + return new ArrayList(); + try { + List roles = new ArrayList(); + NamingEnumeration values = memberAttribute.getAll(); + while (values.hasMore()) { + LdapName dn = new LdapName(values.next().toString()); + roles.add(dn); + } + return roles; + } catch (NamingException e) { + throw new IllegalStateException("Cannot get members", e); + } + + } /** Should only be called from working copy thread. */ protected synchronized Attributes getModifiedAttributes() { @@ -53,10 +82,14 @@ public abstract class AbstractLdapEntry implements LdapEntry { publishedAttributes = modifiedAttributes; } - protected AbstractLdapDirectory getDirectory() { + public AbstractLdapDirectory getDirectory() { return directory; } + public LdapDirectoryDao getDirectoryDao() { + return directory.getDirectoryDao(); + } + @Override public int hashCode() { return dn.hashCode(); diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/LdapUserAdmin.java b/org.argeo.util/src/org/argeo/util/directory/ldap/LdapDao.java similarity index 63% rename from org.argeo.util/src/org/argeo/osgi/useradmin/LdapUserAdmin.java rename to org.argeo.util/src/org/argeo/util/directory/ldap/LdapDao.java index 36419d960..a2d9e7fc3 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/LdapUserAdmin.java +++ b/org.argeo.util/src/org/argeo/util/directory/ldap/LdapDao.java @@ -1,14 +1,12 @@ -package org.argeo.osgi.useradmin; +package org.argeo.util.directory.ldap; import static org.argeo.util.naming.LdapAttrs.objectClass; import java.util.ArrayList; -import java.util.Dictionary; import java.util.List; import javax.naming.AuthenticationNotSupportedException; import javax.naming.Binding; -import javax.naming.Context; import javax.naming.InvalidNameException; import javax.naming.NameNotFoundException; import javax.naming.NamingEnumeration; @@ -18,60 +16,57 @@ import javax.naming.directory.Attributes; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; import javax.naming.ldap.LdapName; +import javax.naming.ldap.Rdn; -import org.argeo.util.directory.DirectoryDigestUtils; import org.argeo.util.directory.HierarchyUnit; -import org.argeo.util.directory.ldap.LdapConnection; -import org.argeo.util.directory.ldap.LdapEntry; -import org.argeo.util.directory.ldap.LdapEntryWorkingCopy; -import org.argeo.util.directory.ldap.LdapHierarchyUnit; import org.argeo.util.naming.LdapObjs; -import org.osgi.framework.Filter; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; /** A user admin based on a LDAP server. */ -public class LdapUserAdmin extends AbstractUserDirectory { +public class LdapDao extends AbstractLdapDirectoryDao { private LdapConnection ldapConnection; - public LdapUserAdmin(Dictionary properties) { - this(properties, false); +// public LdapUserAdmin(Dictionary properties) { +// this(properties, false); +// } + + public LdapDao(AbstractLdapDirectory directory) { + super(directory); } - public LdapUserAdmin(Dictionary properties, boolean scoped) { - super(null, properties, scoped); - ldapConnection = new LdapConnection(getUri().toString(), properties); + @Override + public void init() { + ldapConnection = new LdapConnection(getDirectory().getUri().toString(), getDirectory().getProperties()); } public void destroy() { ldapConnection.destroy(); } - @Override - protected AbstractUserDirectory scope(User user) { - Dictionary credentials = user.getCredentials(); - String username = (String) credentials.get(SHARED_STATE_USERNAME); - if (username == null) - username = user.getName(); - Dictionary properties = cloneProperties(); - properties.put(Context.SECURITY_PRINCIPAL, username.toString()); - Object pwdCred = credentials.get(SHARED_STATE_PASSWORD); - byte[] pwd = (byte[]) pwdCred; - if (pwd != null) { - char[] password = DirectoryDigestUtils.bytesToChars(pwd); - properties.put(Context.SECURITY_CREDENTIALS, new String(password)); - } else { - properties.put(Context.SECURITY_AUTHENTICATION, "GSSAPI"); - } - return new LdapUserAdmin(properties, true); - } +// @Override +// protected AbstractUserDirectory scope(User user) { +// Dictionary credentials = user.getCredentials(); +// String username = (String) credentials.get(SHARED_STATE_USERNAME); +// if (username == null) +// username = user.getName(); +// Dictionary properties = cloneProperties(); +// properties.put(Context.SECURITY_PRINCIPAL, username.toString()); +// Object pwdCred = credentials.get(SHARED_STATE_PASSWORD); +// byte[] pwd = (byte[]) pwdCred; +// if (pwd != null) { +// char[] password = DirectoryDigestUtils.bytesToChars(pwd); +// properties.put(Context.SECURITY_CREDENTIALS, new String(password)); +// } else { +// properties.put(Context.SECURITY_AUTHENTICATION, "GSSAPI"); +// } +// return new LdapUserAdmin(properties, true); +// } // protected InitialLdapContext getLdapContext() { // return initialLdapContext; // } @Override - protected Boolean daoHasEntry(LdapName dn) { + public Boolean daoHasEntry(LdapName dn) { try { return daoGetEntry(dn) != null; } catch (NameNotFoundException e) { @@ -80,19 +75,19 @@ public class LdapUserAdmin extends AbstractUserDirectory { } @Override - protected DirectoryUser daoGetEntry(LdapName name) throws NameNotFoundException { + public LdapEntry daoGetEntry(LdapName name) throws NameNotFoundException { try { Attributes attrs = ldapConnection.getAttributes(name); if (attrs.size() == 0) return null; - int roleType = roleType(name); - DirectoryUser res; - if (roleType == Role.GROUP) +// int roleType = roleType(name); + LdapEntry res; + if (isGroup(name)) res = newGroup(name, attrs); - else if (roleType == Role.USER) - res = newUser(name, attrs); else - throw new IllegalArgumentException("Unsupported LDAP type for " + name); + res = newUser(name, attrs); +// else +// throw new IllegalArgumentException("Unsupported LDAP type for " + name); return res; } catch (NameNotFoundException e) { throw e; @@ -101,13 +96,25 @@ public class LdapUserAdmin extends AbstractUserDirectory { } } + protected boolean isGroup(LdapName dn) { + Rdn technicalRdn = LdapNameUtils.getParentRdn(dn); + if (getDirectory().getGroupBaseRdn().equals(technicalRdn) + || getDirectory().getSystemRoleBaseRdn().equals(technicalRdn)) + return true; + else if (getDirectory().getUserBaseRdn().equals(technicalRdn)) + return false; + else + throw new IllegalArgumentException( + "Cannot dind role type, " + technicalRdn + " is not a technical RDN for " + dn); + } + @Override - protected List doGetEntries(LdapName searchBase, Filter f, boolean deep) { + public List doGetEntries(LdapName searchBase, String f, boolean deep) { ArrayList res = new ArrayList<>(); try { String searchFilter = f != null ? f.toString() - : "(|(" + objectClass + "=" + getUserObjectClass() + ")(" + objectClass + "=" - + getGroupObjectClass() + "))"; + : "(|(" + objectClass + "=" + getDirectory().getUserObjectClass() + ")(" + objectClass + "=" + + getDirectory().getGroupObjectClass() + "))"; SearchControls searchControls = new SearchControls(); // FIXME make one level consistent with deep searchControls.setSearchScope(deep ? SearchControls.SUBTREE_SCOPE : SearchControls.ONELEVEL_SCOPE); @@ -120,12 +127,12 @@ public class LdapUserAdmin extends AbstractUserDirectory { Attributes attrs = searchResult.getAttributes(); Attribute objectClassAttr = attrs.get(objectClass.name()); LdapName dn = toDn(searchBase, searchResult); - DirectoryUser role; - if (objectClassAttr.contains(getGroupObjectClass()) - || objectClassAttr.contains(getGroupObjectClass().toLowerCase())) + LdapEntry role; + if (objectClassAttr.contains(getDirectory().getGroupObjectClass()) + || objectClassAttr.contains(getDirectory().getGroupObjectClass().toLowerCase())) role = newGroup(dn, attrs); - else if (objectClassAttr.contains(getUserObjectClass()) - || objectClassAttr.contains(getUserObjectClass().toLowerCase())) + else if (objectClassAttr.contains(getDirectory().getUserObjectClass()) + || objectClassAttr.contains(getDirectory().getUserObjectClass().toLowerCase())) role = newUser(dn, attrs); else { // log.warn("Unsupported LDAP type for " + searchResult.getName()); @@ -148,16 +155,16 @@ public class LdapUserAdmin extends AbstractUserDirectory { } @Override - protected List getDirectGroups(LdapName dn) { + public List getDirectGroups(LdapName dn) { List directGroups = new ArrayList(); try { - String searchFilter = "(&(" + objectClass + "=" + getGroupObjectClass() + ")(" + getMemberAttributeId() - + "=" + dn + "))"; + String searchFilter = "(&(" + objectClass + "=" + getDirectory().getGroupObjectClass() + ")(" + + getDirectory().getMemberAttributeId() + "=" + dn + "))"; SearchControls searchControls = new SearchControls(); searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); - LdapName searchBase = getBaseDn(); + LdapName searchBase = getDirectory().getBaseDn(); NamingEnumeration results = ldapConnection.search(searchBase, searchFilter, searchControls); while (results.hasMoreElements()) { @@ -213,7 +220,7 @@ public class LdapUserAdmin extends AbstractUserDirectory { SearchResult searchResult = (SearchResult) results.nextElement(); LdapName dn = toDn(searchBase, searchResult); Attributes attrs = searchResult.getAttributes(); - LdapHierarchyUnit hierarchyUnit = new LdapHierarchyUnit(this, dn, attrs); + LdapHierarchyUnit hierarchyUnit = new LdapHierarchyUnit(getDirectory(), dn, attrs); if (functionalOnly) { if (hierarchyUnit.isFunctional()) res.add(hierarchyUnit); @@ -231,7 +238,7 @@ public class LdapUserAdmin extends AbstractUserDirectory { public HierarchyUnit doGetHierarchyUnit(LdapName dn) { try { Attributes attrs = ldapConnection.getAttributes(dn); - return new LdapHierarchyUnit(this, dn, attrs); + return new LdapHierarchyUnit(getDirectory(), dn, attrs); } catch (NamingException e) { throw new IllegalStateException("Cannot get hierarchy unit " + dn, e); } diff --git a/org.argeo.util/src/org/argeo/util/directory/ldap/LdapDirectoryDao.java b/org.argeo.util/src/org/argeo/util/directory/ldap/LdapDirectoryDao.java new file mode 100644 index 000000000..3a0f4e6e0 --- /dev/null +++ b/org.argeo.util/src/org/argeo/util/directory/ldap/LdapDirectoryDao.java @@ -0,0 +1,32 @@ +package org.argeo.util.directory.ldap; + +import java.util.List; + +import javax.naming.NameNotFoundException; +import javax.naming.directory.Attributes; +import javax.naming.ldap.LdapName; + +import org.argeo.util.directory.HierarchyUnit; +import org.argeo.util.transaction.WorkingCopyProcessor; + +public interface LdapDirectoryDao extends WorkingCopyProcessor { + Boolean daoHasEntry(LdapName dn); + + LdapEntry daoGetEntry(LdapName name) throws NameNotFoundException; + + List doGetEntries(LdapName searchBase, String filter, boolean deep); + + List getDirectGroups(LdapName dn); + + Iterable doGetDirectHierarchyUnits(LdapName searchBase, boolean functionalOnly); + + HierarchyUnit doGetHierarchyUnit(LdapName dn); + + LdapEntry newUser(LdapName name, Attributes attrs); + + LdapEntry newGroup(LdapName name, Attributes attrs); + + void init(); + + void destroy(); +} diff --git a/org.argeo.util/src/org/argeo/util/directory/ldap/LdapEntry.java b/org.argeo.util/src/org/argeo/util/directory/ldap/LdapEntry.java index c145a6f0a..3fa23e5f1 100644 --- a/org.argeo.util/src/org/argeo/util/directory/ldap/LdapEntry.java +++ b/org.argeo.util/src/org/argeo/util/directory/ldap/LdapEntry.java @@ -1,5 +1,7 @@ package org.argeo.util.directory.ldap; +import java.util.List; + import javax.naming.directory.Attributes; import javax.naming.ldap.LdapName; @@ -10,4 +12,5 @@ public interface LdapEntry { void publishAttributes(Attributes modifiedAttributes); + public List getReferences(String attributeId); } diff --git a/org.argeo.util/src/org/argeo/util/directory/ldap/LdapHierarchyUnit.java b/org.argeo.util/src/org/argeo/util/directory/ldap/LdapHierarchyUnit.java index d76c449b0..5cfca3192 100644 --- a/org.argeo.util/src/org/argeo/util/directory/ldap/LdapHierarchyUnit.java +++ b/org.argeo.util/src/org/argeo/util/directory/ldap/LdapHierarchyUnit.java @@ -1,32 +1,17 @@ package org.argeo.util.directory.ldap; -import java.util.Objects; - import javax.naming.directory.Attributes; import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; -import org.argeo.util.directory.Directory; import org.argeo.util.directory.HierarchyUnit; /** LDIF/LDAP based implementation of {@link HierarchyUnit}. */ -public class LdapHierarchyUnit implements HierarchyUnit { - private final AbstractLdapDirectory directory; - - private final LdapName dn; +public class LdapHierarchyUnit extends AbstractLdapEntry implements HierarchyUnit { private final boolean functional; - private final Attributes attributes; - -// HierarchyUnit parent; -// List children = new ArrayList<>(); public LdapHierarchyUnit(AbstractLdapDirectory directory, LdapName dn, Attributes attributes) { - Objects.requireNonNull(directory); - Objects.requireNonNull(dn); - - this.directory = directory; - this.dn = dn; - this.attributes = attributes; + super(directory, dn, attributes); Rdn rdn = LdapNameUtils.getLastRdn(dn); functional = !(directory.getUserBaseRdn().equals(rdn) || directory.getGroupBaseRdn().equals(rdn) @@ -35,21 +20,12 @@ public class LdapHierarchyUnit implements HierarchyUnit { @Override public HierarchyUnit getParent() { - return directory.doGetHierarchyUnit(LdapNameUtils.getParent(dn)); + return getDirectoryDao().doGetHierarchyUnit(LdapNameUtils.getParent(getDn())); } @Override public Iterable getDirectHierachyUnits(boolean functionalOnly) { -// List res = new ArrayList<>(); -// if (functionalOnly) -// for (HierarchyUnit hu : children) { -// if (hu.isFunctional()) -// res.add(hu); -// } -// else -// res.addAll(children); -// return Collections.unmodifiableList(res); - return directory.doGetDirectHierarchyUnits(dn, functionalOnly); + return getDirectoryDao().doGetDirectHierarchyUnits(getDn(), functionalOnly); } @Override @@ -59,40 +35,19 @@ public class LdapHierarchyUnit implements HierarchyUnit { @Override public String getHierarchyUnitName() { - String name = LdapNameUtils.getLastRdnValue(dn); + String name = LdapNameUtils.getLastRdnValue(getDn()); // TODO check ou, o, etc. return name; } - public Attributes getAttributes() { - return attributes; - } - @Override public String getContext() { - return dn.toString(); - } - - @Override - public Directory getDirectory() { - return directory; - } - - @Override - public int hashCode() { - return dn.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof LdapHierarchyUnit)) - return false; - return ((LdapHierarchyUnit) obj).dn.equals(dn); + return getDn().toString(); } @Override public String toString() { - return "Hierarchy Unit " + dn.toString(); + return "Hierarchy Unit " + getDn().toString(); } } diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/LdifUserAdmin.java b/org.argeo.util/src/org/argeo/util/directory/ldap/LdifDao.java similarity index 54% rename from org.argeo.util/src/org/argeo/osgi/useradmin/LdifUserAdmin.java rename to org.argeo.util/src/org/argeo/util/directory/ldap/LdifDao.java index c978af4a0..c805d1272 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/LdifUserAdmin.java +++ b/org.argeo.util/src/org/argeo/util/directory/ldap/LdifDao.java @@ -1,4 +1,4 @@ -package org.argeo.osgi.useradmin; +package org.argeo.util.directory.ldap; import static org.argeo.util.naming.LdapAttrs.objectClass; import static org.argeo.util.naming.LdapObjs.inetOrgPerson; @@ -29,65 +29,62 @@ import javax.naming.directory.Attributes; import javax.naming.ldap.LdapName; import org.argeo.util.directory.DirectoryConf; -import org.argeo.util.directory.DirectoryDigestUtils; import org.argeo.util.directory.HierarchyUnit; -import org.argeo.util.directory.ldap.LdapEntry; -import org.argeo.util.directory.ldap.LdapEntryWorkingCopy; -import org.argeo.util.directory.ldap.LdapHierarchyUnit; -import org.argeo.util.directory.ldap.LdifParser; -import org.argeo.util.directory.ldap.LdifWriter; import org.argeo.util.naming.LdapObjs; import org.osgi.framework.Filter; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.InvalidSyntaxException; import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; /** A user admin based on a LDIF files. */ -public class LdifUserAdmin extends AbstractUserDirectory { - private NavigableMap users = new TreeMap<>(); - private NavigableMap groups = new TreeMap<>(); +public class LdifDao extends AbstractLdapDirectoryDao { +// private NavigableMap users = new TreeMap<>(); +// private NavigableMap groups = new TreeMap<>(); + private NavigableMap entries = new TreeMap<>(); private NavigableMap hierarchy = new TreeMap<>(); // private List rootHierarchyUnits = new ArrayList<>(); - public LdifUserAdmin(String uri, String baseDn) { - this(fromUri(uri, baseDn), false); - } +// public LdifUserAdmin(String uri, String baseDn) { +// this(fromUri(uri, baseDn), false); +// } - public LdifUserAdmin(Dictionary properties) { - this(properties, false); + public LdifDao(AbstractLdapDirectory directory) { + super(directory); } - protected LdifUserAdmin(Dictionary properties, boolean scoped) { - super(null, properties, scoped); - } +// protected LdifUserAdmin(Hashtable properties, boolean scoped) { +// super( properties, scoped); +// } - public LdifUserAdmin(URI uri, Dictionary properties) { - super(uri, properties, false); - } +// public LdifUserAdmin(URI uri, Dictionary properties) { +// super(uri, properties, false); +// } - @Override - protected AbstractUserDirectory scope(User user) { - Dictionary credentials = user.getCredentials(); - String username = (String) credentials.get(SHARED_STATE_USERNAME); - if (username == null) - username = user.getName(); - Object pwdCred = credentials.get(SHARED_STATE_PASSWORD); - byte[] pwd = (byte[]) pwdCred; - if (pwd != null) { - char[] password = DirectoryDigestUtils.bytesToChars(pwd); - User directoryUser = (User) getRole(username); - if (!directoryUser.hasCredential(null, password)) - throw new IllegalStateException("Invalid credentials"); - } else { - throw new IllegalStateException("Password is required"); - } - Dictionary properties = cloneProperties(); - properties.put(DirectoryConf.readOnly.name(), "true"); - LdifUserAdmin scopedUserAdmin = new LdifUserAdmin(properties, true); - scopedUserAdmin.groups = Collections.unmodifiableNavigableMap(groups); - scopedUserAdmin.users = Collections.unmodifiableNavigableMap(users); - return scopedUserAdmin; - } +// @Override +// protected AbstractUserDirectory scope(User user) { +// Dictionary credentials = user.getCredentials(); +// String username = (String) credentials.get(SHARED_STATE_USERNAME); +// if (username == null) +// username = user.getName(); +// Object pwdCred = credentials.get(SHARED_STATE_PASSWORD); +// byte[] pwd = (byte[]) pwdCred; +// if (pwd != null) { +// char[] password = DirectoryDigestUtils.bytesToChars(pwd); +// User directoryUser = (User) getRole(username); +// if (!directoryUser.hasCredential(null, password)) +// throw new IllegalStateException("Invalid credentials"); +// } else { +// throw new IllegalStateException("Password is required"); +// } +// Dictionary properties = cloneProperties(); +// properties.put(DirectoryConf.readOnly.name(), "true"); +// LdifUserAdmin scopedUserAdmin = new LdifUserAdmin(properties, true); +//// scopedUserAdmin.groups = Collections.unmodifiableNavigableMap(groups); +//// scopedUserAdmin.users = Collections.unmodifiableNavigableMap(users); +// scopedUserAdmin.entries = Collections.unmodifiableNavigableMap(entries); +// return scopedUserAdmin; +// } private static Dictionary fromUri(String uri, String baseDn) { Hashtable res = new Hashtable(); @@ -99,7 +96,7 @@ public class LdifUserAdmin extends AbstractUserDirectory { public void init() { try { - URI u = new URI(getUri()); + URI u = new URI(getDirectory().getUri()); if (u.getScheme().equals("file")) { File file = new File(u); if (!file.exists()) @@ -107,19 +104,20 @@ public class LdifUserAdmin extends AbstractUserDirectory { } load(u.toURL().openStream()); } catch (IOException | URISyntaxException e) { - throw new IllegalStateException("Cannot open URL " + getUri(), e); + throw new IllegalStateException("Cannot open URL " + getDirectory().getUri(), e); } } public void save() { - if (getUri() == null) + if (getDirectory().getUri() == null) throw new IllegalStateException("Cannot save LDIF user admin: no URI is set"); - if (isReadOnly()) - throw new IllegalStateException("Cannot save LDIF user admin: " + getUri() + " is read-only"); - try (FileOutputStream out = new FileOutputStream(new File(new URI(getUri())))) { + if (getDirectory().isReadOnly()) + throw new IllegalStateException( + "Cannot save LDIF user admin: " + getDirectory().getUri() + " is read-only"); + try (FileOutputStream out = new FileOutputStream(new File(new URI(getDirectory().getUri())))) { save(out); } catch (IOException | URISyntaxException e) { - throw new IllegalStateException("Cannot save user admin to " + getUri(), e); + throw new IllegalStateException("Cannot save user admin to " + getDirectory().getUri(), e); } } @@ -128,19 +126,22 @@ public class LdifUserAdmin extends AbstractUserDirectory { LdifWriter ldifWriter = new LdifWriter(out); for (LdapName name : hierarchy.keySet()) ldifWriter.writeEntry(name, hierarchy.get(name).getAttributes()); - for (LdapName name : groups.keySet()) - ldifWriter.writeEntry(name, groups.get(name).getAttributes()); - for (LdapName name : users.keySet()) - ldifWriter.writeEntry(name, users.get(name).getAttributes()); +// for (LdapName name : groups.keySet()) +// ldifWriter.writeEntry(name, groups.get(name).getAttributes()); +// for (LdapName name : users.keySet()) +// ldifWriter.writeEntry(name, users.get(name).getAttributes()); + for (LdapName name : entries.keySet()) + ldifWriter.writeEntry(name, entries.get(name).getAttributes()); } finally { out.close(); } } - protected void load(InputStream in) { + public void load(InputStream in) { try { - users.clear(); - groups.clear(); +// users.clear(); +// groups.clear(); + entries.clear(); hierarchy.clear(); LdifParser ldifParser = new LdifParser(); @@ -164,21 +165,21 @@ public class LdifUserAdmin extends AbstractUserDirectory { String objectClass = objectClasses.next().toString(); // System.out.println(" " + objectClass); if (objectClass.toLowerCase().equals(inetOrgPerson.name().toLowerCase())) { - users.put(key, newUser(key, attributes)); + entries.put(key, newUser(key, attributes)); break objectClasses; - } else if (objectClass.toLowerCase().equals(getGroupObjectClass().toLowerCase())) { - groups.put(key, newGroup(key, attributes)); + } else if (objectClass.toLowerCase().equals(getDirectory().getGroupObjectClass().toLowerCase())) { + entries.put(key, newGroup(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.organizationalUnit.name())) { -// String name = key.getRdn(key.size() - 1).toString(); +// String name = key.getRdn(key.size() - 1).toStrindirectoryDaog(); // if (getUserBase().equalsIgnoreCase(name) || getGroupBase().equalsIgnoreCase(name)) // break objectClasses; // skip // TODO skip if it does not contain groups or users - hierarchy.put(key, new LdapHierarchyUnit(this, key, attributes)); + hierarchy.put(key, new LdapHierarchyUnit(getDirectory(), key, attributes)); break objectClasses; } } @@ -203,10 +204,12 @@ public class LdifUserAdmin extends AbstractUserDirectory { } public void destroy() { - if (users == null || groups == null) - throw new IllegalStateException("User directory " + getBaseDn() + " is already destroyed"); - users = null; - groups = null; +// if (users == null || groups == null) + if (entries == null) + throw new IllegalStateException("User directory " + getDirectory().getBaseDn() + " is already destroyed"); +// users = null; +// groups = null; + entries = null; } /* @@ -214,64 +217,82 @@ public class LdifUserAdmin extends AbstractUserDirectory { */ @Override - protected DirectoryUser daoGetEntry(LdapName key) throws NameNotFoundException { - if (groups.containsKey(key)) - return (DirectoryUser) groups.get(key); - if (users.containsKey(key)) - return (DirectoryUser) users.get(key); + public LdapEntry daoGetEntry(LdapName key) throws NameNotFoundException { +// if (groups.containsKey(key)) +// return groups.get(key); +// if (users.containsKey(key)) +// return users.get(key); + if (entries.containsKey(key)) + return entries.get(key); throw new NameNotFoundException(key + " not persisted"); } @Override - protected Boolean daoHasEntry(LdapName dn) { - return users.containsKey(dn) || groups.containsKey(dn); + public Boolean daoHasEntry(LdapName dn) { + return entries.containsKey(dn);// || groups.containsKey(dn); } @Override - protected List doGetEntries(LdapName searchBase, Filter f, boolean deep) { + public List doGetEntries(LdapName searchBase, String f, boolean deep) { Objects.requireNonNull(searchBase); ArrayList res = new ArrayList<>(); - if (f == null && deep && getBaseDn().equals(searchBase)) { - res.addAll(users.values()); - res.addAll(groups.values()); + if (f == null && deep && getDirectory().getBaseDn().equals(searchBase)) { +// res.addAll(users.values()); +// res.addAll(groups.values()); + res.addAll(entries.values()); } else { - filterRoles(users, searchBase, f, deep, res); - filterRoles(groups, searchBase, f, deep, res); +// filterRoles(users, searchBase, f, deep, res); +// filterRoles(groups, searchBase, f, deep, res); + filterRoles(entries, searchBase, f, deep, res); } return res; } - private void filterRoles(SortedMap map, LdapName searchBase, Filter f, boolean deep, + private void filterRoles(SortedMap map, LdapName searchBase, String f, boolean deep, List res) { - // TODO reduce map with search base ? - roles: for (LdapEntry user : map.values()) { - LdapName dn = user.getDn(); - if (dn.startsWith(searchBase)) { - if (!deep && dn.size() != (searchBase.size() + 1)) - continue roles; - if (f == null) - res.add(user); - else { - if (f.match(((DirectoryUser) user).getProperties())) + // FIXME get rid of OSGi references + try { + // TODO reduce map with search base ? + Filter filter = f != null ? FrameworkUtil.createFilter(f) : null; + roles: for (LdapEntry user : map.values()) { + LdapName dn = user.getDn(); + if (dn.startsWith(searchBase)) { + if (!deep && dn.size() != (searchBase.size() + 1)) + continue roles; + if (filter == null) res.add(user); + else { + if (user instanceof Role) { + if (filter.match(((Role) user).getProperties())) + res.add(user); + } + } } } + } catch (InvalidSyntaxException e) { + throw new IllegalArgumentException("Cannot create filter " + f, e); } } @Override - protected List getDirectGroups(LdapName dn) { + public List getDirectGroups(LdapName dn) { List directGroups = new ArrayList(); - for (LdapName name : groups.keySet()) { - DirectoryGroup group; + entries: for (LdapName name : entries.keySet()) { + LdapEntry group; try { - group = (DirectoryGroup) daoGetEntry(name); + LdapEntry entry = daoGetEntry(name); + if (AbstractLdapDirectory.hasObjectClass(entry.getAttributes(), getDirectory().getGroupObjectClass())) { + group = entry; + } else { + continue entries; + } } catch (NameNotFoundException e) { throw new IllegalArgumentException("Group " + dn + " not found", e); } - if (group.getMemberNames().contains(dn)) + if (group.getReferences(getDirectory().getMemberAttributeId()).contains(dn)) { directGroups.add(group.getDn()); + } } return directGroups; } @@ -280,29 +301,33 @@ public class LdifUserAdmin extends AbstractUserDirectory { public void prepare(LdapEntryWorkingCopy wc) { // delete for (LdapName dn : wc.getDeletedData().keySet()) { - if (users.containsKey(dn)) - users.remove(dn); - else if (groups.containsKey(dn)) - groups.remove(dn); + if (entries.containsKey(dn)) + entries.remove(dn); +// if (users.containsKey(dn)) +// users.remove(dn); +// else if (groups.containsKey(dn)) +// groups.remove(dn); else throw new IllegalStateException("User to delete not found " + dn); } // add for (LdapName dn : wc.getNewData().keySet()) { - DirectoryUser user = (DirectoryUser) wc.getNewData().get(dn); - if (users.containsKey(dn) || groups.containsKey(dn)) + LdapEntry user = (LdapEntry) wc.getNewData().get(dn); +// if (users.containsKey(dn) || groups.containsKey(dn)) + if (entries.containsKey(dn)) throw new IllegalStateException("User to create found " + dn); - else if (Role.USER == user.getType()) - users.put(dn, user); - else if (Role.GROUP == user.getType()) - groups.put(dn, (DirectoryGroup) user); - else - throw new IllegalStateException("Unsupported role type " + user.getType() + " for new user " + dn); + entries.put(dn, user); +// else if (Role.USER == user.getType()) +// users.put(dn, user); +// else if (Role.GROUP == user.getType()) +// groups.put(dn, (DirectoryGroup) user); +// else +// throw new IllegalStateException("Unsupported role type " + user.getType() + " for new user " + dn); } // modify for (LdapName dn : wc.getModifiedData().keySet()) { Attributes modifiedAttrs = wc.getModifiedData().get(dn); - DirectoryUser user; + LdapEntry user; try { user = daoGetEntry(dn); } catch (NameNotFoundException e) { @@ -361,6 +386,10 @@ public class LdifUserAdmin extends AbstractUserDirectory { return res; } + public void scope(LdifDao scoped) { + scoped.entries = Collections.unmodifiableNavigableMap(entries); + } + // @Override // public Iterable getDirectHierarchyUnits(boolean functionalOnly) { // if (functionalOnly) { -- 2.30.2