X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.util%2Fsrc%2Forg%2Fargeo%2Fosgi%2Fuseradmin%2FAbstractUserDirectory.java;h=e13f56289ce8b44b2f9629d2e297aadccbf03c03;hb=eb4324be6ac9cdff15828a21ee7d3f6ca2f19fb9;hp=e028e384e1a8a7af1b5656124e0a66e6f50e9c00;hpb=820bdbaef7c69ad478b780ae4241e19563d771ca;p=lgpl%2Fargeo-commons.git 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 e028e384e..e13f56289 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java @@ -17,10 +17,13 @@ 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; import javax.naming.NamingEnumeration; +import javax.naming.NamingException; import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; import javax.naming.directory.BasicAttribute; @@ -30,6 +33,7 @@ import javax.naming.ldap.Rdn; import org.argeo.osgi.transaction.WorkControl; import org.argeo.util.naming.LdapAttrs; +import org.argeo.util.naming.LdapObjs; import org.osgi.framework.Filter; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.InvalidSyntaxException; @@ -39,13 +43,15 @@ import org.osgi.service.useradmin.User; import org.osgi.service.useradmin.UserAdmin; /** Base class for a {@link UserDirectory}. */ -public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { +abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { 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, userBaseDn, groupBaseDn; - private final String userObjectClass, userBase, groupObjectClass, groupBase; + 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; @@ -91,22 +97,31 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory forcedPassword = UserAdminConf.forcedPassword.getValue(properties); userObjectClass = UserAdminConf.userObjectClass.getValue(properties); - userBase = UserAdminConf.userBase.getValue(properties); + String userBase = UserAdminConf.userBase.getValue(properties); groupObjectClass = UserAdminConf.groupObjectClass.getValue(properties); - groupBase = UserAdminConf.groupBase.getValue(properties); + String groupBase = UserAdminConf.groupBase.getValue(properties); + String systemRoleBase = UserAdminConf.systemRoleBase.getValue(properties); try { baseDn = new LdapName(UserAdminConf.baseDn.getValue(properties)); - userBaseDn = new LdapName(userBase + "," + baseDn); - groupBaseDn = new LdapName(groupBase + "," + baseDn); + 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 UserDirectoryException("Badly formated base DN " + UserAdminConf.baseDn.getValue(properties), 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); @@ -121,7 +136,7 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory protected abstract DirectoryUser daoGetRole(LdapName key) throws NameNotFoundException; - protected abstract List doGetRoles(Filter f); + protected abstract List doGetRoles(LdapName searchBase, Filter f, boolean deep); protected abstract AbstractUserDirectory scope(User user); @@ -133,6 +148,90 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory } + /* + * 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()); + } + + @Override + public String getRoleSimpleName(Role role) { + LdapName dn = LdapNameUtils.toLdapName(role.getName()); + String name = LdapNameUtils.getLastRdnValue(dn); + return name; + } + + protected String nameToRelativePath(LdapName dn) { + LdapName name = LdapNameUtils.relativeName(getBaseDn(), dn); + return nameToSimple(name, "/"); + } + + protected String nameToSimple(LdapName name, String separator) { + StringJoiner path = new StringJoiner(separator); + for (int i = 0; i < name.size(); i++) { + path.add(name.getRdn(i).getValue().toString()); + } + return path.toString(); + + } + + protected LdapName pathToName(String path) { + try { + LdapName name = (LdapName) getBaseDn().clone(); + String[] segments = path.split("/"); + 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; } @@ -145,20 +244,11 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory } protected void checkEdit() { -// Transaction transaction; -// try { -// transaction = transactionManager.getTransaction(); -// } catch (SystemException e) { -// throw new UserDirectoryException("Cannot get transaction", e); -// } -// if (transaction == null) -// throw new UserDirectoryException("A transaction needs to be active in order to edit"); if (xaResource.wc() == null) { try { -// transaction.enlistResource(xaResource); transactionControl.getWorkContext().registerXAResource(xaResource, null); } catch (Exception e) { - throw new UserDirectoryException("Cannot enlist " + xaResource, e); + throw new IllegalStateException("Cannot enlist " + xaResource, e); } } else { } @@ -189,8 +279,8 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory if (group != null) allRoles.add(group); } - } catch (Exception e) { - throw new UserDirectoryException("Cannot get memberOf groups for " + user, e); + } catch (NamingException e) { + throw new IllegalStateException("Cannot get memberOf groups for " + user, e); } } else { for (LdapName groupDn : getDirectGroups(user.getDn())) { @@ -211,7 +301,7 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory // USER ADMIN @Override public Role getRole(String name) { - return doGetRole(toDn(name)); + return doGetRole(toLdapName(name)); } protected DirectoryUser doGetRole(LdapName dn) { @@ -233,9 +323,14 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory @Override public Role[] getRoles(String filter) throws InvalidSyntaxException { + List res = getRoles(getBaseDn(), filter, true); + return res.toArray(new Role[res.size()]); + } + + List getRoles(LdapName searchBase, String filter, boolean deep) throws InvalidSyntaxException { UserDirectoryWorkingCopy wc = getWorkingCopy(); Filter f = filter != null ? FrameworkUtil.createFilter(filter) : null; - List res = doGetRoles(f); + List res = doGetRoles(searchBase, f, deep); if (wc != null) { for (Iterator it = res.iterator(); it.hasNext();) { DirectoryUser user = it.next(); @@ -250,7 +345,22 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory // no need to check modified users, // since doGetRoles was already based on the modified attributes } - return res.toArray(new Role[res.size()]); + + // if non deep we also search users and groups +// if (!deep) { +// try { +// if (!(searchBase.endsWith(new LdapName(getUserBase())) +// || searchBase.endsWith(new LdapName(getGroupBase())))) { +// LdapName usersBase = (LdapName) ((LdapName) searchBase.clone()).add(getUserBase()); +// res.addAll(getRoles(usersBase, filter, false)); +// LdapName groupsBase = (LdapName) ((LdapName) searchBase.clone()).add(getGroupBase()); +// res.addAll(getRoles(groupsBase, filter, false)); +// } +// } catch (InvalidNameException e) { +// throw new IllegalStateException("Cannot search users and groups", e); +// } +// } + return res; } @Override @@ -260,7 +370,7 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory if (key != null) { doGetUser(key, value, collectedUsers); } else { - throw new UserDirectoryException("Key cannot be null"); + throw new IllegalArgumentException("Key cannot be null"); } if (collectedUsers.size() == 1) { @@ -275,10 +385,10 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory protected void doGetUser(String key, String value, List collectedUsers) { try { Filter f = FrameworkUtil.createFilter("(" + key + "=" + value + ")"); - List users = doGetRoles(f); + List users = doGetRoles(getBaseDn(), f, true); collectedUsers.addAll(users); } catch (InvalidSyntaxException e) { - throw new UserDirectoryException("Cannot get user with " + key + "=" + value, e); + throw new IllegalArgumentException("Cannot get user with " + key + "=" + value, e); } } @@ -292,7 +402,7 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory try { DirectoryUser directoryUser = (DirectoryUser) scopedUserAdmin.getRole(user.getName()); if (directoryUser == null) - throw new UserDirectoryException("No scoped user found for " + user); + throw new IllegalStateException("No scoped user found for " + user); LdifAuthorization authorization = new LdifAuthorization(directoryUser, scopedUserAdmin.getAllRoles(directoryUser)); return authorization; @@ -306,9 +416,9 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory public Role createRole(String name, int type) { checkEdit(); UserDirectoryWorkingCopy wc = getWorkingCopy(); - LdapName dn = toDn(name); + LdapName dn = toLdapName(name); if ((daoHasRole(dn) && !wc.getDeletedUsers().containsKey(dn)) || wc.getNewUsers().containsKey(dn)) - throw new UserDirectoryException("Already a role " + name); + throw new IllegalArgumentException("Already a role " + name); BasicAttributes attrs = new BasicAttributes(true); // attrs.put(LdifName.dn.name(), dn.toString()); Rdn nameRdn = dn.getRdn(dn.size() - 1); @@ -327,7 +437,7 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory } protected DirectoryUser newRole(LdapName dn, int type, Attributes attrs) { - LdifUser newRole; + DirectoryUser newRole; BasicAttribute objClass = new BasicAttribute(objectClass.name()); if (type == Role.USER) { String userObjClass = newUserObjectClass(dn); @@ -341,16 +451,16 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory objClass.add(top.name()); objClass.add(extensibleObject.name()); attrs.put(objClass); - newRole = new LdifUser(this, dn, attrs); + newRole = newUser(dn, attrs); } else if (type == Role.GROUP) { String groupObjClass = getGroupObjectClass(); objClass.add(groupObjClass); // objClass.add(LdifName.extensibleObject.name()); objClass.add(top.name()); attrs.put(objClass); - newRole = new LdifGroup(this, dn, attrs); + newRole = newGroup(dn, attrs); } else - throw new UserDirectoryException("Unsupported type " + type); + throw new IllegalArgumentException("Unsupported type " + type); return newRole; } @@ -358,7 +468,7 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory public boolean removeRole(String name) { checkEdit(); UserDirectoryWorkingCopy wc = getWorkingCopy(); - LdapName dn = toDn(name); + LdapName dn = toLdapName(name); boolean actuallyDeleted; if (daoHasRole(dn) || wc.getNewUsers().containsKey(dn)) { DirectoryUser user = (DirectoryUser) getRole(name); @@ -387,12 +497,92 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory } - // UTILITIES - protected LdapName toDn(String name) { + /* + * HIERARCHY + */ +// @Override +// public int getHierarchyChildCount() { +// return 0; +// } +// +// @Override +// public HierarchyUnit getHierarchyChild(int i) { +// throw new IllegalArgumentException("No child hierarchy unit available"); +// } +// +// @Override +// public HierarchyUnit getParent() { +// return null; +// } +// +// @Override +// public int getHierarchyUnitType() { +// return 0; +// } +// +// @Override +// public String getHierarchyUnitName() { +// String name = LdapNameUtils.getLastRdnValue(baseDn); +// // TODO check ou, o, etc. +// return name; +// } + + @Override + public HierarchyUnit getHierarchyUnit(String path) { + throw new UnsupportedOperationException(); + } + + @Override + public HierarchyUnit getHierarchyUnit(Role role) { + throw new UnsupportedOperationException(); + } + +// @Override +// public List getHierarchyUnitRoles(String filter, boolean deep) { +// try { +// return getRoles(getBaseDn(), filter, deep); +// } catch (InvalidSyntaxException e) { +// throw new IllegalArgumentException("Cannot filter " + filter + " " + getBaseDn(), e); +// } +// } + + @Override + public Iterable getDirectHierarchyUnits(boolean functionalOnly) { + throw new UnsupportedOperationException(); + } + + /* + * ROLES CREATION + */ + protected DirectoryUser newUser(LdapName name, Attributes attrs) { + // TODO support devices, applications, etc. + return new LdifUser.LdifPerson(this, name, attrs); + } + + protected DirectoryGroup newGroup(LdapName name, Attributes attrs) { + if (LdapNameUtils.getParentRdn(name).equals(getSystemRoleBaseRdn())) + return new LdifGroup.LdifSystemPermissions(this, name, attrs); + + if (hasObjectClass(attrs, LdapObjs.organization)) + return new LdifGroup.LdifOrganization(this, name, attrs); + else + return new LdifGroup.LdifFunctionalGroup(this, name, attrs); + + } + + private boolean hasObjectClass(Attributes attrs, LdapObjs objectClass) { try { - return new LdapName(name); - } catch (InvalidNameException e) { - throw new UserDirectoryException("Badly formatted name", e); + 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); } } @@ -449,12 +639,14 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory } protected int roleType(LdapName dn) { - if (dn.startsWith(groupBaseDn)) + Rdn technicalRdn = LdapNameUtils.getParentRdn(dn); + if (getGroupBaseRdn().equals(technicalRdn) || getSystemRoleBaseRdn().equals(technicalRdn)) return Role.GROUP; - else if (dn.startsWith(userBaseDn)) + else if (userBaseRdn.equals(technicalRdn)) return Role.USER; else - return Role.GROUP; + 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. */ @@ -462,8 +654,8 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory return userObjectClass; } - public String getUserBase() { - return userBase; + Rdn getUserBaseRdn() { + return userBaseRdn; } protected String newUserObjectClass(LdapName dn) { @@ -474,11 +666,15 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory return groupObjectClass; } - public String getGroupBase() { - return groupBase; + Rdn getGroupBaseRdn() { + return groupBaseRdn; + } + + Rdn getSystemRoleBaseRdn() { + return systemRoleBaseRdn; } - public LdapName getBaseDn() { + LdapName getBaseDn() { return (LdapName) baseDn.clone(); } @@ -514,4 +710,24 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory return scoped; } + @Override + public int hashCode() { + return baseDn.hashCode(); + } + + @Override + public String toString() { + return "User Directory " + baseDn.toString(); + } + + /* + * STATIC UTILITIES + */ + static LdapName toLdapName(String name) { + try { + return new LdapName(name); + } catch (InvalidNameException e) { + throw new IllegalArgumentException(name + " is not an LDAP name", e); + } + } }