From e2ffdf6872592aa22d0de2b0ec69ee4eca698c45 Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Wed, 22 Jun 2022 10:05:00 +0200 Subject: [PATCH] Decorrelate directory implementation from user admin --- .../argeo/cms/e4/users/UserAdminWrapper.java | 4 +- .../argeo/cms/e4/users/handlers/NewGroup.java | 6 +- .../argeo/cms/e4/users/handlers/NewUser.java | 6 +- org.argeo.cms/OSGI-INF/cmsUserManager.xml | 2 +- org.argeo.cms/OSGI-INF/nodeUserAdmin.xml | 4 +- .../OSGI-INF/simpleTransactionManager.xml | 6 +- org.argeo.cms/bnd.bnd | 1 - .../cms/acr/directory/DirectoryContent.java | 8 +- .../directory/DirectoryContentProvider.java | 2 +- .../acr/directory/HierarchyUnitContent.java | 7 +- .../argeo/cms/acr/directory/RoleContent.java | 3 +- .../argeo/cms/auth/SingleUserLoginModule.java | 2 +- .../argeo/cms/auth/UserAdminLoginModule.java | 2 +- .../cms/internal/auth/CmsUserManagerImpl.java | 10 +- .../cms/internal/osgi/CmsOsgiLogger.java | 6 +- .../argeo/cms/internal/osgi/DeployConfig.java | 18 +- .../cms/internal/osgi/NodeUserAdmin.java | 6 +- .../cms/internal/runtime/CmsUserAdmin.java | 16 +- .../argeo/cms/internal/runtime/InitUtils.java | 6 +- .../osgi/useradmin/AbstractUserDirectory.java | 382 ++---------------- .../osgi/useradmin/AggregatingUserAdmin.java | 2 +- .../osgi/useradmin/AuthenticatingUser.java | 3 +- .../argeo/osgi/useradmin/DirectoryUser.java | 11 +- .../useradmin/DirectoryUserWorkingCopy.java | 19 - .../argeo/osgi/useradmin/LdapUserAdmin.java | 32 +- .../org/argeo/osgi/useradmin/LdifUser.java | 127 ++---- .../argeo/osgi/useradmin/LdifUserAdmin.java | 80 ++-- .../argeo/osgi/useradmin/OsUserDirectory.java | 22 +- .../argeo/osgi/useradmin/UserDirectory.java | 43 +- .../argeo/osgi/useradmin/WcXaResource.java | 135 ------- .../org/argeo/util/directory/Directory.java | 36 ++ .../directory/DirectoryConf.java} | 32 +- .../directory/DirectoryDigestUtils.java} | 21 +- .../directory}/HierarchyUnit.java | 10 +- .../directory/ldap/AbstractLdapDirectory.java | 376 +++++++++++++++++ .../directory/ldap/AbstractLdapEntry.java | 81 ++++ .../ldap/AttributesDictionary.java | 2 +- .../ldap/AuthPassword.java | 2 +- .../directory/ldap}/IpaUtils.java | 27 +- .../directory/ldap}/LdapConnection.java | 13 +- .../argeo/util/directory/ldap/LdapEntry.java | 13 + .../directory/ldap/LdapEntryWorkingCopy.java | 19 + .../directory/ldap/LdapHierarchyUnit.java} | 28 +- .../directory/ldap}/LdapNameUtils.java | 18 +- .../ldap/LdifParser.java | 2 +- .../ldap/LdifWriter.java | 2 +- .../org/argeo/util/naming/SharedSecret.java | 2 +- .../argeo/cms/web/AbstractCmsEntryPoint.java | 2 +- 48 files changed, 808 insertions(+), 849 deletions(-) delete mode 100644 org.argeo.util/src/org/argeo/osgi/useradmin/DirectoryUserWorkingCopy.java delete mode 100644 org.argeo.util/src/org/argeo/osgi/useradmin/WcXaResource.java create mode 100644 org.argeo.util/src/org/argeo/util/directory/Directory.java rename org.argeo.util/src/org/argeo/{osgi/useradmin/UserAdminConf.java => util/directory/DirectoryConf.java} (90%) rename org.argeo.util/src/org/argeo/{osgi/useradmin/DigestUtils.java => util/directory/DirectoryDigestUtils.java} (87%) rename org.argeo.util/src/org/argeo/{osgi/useradmin => util/directory}/HierarchyUnit.java (62%) create mode 100644 org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapDirectory.java create mode 100644 org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapEntry.java rename org.argeo.util/src/org/argeo/util/{naming => directory}/ldap/AttributesDictionary.java (99%) rename org.argeo.util/src/org/argeo/util/{naming => directory}/ldap/AuthPassword.java (99%) rename org.argeo.util/src/org/argeo/{osgi/useradmin => util/directory/ldap}/IpaUtils.java (82%) rename org.argeo.util/src/org/argeo/{osgi/useradmin => util/directory/ldap}/LdapConnection.java (91%) create mode 100644 org.argeo.util/src/org/argeo/util/directory/ldap/LdapEntry.java create mode 100644 org.argeo.util/src/org/argeo/util/directory/ldap/LdapEntryWorkingCopy.java rename org.argeo.util/src/org/argeo/{osgi/useradmin/LdifHierarchyUnit.java => util/directory/ldap/LdapHierarchyUnit.java} (71%) rename org.argeo.util/src/org/argeo/{osgi/useradmin => util/directory/ldap}/LdapNameUtils.java (73%) rename org.argeo.util/src/org/argeo/util/{naming => directory}/ldap/LdifParser.java (99%) rename org.argeo.util/src/org/argeo/util/{naming => directory}/ldap/LdifWriter.java (99%) 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 cd8d4d7dc..1e0fc6eb3 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 @@ -10,8 +10,8 @@ import java.util.Map; import org.argeo.api.cms.CmsConstants; import org.argeo.cms.CmsException; -import org.argeo.osgi.useradmin.UserAdminConf; import org.argeo.osgi.useradmin.UserDirectory; +import org.argeo.util.directory.DirectoryConf; import org.argeo.util.transaction.WorkTransaction; import org.osgi.service.useradmin.UserAdmin; import org.osgi.service.useradmin.UserAdminEvent; @@ -99,7 +99,7 @@ public class UserAdminWrapper { continue; if (baseDn.equalsIgnoreCase(CmsConstants.TOKENS_BASEDN)) continue; - dns.put(baseDn, UserAdminConf.propertiesAsUri(userDirectories.get(userDirectory)).toString()); + dns.put(baseDn, DirectoryConf.propertiesAsUri(userDirectories.get(userDirectory)).toString()); } // for (String uri : uris) { diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewGroup.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewGroup.java index d2ffa791b..f02a26b1c 100644 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewGroup.java +++ b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewGroup.java @@ -9,7 +9,7 @@ import org.argeo.cms.CmsException; import org.argeo.cms.e4.users.UserAdminWrapper; import org.argeo.eclipse.ui.EclipseUiUtils; import org.argeo.eclipse.ui.dialogs.ErrorFeedback; -import org.argeo.osgi.useradmin.UserAdminConf; +import org.argeo.util.directory.DirectoryConf; import org.argeo.util.naming.LdapAttrs; import org.eclipse.e4.core.di.annotations.Execute; import org.eclipse.jface.wizard.Wizard; @@ -179,8 +179,8 @@ public class NewGroup { Map dns = getDns(); String bdn = baseDnCmb.getText(); if (EclipseUiUtils.notEmpty(bdn)) { - Dictionary props = UserAdminConf.uriAsProperties(dns.get(bdn)); - String dn = LdapAttrs.cn.name() + "=" + cn + "," + UserAdminConf.groupBase.getValue(props) + "," + bdn; + Dictionary props = DirectoryConf.uriAsProperties(dns.get(bdn)); + String dn = LdapAttrs.cn.name() + "=" + cn + "," + DirectoryConf.groupBase.getValue(props) + "," + bdn; return dn; } return null; diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewUser.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewUser.java index 07d82c749..24f7e6250 100644 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewUser.java +++ b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewUser.java @@ -15,7 +15,7 @@ import org.argeo.cms.e4.users.UiAdminUtils; import org.argeo.cms.e4.users.UserAdminWrapper; import org.argeo.eclipse.ui.EclipseUiUtils; import org.argeo.eclipse.ui.dialogs.ErrorFeedback; -import org.argeo.osgi.useradmin.UserAdminConf; +import org.argeo.util.directory.DirectoryConf; import org.argeo.util.naming.LdapAttrs; import org.eclipse.e4.core.di.annotations.Execute; import org.eclipse.jface.wizard.Wizard; @@ -241,8 +241,8 @@ public class NewUser { Map dns = getDns(); String bdn = baseDnCmb.getText(); if (EclipseUiUtils.notEmpty(bdn)) { - Dictionary props = UserAdminConf.uriAsProperties(dns.get(bdn)); - String dn = LdapAttrs.uid.name() + "=" + uid + "," + UserAdminConf.userBase.getValue(props) + "," + bdn; + Dictionary props = DirectoryConf.uriAsProperties(dns.get(bdn)); + String dn = LdapAttrs.uid.name() + "=" + uid + "," + DirectoryConf.userBase.getValue(props) + "," + bdn; return dn; } return null; diff --git a/org.argeo.cms/OSGI-INF/cmsUserManager.xml b/org.argeo.cms/OSGI-INF/cmsUserManager.xml index 524c054ec..2e8f868ae 100644 --- a/org.argeo.cms/OSGI-INF/cmsUserManager.xml +++ b/org.argeo.cms/OSGI-INF/cmsUserManager.xml @@ -5,6 +5,6 @@ - + diff --git a/org.argeo.cms/OSGI-INF/nodeUserAdmin.xml b/org.argeo.cms/OSGI-INF/nodeUserAdmin.xml index eb048d9f5..cae688b0a 100644 --- a/org.argeo.cms/OSGI-INF/nodeUserAdmin.xml +++ b/org.argeo.cms/OSGI-INF/nodeUserAdmin.xml @@ -5,7 +5,7 @@ - - + + diff --git a/org.argeo.cms/OSGI-INF/simpleTransactionManager.xml b/org.argeo.cms/OSGI-INF/simpleTransactionManager.xml index c331aa430..81997476e 100644 --- a/org.argeo.cms/OSGI-INF/simpleTransactionManager.xml +++ b/org.argeo.cms/OSGI-INF/simpleTransactionManager.xml @@ -1,8 +1,8 @@ - + - - + + diff --git a/org.argeo.cms/bnd.bnd b/org.argeo.cms/bnd.bnd index 0d85a873f..52987f3da 100644 --- a/org.argeo.cms/bnd.bnd +++ b/org.argeo.cms/bnd.bnd @@ -1,7 +1,6 @@ Bundle-Activator: org.argeo.cms.internal.osgi.CmsActivator Import-Package: \ -org.argeo.osgi.transaction, \ org.apache.commons.httpclient.cookie;resolution:=optional,\ !com.sun.security.jgss,\ org.osgi.*;version=0.0.0,\ 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 index eef7055df..e1ad96077 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/directory/DirectoryContent.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/directory/DirectoryContent.java @@ -11,14 +11,14 @@ 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; +import org.argeo.util.directory.Directory; +import org.argeo.util.directory.HierarchyUnit; class DirectoryContent extends AbstractContent { - private UserDirectory directory; + private Directory directory; private DirectoryContentProvider provider; - public DirectoryContent(ProvidedSession session, DirectoryContentProvider provider, UserDirectory directory) { + public DirectoryContent(ProvidedSession session, DirectoryContentProvider provider, Directory directory) { super(session); this.provider = provider; this.directory = directory; 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 bd4117ead..f4afbdd53 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 @@ -17,8 +17,8 @@ import org.argeo.api.acr.spi.ProvidedSession; 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.UserDirectory; +import org.argeo.util.directory.HierarchyUnit; import org.osgi.service.useradmin.User; public class DirectoryContentProvider implements ContentProvider { 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 78bc72f5d..f6a0e3b52 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 @@ -13,7 +13,8 @@ import org.argeo.api.acr.CrName; 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; +import org.argeo.util.directory.HierarchyUnit; import org.osgi.service.useradmin.Role; class HierarchyUnitContent extends AbstractContent { @@ -59,7 +60,8 @@ class HierarchyUnitContent extends AbstractContent { for (HierarchyUnit hu : hierarchyUnit.getDirectHierachyUnits(false)) lst.add(new HierarchyUnitContent(getSession(), provider, hu)); - for (Role role : hierarchyUnit.getHierarchyUnitRoles(null, false)) + for (Role role : ((UserDirectory) hierarchyUnit.getDirectory()).getHierarchyUnitRoles(hierarchyUnit, null, + false)) lst.add(new RoleContent(getSession(), provider, this, role)); return lst.iterator(); } @@ -82,5 +84,4 @@ class HierarchyUnitContent extends AbstractContent { 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 bf3b319f4..2a22f023c 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 @@ -17,6 +17,7 @@ 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.UserDirectory; import org.argeo.util.naming.LdapAttrs; import org.argeo.util.naming.LdapObjs; import org.osgi.service.useradmin.Role; @@ -43,7 +44,7 @@ class RoleContent extends AbstractContent { @Override public QName getName() { - String name = parent.getHierarchyUnit().getDirectory().getRoleSimpleName(role); + String name = ((UserDirectory) parent.getHierarchyUnit().getDirectory()).getRoleSimpleName(role); return new ContentName(name); } diff --git a/org.argeo.cms/src/org/argeo/cms/auth/SingleUserLoginModule.java b/org.argeo.cms/src/org/argeo/cms/auth/SingleUserLoginModule.java index 08380ac5a..972d6a245 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/SingleUserLoginModule.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/SingleUserLoginModule.java @@ -14,8 +14,8 @@ import javax.security.auth.spi.LoginModule; import javax.security.auth.x500.X500Principal; import org.argeo.api.cms.CmsLog; -import org.argeo.osgi.useradmin.IpaUtils; import org.argeo.osgi.useradmin.OsUserUtils; +import org.argeo.util.directory.ldap.IpaUtils; import org.argeo.util.naming.LdapAttrs; import org.osgi.service.useradmin.Authorization; diff --git a/org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java b/org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java index ea7d1f370..f6832ad35 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java @@ -30,8 +30,8 @@ import org.argeo.cms.internal.osgi.NodeUserAdmin; import org.argeo.cms.internal.runtime.CmsContextImpl; import org.argeo.cms.security.CryptoKeyring; import org.argeo.osgi.useradmin.AuthenticatingUser; -import org.argeo.osgi.useradmin.IpaUtils; import org.argeo.osgi.useradmin.TokenUtils; +import org.argeo.util.directory.ldap.IpaUtils; import org.argeo.util.naming.LdapAttrs; import org.osgi.framework.BundleContext; import org.osgi.framework.FrameworkUtil; 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 9b7a2ed42..7db3cdc4c 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 @@ -32,8 +32,8 @@ import org.argeo.cms.CmsUserManager; import org.argeo.cms.auth.CurrentUser; import org.argeo.cms.auth.UserAdminUtils; import org.argeo.osgi.useradmin.TokenUtils; -import org.argeo.osgi.useradmin.UserAdminConf; import org.argeo.osgi.useradmin.UserDirectory; +import org.argeo.util.directory.DirectoryConf; import org.argeo.util.naming.LdapAttrs; import org.argeo.util.naming.NamingUtils; import org.argeo.util.naming.SharedSecret; @@ -240,7 +240,7 @@ public class CmsUserManagerImpl implements CmsUserManager { continue; if (baseDn.equalsIgnoreCase(CmsConstants.TOKENS_BASEDN)) continue; - dns.put(baseDn, UserAdminConf.propertiesAsUri(userDirectories.get(userDirectory)).toString()); + dns.put(baseDn, DirectoryConf.propertiesAsUri(userDirectories.get(userDirectory)).toString()); } return dns; @@ -254,12 +254,12 @@ public class CmsUserManagerImpl implements CmsUserManager { public String buildDistinguishedName(String localId, String baseDn, int type) { Map dns = getKnownBaseDns(true); - Dictionary props = UserAdminConf.uriAsProperties(dns.get(baseDn)); + Dictionary props = DirectoryConf.uriAsProperties(dns.get(baseDn)); String dn = null; if (Role.GROUP == type) - dn = LdapAttrs.cn.name() + "=" + localId + "," + UserAdminConf.groupBase.getValue(props) + "," + baseDn; + dn = LdapAttrs.cn.name() + "=" + localId + "," + DirectoryConf.groupBase.getValue(props) + "," + baseDn; else if (Role.USER == type) - dn = LdapAttrs.uid.name() + "=" + localId + "," + UserAdminConf.userBase.getValue(props) + "," + baseDn; + dn = LdapAttrs.uid.name() + "=" + localId + "," + DirectoryConf.userBase.getValue(props) + "," + baseDn; else throw new IllegalStateException("Unknown role type. " + "Cannot deduce dn for " + localId); return dn; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/osgi/CmsOsgiLogger.java b/org.argeo.cms/src/org/argeo/cms/internal/osgi/CmsOsgiLogger.java index 6898c4348..c167af025 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/osgi/CmsOsgiLogger.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/osgi/CmsOsgiLogger.java @@ -28,7 +28,7 @@ import org.argeo.cms.ArgeoLogger; import org.argeo.cms.CmsException; import org.argeo.cms.auth.CurrentUser; import org.argeo.cms.internal.runtime.KernelConstants; -import org.argeo.osgi.useradmin.UserAdminConf; +import org.argeo.util.directory.DirectoryConf; import org.osgi.framework.Bundle; import org.osgi.framework.Constants; import org.osgi.framework.ServiceReference; @@ -218,9 +218,9 @@ public class CmsOsgiLogger implements ArgeoLogger, LogListener { sb.append(" " + KernelConstants.CONTEXT_NAME_PROP + ": " + contextName); // user directories - Object baseDn = sr.getProperty(UserAdminConf.baseDn.name()); + Object baseDn = sr.getProperty(DirectoryConf.baseDn.name()); if (baseDn != null) - sb.append(" " + UserAdminConf.baseDn.name() + ": " + baseDn); + sb.append(" " + DirectoryConf.baseDn.name() + ": " + baseDn); } return sb.toString(); diff --git a/org.argeo.cms/src/org/argeo/cms/internal/osgi/DeployConfig.java b/org.argeo.cms/src/org/argeo/cms/internal/osgi/DeployConfig.java index 71c349327..b8fa8a73f 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/osgi/DeployConfig.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/osgi/DeployConfig.java @@ -26,10 +26,10 @@ import org.argeo.api.cms.CmsLog; import org.argeo.cms.internal.runtime.InitUtils; import org.argeo.cms.internal.runtime.KernelConstants; import org.argeo.cms.internal.runtime.KernelUtils; -import org.argeo.osgi.useradmin.UserAdminConf; -import org.argeo.util.naming.ldap.AttributesDictionary; -import org.argeo.util.naming.ldap.LdifParser; -import org.argeo.util.naming.ldap.LdifWriter; +import org.argeo.util.directory.DirectoryConf; +import org.argeo.util.directory.ldap.AttributesDictionary; +import org.argeo.util.directory.ldap.LdifParser; +import org.argeo.util.directory.ldap.LdifWriter; import org.osgi.framework.InvalidSyntaxException; import org.osgi.service.cm.Configuration; import org.osgi.service.cm.ConfigurationAdmin; @@ -75,12 +75,12 @@ public class DeployConfig { List activeCns = new ArrayList<>(); for (int i = 0; i < userDirectoryConfigs.size(); i++) { Dictionary userDirectoryConfig = userDirectoryConfigs.get(i); - String baseDn = (String) userDirectoryConfig.get(UserAdminConf.baseDn.name()); + String baseDn = (String) userDirectoryConfig.get(DirectoryConf.baseDn.name()); String cn; if (CmsConstants.ROLES_BASEDN.equals(baseDn)) cn = ROLES; else - cn = UserAdminConf.baseDnHash(userDirectoryConfig); + cn = DirectoryConf.baseDnHash(userDirectoryConfig); activeCns.add(cn); userDirectoryConfig.put(CmsConstants.CN, cn); putFactoryDeployConfig(CmsConstants.NODE_USER_ADMIN_PID, userDirectoryConfig); @@ -93,7 +93,7 @@ public class DeployConfig { Attributes attrs = deployConfigs.get(name); String cn = name.getRdn(name.size() - 1).getValue().toString(); if (!activeCns.contains(cn)) { - attrs.put(UserAdminConf.disabled.name(), "true"); + attrs.put(DirectoryConf.disabled.name(), "true"); } // } catch (Exception e) { // throw new CmsException("Cannot disable user directory " + name, e); @@ -206,7 +206,7 @@ public class DeployConfig { // service factory definition } } else { - Attribute disabled = deployConfig.get(UserAdminConf.disabled.name()); + Attribute disabled = deployConfig.get(DirectoryConf.disabled.name()); if (disabled != null) continue deployConfigs; // service factory service @@ -378,7 +378,7 @@ public class DeployConfig { boolean hasDomain = false; for (Configuration config : configs) { - Object realm = config.getProperties().get(UserAdminConf.realm.name()); + Object realm = config.getProperties().get(DirectoryConf.realm.name()); if (realm != null) { log.debug("Found realm: " + realm); hasDomain = true; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/osgi/NodeUserAdmin.java b/org.argeo.cms/src/org/argeo/cms/internal/osgi/NodeUserAdmin.java index 0746d4301..e534d9fe3 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/osgi/NodeUserAdmin.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/osgi/NodeUserAdmin.java @@ -9,8 +9,8 @@ import org.argeo.api.cms.CmsConstants; import org.argeo.api.cms.CmsLog; import org.argeo.cms.internal.runtime.CmsUserAdmin; import org.argeo.cms.internal.runtime.KernelConstants; -import org.argeo.osgi.useradmin.UserAdminConf; import org.argeo.osgi.useradmin.UserDirectory; +import org.argeo.util.directory.DirectoryConf; import org.osgi.framework.Constants; import org.osgi.service.cm.ConfigurationException; import org.osgi.service.cm.ManagedServiceFactory; @@ -29,7 +29,7 @@ public class NodeUserAdmin extends CmsUserAdmin implements ManagedServiceFactory @Override public void updated(String pid, Dictionary properties) throws ConfigurationException { - String basePath = (String) properties.get(UserAdminConf.baseDn.name()); + String basePath = (String) properties.get(DirectoryConf.baseDn.name()); // FIXME make updates more robust if (pidToBaseDn.containsValue(basePath)) { @@ -44,7 +44,7 @@ public class NodeUserAdmin extends CmsUserAdmin implements ManagedServiceFactory regProps.put(Constants.SERVICE_PID, pid); if (isSystemRolesBaseDn(basePath)) regProps.put(Constants.SERVICE_RANKING, Integer.MAX_VALUE); - regProps.put(UserAdminConf.baseDn.name(), basePath); + regProps.put(DirectoryConf.baseDn.name(), basePath); CmsActivator.getBundleContext().registerService(UserDirectory.class, userDirectory, regProps); pidToBaseDn.put(pid, basePath); 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 965330237..64b25c99e 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 @@ -37,8 +37,8 @@ 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.UserAdminConf; import org.argeo.osgi.useradmin.UserDirectory; +import org.argeo.util.directory.DirectoryConf; import org.argeo.util.naming.dns.DnsBrowser; import org.argeo.util.transaction.WorkControl; import org.argeo.util.transaction.WorkTransaction; @@ -78,12 +78,12 @@ public class CmsUserAdmin extends AggregatingUserAdmin { } public UserDirectory enableUserDirectory(Dictionary properties) { - String uri = (String) properties.get(UserAdminConf.uri.name()); - Object realm = properties.get(UserAdminConf.realm.name()); + String uri = (String) properties.get(DirectoryConf.uri.name()); + Object realm = properties.get(DirectoryConf.realm.name()); URI u; try { if (uri == null) { - String baseDn = (String) properties.get(UserAdminConf.baseDn.name()); + String baseDn = (String) properties.get(DirectoryConf.baseDn.name()); u = KernelUtils.getOsgiInstanceUri(KernelConstants.DIR_NODE + '/' + baseDn + ".ldif"); } else if (realm != null) { u = null; @@ -96,12 +96,12 @@ public class CmsUserAdmin extends AggregatingUserAdmin { // Create UserDirectory userDirectory; - if (realm != null || UserAdminConf.SCHEME_LDAP.equals(u.getScheme()) - || UserAdminConf.SCHEME_LDAPS.equals(u.getScheme())) { + if (realm != null || DirectoryConf.SCHEME_LDAP.equals(u.getScheme()) + || DirectoryConf.SCHEME_LDAPS.equals(u.getScheme())) { userDirectory = new LdapUserAdmin(properties); - } else if (UserAdminConf.SCHEME_FILE.equals(u.getScheme())) { + } else if (DirectoryConf.SCHEME_FILE.equals(u.getScheme())) { userDirectory = new LdifUserAdmin(u, properties); - } else if (UserAdminConf.SCHEME_OS.equals(u.getScheme())) { + } else if (DirectoryConf.SCHEME_OS.equals(u.getScheme())) { userDirectory = new OsUserDirectory(u, properties); singleUser = true; } else { diff --git a/org.argeo.cms/src/org/argeo/cms/internal/runtime/InitUtils.java b/org.argeo.cms/src/org/argeo/cms/internal/runtime/InitUtils.java index 821808017..986f7914d 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/runtime/InitUtils.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/runtime/InitUtils.java @@ -24,7 +24,7 @@ import org.apache.commons.io.FileUtils; import org.argeo.api.cms.CmsConstants; import org.argeo.api.cms.CmsLog; import org.argeo.cms.internal.http.InternalHttpConstants; -import org.argeo.osgi.useradmin.UserAdminConf; +import org.argeo.util.directory.DirectoryConf; /** * Interprets framework properties in order to generate the initial deploy @@ -201,13 +201,13 @@ public class InitUtils { u = new URI(uri); } else throw new IllegalArgumentException("Cannot interpret " + uri + " as an uri"); - } else if (u.getScheme().equals(UserAdminConf.SCHEME_FILE)) { + } else if (u.getScheme().equals(DirectoryConf.SCHEME_FILE)) { u = new File(u).getCanonicalFile().toURI(); } } catch (Exception e) { throw new RuntimeException("Cannot interpret " + uri + " as an uri", e); } - Dictionary properties = UserAdminConf.uriAsProperties(u.toString()); + Dictionary properties = DirectoryConf.uriAsProperties(u.toString()); res.add(properties); } 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 838c2ce0b..4b13728af 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java @@ -7,18 +7,11 @@ import static org.argeo.util.naming.LdapObjs.organizationalPerson; import static org.argeo.util.naming.LdapObjs.person; import static org.argeo.util.naming.LdapObjs.top; -import java.io.File; import java.net.URI; -import java.net.URISyntaxException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Dictionary; -import java.util.Enumeration; -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; @@ -30,14 +23,14 @@ import javax.naming.directory.BasicAttribute; import javax.naming.directory.BasicAttributes; import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; -import javax.transaction.xa.XAResource; +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.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; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.InvalidSyntaxException; @@ -47,112 +40,28 @@ import org.osgi.service.useradmin.User; import org.osgi.service.useradmin.UserAdmin; /** Base class for a {@link UserDirectory}. */ -abstract class AbstractUserDirectory - implements UserAdmin, UserDirectory, WorkingCopyProcessor, XAResourceProvider { - static final String SHARED_STATE_USERNAME = "javax.security.auth.login.name"; - static final String SHARED_STATE_PASSWORD = "javax.security.auth.login.password"; - - private final Hashtable properties; - private final LdapName baseDn; - // private final LdapName userBaseDn, groupBaseDn; - private final Rdn userBaseRdn, groupBaseRdn, systemRoleBaseRdn; - private final String userObjectClass, groupObjectClass; - - private final boolean readOnly; - private final boolean disabled; - private final String uri; +abstract class AbstractUserDirectory extends AbstractLdapDirectory implements UserAdmin, UserDirectory { private UserAdmin externalRoles; // private List indexedUserProperties = Arrays // .asList(new String[] { LdapAttrs.uid.name(), LdapAttrs.mail.name(), // LdapAttrs.cn.name() }); - private final boolean scoped; - - private String memberAttributeId = "member"; - private List credentialAttributeIds = Arrays - .asList(new String[] { LdapAttrs.userPassword.name(), LdapAttrs.authPassword.name() }); - // Transaction // private TransactionManager transactionManager; - private WorkControl transactionControl; - private WorkingCopyXaResource xaResource = new WorkingCopyXaResource<>(this); - - private String forcedPassword; - AbstractUserDirectory(URI uriArg, Dictionary props, boolean scoped) { - this.scoped = scoped; - properties = new Hashtable(); - for (Enumeration keys = props.keys(); keys.hasMoreElements();) { - String key = keys.nextElement(); - properties.put(key, props.get(key)); - } - - if (uriArg != null) { - uri = uriArg.toString(); - // uri from properties is ignored - } else { - String uriStr = UserAdminConf.uri.getValue(properties); - if (uriStr == null) - uri = null; - else - uri = uriStr; - } - - forcedPassword = UserAdminConf.forcedPassword.getValue(properties); - - userObjectClass = UserAdminConf.userObjectClass.getValue(properties); - String userBase = UserAdminConf.userBase.getValue(properties); - groupObjectClass = UserAdminConf.groupObjectClass.getValue(properties); - String groupBase = UserAdminConf.groupBase.getValue(properties); - String systemRoleBase = UserAdminConf.systemRoleBase.getValue(properties); - try { - baseDn = new LdapName(UserAdminConf.baseDn.getValue(properties)); - userBaseRdn = new Rdn(userBase); -// userBaseDn = new LdapName(userBase + "," + baseDn); - groupBaseRdn = new Rdn(groupBase); -// groupBaseDn = new LdapName(groupBase + "," + baseDn); - systemRoleBaseRdn = new Rdn(systemRoleBase); - } catch (InvalidNameException e) { - 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); - else - disabled = false; + super(uriArg, props, scoped); } /* * ABSTRACT METHODS */ + protected abstract AbstractLdapDirectory scope(User user); + /** Returns the groups this user is a direct member of. */ protected abstract List getDirectGroups(LdapName dn); - protected abstract Boolean daoHasRole(LdapName dn); - - protected abstract DirectoryUser daoGetRole(LdapName key) throws NameNotFoundException; - - protected abstract List doGetRoles(LdapName searchBase, Filter f, boolean deep); - - protected abstract AbstractUserDirectory scope(User user); - - protected abstract HierarchyUnit doGetHierarchyUnit(LdapName dn); - - protected abstract Iterable doGetDirectHierarchyUnits(LdapName searchBase, boolean functionalOnly); - /* * INITIALIZATION */ @@ -165,20 +74,6 @@ abstract class AbstractUserDirectory } - /* - * PATHS - */ - - @Override - public String getContext() { - return getBaseDn().toString(); - } - - @Override - public String getName() { - return nameToSimple(getBaseDn(), "."); - } - @Override public String getRolePath(Role role) { return nameToRelativePath(((DirectoryUser) role).getDn()); @@ -191,86 +86,11 @@ abstract class AbstractUserDirectory 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("/"); - Rdn parentRdn = null; - for (String segment : segments) { - // TODO make attr names configurable ? - String attr = LdapAttrs.ou.name(); - if (parentRdn != null) { - if (getUserBaseRdn().equals(parentRdn)) - attr = LdapAttrs.uid.name(); - else if (getGroupBaseRdn().equals(parentRdn)) - attr = LdapAttrs.cn.name(); - else if (getSystemRoleBaseRdn().equals(parentRdn)) - attr = LdapAttrs.cn.name(); - } - Rdn rdn = new Rdn(attr, segment); - name.add(rdn); - parentRdn = rdn; - } - 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()); - if (realm == null) - return Optional.empty(); - return Optional.of(realm.toString()); - } - - /* - * EDITION - */ - - protected boolean isEditing() { - return xaResource.wc() != null; - } - - protected DirectoryUserWorkingCopy getWorkingCopy() { - DirectoryUserWorkingCopy wc = xaResource.wc(); - if (wc == null) - return null; - return wc; - } - - protected void checkEdit() { - if (xaResource.wc() == null) { - try { - transactionControl.getWorkContext().registerXAResource(xaResource, null); - } catch (Exception e) { - throw new IllegalStateException("Cannot enlist " + xaResource, e); - } - } else { - } - } - protected List getAllRoles(DirectoryUser user) { List allRoles = new ArrayList(); if (user != null) { @@ -322,16 +142,16 @@ abstract class AbstractUserDirectory } protected DirectoryUser doGetRole(LdapName dn) { - DirectoryUserWorkingCopy wc = getWorkingCopy(); + LdapEntryWorkingCopy wc = getWorkingCopy(); DirectoryUser user; try { - user = daoGetRole(dn); + user = (DirectoryUser) daoGetEntry(dn); } catch (NameNotFoundException e) { user = null; } if (wc != null) { if (user == null && wc.getNewData().containsKey(dn)) - user = wc.getNewData().get(dn); + user = (DirectoryUser) wc.getNewData().get(dn); else if (wc.getDeletedData().containsKey(dn)) user = null; } @@ -345,17 +165,21 @@ abstract class AbstractUserDirectory } List getRoles(LdapName searchBase, String filter, boolean deep) throws InvalidSyntaxException { - DirectoryUserWorkingCopy wc = getWorkingCopy(); + LdapEntryWorkingCopy wc = getWorkingCopy(); Filter f = filter != null ? FrameworkUtil.createFilter(filter) : null; - List res = doGetRoles(searchBase, f, deep); + List searchRes = doGetEntries(searchBase, f, 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 = it.next(); + DirectoryUser user = (DirectoryUser) it.next(); LdapName dn = user.getDn(); if (wc.getDeletedData().containsKey(dn)) it.remove(); } - for (DirectoryUser user : wc.getNewData().values()) { + for (LdapEntry ldapEntry : wc.getNewData().values()) { + DirectoryUser user = (DirectoryUser) ldapEntry; if (f == null || f.match(user.getProperties())) res.add(user); } @@ -402,8 +226,9 @@ abstract class AbstractUserDirectory protected void doGetUser(String key, String value, List collectedUsers) { try { Filter f = FrameworkUtil.createFilter("(" + key + "=" + value + ")"); - List users = doGetRoles(getBaseDn(), f, true); - collectedUsers.addAll(users); + 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); } @@ -415,7 +240,7 @@ abstract class AbstractUserDirectory return new LdifAuthorization(user, getAllRoles((DirectoryUser) user)); } else { // bind - AbstractUserDirectory scopedUserAdmin = scope(user); + AbstractUserDirectory scopedUserAdmin = (AbstractUserDirectory) scope(user); try { DirectoryUser directoryUser = (DirectoryUser) scopedUserAdmin.getRole(user.getName()); if (directoryUser == null) @@ -432,9 +257,9 @@ abstract class AbstractUserDirectory @Override public Role createRole(String name, int type) { checkEdit(); - DirectoryUserWorkingCopy wc = getWorkingCopy(); + LdapEntryWorkingCopy wc = getWorkingCopy(); LdapName dn = toLdapName(name); - if ((daoHasRole(dn) && !wc.getDeletedData().containsKey(dn)) || wc.getNewData().containsKey(dn)) + if ((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()); @@ -484,10 +309,10 @@ abstract class AbstractUserDirectory @Override public boolean removeRole(String name) { checkEdit(); - DirectoryUserWorkingCopy wc = getWorkingCopy(); + LdapEntryWorkingCopy wc = getWorkingCopy(); LdapName dn = toLdapName(name); boolean actuallyDeleted; - if (daoHasRole(dn) || wc.getNewData().containsKey(dn)) { + if (daoHasEntry(dn) || wc.getNewData().containsKey(dn)) { DirectoryUser user = (DirectoryUser) getRole(name); wc.getDeletedData().put(dn, user); actuallyDeleted = true; @@ -501,23 +326,9 @@ abstract class AbstractUserDirectory return actuallyDeleted; } - /* - * TRANSACTION - */ - @Override - public DirectoryUserWorkingCopy newWorkingCopy() { - return new DirectoryUserWorkingCopy(); - } - /* * HIERARCHY */ - @Override - public HierarchyUnit getHierarchyUnit(String path) { - LdapName dn = pathToName(path); - return doGetHierarchyUnit(dn); - } - @Override public HierarchyUnit getHierarchyUnit(Role role) { LdapName dn = LdapNameUtils.toLdapName(role.getName()); @@ -529,8 +340,13 @@ abstract class AbstractUserDirectory } @Override - public Iterable getDirectHierarchyUnits(boolean functionalOnly) { - return doGetDirectHierarchyUnits(baseDn, functionalOnly); + public Iterable getHierarchyUnitRoles(HierarchyUnit hierarchyUnit, String filter, boolean deep) { + LdapName dn = LdapNameUtils.toLdapName(hierarchyUnit.getContext()); + try { + return getRoles(dn, filter, deep); + } catch (InvalidSyntaxException e) { + throw new IllegalArgumentException("Cannot filter " + filter + " " + dn, e); + } } /* @@ -552,70 +368,7 @@ abstract class AbstractUserDirectory } - private boolean hasObjectClass(Attributes attrs, LdapObjs 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())) - return true; - - } - return false; - } catch (NamingException e) { - throw new IllegalStateException("Cannot search for objectClass " + objectClass.name(), e); - } - } - // GETTERS - protected String getMemberAttributeId() { - return memberAttributeId; - } - - protected List getCredentialAttributeIds() { - return credentialAttributeIds; - } - - protected String getUri() { - return uri; - } - - private static boolean readOnlyDefault(String uriStr) { - if (uriStr == null) - return true; - /// TODO make it more generic - URI uri; - try { - uri = new URI(uriStr.split(" ")[0]); - } catch (URISyntaxException e) { - throw new IllegalArgumentException(e); - } - if (uri.getScheme() == null) - return false;// assume relative file to be writable - if (uri.getScheme().equals(UserAdminConf.SCHEME_FILE)) { - File file = new File(uri); - if (file.exists()) - return !file.canWrite(); - else - return !file.getParentFile().canWrite(); - } else if (uri.getScheme().equals(UserAdminConf.SCHEME_LDAP)) { - if (uri.getAuthority() != null)// assume writable if authenticated - return false; - } else if (uri.getScheme().equals(UserAdminConf.SCHEME_OS)) { - return true; - } - return true;// read only by default - } - - public boolean isReadOnly() { - return readOnly; - } - - public boolean isDisabled() { - return disabled; - } - protected UserAdmin getExternalRoles() { return externalRoles; } @@ -624,50 +377,13 @@ abstract class AbstractUserDirectory Rdn technicalRdn = LdapNameUtils.getParentRdn(dn); if (getGroupBaseRdn().equals(technicalRdn) || getSystemRoleBaseRdn().equals(technicalRdn)) return Role.GROUP; - else if (userBaseRdn.equals(technicalRdn)) + else if (getUserBaseRdn().equals(technicalRdn)) return Role.USER; else throw new IllegalArgumentException( "Cannot dind role type, " + technicalRdn + " is not a technical RDN for " + dn); } - /** dn can be null, in that case a default should be returned. */ - public String getUserObjectClass() { - return userObjectClass; - } - - Rdn getUserBaseRdn() { - return userBaseRdn; - } - - protected String newUserObjectClass(LdapName dn) { - return getUserObjectClass(); - } - - public String getGroupObjectClass() { - return groupObjectClass; - } - - Rdn getGroupBaseRdn() { - return groupBaseRdn; - } - - Rdn getSystemRoleBaseRdn() { - return systemRoleBaseRdn; - } - - LdapName getBaseDn() { - return (LdapName) baseDn.clone(); - } - - public Dictionary getProperties() { - return properties; - } - - public Dictionary cloneProperties() { - return new Hashtable<>(properties); - } - public void setExternalRoles(UserAdmin externalRoles) { this.externalRoles = externalRoles; } @@ -676,32 +392,6 @@ abstract class AbstractUserDirectory // this.transactionManager = transactionManager; // } - public String getForcedPassword() { - return forcedPassword; - } - - public void setTransactionControl(WorkControl transactionControl) { - this.transactionControl = transactionControl; - } - - public XAResource getXaResource() { - return xaResource; - } - - public boolean isScoped() { - return scoped; - } - - @Override - public int hashCode() { - return baseDn.hashCode(); - } - - @Override - public String toString() { - return "User Directory " + baseDn.toString(); - } - /* * STATIC UTILITIES */ 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 ac641de97..955178ce4 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java @@ -117,7 +117,7 @@ public class AggregatingUserAdmin implements UserAdmin { if (user instanceof DirectoryUser) { userAdminToUse = userReferentialOfThisUser; } else if (user instanceof AuthenticatingUser) { - userAdminToUse = userReferentialOfThisUser.scope(user); + userAdminToUse = (AbstractUserDirectory) userReferentialOfThisUser.scope(user); } else { throw new IllegalArgumentException("Unsupported user type " + user.getClass()); } diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/AuthenticatingUser.java b/org.argeo.util/src/org/argeo/osgi/useradmin/AuthenticatingUser.java index 01db8be98..ba1f3f753 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/AuthenticatingUser.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/AuthenticatingUser.java @@ -5,6 +5,7 @@ import java.util.Hashtable; import javax.naming.ldap.LdapName; +import org.argeo.util.directory.DirectoryDigestUtils; import org.osgi.service.useradmin.User; /** @@ -38,7 +39,7 @@ public class AuthenticatingUser implements User { this.name = name; credentials = new Hashtable<>(); credentials.put(SHARED_STATE_NAME, name); - byte[] pwd = DigestUtils.charsToBytes(password); + byte[] pwd = DirectoryDigestUtils.charsToBytes(password); credentials.put(SHARED_STATE_PWD, pwd); } 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 146b80578..c82c5a01d 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/DirectoryUser.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/DirectoryUser.java @@ -1,15 +1,8 @@ package org.argeo.osgi.useradmin; -import javax.naming.directory.Attributes; -import javax.naming.ldap.LdapName; - +import org.argeo.util.directory.ldap.LdapEntry; import org.osgi.service.useradmin.User; /** A user in a user directory. */ -interface DirectoryUser extends User { - LdapName getDn(); - - Attributes getAttributes(); - - void publishAttributes(Attributes modifiedAttributes); +interface DirectoryUser extends User, LdapEntry { } diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/DirectoryUserWorkingCopy.java b/org.argeo.util/src/org/argeo/osgi/useradmin/DirectoryUserWorkingCopy.java deleted file mode 100644 index 2aed145c7..000000000 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/DirectoryUserWorkingCopy.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.argeo.osgi.useradmin; - -import javax.naming.directory.Attributes; -import javax.naming.ldap.LdapName; - -import org.argeo.util.transaction.AbstractWorkingCopy; - -/** Working copy for a user directory being edited. */ -class DirectoryUserWorkingCopy extends AbstractWorkingCopy { - @Override - protected LdapName getId(DirectoryUser user) { - return user.getDn(); - } - - @Override - protected Attributes cloneAttributes(DirectoryUser user) { - return (Attributes) user.getAttributes().clone(); - } -} diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/LdapUserAdmin.java b/org.argeo.util/src/org/argeo/osgi/useradmin/LdapUserAdmin.java index 879d5da04..36419d960 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/LdapUserAdmin.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/LdapUserAdmin.java @@ -19,6 +19,12 @@ import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; import javax.naming.ldap.LdapName; +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; @@ -52,7 +58,7 @@ public class LdapUserAdmin extends AbstractUserDirectory { Object pwdCred = credentials.get(SHARED_STATE_PASSWORD); byte[] pwd = (byte[]) pwdCred; if (pwd != null) { - char[] password = DigestUtils.bytesToChars(pwd); + char[] password = DirectoryDigestUtils.bytesToChars(pwd); properties.put(Context.SECURITY_CREDENTIALS, new String(password)); } else { properties.put(Context.SECURITY_AUTHENTICATION, "GSSAPI"); @@ -65,16 +71,16 @@ public class LdapUserAdmin extends AbstractUserDirectory { // } @Override - protected Boolean daoHasRole(LdapName dn) { + protected Boolean daoHasEntry(LdapName dn) { try { - return daoGetRole(dn) != null; + return daoGetEntry(dn) != null; } catch (NameNotFoundException e) { return false; } } @Override - protected DirectoryUser daoGetRole(LdapName name) throws NameNotFoundException { + protected DirectoryUser daoGetEntry(LdapName name) throws NameNotFoundException { try { Attributes attrs = ldapConnection.getAttributes(name); if (attrs.size() == 0) @@ -96,8 +102,8 @@ public class LdapUserAdmin extends AbstractUserDirectory { } @Override - protected List doGetRoles(LdapName searchBase, Filter f, boolean deep) { - ArrayList res = new ArrayList(); + protected List doGetEntries(LdapName searchBase, Filter f, boolean deep) { + ArrayList res = new ArrayList<>(); try { String searchFilter = f != null ? f.toString() : "(|(" + objectClass + "=" + getUserObjectClass() + ")(" + objectClass + "=" @@ -165,7 +171,7 @@ public class LdapUserAdmin extends AbstractUserDirectory { } @Override - public void prepare(DirectoryUserWorkingCopy wc) { + public void prepare(LdapEntryWorkingCopy wc) { try { ldapConnection.prepareChanges(wc); } catch (NamingException e) { @@ -174,7 +180,7 @@ public class LdapUserAdmin extends AbstractUserDirectory { } @Override - public void commit(DirectoryUserWorkingCopy wc) { + public void commit(LdapEntryWorkingCopy wc) { try { ldapConnection.commitChanges(wc); } catch (NamingException e) { @@ -183,7 +189,7 @@ public class LdapUserAdmin extends AbstractUserDirectory { } @Override - public void rollback(DirectoryUserWorkingCopy wc) { + public void rollback(LdapEntryWorkingCopy wc) { // prepare not impacting } @@ -192,7 +198,7 @@ public class LdapUserAdmin extends AbstractUserDirectory { */ @Override - protected Iterable doGetDirectHierarchyUnits(LdapName searchBase, boolean functionalOnly) { + public Iterable doGetDirectHierarchyUnits(LdapName searchBase, boolean functionalOnly) { List res = new ArrayList<>(); try { String searchFilter = "(|(" + objectClass + "=" + LdapObjs.organizationalUnit.name() + ")(" + objectClass @@ -207,7 +213,7 @@ public class LdapUserAdmin extends AbstractUserDirectory { SearchResult searchResult = (SearchResult) results.nextElement(); LdapName dn = toDn(searchBase, searchResult); Attributes attrs = searchResult.getAttributes(); - LdifHierarchyUnit hierarchyUnit = new LdifHierarchyUnit(this, dn, attrs); + LdapHierarchyUnit hierarchyUnit = new LdapHierarchyUnit(this, dn, attrs); if (functionalOnly) { if (hierarchyUnit.isFunctional()) res.add(hierarchyUnit); @@ -222,10 +228,10 @@ public class LdapUserAdmin extends AbstractUserDirectory { } @Override - protected HierarchyUnit doGetHierarchyUnit(LdapName dn) { + public HierarchyUnit doGetHierarchyUnit(LdapName dn) { try { Attributes attrs = ldapConnection.getAttributes(dn); - return new LdifHierarchyUnit(this, dn, attrs); + return new LdapHierarchyUnit(this, dn, attrs); } catch (NamingException e) { throw new IllegalStateException("Cannot get hierarchy unit " + dn, e); } 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 aaac50272..6cf6725cc 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/LdifUser.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/LdifUser.java @@ -21,40 +21,28 @@ import javax.naming.directory.Attributes; import javax.naming.directory.BasicAttribute; import javax.naming.ldap.LdapName; +import org.argeo.util.directory.DirectoryDigestUtils; import org.argeo.util.directory.Person; +import org.argeo.util.directory.ldap.AbstractLdapEntry; +import org.argeo.util.directory.ldap.AuthPassword; import org.argeo.util.naming.LdapAttrs; import org.argeo.util.naming.LdapObjs; import org.argeo.util.naming.SharedSecret; -import org.argeo.util.naming.ldap.AuthPassword; /** Directory user implementation */ -abstract class LdifUser implements DirectoryUser { - private final AbstractUserDirectory userAdmin; - - private final LdapName dn; - - private final boolean frozen; - private Attributes publishedAttributes; - +abstract class LdifUser extends AbstractLdapEntry implements DirectoryUser { private final AttributeDictionary properties; private final AttributeDictionary credentials; LdifUser(AbstractUserDirectory userAdmin, LdapName dn, Attributes attributes) { - this(userAdmin, dn, attributes, false); - } - - private LdifUser(AbstractUserDirectory userAdmin, LdapName dn, Attributes attributes, boolean frozen) { - this.userAdmin = userAdmin; - this.dn = dn; - this.publishedAttributes = attributes; + super(userAdmin, dn, attributes); properties = new AttributeDictionary(false); credentials = new AttributeDictionary(true); - this.frozen = frozen; } @Override public String getName() { - return dn.toString(); + return getDn().toString(); } @Override @@ -78,9 +66,10 @@ abstract class LdifUser implements DirectoryUser { // TODO check other sources (like PKCS12) // String pwd = new String((char[]) value); // authPassword (RFC 312 https://tools.ietf.org/html/rfc3112) - char[] password = DigestUtils.bytesToChars(value); + char[] password = DirectoryDigestUtils.bytesToChars(value); - if (userAdmin.getForcedPassword() != null && userAdmin.getForcedPassword().equals(new String(password))) + if (getDirectory().getForcedPassword() != null + && getDirectory().getForcedPassword().equals(new String(password))) return true; AuthPassword authPassword = AuthPassword.matchAuthValue(getAttributes(), password); @@ -104,7 +93,7 @@ abstract class LdifUser implements DirectoryUser { // Regular password // byte[] hashedPassword = hash(password, DigestUtils.PASSWORD_SCHEME_PBKDF2_SHA256); - if (hasCredential(LdapAttrs.userPassword.name(), DigestUtils.charsToBytes(password))) + if (hasCredential(LdapAttrs.userPassword.name(), DirectoryDigestUtils.charsToBytes(password))) return true; return false; } @@ -125,11 +114,12 @@ abstract class LdifUser implements DirectoryUser { passwordScheme = storedBase64.substring(1, index); String storedValueBase64 = storedBase64.substring(index + 1); byte[] storedValueBytes = Base64.getDecoder().decode(storedValueBase64); - char[] passwordValue = DigestUtils.bytesToChars((byte[]) value); + char[] passwordValue = DirectoryDigestUtils.bytesToChars((byte[]) value); byte[] valueBytes; - if (DigestUtils.PASSWORD_SCHEME_SHA.equals(passwordScheme)) { - valueBytes = DigestUtils.toPasswordScheme(passwordScheme, passwordValue, null, null, null); - } else if (DigestUtils.PASSWORD_SCHEME_PBKDF2_SHA256.equals(passwordScheme)) { + if (DirectoryDigestUtils.PASSWORD_SCHEME_SHA.equals(passwordScheme)) { + valueBytes = DirectoryDigestUtils.toPasswordScheme(passwordScheme, passwordValue, null, null, + null); + } else if (DirectoryDigestUtils.PASSWORD_SCHEME_PBKDF2_SHA256.equals(passwordScheme)) { // see https://www.thesubtlety.com/post/a-389-ds-pbkdf2-password-checker/ byte[] iterationsArr = Arrays.copyOfRange(storedValueBytes, 0, 4); BigInteger iterations = new BigInteger(iterationsArr); @@ -138,7 +128,7 @@ abstract class LdifUser implements DirectoryUser { byte[] keyArr = Arrays.copyOfRange(storedValueBytes, iterationsArr.length + salt.length, storedValueBytes.length); int keyLengthBits = keyArr.length * 8; - valueBytes = DigestUtils.toPasswordScheme(passwordScheme, passwordValue, salt, + valueBytes = DirectoryDigestUtils.toPasswordScheme(passwordScheme, passwordValue, salt, iterations.intValue(), keyLengthBits); } else { throw new UnsupportedOperationException("Unknown password scheme " + passwordScheme); @@ -155,8 +145,8 @@ abstract class LdifUser implements DirectoryUser { /** Hash the password */ byte[] sha1hash(char[] password) { - byte[] hashedPassword = ("{SHA}" - + Base64.getEncoder().encodeToString(DigestUtils.sha1(DigestUtils.charsToBytes(password)))) + byte[] hashedPassword = ("{SHA}" + Base64.getEncoder() + .encodeToString(DirectoryDigestUtils.sha1(DirectoryDigestUtils.charsToBytes(password)))) .getBytes(StandardCharsets.UTF_8); return hashedPassword; } @@ -170,71 +160,8 @@ abstract class LdifUser implements DirectoryUser { // return hashedPassword; // } - @Override - public LdapName getDn() { - return dn; - } - - @Override - public synchronized Attributes getAttributes() { - return isEditing() ? getModifiedAttributes() : publishedAttributes; - } - - /** Should only be called from working copy thread. */ - private synchronized Attributes getModifiedAttributes() { - assert getWc() != null; - return getWc().getModifiedData().get(getDn()); - } - - protected synchronized boolean isEditing() { - return getWc() != null && getModifiedAttributes() != null; - } - - private synchronized DirectoryUserWorkingCopy getWc() { - return userAdmin.getWorkingCopy(); - } - - protected synchronized void startEditing() { - if (frozen) - throw new IllegalStateException("Cannot edit frozen view"); - if (getUserAdmin().isReadOnly()) - throw new IllegalStateException("User directory is read-only"); - assert getModifiedAttributes() == null; - getWc().startEditing(this); - // modifiedAttributes = (Attributes) publishedAttributes.clone(); - } - - public synchronized void publishAttributes(Attributes modifiedAttributes) { - publishedAttributes = modifiedAttributes; - } - -// public DirectoryUser getPublished() { -// return new LdifUser(userAdmin, dn, publishedAttributes, true); -// } - - @Override - public int hashCode() { - return dn.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj instanceof LdifUser) { - LdifUser that = (LdifUser) obj; - return this.dn.equals(that.dn); - } - return false; - } - - @Override - public String toString() { - return dn.toString(); - } - protected AbstractUserDirectory getUserAdmin() { - return userAdmin; + return (AbstractUserDirectory) getDirectory(); } private class AttributeDictionary extends Dictionary { @@ -243,7 +170,7 @@ abstract class LdifUser implements DirectoryUser { private final Boolean includeFilter; public AttributeDictionary(Boolean credentials) { - this.attrFilter = userAdmin.getCredentialAttributeIds(); + this.attrFilter = getDirectory().getCredentialAttributeIds(); this.includeFilter = credentials; try { NamingEnumeration ids = getAttributes().getIDs(); @@ -322,10 +249,10 @@ abstract class LdifUser implements DirectoryUser { 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 (v.equalsIgnoreCase(getDirectory().getUserObjectClass())) + return getDirectory().getUserObjectClass(); + else if (v.equalsIgnoreCase(getDirectory().getGroupObjectClass())) + return getDirectory().getGroupObjectClass(); } if (first != null) return first; @@ -350,7 +277,7 @@ abstract class LdifUser implements DirectoryUser { public Object put(String key, Object value) { if (key == null) { // TODO persist to other sources (like PKCS12) - char[] password = DigestUtils.bytesToChars(value); + char[] password = DirectoryDigestUtils.bytesToChars(value); byte[] hashedPassword = sha1hash(password); return put(LdapAttrs.userPassword.name(), hashedPassword); } @@ -358,7 +285,7 @@ abstract class LdifUser implements DirectoryUser { return put(LdapAttrs.authPassword.name(), value); } - userAdmin.checkEdit(); + getDirectory().checkEdit(); if (!isEditing()) startEditing(); @@ -390,7 +317,7 @@ abstract class LdifUser implements DirectoryUser { @Override public Object remove(Object key) { - userAdmin.checkEdit(); + getDirectory().checkEdit(); if (!isEditing()) startEditing(); 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 26d3d134c..c978af4a0 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/LdifUserAdmin.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/LdifUserAdmin.java @@ -28,19 +28,25 @@ import javax.naming.NamingException; 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.argeo.util.naming.ldap.LdifParser; -import org.argeo.util.naming.ldap.LdifWriter; import org.osgi.framework.Filter; 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<>(); + private NavigableMap users = new TreeMap<>(); + private NavigableMap groups = new TreeMap<>(); - private NavigableMap hierarchy = new TreeMap<>(); + private NavigableMap hierarchy = new TreeMap<>(); // private List rootHierarchyUnits = new ArrayList<>(); public LdifUserAdmin(String uri, String baseDn) { @@ -68,7 +74,7 @@ public class LdifUserAdmin extends AbstractUserDirectory { Object pwdCred = credentials.get(SHARED_STATE_PASSWORD); byte[] pwd = (byte[]) pwdCred; if (pwd != null) { - char[] password = DigestUtils.bytesToChars(pwd); + char[] password = DirectoryDigestUtils.bytesToChars(pwd); User directoryUser = (User) getRole(username); if (!directoryUser.hasCredential(null, password)) throw new IllegalStateException("Invalid credentials"); @@ -76,7 +82,7 @@ public class LdifUserAdmin extends AbstractUserDirectory { throw new IllegalStateException("Password is required"); } Dictionary properties = cloneProperties(); - properties.put(UserAdminConf.readOnly.name(), "true"); + properties.put(DirectoryConf.readOnly.name(), "true"); LdifUserAdmin scopedUserAdmin = new LdifUserAdmin(properties, true); scopedUserAdmin.groups = Collections.unmodifiableNavigableMap(groups); scopedUserAdmin.users = Collections.unmodifiableNavigableMap(users); @@ -85,8 +91,8 @@ public class LdifUserAdmin extends AbstractUserDirectory { private static Dictionary fromUri(String uri, String baseDn) { Hashtable res = new Hashtable(); - res.put(UserAdminConf.uri.name(), uri); - res.put(UserAdminConf.baseDn.name(), baseDn); + res.put(DirectoryConf.uri.name(), uri); + res.put(DirectoryConf.baseDn.name(), baseDn); return res; } @@ -172,7 +178,7 @@ public class LdifUserAdmin extends AbstractUserDirectory { // 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, attributes)); + hierarchy.put(key, new LdapHierarchyUnit(this, key, attributes)); break objectClasses; } } @@ -208,23 +214,23 @@ public class LdifUserAdmin extends AbstractUserDirectory { */ @Override - protected DirectoryUser daoGetRole(LdapName key) throws NameNotFoundException { + protected DirectoryUser daoGetEntry(LdapName key) throws NameNotFoundException { if (groups.containsKey(key)) - return groups.get(key); + return (DirectoryUser) groups.get(key); if (users.containsKey(key)) - return users.get(key); + return (DirectoryUser) users.get(key); throw new NameNotFoundException(key + " not persisted"); } @Override - protected Boolean daoHasRole(LdapName dn) { + protected Boolean daoHasEntry(LdapName dn) { return users.containsKey(dn) || groups.containsKey(dn); } @Override - protected List doGetRoles(LdapName searchBase, Filter f, boolean deep) { + protected List doGetEntries(LdapName searchBase, Filter f, boolean deep) { Objects.requireNonNull(searchBase); - ArrayList res = new ArrayList(); + ArrayList res = new ArrayList<>(); if (f == null && deep && getBaseDn().equals(searchBase)) { res.addAll(users.values()); res.addAll(groups.values()); @@ -235,18 +241,20 @@ public class LdifUserAdmin extends AbstractUserDirectory { return res; } - private void filterRoles(SortedMap map, LdapName searchBase, Filter f, - boolean deep, List res) { + private void filterRoles(SortedMap map, LdapName searchBase, Filter f, boolean deep, + List res) { // TODO reduce map with search base ? - roles: for (DirectoryUser user : map.values()) { + 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(user.getProperties())) - res.add(user); + else { + if (f.match(((DirectoryUser) user).getProperties())) + res.add(user); + } } } @@ -256,7 +264,12 @@ public class LdifUserAdmin extends AbstractUserDirectory { protected List getDirectGroups(LdapName dn) { List directGroups = new ArrayList(); for (LdapName name : groups.keySet()) { - DirectoryGroup group = groups.get(name); + DirectoryGroup group; + try { + group = (DirectoryGroup) daoGetEntry(name); + } catch (NameNotFoundException e) { + throw new IllegalArgumentException("Group " + dn + " not found", e); + } if (group.getMemberNames().contains(dn)) directGroups.add(group.getDn()); } @@ -264,7 +277,7 @@ public class LdifUserAdmin extends AbstractUserDirectory { } @Override - public void prepare(DirectoryUserWorkingCopy wc) { + public void prepare(LdapEntryWorkingCopy wc) { // delete for (LdapName dn : wc.getDeletedData().keySet()) { if (users.containsKey(dn)) @@ -276,7 +289,7 @@ public class LdifUserAdmin extends AbstractUserDirectory { } // add for (LdapName dn : wc.getNewData().keySet()) { - DirectoryUser user = wc.getNewData().get(dn); + DirectoryUser user = (DirectoryUser) wc.getNewData().get(dn); if (users.containsKey(dn) || groups.containsKey(dn)) throw new IllegalStateException("User to create found " + dn); else if (Role.USER == user.getType()) @@ -290,23 +303,24 @@ public class LdifUserAdmin extends AbstractUserDirectory { for (LdapName dn : wc.getModifiedData().keySet()) { Attributes modifiedAttrs = wc.getModifiedData().get(dn); DirectoryUser user; - if (users.containsKey(dn)) - user = users.get(dn); - else if (groups.containsKey(dn)) - user = groups.get(dn); - else + try { + user = daoGetEntry(dn); + } catch (NameNotFoundException e) { + throw new IllegalStateException("User to modify no found " + dn, e); + } + if (user == null) throw new IllegalStateException("User to modify no found " + dn); user.publishAttributes(modifiedAttrs); } } @Override - public void commit(DirectoryUserWorkingCopy wc) { + public void commit(LdapEntryWorkingCopy wc) { save(); } @Override - public void rollback(DirectoryUserWorkingCopy wc) { + public void rollback(LdapEntryWorkingCopy wc) { init(); } @@ -324,12 +338,12 @@ public class LdifUserAdmin extends AbstractUserDirectory { // return rootHierarchyUnits.get(i); // } @Override - protected HierarchyUnit doGetHierarchyUnit(LdapName dn) { + public HierarchyUnit doGetHierarchyUnit(LdapName dn) { return hierarchy.get(dn); } @Override - protected Iterable doGetDirectHierarchyUnits(LdapName searchBase, boolean functionalOnly) { + public Iterable doGetDirectHierarchyUnits(LdapName searchBase, boolean functionalOnly) { List res = new ArrayList<>(); for (LdapName n : hierarchy.keySet()) { if (n.size() == searchBase.size() + 1) { 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 69c06c848..1f428ecbd 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/OsUserDirectory.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/OsUserDirectory.java @@ -11,6 +11,9 @@ import javax.naming.directory.Attributes; import javax.naming.directory.BasicAttributes; import javax.naming.ldap.LdapName; +import org.argeo.util.directory.HierarchyUnit; +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; @@ -40,12 +43,12 @@ public class OsUserDirectory extends AbstractUserDirectory { } @Override - protected Boolean daoHasRole(LdapName dn) { + protected Boolean daoHasEntry(LdapName dn) { return osUserDn.equals(dn); } @Override - protected DirectoryUser daoGetRole(LdapName key) throws NameNotFoundException { + protected DirectoryUser daoGetEntry(LdapName key) throws NameNotFoundException { if (osUserDn.equals(key)) return osUser; else @@ -53,8 +56,8 @@ public class OsUserDirectory extends AbstractUserDirectory { } @Override - protected List doGetRoles(LdapName searchBase, Filter f, boolean deep) { - List res = new ArrayList<>(); + protected List doGetEntries(LdapName searchBase, Filter f, boolean deep) { + List res = new ArrayList<>(); if (f == null || f.match(osUser.getProperties())) res.add(osUser); return res; @@ -66,26 +69,25 @@ public class OsUserDirectory extends AbstractUserDirectory { } @Override - protected HierarchyUnit doGetHierarchyUnit(LdapName dn) { + public HierarchyUnit doGetHierarchyUnit(LdapName dn) { return null; } @Override - protected Iterable doGetDirectHierarchyUnits(LdapName searchBase, boolean functionalOnly) { + public Iterable doGetDirectHierarchyUnits(LdapName searchBase, boolean functionalOnly) { return new ArrayList<>(); } - public void prepare(DirectoryUserWorkingCopy wc) { + public void prepare(LdapEntryWorkingCopy wc) { } - public void commit(DirectoryUserWorkingCopy wc) { + public void commit(LdapEntryWorkingCopy wc) { } - public void rollback(DirectoryUserWorkingCopy wc) { + public void rollback(LdapEntryWorkingCopy wc) { } - } 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 2c070d66d..05ed7cf7c 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/UserDirectory.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/UserDirectory.java @@ -1,52 +1,19 @@ package org.argeo.osgi.useradmin; -import java.util.Optional; - -import org.argeo.util.transaction.WorkControl; +import org.argeo.util.directory.Directory; +import org.argeo.util.directory.HierarchyUnit; import org.osgi.service.useradmin.Role; /** Information about a user directory. */ -public interface UserDirectory { - /** - * The base of the hierarchy defined by this directory. This could typically be - * an LDAP base DN. - */ - String getContext(); - - String getName(); - -// /** The base DN of all entries in this user directory */ -// LdapName getBaseDn(); - -// /** The related {@link XAResource} */ -// XAResource getXaResource(); - - boolean isReadOnly(); - - boolean isDisabled(); - - String getUserObjectClass(); - -// String getUserBase(); - - String getGroupObjectClass(); - -// String getGroupBase(); - - Optional getRealm(); - - Iterable getDirectHierarchyUnits(boolean functionalOnly); - - HierarchyUnit getHierarchyUnit(String path); +public interface UserDirectory extends Directory { HierarchyUnit getHierarchyUnit(Role role); + Iterable getHierarchyUnitRoles(HierarchyUnit hierarchyUnit, String filter, boolean deep); + 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/osgi/useradmin/WcXaResource.java b/org.argeo.util/src/org/argeo/osgi/useradmin/WcXaResource.java deleted file mode 100644 index 32bb401bc..000000000 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/WcXaResource.java +++ /dev/null @@ -1,135 +0,0 @@ -package org.argeo.osgi.useradmin; - -import java.util.HashMap; -import java.util.Map; - -import javax.transaction.xa.XAException; -import javax.transaction.xa.XAResource; -import javax.transaction.xa.Xid; - -/** {@link XAResource} for a user directory being edited. */ -class WcXaResource implements XAResource { - private final AbstractUserDirectory userDirectory; - - private Map workingCopies = new HashMap(); - private Xid editingXid = null; - private int transactionTimeout = 0; - - public WcXaResource(AbstractUserDirectory userDirectory) { - this.userDirectory = userDirectory; - } - - @Override - public synchronized void start(Xid xid, int flags) throws XAException { - if (editingXid != null) - throw new IllegalStateException("Already editing " + editingXid); - DirectoryUserWorkingCopy wc = workingCopies.put(xid, new DirectoryUserWorkingCopy()); - if (wc != null) - throw new IllegalStateException("There is already a working copy for " + xid); - this.editingXid = xid; - } - - @Override - public void end(Xid xid, int flags) throws XAException { - checkXid(xid); - } - - private DirectoryUserWorkingCopy wc(Xid xid) { - return workingCopies.get(xid); - } - - synchronized DirectoryUserWorkingCopy wc() { - if (editingXid == null) - return null; - DirectoryUserWorkingCopy wc = workingCopies.get(editingXid); - if (wc == null) - throw new IllegalStateException("No working copy found for " + editingXid); - return wc; - } - - private synchronized void cleanUp(Xid xid) { - wc(xid).cleanUp(); - workingCopies.remove(xid); - editingXid = null; - } - - @Override - public int prepare(Xid xid) throws XAException { - checkXid(xid); - DirectoryUserWorkingCopy wc = wc(xid); - if (wc.noModifications()) - return XA_RDONLY; - try { - userDirectory.prepare(wc); - } catch (Exception e) { - e.printStackTrace(); - throw new XAException(XAException.XAER_RMERR); - } - return XA_OK; - } - - @Override - public void commit(Xid xid, boolean onePhase) throws XAException { - try { - checkXid(xid); - DirectoryUserWorkingCopy wc = wc(xid); - if (wc.noModifications()) - return; - if (onePhase) - userDirectory.prepare(wc); - userDirectory.commit(wc); - } catch (Exception e) { - e.printStackTrace(); - throw new XAException(XAException.XAER_RMERR); - } finally { - cleanUp(xid); - } - } - - @Override - public void rollback(Xid xid) throws XAException { - try { - checkXid(xid); - userDirectory.rollback(wc(xid)); - } catch (Exception e) { - e.printStackTrace(); - throw new XAException(XAException.XAER_RMERR); - } finally { - cleanUp(xid); - } - } - - @Override - public void forget(Xid xid) throws XAException { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isSameRM(XAResource xares) throws XAException { - return xares == this; - } - - @Override - public Xid[] recover(int flag) throws XAException { - return new Xid[0]; - } - - @Override - public int getTransactionTimeout() throws XAException { - return transactionTimeout; - } - - @Override - public boolean setTransactionTimeout(int seconds) throws XAException { - transactionTimeout = seconds; - return true; - } - - private void checkXid(Xid xid) throws XAException { - if (xid == null) - throw new XAException(XAException.XAER_OUTSIDE); - if (!xid.equals(xid)) - throw new XAException(XAException.XAER_NOTA); - } - -} diff --git a/org.argeo.util/src/org/argeo/util/directory/Directory.java b/org.argeo.util/src/org/argeo/util/directory/Directory.java new file mode 100644 index 000000000..b3dfa8b05 --- /dev/null +++ b/org.argeo.util/src/org/argeo/util/directory/Directory.java @@ -0,0 +1,36 @@ +package org.argeo.util.directory; + +import java.util.Optional; + +import org.argeo.util.transaction.WorkControl; + +public interface Directory { + /** + * The base of the hierarchy defined by this directory. This could typically be + * an LDAP base DN. + */ + String getContext(); + + String getName(); + + boolean isReadOnly(); + + boolean isDisabled(); + + String getUserObjectClass(); + + String getGroupObjectClass(); + + Optional getRealm(); + + void setTransactionControl(WorkControl transactionControl); + + /* + * HIERARCHY + */ + + Iterable getDirectHierarchyUnits(boolean functionalOnly); + + HierarchyUnit getHierarchyUnit(String path); + +} diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/UserAdminConf.java b/org.argeo.util/src/org/argeo/util/directory/DirectoryConf.java similarity index 90% rename from org.argeo.util/src/org/argeo/osgi/useradmin/UserAdminConf.java rename to org.argeo.util/src/org/argeo/util/directory/DirectoryConf.java index 7fd9e1895..c0f96ee75 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/UserAdminConf.java +++ b/org.argeo.util/src/org/argeo/util/directory/DirectoryConf.java @@ -1,4 +1,4 @@ -package org.argeo.osgi.useradmin; +package org.argeo.util.directory; import java.net.InetAddress; import java.net.URI; @@ -9,14 +9,11 @@ import java.util.Hashtable; import java.util.List; import java.util.Map; -import javax.naming.Context; -import javax.naming.InvalidNameException; -import javax.naming.ldap.LdapName; - +import org.argeo.util.directory.ldap.IpaUtils; import org.argeo.util.naming.NamingUtils; /** Properties used to configure user admins. */ -public enum UserAdminConf { +public enum DirectoryConf { /** Base DN (cannot be configured externally) */ baseDn("dc=example,dc=com"), @@ -58,10 +55,13 @@ public enum UserAdminConf { public final static String SCHEME_OS = "os"; public final static String SCHEME_IPA = "ipa"; + private final static String SECURITY_PRINCIPAL = "java.naming.security.principal"; + private final static String SECURITY_CREDENTIALS = "java.naming.security.credentials"; + /** The default value. */ private Object def; - UserAdminConf(Object def) { + DirectoryConf(Object def) { this.def = def; } @@ -96,8 +96,8 @@ public enum UserAdminConf { /** @deprecated use {@link #valueOf(String)} instead */ @Deprecated - public static UserAdminConf local(String property) { - return UserAdminConf.valueOf(property); + public static DirectoryConf local(String property) { + return DirectoryConf.valueOf(property); } /** Hides host and credentials. */ @@ -121,7 +121,7 @@ public enum UserAdminConf { // } // } - keys: for (UserAdminConf key : UserAdminConf.values()) { + keys: for (DirectoryConf key : DirectoryConf.values()) { if (key.equals(baseDn) || key.equals(uri)) continue keys; Object value = properties.get(key.name()); @@ -166,7 +166,7 @@ public enum UserAdminConf { bDn = bDn.substring(0, bDn.length() - ".ldif".length()); // Normalize base DN as LDAP name - bDn = new LdapName(bDn).toString(); +// bDn = new LdapName(bDn).toString(); String principal = null; String credentials = null; @@ -185,7 +185,7 @@ public enum UserAdminConf { throw new IllegalArgumentException("Unsupported scheme " + scheme); Map> query = NamingUtils.queryToMap(u); for (String key : query.keySet()) { - UserAdminConf ldapProp = UserAdminConf.valueOf(key); + DirectoryConf ldapProp = DirectoryConf.valueOf(key); List values = query.get(key); if (values.size() == 1) { res.put(ldapProp.name(), values.get(0)); @@ -197,9 +197,9 @@ public enum UserAdminConf { if (SCHEME_OS.equals(scheme)) res.put(readOnly.name(), "true"); if (principal != null) - res.put(Context.SECURITY_PRINCIPAL, principal); + res.put(SECURITY_PRINCIPAL, principal); if (credentials != null) - res.put(Context.SECURITY_CREDENTIALS, credentials); + res.put(SECURITY_CREDENTIALS, credentials); if (scheme != null) {// relative URIs are dealt with externally if (SCHEME_OS.equals(scheme)) { res.put(uri.name(), SCHEME_OS + ":///"); @@ -210,7 +210,7 @@ public enum UserAdminConf { } } return res; - } catch (URISyntaxException | InvalidNameException e) { + } catch (URISyntaxException e) { throw new IllegalArgumentException("Cannot convert " + uri + " to properties", e); } } @@ -241,6 +241,6 @@ public enum UserAdminConf { String bDn = (String) properties.get(baseDn.name()); if (bDn == null) throw new IllegalStateException("No baseDn in " + properties); - return DigestUtils.sha1str(bDn); + return DirectoryDigestUtils.sha1str(bDn); } } diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/DigestUtils.java b/org.argeo.util/src/org/argeo/util/directory/DirectoryDigestUtils.java similarity index 87% rename from org.argeo.util/src/org/argeo/osgi/useradmin/DigestUtils.java rename to org.argeo.util/src/org/argeo/util/directory/DirectoryDigestUtils.java index 55d24d994..d07d2d2ed 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/DigestUtils.java +++ b/org.argeo.util/src/org/argeo/util/directory/DirectoryDigestUtils.java @@ -1,4 +1,4 @@ -package org.argeo.osgi.useradmin; +package org.argeo.util.directory; import java.math.BigInteger; import java.nio.ByteBuffer; @@ -14,11 +14,11 @@ import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; /** Utilities around digests, mostly those related to passwords. */ -class DigestUtils { - final static String PASSWORD_SCHEME_SHA = "SHA"; - final static String PASSWORD_SCHEME_PBKDF2_SHA256 = "PBKDF2_SHA256"; +public class DirectoryDigestUtils { + public final static String PASSWORD_SCHEME_SHA = "SHA"; + public final static String PASSWORD_SCHEME_PBKDF2_SHA256 = "PBKDF2_SHA256"; - static byte[] sha1(byte[] bytes) { + public static byte[] sha1(byte[] bytes) { try { MessageDigest digest = MessageDigest.getInstance("SHA1"); digest.update(bytes); @@ -29,7 +29,7 @@ class DigestUtils { } } - static byte[] toPasswordScheme(String passwordScheme, char[] password, byte[] salt, Integer iterations, + public static byte[] toPasswordScheme(String passwordScheme, char[] password, byte[] salt, Integer iterations, Integer keyLength) { try { if (PASSWORD_SCHEME_SHA.equals(passwordScheme)) { @@ -63,7 +63,7 @@ class DigestUtils { } } - static char[] bytesToChars(Object obj) { + public static char[] bytesToChars(Object obj) { if (obj instanceof char[]) return (char[]) obj; if (!(obj instanceof byte[])) @@ -77,7 +77,7 @@ class DigestUtils { return res; } - static byte[] charsToBytes(char[] chars) { + public static byte[] charsToBytes(char[] chars) { CharBuffer charBuffer = CharBuffer.wrap(chars); ByteBuffer byteBuffer = StandardCharsets.UTF_8.encode(charBuffer); byte[] bytes = Arrays.copyOfRange(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit()); @@ -86,7 +86,7 @@ class DigestUtils { return bytes; } - static String sha1str(String str) { + public static String sha1str(String str) { byte[] hash = sha1(str.getBytes(StandardCharsets.UTF_8)); return encodeHexString(hash); } @@ -108,6 +108,7 @@ class DigestUtils { return new String(hexChars); } - private DigestUtils() { + /** singleton */ + private DirectoryDigestUtils() { } } diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/HierarchyUnit.java b/org.argeo.util/src/org/argeo/util/directory/HierarchyUnit.java similarity index 62% rename from org.argeo.util/src/org/argeo/osgi/useradmin/HierarchyUnit.java rename to org.argeo.util/src/org/argeo/util/directory/HierarchyUnit.java index 2c21342e3..0194ffc89 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/HierarchyUnit.java +++ b/org.argeo.util/src/org/argeo/util/directory/HierarchyUnit.java @@ -1,8 +1,4 @@ -package org.argeo.osgi.useradmin; - -import java.util.List; - -import org.osgi.service.useradmin.Role; +package org.argeo.util.directory; /** A unit within the high-level organisational structure of a directory. */ public interface HierarchyUnit { @@ -16,9 +12,7 @@ public interface HierarchyUnit { String getContext(); - List getHierarchyUnitRoles(String filter, boolean deep); - - UserDirectory getDirectory(); + Directory getDirectory(); // Map getHierarchyProperties(); } 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 new file mode 100644 index 000000000..27f9c55e3 --- /dev/null +++ b/org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapDirectory.java @@ -0,0 +1,376 @@ +package org.argeo.util.directory.ldap; + +import static org.argeo.util.directory.ldap.LdapNameUtils.toLdapName; + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.List; +import java.util.Optional; +import java.util.StringJoiner; + +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.ldap.LdapName; +import javax.naming.ldap.Rdn; +import javax.transaction.xa.XAResource; + +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 { + protected static final String SHARED_STATE_USERNAME = "javax.security.auth.login.name"; + protected static final String SHARED_STATE_PASSWORD = "javax.security.auth.login.password"; + + protected final LdapName baseDn; + protected final Hashtable properties; + private final Rdn userBaseRdn, groupBaseRdn, systemRoleBaseRdn; + private final String userObjectClass, groupObjectClass; + + private final boolean readOnly; + private final boolean disabled; + private final String uri; + + private String forcedPassword; + + 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); + + public AbstractLdapDirectory(URI uriArg, Dictionary props, boolean scoped) { + this.properties = new Hashtable(); + for (Enumeration keys = props.keys(); keys.hasMoreElements();) { + String key = keys.nextElement(); + properties.put(key, props.get(key)); + } + baseDn = toLdapName(DirectoryConf.baseDn.getValue(properties)); + this.scoped = scoped; + + if (uriArg != null) { + uri = uriArg.toString(); + // uri from properties is ignored + } else { + String uriStr = DirectoryConf.uri.getValue(properties); + if (uriStr == null) + uri = null; + else + uri = uriStr; + } + + forcedPassword = DirectoryConf.forcedPassword.getValue(properties); + + userObjectClass = DirectoryConf.userObjectClass.getValue(properties); + String userBase = DirectoryConf.userBase.getValue(properties); + groupObjectClass = DirectoryConf.groupObjectClass.getValue(properties); + String groupBase = DirectoryConf.groupBase.getValue(properties); + String systemRoleBase = DirectoryConf.systemRoleBase.getValue(properties); + try { +// baseDn = new LdapName(UserAdminConf.baseDn.getValue(properties)); + userBaseRdn = new Rdn(userBase); +// userBaseDn = new LdapName(userBase + "," + baseDn); + groupBaseRdn = new Rdn(groupBase); +// groupBaseDn = new LdapName(groupBase + "," + baseDn); + systemRoleBaseRdn = new Rdn(systemRoleBase); + } catch (InvalidNameException e) { + throw new IllegalArgumentException("Badly formated base DN " + DirectoryConf.baseDn.getValue(properties), + e); + } + + // read only + String readOnlyStr = DirectoryConf.readOnly.getValue(properties); + if (readOnlyStr == null) { + readOnly = readOnlyDefault(uri); + properties.put(DirectoryConf.readOnly.name(), Boolean.toString(readOnly)); + } else + readOnly = Boolean.parseBoolean(readOnlyStr); + + // disabled + String disabledStr = DirectoryConf.disabled.getValue(properties); + if (disabledStr != null) + disabled = Boolean.parseBoolean(disabledStr); + else + disabled = false; + } + + /* + * ABSTRACT METHODS + */ + + 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); + + /* + * EDITION + */ + + public boolean isEditing() { + return xaResource.wc() != null; + } + + public LdapEntryWorkingCopy getWorkingCopy() { + LdapEntryWorkingCopy wc = xaResource.wc(); + if (wc == null) + return null; + return wc; + } + + public void checkEdit() { + if (xaResource.wc() == null) { + try { + transactionControl.getWorkContext().registerXAResource(xaResource, null); + } catch (Exception e) { + throw new IllegalStateException("Cannot enlist " + xaResource, e); + } + } else { + } + } + + public void setTransactionControl(WorkControl transactionControl) { + this.transactionControl = transactionControl; + } + + public XAResource getXaResource() { + return xaResource; + } + + @Override + public LdapEntryWorkingCopy newWorkingCopy() { + return new LdapEntryWorkingCopy(); + } + + /* + * HIERARCHY + */ + @Override + public HierarchyUnit getHierarchyUnit(String path) { + LdapName dn = pathToName(path); + return doGetHierarchyUnit(dn); + } + + @Override + public Iterable getDirectHierarchyUnits(boolean functionalOnly) { + return doGetDirectHierarchyUnits(baseDn, functionalOnly); + } + + /* + * PATHS + */ + + @Override + public String getContext() { + return getBaseDn().toString(); + } + + @Override + public String getName() { + return nameToSimple(getBaseDn(), "."); + } + + 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("/"); + Rdn parentRdn = null; + for (String segment : segments) { + // TODO make attr names configurable ? + String attr = LdapAttrs.ou.name(); + if (parentRdn != null) { + if (getUserBaseRdn().equals(parentRdn)) + attr = LdapAttrs.uid.name(); + else if (getGroupBaseRdn().equals(parentRdn)) + attr = LdapAttrs.cn.name(); + else if (getSystemRoleBaseRdn().equals(parentRdn)) + attr = LdapAttrs.cn.name(); + } + Rdn rdn = new Rdn(attr, segment); + name.add(rdn); + parentRdn = rdn; + } + return name; + } catch (InvalidNameException e) { + throw new IllegalStateException("Cannot get role " + path, e); + } + + } + + /* + * UTILITIES + */ + + protected static boolean hasObjectClass(Attributes attrs, LdapObjs 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())) + return true; + + } + return false; + } catch (NamingException e) { + throw new IllegalStateException("Cannot search for objectClass " + objectClass.name(), e); + } + } + + private static boolean readOnlyDefault(String uriStr) { + if (uriStr == null) + return true; + /// TODO make it more generic + URI uri; + try { + uri = new URI(uriStr.split(" ")[0]); + } catch (URISyntaxException e) { + throw new IllegalArgumentException(e); + } + if (uri.getScheme() == null) + return false;// assume relative file to be writable + if (uri.getScheme().equals(DirectoryConf.SCHEME_FILE)) { + File file = new File(uri); + if (file.exists()) + return !file.canWrite(); + else + return !file.getParentFile().canWrite(); + } else if (uri.getScheme().equals(DirectoryConf.SCHEME_LDAP)) { + if (uri.getAuthority() != null)// assume writable if authenticated + return false; + } else if (uri.getScheme().equals(DirectoryConf.SCHEME_OS)) { + return true; + } + return true;// read only by default + } + + /* + * ACCESSORS + */ + @Override + public Optional getRealm() { + Object realm = getProperties().get(DirectoryConf.realm.name()); + if (realm == null) + return Optional.empty(); + return Optional.of(realm.toString()); + } + + protected LdapName getBaseDn() { + return (LdapName) baseDn.clone(); + } + + public boolean isReadOnly() { + return readOnly; + } + + public boolean isDisabled() { + 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; + } + + public Rdn getSystemRoleBaseRdn() { + return systemRoleBaseRdn; + } + + public Dictionary getProperties() { + return properties; + } + + public Dictionary cloneProperties() { + return new Hashtable<>(properties); + } + + public String getForcedPassword() { + return forcedPassword; + } + + public boolean isScoped() { + return scoped; + } + + public String getMemberAttributeId() { + return memberAttributeId; + } + + public List getCredentialAttributeIds() { + return credentialAttributeIds; + } + + protected String getUri() { + return uri; + } + + /* + * OBJECT METHODS + */ + + @Override + public int hashCode() { + return baseDn.hashCode(); + } + + @Override + public String toString() { + return "Directory " + baseDn.toString(); + } + +} 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 new file mode 100644 index 000000000..be919c020 --- /dev/null +++ b/org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapEntry.java @@ -0,0 +1,81 @@ +package org.argeo.util.directory.ldap; + +import javax.naming.directory.Attributes; +import javax.naming.ldap.LdapName; + +public abstract class AbstractLdapEntry implements LdapEntry { + private final AbstractLdapDirectory directory; + + private final LdapName dn; + + private Attributes publishedAttributes; + + protected AbstractLdapEntry(AbstractLdapDirectory userAdmin, LdapName dn, Attributes attributes) { + this.directory = userAdmin; + this.dn = dn; + this.publishedAttributes = attributes; + } + + @Override + public LdapName getDn() { + return dn; + } + + public synchronized Attributes getAttributes() { + return isEditing() ? getModifiedAttributes() : publishedAttributes; + } + + /** Should only be called from working copy thread. */ + protected synchronized Attributes getModifiedAttributes() { + assert getWc() != null; + return getWc().getModifiedData().get(getDn()); + } + + protected synchronized boolean isEditing() { + return getWc() != null && getModifiedAttributes() != null; + } + + private synchronized LdapEntryWorkingCopy getWc() { + return directory.getWorkingCopy(); + } + + protected synchronized void startEditing() { +// if (frozen) +// throw new IllegalStateException("Cannot edit frozen view"); + if (directory.isReadOnly()) + throw new IllegalStateException("User directory is read-only"); + assert getModifiedAttributes() == null; + getWc().startEditing(this); + // modifiedAttributes = (Attributes) publishedAttributes.clone(); + } + + public synchronized void publishAttributes(Attributes modifiedAttributes) { + publishedAttributes = modifiedAttributes; + } + + protected AbstractLdapDirectory getDirectory() { + return directory; + } + + @Override + public int hashCode() { + return dn.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj instanceof LdapEntry) { + LdapEntry that = (LdapEntry) obj; + return this.dn.equals(that.getDn()); + } + return false; + } + + @Override + public String toString() { + return dn.toString(); + } + +} diff --git a/org.argeo.util/src/org/argeo/util/naming/ldap/AttributesDictionary.java b/org.argeo.util/src/org/argeo/util/directory/ldap/AttributesDictionary.java similarity index 99% rename from org.argeo.util/src/org/argeo/util/naming/ldap/AttributesDictionary.java rename to org.argeo.util/src/org/argeo/util/directory/ldap/AttributesDictionary.java index 0bbeb03fe..7b0095fbe 100644 --- a/org.argeo.util/src/org/argeo/util/naming/ldap/AttributesDictionary.java +++ b/org.argeo.util/src/org/argeo/util/directory/ldap/AttributesDictionary.java @@ -1,4 +1,4 @@ -package org.argeo.util.naming.ldap; +package org.argeo.util.directory.ldap; import java.util.Dictionary; import java.util.Enumeration; diff --git a/org.argeo.util/src/org/argeo/util/naming/ldap/AuthPassword.java b/org.argeo.util/src/org/argeo/util/directory/ldap/AuthPassword.java similarity index 99% rename from org.argeo.util/src/org/argeo/util/naming/ldap/AuthPassword.java rename to org.argeo.util/src/org/argeo/util/directory/ldap/AuthPassword.java index b11684020..e10f45756 100644 --- a/org.argeo.util/src/org/argeo/util/naming/ldap/AuthPassword.java +++ b/org.argeo.util/src/org/argeo/util/directory/ldap/AuthPassword.java @@ -1,4 +1,4 @@ -package org.argeo.util.naming.ldap; +package org.argeo.util.directory.ldap; import java.io.IOException; import java.util.Arrays; diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/IpaUtils.java b/org.argeo.util/src/org/argeo/util/directory/ldap/IpaUtils.java similarity index 82% rename from org.argeo.util/src/org/argeo/osgi/useradmin/IpaUtils.java rename to org.argeo.util/src/org/argeo/util/directory/ldap/IpaUtils.java index e1c8136f5..861eb4f1f 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/IpaUtils.java +++ b/org.argeo.util/src/org/argeo/util/directory/ldap/IpaUtils.java @@ -1,4 +1,4 @@ -package org.argeo.osgi.useradmin; +package org.argeo.util.directory.ldap; import java.io.IOException; import java.net.InetAddress; @@ -13,6 +13,7 @@ import javax.naming.InvalidNameException; import javax.naming.NamingException; import javax.naming.ldap.LdapName; +import org.argeo.util.directory.DirectoryConf; import org.argeo.util.naming.LdapAttrs; import org.argeo.util.naming.dns.DnsBrowser; @@ -25,21 +26,21 @@ public class IpaUtils { private final static String KRB_PRINCIPAL_NAME = LdapAttrs.krbPrincipalName.name().toLowerCase(); - public final static String IPA_USER_DIRECTORY_CONFIG = UserAdminConf.userBase + "=" + IPA_USER_BASE + "&" - + UserAdminConf.groupBase + "=" + IPA_GROUP_BASE + "&" + UserAdminConf.readOnly + "=true"; + public final static String IPA_USER_DIRECTORY_CONFIG = DirectoryConf.userBase + "=" + IPA_USER_BASE + "&" + + DirectoryConf.groupBase + "=" + IPA_GROUP_BASE + "&" + DirectoryConf.readOnly + "=true"; @Deprecated static String domainToUserDirectoryConfigPath(String realm) { - return domainToBaseDn(realm) + "?" + IPA_USER_DIRECTORY_CONFIG + "&" + UserAdminConf.realm.name() + "=" + realm; + return domainToBaseDn(realm) + "?" + IPA_USER_DIRECTORY_CONFIG + "&" + DirectoryConf.realm.name() + "=" + realm; } public static void addIpaConfig(String realm, Dictionary properties) { - properties.put(UserAdminConf.baseDn.name(), domainToBaseDn(realm)); - properties.put(UserAdminConf.realm.name(), realm); - properties.put(UserAdminConf.userBase.name(), IPA_USER_BASE); - properties.put(UserAdminConf.groupBase.name(), IPA_GROUP_BASE); - properties.put(UserAdminConf.systemRoleBase.name(), IPA_ROLE_BASE); - properties.put(UserAdminConf.readOnly.name(), Boolean.TRUE.toString()); + properties.put(DirectoryConf.baseDn.name(), domainToBaseDn(realm)); + properties.put(DirectoryConf.realm.name(), realm); + properties.put(DirectoryConf.userBase.name(), IPA_USER_BASE); + properties.put(DirectoryConf.groupBase.name(), IPA_GROUP_BASE); + properties.put(DirectoryConf.systemRoleBase.name(), IPA_ROLE_BASE); + properties.put(DirectoryConf.readOnly.name(), Boolean.TRUE.toString()); } public static String domainToBaseDn(String domain) { @@ -101,13 +102,13 @@ public class IpaUtils { throw new IllegalStateException("No Kerberos domain available for " + uri); // TODO intergrate CA certificate in truststore // String schemeToUse = SCHEME_LDAPS; - String schemeToUse = UserAdminConf.SCHEME_LDAP; + String schemeToUse = DirectoryConf.SCHEME_LDAP; List ldapHosts; String ldapHostsStr = uri.getHost(); if (ldapHostsStr == null || ldapHostsStr.trim().equals("")) { try (DnsBrowser dnsBrowser = new DnsBrowser()) { ldapHosts = dnsBrowser.getSrvRecordsAsHosts("_ldap._tcp." + kerberosRealm.toLowerCase(), - schemeToUse.equals(UserAdminConf.SCHEME_LDAP) ? true : false); + schemeToUse.equals(DirectoryConf.SCHEME_LDAP) ? true : false); if (ldapHosts == null || ldapHosts.size() == 0) { throw new IllegalStateException("Cannot configure LDAP for IPA " + uri); } else { @@ -132,7 +133,7 @@ public class IpaUtils { } Hashtable res = new Hashtable<>(); - res.put(UserAdminConf.uri.name(), uriStr.toString()); + res.put(DirectoryConf.uri.name(), uriStr.toString()); addIpaConfig(kerberosRealm, res); return res; } diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/LdapConnection.java b/org.argeo.util/src/org/argeo/util/directory/ldap/LdapConnection.java similarity index 91% rename from org.argeo.util/src/org/argeo/osgi/useradmin/LdapConnection.java rename to org.argeo.util/src/org/argeo/util/directory/ldap/LdapConnection.java index 1fe7eb9df..f7838381d 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/LdapConnection.java +++ b/org.argeo.util/src/org/argeo/util/directory/ldap/LdapConnection.java @@ -1,4 +1,4 @@ -package org.argeo.osgi.useradmin; +package org.argeo.util.directory.ldap; import java.util.Dictionary; import java.util.Hashtable; @@ -16,13 +16,14 @@ import javax.naming.ldap.InitialLdapContext; import javax.naming.ldap.LdapName; import org.argeo.util.naming.LdapAttrs; +import org.argeo.util.transaction.WorkingCopy; /** A synchronized wrapper for a single {@link InitialLdapContext}. */ // TODO implement multiple contexts and connection pooling. -class LdapConnection { +public class LdapConnection { private InitialLdapContext initialLdapContext = null; - LdapConnection(String url, Dictionary properties) { + public LdapConnection(String url, Dictionary properties) { try { Hashtable connEnv = new Hashtable(); connEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); @@ -98,7 +99,7 @@ class LdapConnection { } } - synchronized void prepareChanges(DirectoryUserWorkingCopy wc) throws NamingException { + public synchronized void prepareChanges(WorkingCopy wc) throws NamingException { // make sure connection will work reconnect(); @@ -128,14 +129,14 @@ class LdapConnection { } } - synchronized void commitChanges(DirectoryUserWorkingCopy wc) throws NamingException { + public synchronized void commitChanges(LdapEntryWorkingCopy wc) throws NamingException { // delete for (LdapName dn : wc.getDeletedData().keySet()) { getLdapContext().destroySubcontext(dn); } // add for (LdapName dn : wc.getNewData().keySet()) { - DirectoryUser user = wc.getNewData().get(dn); + LdapEntry user = wc.getNewData().get(dn); getLdapContext().createSubcontext(dn, user.getAttributes()); } // modify 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 new file mode 100644 index 000000000..c145a6f0a --- /dev/null +++ b/org.argeo.util/src/org/argeo/util/directory/ldap/LdapEntry.java @@ -0,0 +1,13 @@ +package org.argeo.util.directory.ldap; + +import javax.naming.directory.Attributes; +import javax.naming.ldap.LdapName; + +public interface LdapEntry { + LdapName getDn(); + + Attributes getAttributes(); + + void publishAttributes(Attributes modifiedAttributes); + +} diff --git a/org.argeo.util/src/org/argeo/util/directory/ldap/LdapEntryWorkingCopy.java b/org.argeo.util/src/org/argeo/util/directory/ldap/LdapEntryWorkingCopy.java new file mode 100644 index 000000000..381c11b2f --- /dev/null +++ b/org.argeo.util/src/org/argeo/util/directory/ldap/LdapEntryWorkingCopy.java @@ -0,0 +1,19 @@ +package org.argeo.util.directory.ldap; + +import javax.naming.directory.Attributes; +import javax.naming.ldap.LdapName; + +import org.argeo.util.transaction.AbstractWorkingCopy; + +/** Working copy for a user directory being edited. */ +public class LdapEntryWorkingCopy extends AbstractWorkingCopy { + @Override + protected LdapName getId(LdapEntry entry) { + return entry.getDn(); + } + + @Override + protected Attributes cloneAttributes(LdapEntry entry) { + return (Attributes) entry.getAttributes().clone(); + } +} diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/LdifHierarchyUnit.java b/org.argeo.util/src/org/argeo/util/directory/ldap/LdapHierarchyUnit.java similarity index 71% rename from org.argeo.util/src/org/argeo/osgi/useradmin/LdifHierarchyUnit.java rename to org.argeo.util/src/org/argeo/util/directory/ldap/LdapHierarchyUnit.java index a847e49ae..d76c449b0 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/LdifHierarchyUnit.java +++ b/org.argeo.util/src/org/argeo/util/directory/ldap/LdapHierarchyUnit.java @@ -1,18 +1,17 @@ -package org.argeo.osgi.useradmin; +package org.argeo.util.directory.ldap; -import java.util.List; import java.util.Objects; import javax.naming.directory.Attributes; import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.service.useradmin.Role; +import org.argeo.util.directory.Directory; +import org.argeo.util.directory.HierarchyUnit; /** LDIF/LDAP based implementation of {@link HierarchyUnit}. */ -class LdifHierarchyUnit implements HierarchyUnit { - private final AbstractUserDirectory directory; +public class LdapHierarchyUnit implements HierarchyUnit { + private final AbstractLdapDirectory directory; private final LdapName dn; private final boolean functional; @@ -21,7 +20,7 @@ class LdifHierarchyUnit implements HierarchyUnit { // HierarchyUnit parent; // List children = new ArrayList<>(); - LdifHierarchyUnit(AbstractUserDirectory directory, LdapName dn, Attributes attributes) { + public LdapHierarchyUnit(AbstractLdapDirectory directory, LdapName dn, Attributes attributes) { Objects.requireNonNull(directory); Objects.requireNonNull(dn); @@ -75,16 +74,7 @@ class LdifHierarchyUnit implements HierarchyUnit { } @Override - public List getHierarchyUnitRoles(String filter, boolean deep) { - try { - return directory.getRoles(dn, filter, deep); - } catch (InvalidSyntaxException e) { - throw new IllegalArgumentException("Cannot filter " + filter + " " + dn, e); - } - } - - @Override - public UserDirectory getDirectory() { + public Directory getDirectory() { return directory; } @@ -95,9 +85,9 @@ class LdifHierarchyUnit implements HierarchyUnit { @Override public boolean equals(Object obj) { - if (!(obj instanceof LdifHierarchyUnit)) + if (!(obj instanceof LdapHierarchyUnit)) return false; - return ((LdifHierarchyUnit) obj).dn.equals(dn); + return ((LdapHierarchyUnit) obj).dn.equals(dn); } @Override diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/LdapNameUtils.java b/org.argeo.util/src/org/argeo/util/directory/ldap/LdapNameUtils.java similarity index 73% rename from org.argeo.util/src/org/argeo/osgi/useradmin/LdapNameUtils.java rename to org.argeo.util/src/org/argeo/util/directory/ldap/LdapNameUtils.java index 7e763456a..689ef2329 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/LdapNameUtils.java +++ b/org.argeo.util/src/org/argeo/util/directory/ldap/LdapNameUtils.java @@ -1,13 +1,13 @@ -package org.argeo.osgi.useradmin; +package org.argeo.util.directory.ldap; import javax.naming.InvalidNameException; import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; /** Utilities to simplify using {@link LdapName}. */ -class LdapNameUtils { +public class LdapNameUtils { - static LdapName relativeName(LdapName prefix, LdapName dn) { + public static LdapName relativeName(LdapName prefix, LdapName dn) { try { if (!dn.startsWith(prefix)) throw new IllegalArgumentException("Prefix " + prefix + " not consistent with " + dn); @@ -21,7 +21,7 @@ class LdapNameUtils { } } - static LdapName getParent(LdapName dn) { + public static LdapName getParent(LdapName dn) { try { LdapName parent = (LdapName) dn.clone(); parent.remove(parent.size() - 1); @@ -31,14 +31,14 @@ class LdapNameUtils { } } - static Rdn getParentRdn(LdapName dn) { + public static Rdn getParentRdn(LdapName dn) { if (dn.size() < 2) throw new IllegalArgumentException(dn + " has no parent"); Rdn parentRdn = dn.getRdn(dn.size() - 2); return parentRdn; } - static LdapName toLdapName(String distinguishedName) { + public static LdapName toLdapName(String distinguishedName) { try { return new LdapName(distinguishedName); } catch (InvalidNameException e) { @@ -46,15 +46,15 @@ class LdapNameUtils { } } - static Rdn getLastRdn(LdapName dn) { + public static Rdn getLastRdn(LdapName dn) { return dn.getRdn(dn.size() - 1); } - static String getLastRdnAsString(LdapName dn) { + public static String getLastRdnAsString(LdapName dn) { return getLastRdn(dn).toString(); } - static String getLastRdnValue(LdapName dn) { + public static String getLastRdnValue(LdapName dn) { return getLastRdn(dn).getValue().toString(); } diff --git a/org.argeo.util/src/org/argeo/util/naming/ldap/LdifParser.java b/org.argeo.util/src/org/argeo/util/directory/ldap/LdifParser.java similarity index 99% rename from org.argeo.util/src/org/argeo/util/naming/ldap/LdifParser.java rename to org.argeo.util/src/org/argeo/util/directory/ldap/LdifParser.java index 3c4ae0a85..0022943e1 100644 --- a/org.argeo.util/src/org/argeo/util/naming/ldap/LdifParser.java +++ b/org.argeo.util/src/org/argeo/util/directory/ldap/LdifParser.java @@ -1,4 +1,4 @@ -package org.argeo.util.naming.ldap; +package org.argeo.util.directory.ldap; import java.io.BufferedReader; import java.io.IOException; diff --git a/org.argeo.util/src/org/argeo/util/naming/ldap/LdifWriter.java b/org.argeo.util/src/org/argeo/util/directory/ldap/LdifWriter.java similarity index 99% rename from org.argeo.util/src/org/argeo/util/naming/ldap/LdifWriter.java rename to org.argeo.util/src/org/argeo/util/directory/ldap/LdifWriter.java index 3e25dcfcb..a10f16938 100644 --- a/org.argeo.util/src/org/argeo/util/naming/ldap/LdifWriter.java +++ b/org.argeo.util/src/org/argeo/util/directory/ldap/LdifWriter.java @@ -1,4 +1,4 @@ -package org.argeo.util.naming.ldap; +package org.argeo.util.directory.ldap; import static org.argeo.util.naming.LdapAttrs.DN; import static org.argeo.util.naming.LdapAttrs.member; diff --git a/org.argeo.util/src/org/argeo/util/naming/SharedSecret.java b/org.argeo.util/src/org/argeo/util/naming/SharedSecret.java index e38bc2f29..7661d4c51 100644 --- a/org.argeo.util/src/org/argeo/util/naming/SharedSecret.java +++ b/org.argeo.util/src/org/argeo/util/naming/SharedSecret.java @@ -4,7 +4,7 @@ import java.time.Instant; import java.time.ZoneOffset; import java.time.ZonedDateTime; -import org.argeo.util.naming.ldap.AuthPassword; +import org.argeo.util.directory.ldap.AuthPassword; public class SharedSecret extends AuthPassword { public final static String X_SHARED_SECRET = "X-SharedSecret"; diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/AbstractCmsEntryPoint.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/AbstractCmsEntryPoint.java index 36ed3da7a..a4d04568e 100644 --- a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/AbstractCmsEntryPoint.java +++ b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/AbstractCmsEntryPoint.java @@ -34,8 +34,8 @@ import org.argeo.cms.swt.CmsStyles; import org.argeo.cms.swt.CmsSwtUtils; import org.argeo.eclipse.ui.specific.UiContext; import org.argeo.jcr.JcrUtils; +import org.argeo.util.directory.ldap.AuthPassword; import org.argeo.util.naming.SharedSecret; -import org.argeo.util.naming.ldap.AuthPassword; import org.eclipse.rap.rwt.RWT; import org.eclipse.rap.rwt.application.AbstractEntryPoint; import org.eclipse.rap.rwt.client.WebClient; -- 2.30.2