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;
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;
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<String, Object> 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;
private WorkControl transactionControl;
private WcXaResource xaResource = new WcXaResource(this);
+ private String forcedPassword;
+
AbstractUserDirectory(URI uriArg, Dictionary<String, ?> props, boolean scoped) {
this.scoped = scoped;
properties = new Hashtable<String, Object>();
uri = uriStr;
}
+ 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);
protected abstract DirectoryUser daoGetRole(LdapName key) throws NameNotFoundException;
- protected abstract List<DirectoryUser> doGetRoles(Filter f);
+ protected abstract List<DirectoryUser> doGetRoles(LdapName searchBase, Filter f, boolean deep);
protected abstract AbstractUserDirectory scope(User user);
}
+ /*
+ * 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<String> 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 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 {
}
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())) {
// USER ADMIN
@Override
public Role getRole(String name) {
- return doGetRole(toDn(name));
+ return doGetRole(toLdapName(name));
}
protected DirectoryUser doGetRole(LdapName dn) {
@Override
public Role[] getRoles(String filter) throws InvalidSyntaxException {
+ List<? extends Role> res = getRoles(getBaseDn(), filter, true);
+ return res.toArray(new Role[res.size()]);
+ }
+
+ List<DirectoryUser> getRoles(LdapName searchBase, String filter, boolean deep) throws InvalidSyntaxException {
UserDirectoryWorkingCopy wc = getWorkingCopy();
Filter f = filter != null ? FrameworkUtil.createFilter(filter) : null;
- List<DirectoryUser> res = doGetRoles(f);
+ List<DirectoryUser> res = doGetRoles(searchBase, f, deep);
if (wc != null) {
for (Iterator<DirectoryUser> it = res.iterator(); it.hasNext();) {
DirectoryUser user = it.next();
// 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
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) {
protected void doGetUser(String key, String value, List<DirectoryUser> collectedUsers) {
try {
Filter f = FrameworkUtil.createFilter("(" + key + "=" + value + ")");
- List<DirectoryUser> users = doGetRoles(f);
+ List<DirectoryUser> 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);
}
}
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;
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);
}
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);
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;
}
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);
}
- // 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<? extends Role> 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<HierarchyUnit> 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);
}
}
}
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. */
return userObjectClass;
}
- public String getUserBase() {
- return userBase;
+ Rdn getUserBaseRdn() {
+ return userBaseRdn;
}
protected String newUserObjectClass(LdapName dn) {
return groupObjectClass;
}
- public String getGroupBase() {
- return groupBase;
+ Rdn getGroupBaseRdn() {
+ return groupBaseRdn;
+ }
+
+ Rdn getSystemRoleBaseRdn() {
+ return systemRoleBaseRdn;
}
- public LdapName getBaseDn() {
+ LdapName getBaseDn() {
return (LdapName) baseDn.clone();
}
// this.transactionManager = transactionManager;
// }
+ public String getForcedPassword() {
+ return forcedPassword;
+ }
+
public void setTransactionControl(WorkControl transactionControl) {
this.transactionControl = transactionControl;
}
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);
+ }
+ }
}