X-Git-Url: http://git.argeo.org/?a=blobdiff_plain;f=org.argeo.security.core%2Fsrc%2Forg%2Fargeo%2Fosgi%2Fuseradmin%2FAbstractUserDirectory.java;h=f871cd25e8af8c8c97e96e06ab9f62ff2136bcaa;hb=6342d1d28f8338866c876f8b6364ce3f1eac28aa;hp=e79800f102e1efb46822526a3c098afd0136f3a7;hpb=f708c8d4cfc7ca9446b61b9f26244394cb447e4a;p=lgpl%2Fargeo-commons.git diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java index e79800f10..f871cd25e 100644 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java +++ b/org.argeo.security.core/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java @@ -1,10 +1,15 @@ package org.argeo.osgi.useradmin; +import static org.argeo.osgi.useradmin.LdifName.gidNumber; +import static org.argeo.osgi.useradmin.LdifName.homeDirectory; import static org.argeo.osgi.useradmin.LdifName.inetOrgPerson; import static org.argeo.osgi.useradmin.LdifName.objectClass; import static org.argeo.osgi.useradmin.LdifName.organizationalPerson; import static org.argeo.osgi.useradmin.LdifName.person; +import static org.argeo.osgi.useradmin.LdifName.posixAccount; import static org.argeo.osgi.useradmin.LdifName.top; +import static org.argeo.osgi.useradmin.LdifName.uid; +import static org.argeo.osgi.useradmin.LdifName.uidNumber; import java.io.File; import java.net.URI; @@ -18,6 +23,7 @@ import java.util.Iterator; import java.util.List; import javax.naming.InvalidNameException; +import javax.naming.NamingException; import javax.naming.directory.Attributes; import javax.naming.directory.BasicAttribute; import javax.naming.directory.BasicAttributes; @@ -26,8 +32,10 @@ import javax.naming.ldap.Rdn; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; -import javax.transaction.xa.Xid; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.ArgeoException; import org.osgi.framework.Filter; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.InvalidSyntaxException; @@ -38,6 +46,9 @@ import org.osgi.service.useradmin.UserAdmin; /** Base class for a {@link UserDirectory}. */ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { + private final static Log log = LogFactory + .getLog(AbstractUserDirectory.class); + private final Hashtable properties; private final String baseDn; private final String userObjectClass; @@ -52,11 +63,15 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { private String memberAttributeId = "member"; private List credentialAttributeIds = Arrays - .asList(new String[] { LdifName.userpassword.name() }); + .asList(new String[] { LdifName.userPassword.name() }); private TransactionManager transactionManager; - private ThreadLocal workingCopy = new ThreadLocal(); - private Xid editingTransactionXid = null; + // private TransactionSynchronizationRegistry transactionRegistry; + // private Xid editingTransactionXid = null; + private WcXaResource xaResource = new WcXaResource(this); + + // POSIX + private String homeDirectoryBase = "/home"; AbstractUserDirectory(Dictionary props) { properties = new Hashtable(); @@ -107,19 +122,21 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { } boolean isEditing() { - if (editingTransactionXid == null) - return false; - return workingCopy.get() != null; + // if (editingTransactionXid == null) + // return false; + // return workingCopy.get() != null; + return xaResource.wc() != null; } protected UserDirectoryWorkingCopy getWorkingCopy() { - UserDirectoryWorkingCopy wc = workingCopy.get(); + // UserDirectoryWorkingCopy wc = workingCopy.get(); + UserDirectoryWorkingCopy wc = xaResource.wc(); if (wc == null) return null; - if (wc.getXid() == null) { - workingCopy.set(null); - return null; - } + // if (wc.getXid() == null) { + // workingCopy.set(null); + // return null; + // } return wc; } @@ -133,23 +150,26 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { if (transaction == null) throw new UserDirectoryException( "A transaction needs to be active in order to edit"); - if (editingTransactionXid == null) { - UserDirectoryWorkingCopy wc = new UserDirectoryWorkingCopy(this); + if (xaResource.wc() == null) { + // UserDirectoryWorkingCopy wc = new UserDirectoryWorkingCopy(this); try { - transaction.enlistResource(wc); - editingTransactionXid = wc.getXid(); - workingCopy.set(wc); + transaction.enlistResource(xaResource); + // editingTransactionXid = wc.getXid(); + // workingCopy.set(wc); } catch (Exception e) { - throw new UserDirectoryException("Cannot enlist " + wc, e); + throw new UserDirectoryException("Cannot enlist " + xaResource, + e); } } else { - if (workingCopy.get() == null) - throw new UserDirectoryException("Transaction " - + editingTransactionXid + " already editing"); - else if (!editingTransactionXid.equals(workingCopy.get().getXid())) - throw new UserDirectoryException("Working copy Xid " - + workingCopy.get().getXid() + " inconsistent with" - + editingTransactionXid); + // UserDirectoryWorkingCopy wc = xaResource.wc(); + // if (wc == null) + // throw new UserDirectoryException("Transaction " + // + editingTransactionXid + " already editing"); + // else if + // (!editingTransactionXid.equals(workingCopy.get().getXid())) + // throw new UserDirectoryException("Working copy Xid " + // + workingCopy.get().getXid() + " inconsistent with" + // + editingTransactionXid); } } @@ -240,14 +260,17 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { } if (collectedUsers.size() == 1) return collectedUsers.get(0); + else if (collectedUsers.size() > 1) + log.warn(collectedUsers.size() + " users for " + + (key != null ? key + "=" : "") + value); return null; } protected void doGetUser(String key, String value, List collectedUsers) { try { - Filter f = FrameworkUtil.createFilter("(&(" + objectClass + "=" - + getUserObjectClass() + ")(" + key + "=" + value + "))"); + Filter f = FrameworkUtil + .createFilter("(" + key + "=" + value + ")"); List users = doGetRoles(f); collectedUsers.addAll(users); } catch (InvalidSyntaxException e) { @@ -270,7 +293,7 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { if ((daoHasRole(dn) && !wc.getDeletedUsers().containsKey(dn)) || wc.getNewUsers().containsKey(dn)) throw new UserDirectoryException("Already a role " + name); - BasicAttributes attrs = new BasicAttributes(); + BasicAttributes attrs = new BasicAttributes(true); attrs.put("dn", dn.toString()); Rdn nameRdn = dn.getRdn(dn.size() - 1); // TODO deal with multiple attr RDN @@ -290,9 +313,29 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { LdifUser newRole; BasicAttribute objClass = new BasicAttribute(objectClass.name()); if (type == Role.USER) { - String userObjClass = getUserObjectClass(); + String userObjClass = newUserObjectClass(dn); objClass.add(userObjClass); - if (inetOrgPerson.name().equals(userObjClass)) { + if (posixAccount.name().equals(userObjClass)) { + objClass.add(inetOrgPerson.name()); + objClass.add(organizationalPerson.name()); + objClass.add(person.name()); + + String username; + try { + username = dn.getRdn(dn.size() - 1).toAttributes() + .get(uid.name()).get().toString(); + } catch (NamingException e) { + throw new UserDirectoryException( + "Cannot extract username from " + dn, e); + } + // TODO look for uid in attributes too? + attrs.put(uidNumber.name(), new Long(max(uidNumber.name()) + 1)); + attrs.put(homeDirectory.name(), generateHomeDirectory(username)); + // TODO create user private group + // NB: on RHEL, the 'users' group has gid 100 + attrs.put(gidNumber.name(), 100); + // attrs.put(LdifName.loginShell.name(),"/sbin/nologin"); + } else if (inetOrgPerson.name().equals(userObjClass)) { objClass.add(organizationalPerson.name()); objClass.add(person.name()); } else if (organizationalPerson.name().equals(userObjClass)) { @@ -302,7 +345,10 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { attrs.put(objClass); newRole = new LdifUser(this, dn, attrs); } else if (type == Role.GROUP) { - objClass.add(getGroupObjectClass()); + String groupObjClass = getGroupObjectClass(); + objClass.add(groupObjClass); + objClass.add(LdifName.extensibleObject.name()); + attrs.put(gidNumber.name(), new Long(max(gidNumber.name()) + 1)); objClass.add(top); attrs.put(objClass); newRole = new LdifGroup(this, dn, attrs); @@ -332,6 +378,60 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { return actuallyDeleted; } + // POSIX + /** Generate path for a new user home */ + protected String generateHomeDirectory(String username) { + String base = homeDirectoryBase; + int atIndex = username.indexOf('@'); + if (atIndex > 0) { + String domain = username.substring(0, atIndex); + String name = username.substring(atIndex + 1); + return base + '/' + firstCharsToPath(domain, 2) + '/' + domain + + '/' + firstCharsToPath(name, 2) + '/' + name; + } else if (atIndex == 0 || atIndex == (username.length() - 1)) { + throw new ArgeoException("Unsupported username " + username); + } else { + return base + '/' + firstCharsToPath(username, 2) + '/' + username; + } + } + + protected long max(String attr) { + long max; + try { + List users = doGetRoles(FrameworkUtil + .createFilter("(" + attr + "=*)")); + max = 1000; + for (DirectoryUser user : users) { + long uid = Long.parseLong(user.getAttributes().get(attr).get() + .toString()); + if (uid > max) + max = uid; + } + } catch (Exception e) { + throw new UserDirectoryException("Cannot get max of " + attr, e); + } + return max; + } + + /** + * Creates depth from a string (typically a username) by adding levels based + * on its first characters: "aBcD",2 => a/aB + */ + public static String firstCharsToPath(String str, Integer nbrOfChars) { + if (str.length() < nbrOfChars) + throw new ArgeoException("String " + str + + " length must be greater or equal than " + nbrOfChars); + StringBuffer path = new StringBuffer(""); + StringBuffer curr = new StringBuffer(""); + for (int i = 0; i < nbrOfChars; i++) { + curr.append(str.charAt(i)); + path.append(curr); + if (i < nbrOfChars - 1) + path.append('/'); + } + return path.toString(); + } + // TRANSACTION protected void prepare(UserDirectoryWorkingCopy wc) { @@ -345,9 +445,9 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { } - void clearEditingTransactionXid() { - editingTransactionXid = null; - } + // void clearEditingTransactionXid() { + // editingTransactionXid = null; + // } // UTILITIES protected LdapName toDn(String name) { @@ -405,10 +505,19 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { return baseDn; } + /** dn can be null, in that case a default should be returned. */ protected String getUserObjectClass() { return userObjectClass; } + protected String newUserObjectClass(LdapName dn) { + if (dn != null + && dn.getRdn(dn.size() - 1).toAttributes().get(uid.name()) != null) + return posixAccount.name(); + else + return getUserObjectClass(); + } + protected String getGroupObjectClass() { return groupObjectClass; } @@ -425,4 +534,8 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { this.transactionManager = transactionManager; } + public WcXaResource getXaResource() { + return xaResource; + } + }