private int status = Status.STATUS_ACTIVE;
private final List<XAResource> xaResources = new ArrayList<XAResource>();
- public SimpleTransaction() {
- xid = new UuidXid();
+ private final SimpleTransactionManager transactionManager;
+
+ public SimpleTransaction(SimpleTransactionManager transactionManager) {
+ this.xid = new UuidXid();
+ this.transactionManager = transactionManager;
}
@Override
rollback();
throw new RollbackException();
}
+
+ // complete
status = STATUS_COMMITTED;
+ if (log.isDebugEnabled())
+ log.debug("COMMITTED " + xid);
+ clearResources(XAResource.TMSUCCESS);
+ transactionManager.unregister(xid);
}
@Override
- public synchronized boolean delistResource(XAResource xaRes, int flag)
- throws IllegalStateException, SystemException {
- return xaResources.remove(xaRes);
+ public synchronized void rollback() throws IllegalStateException,
+ SystemException {
+ status = STATUS_ROLLING_BACK;
+ for (XAResource xaRes : xaResources) {
+ try {
+ xaRes.rollback(xid);
+ } catch (XAException e) {
+ log.error("Cannot rollback " + xaRes + " for " + xid, e);
+ }
+ }
+
+ // complete
+ status = STATUS_ROLLEDBACK;
+ if (log.isDebugEnabled())
+ log.debug("ROLLEDBACK " + xid);
+ clearResources(XAResource.TMFAIL);
+ transactionManager.unregister(xid);
}
@Override
public synchronized boolean enlistResource(XAResource xaRes)
throws RollbackException, IllegalStateException, SystemException {
- return xaResources.add(xaRes);
+ if (xaResources.add(xaRes)) {
+ try {
+ xaRes.start(getXid(), XAResource.TMNOFLAGS);
+ return true;
+ } catch (XAException e) {
+ log.error("Cannot enlist " + xaRes, e);
+ return false;
+ }
+ } else
+ return false;
+ }
+
+ @Override
+ public synchronized boolean delistResource(XAResource xaRes, int flag)
+ throws IllegalStateException, SystemException {
+ if (xaResources.remove(xaRes)) {
+ try {
+ xaRes.end(getXid(), flag);
+ } catch (XAException e) {
+ log.error("Cannot delist " + xaRes, e);
+ return false;
+ }
+ return true;
+ } else
+ return false;
+ }
+
+ protected void clearResources(int flag) {
+ for (XAResource xaRes : xaResources)
+ try {
+ xaRes.end(getXid(), flag);
+ } catch (XAException e) {
+ log.error("Cannot end " + xaRes, e);
+ }
+ xaResources.clear();
}
@Override
throw new UnsupportedOperationException();
}
- @Override
- public synchronized void rollback() throws IllegalStateException,
- SystemException {
- status = STATUS_ROLLING_BACK;
- for (XAResource xaRes : xaResources) {
- try {
- xaRes.rollback(xid);
- } catch (XAException e) {
- log.error("Cannot rollback " + xaRes + " for " + xid, e);
- }
- }
- status = STATUS_ROLLEDBACK;
- }
-
@Override
public void setRollbackOnly() throws IllegalStateException, SystemException {
status = STATUS_MARKED_ROLLBACK;
return xid.hashCode();
}
- public Xid getXid() {
+ Xid getXid() {
return xid;
}
if (getCurrent() != null)
throw new NotSupportedException(
"Nested transactions are not supported");
- SimpleTransaction transaction = new SimpleTransaction();
+ SimpleTransaction transaction = new SimpleTransaction(this);
knownTransactions.put(transaction.getXid(), transaction);
current.set(transaction);
if (log.isDebugEnabled())
- log.debug("Started transaction " + transaction.getXid());
+ log.debug("STARTED " + transaction.getXid());
}
@Override
if (Status.STATUS_COMMITTED == status
|| Status.STATUS_ROLLEDBACK == status) {
current.remove();
- knownTransactions.remove(transaction.getXid());
- if (log.isDebugEnabled())
- log.debug("Completed transaction "
- + transaction.getXid()
- + " ["
- + (status == Status.STATUS_ROLLEDBACK ? "FAILED" : "OK")
- + "]");
return null;
}
return transaction;
}
+
+ void unregister(Xid xid){
+ knownTransactions.remove(xid);
+ }
@Override
public void resume(Transaction tobj) throws InvalidTransactionException,
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
+import java.util.Map;
-import javax.transaction.RollbackException;
+import javax.naming.InvalidNameException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttributes;
+import javax.naming.ldap.LdapName;
+import javax.naming.ldap.Rdn;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.useradmin.Authorization;
import org.osgi.service.useradmin.Group;
import org.osgi.service.useradmin.Role;
import org.osgi.service.useradmin.User;
import org.osgi.service.useradmin.UserAdmin;
public abstract class AbstractUserDirectory implements UserAdmin {
+ private final static Log log = LogFactory
+ .getLog(AbstractUserDirectory.class);
private boolean isReadOnly;
private URI uri;
private List<String> credentialAttributeIds = Arrays
.asList(new String[] { "userpassword" });
- private TransactionSynchronizationRegistry syncRegistry;
- private Object editingTransactionKey = null;
+ // private TransactionSynchronizationRegistry syncRegistry;
+ // private Object editingTransactionKey = null;
+
private TransactionManager transactionManager;
- private Transaction editingTransaction;
+ private ThreadLocal<WorkingCopy> workingCopy = new ThreadLocal<AbstractUserDirectory.WorkingCopy>();
+ private Xid editingTransactionXid = null;
public AbstractUserDirectory() {
}
}
/** Returns the {@link Group}s this user is a direct member of. */
- protected abstract List<? extends Group> getDirectGroups(User user);
+ protected abstract List<? extends DirectoryGroup> getDirectGroups(User user);
+
+ protected abstract Boolean daoHasRole(LdapName dn);
+
+ protected abstract DirectoryUser daoGetRole(LdapName key);
+
+ protected abstract List<DirectoryUser> doGetRoles(Filter f);
+
+ protected abstract void doGetUser(String key, String value,
+ List<DirectoryUser> collectedUsers);
public void init() {
}
boolean isEditing() {
- if (editingTransactionKey == null)
- return false;
- Object currentTrKey = syncRegistry.getTransactionKey();
- if (currentTrKey == null)
+ if (editingTransactionXid == null)
return false;
- return editingTransactionKey.equals(currentTrKey);
+ return workingCopy.get() != null;
+ // Object currentTrKey = syncRegistry.getTransactionKey();
+ // if (currentTrKey == null)
+ // return false;
+ // return editingTransactionKey.equals(currentTrKey);
+ }
+
+ protected WorkingCopy getWorkingCopy() {
+ WorkingCopy wc = workingCopy.get();
+ if (wc == null)
+ return null;
+ if (wc.xid == null) {
+ workingCopy.set(null);
+ return null;
+ }
+ return wc;
}
void checkEdit() {
- Object currentTrKey = syncRegistry.getTransactionKey();
- if (currentTrKey == null)
+ 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 (editingTransactionKey == null) {
- editingTransactionKey = currentTrKey;
- XAResource xaRes = getXAResource();
- if (xaRes != null)
- try {
- transactionManager.getTransaction().enlistResource(xaRes);
- } catch (Exception e) {
- throw new UserDirectoryException("Cannot enlist " + this, e);
- }
+ if (editingTransactionXid == null) {
+ WorkingCopy wc = new WorkingCopy();
+ try {
+ transaction.enlistResource(wc);
+ editingTransactionXid = wc.getXid();
+ workingCopy.set(wc);
+ } catch (Exception e) {
+ throw new UserDirectoryException("Cannot enlist " + wc, e);
+ }
} else {
- if (!editingTransactionKey.equals(currentTrKey))
+ if (workingCopy.get() == null)
throw new UserDirectoryException("Transaction "
- + editingTransactionKey + " already editing");
+ + editingTransactionXid + " already editing");
+ else if (!editingTransactionXid.equals(workingCopy.get().getXid()))
+ throw new UserDirectoryException("Working copy Xid "
+ + workingCopy.get().getXid() + " inconsistent with"
+ + editingTransactionXid);
}
}
// TODO gather anonymous roles
}
- public XAResource getXAResource() {
+ // USER ADMIN
+ @Override
+ public Role getRole(String name) {
+ LdapName key = toDn(name);
+ WorkingCopy wc = getWorkingCopy();
+ DirectoryUser user = daoGetRole(key);
+ if (wc != null) {
+ if (user == null && wc.getNewUsers().containsKey(key))
+ user = wc.getNewUsers().get(key);
+ else if (wc.getDeletedUsers().containsKey(key))
+ user = null;
+ }
+ return user;
+ }
+
+ @Override
+ public Role[] getRoles(String filter) throws InvalidSyntaxException {
+ WorkingCopy wc = getWorkingCopy();
+ Filter f = filter != null ? FrameworkUtil.createFilter(filter) : null;
+ List<DirectoryUser> res = doGetRoles(f);
+ if (wc != null) {
+ for (Iterator<DirectoryUser> it = res.iterator(); it.hasNext();) {
+ DirectoryUser user = it.next();
+ LdapName dn = user.getDn();
+ if (wc.getDeletedUsers().containsKey(dn))
+ it.remove();
+ }
+ for (DirectoryUser user : wc.getNewUsers().values()) {
+ if (f == null || f.match(user.getProperties()))
+ res.add(user);
+ }
+ // no need to check modified users,
+ // since doGetRoles was already based on the modified attributes
+ }
+ return res.toArray(new Role[res.size()]);
+ }
+
+ @Override
+ public User getUser(String key, String value) {
+ // TODO check value null or empty
+ List<DirectoryUser> collectedUsers = new ArrayList<DirectoryUser>(
+ getIndexedUserProperties().size());
+ if (key != null) {
+ doGetUser(key, value, collectedUsers);
+ } else {
+ // try dn
+ DirectoryUser user = null;
+ try {
+ user = (DirectoryUser) getRole(value);
+ if (user != null)
+ collectedUsers.add(user);
+ } catch (Exception e) {
+ // silent
+ }
+ // try all indexes
+ for (String attr : getIndexedUserProperties())
+ doGetUser(attr, value, collectedUsers);
+ }
+ if (collectedUsers.size() == 1)
+ return collectedUsers.get(0);
return null;
}
+ @Override
+ public Authorization getAuthorization(User user) {
+ return new LdifAuthorization((DirectoryUser) user,
+ getAllRoles((DirectoryUser) user));
+ }
+
+ @Override
+ public Role createRole(String name, int type) {
+ checkEdit();
+ WorkingCopy wc = getWorkingCopy();
+ LdapName dn = toDn(name);
+ if ((daoHasRole(dn) && !wc.getDeletedUsers().containsKey(dn))
+ || wc.getNewUsers().containsKey(dn))
+ throw new UserDirectoryException("Already a role " + name);
+ BasicAttributes attrs = new BasicAttributes();
+ attrs.put("dn", dn.toString());
+ Rdn nameRdn = dn.getRdn(dn.size() - 1);
+ // TODO deal with multiple attr RDN
+ attrs.put(nameRdn.getType(), nameRdn.getValue());
+ if (wc.getDeletedUsers().containsKey(dn)) {
+ wc.getDeletedUsers().remove(dn);
+ wc.getModifiedUsers().put(dn, attrs);
+ } else {
+ wc.getModifiedUsers().put(dn, attrs);
+ DirectoryUser newRole = newRole(dn, type, attrs);
+ wc.getNewUsers().put(dn, newRole);
+ }
+ return getRole(name);
+ }
+
+ protected DirectoryUser newRole(LdapName dn, int type, Attributes attrs) {
+ LdifUser newRole;
+ if (type == Role.USER) {
+ newRole = new LdifUser(this, dn, attrs);
+ // users.put(dn, newRole);
+ } else if (type == Role.GROUP) {
+ newRole = new LdifGroup(this, dn, attrs);
+ // groups.put(dn, (LdifGroup) newRole);
+ } else
+ throw new UserDirectoryException("Unsupported type " + type);
+ return newRole;
+ }
+
+ @Override
+ public boolean removeRole(String name) {
+ checkEdit();
+ WorkingCopy wc = getWorkingCopy();
+ LdapName dn = toDn(name);
+ if (!daoHasRole(dn) && !wc.getNewUsers().containsKey(dn))
+ return false;
+ DirectoryUser user = (DirectoryUser) getRole(name);
+ wc.getDeletedUsers().put(dn, user);
+ // FIXME clarify directgroups
+ for (DirectoryGroup group : getDirectGroups(user)) {
+ group.getAttributes().get(getMemberAttributeId())
+ .remove(dn.toString());
+ }
+ return true;
+ }
+
+ // TRANSACTION
+ protected void prepare(WorkingCopy wc) {
+
+ }
+
+ protected void commit(WorkingCopy wc) {
+
+ }
+
+ protected void rollback(WorkingCopy wc) {
+
+ }
+
+ // UTILITIES
+ protected LdapName toDn(String name) {
+ try {
+ return new LdapName(name);
+ } catch (InvalidNameException e) {
+ throw new UserDirectoryException("Badly formatted name", e);
+ }
+ }
+
+ // GETTERS
+
String getMemberAttributeId() {
return memberAttributeId;
}
}
public void setSyncRegistry(TransactionSynchronizationRegistry syncRegistry) {
- this.syncRegistry = syncRegistry;
+ // this.syncRegistry = syncRegistry;
}
public void setTransactionManager(TransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
+ //
+ // XA RESOURCE
+ //
+ protected class WorkingCopy implements XAResource {
+ private Xid xid;
+ private int transactionTimeout = 0;
+
+ private Map<LdapName, DirectoryUser> newUsers = new HashMap<LdapName, DirectoryUser>();
+ private Map<LdapName, Attributes> modifiedUsers = new HashMap<LdapName, Attributes>();
+ private Map<LdapName, DirectoryUser> deletedUsers = new HashMap<LdapName, DirectoryUser>();
+
+ @Override
+ public void start(Xid xid, int flags) throws XAException {
+ if (editingTransactionXid != null)
+ throw new UserDirectoryException("Transaction "
+ + editingTransactionXid + " already editing");
+ this.xid = xid;
+ }
+
+ @Override
+ public void end(Xid xid, int flags) throws XAException {
+ checkXid(xid);
+
+ // clean collections
+ newUsers.clear();
+ newUsers = null;
+ modifiedUsers.clear();
+ modifiedUsers = null;
+ deletedUsers.clear();
+ deletedUsers = null;
+
+ // clean IDs
+ this.xid = null;
+ editingTransactionXid = null;
+ }
+
+ @Override
+ public int prepare(Xid xid) throws XAException {
+ checkXid(xid);
+ if (noModifications())
+ return XA_RDONLY;
+ try {
+ AbstractUserDirectory.this.prepare(this);
+ } catch (Exception e) {
+ log.error("Cannot prepare " + xid, e);
+ throw new XAException(XAException.XA_RBOTHER);
+ }
+ return XA_OK;
+ }
+
+ @Override
+ public void commit(Xid xid, boolean onePhase) throws XAException {
+ checkXid(xid);
+ if (noModifications())
+ return;
+ try {
+ if (onePhase)
+ AbstractUserDirectory.this.prepare(this);
+ AbstractUserDirectory.this.commit(this);
+ } catch (Exception e) {
+ log.error("Cannot commit " + xid, e);
+ throw new XAException(XAException.XA_RBOTHER);
+ }
+ }
+
+ @Override
+ public void rollback(Xid xid) throws XAException {
+ checkXid(xid);
+ try {
+ AbstractUserDirectory.this.rollback(this);
+ } catch (Exception e) {
+ log.error("Cannot rollback " + xid, e);
+ throw new XAException(XAException.XA_HEURMIX);
+ }
+ }
+
+ @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 {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getTransactionTimeout() throws XAException {
+ return transactionTimeout;
+ }
+
+ @Override
+ public boolean setTransactionTimeout(int seconds) throws XAException {
+ transactionTimeout = seconds;
+ return true;
+ }
+
+ private Xid getXid() {
+ return xid;
+ }
+
+ private void checkXid(Xid xid) throws XAException {
+ if (this.xid == null)
+ throw new XAException(XAException.XAER_OUTSIDE);
+ if (!this.xid.equals(xid))
+ throw new XAException(XAException.XAER_NOTA);
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ if (editingTransactionXid != null)
+ log.warn("Editing transaction still referenced but no working copy "
+ + editingTransactionXid);
+ editingTransactionXid = null;
+ }
+
+ public boolean noModifications() {
+ return newUsers.size() == 0 && modifiedUsers.size() == 0
+ && deletedUsers.size() == 0;
+ }
+
+ public Attributes getAttributes(LdapName dn) {
+ if (modifiedUsers.containsKey(dn))
+ return modifiedUsers.get(dn);
+ return null;
+ }
+
+ public void startEditing(DirectoryUser user) {
+ LdapName dn = user.getDn();
+ if (modifiedUsers.containsKey(dn))
+ throw new UserDirectoryException("Already editing " + dn);
+ modifiedUsers.put(dn, (Attributes) user.getAttributes().clone());
+ }
+
+ public Map<LdapName, DirectoryUser> getNewUsers() {
+ return newUsers;
+ }
+
+ public Map<LdapName, DirectoryUser> getDeletedUsers() {
+ return deletedUsers;
+ }
+
+ public Map<LdapName, Attributes> getModifiedUsers() {
+ return modifiedUsers;
+ }
+
+ }
}
LdapName getDn();
Attributes getAttributes();
+
+ void publishAttributes(Attributes modifiedAttributes);
}
+++ /dev/null
-package org.argeo.osgi.useradmin;
-
-import org.osgi.service.useradmin.Role;
-
-public interface EditorRole extends Role {
- public static final int EDITOR = -1;
-
-
-}
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.ModificationItem;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
import javax.naming.ldap.LdapName;
+import javax.transaction.xa.XAException;
+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.InvalidSyntaxException;
import org.osgi.service.useradmin.Authorization;
import org.osgi.service.useradmin.Group;
}
@Override
- public Role createRole(String name, int type) {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public boolean removeRole(String name) {
- // TODO Auto-generated method stub
- return false;
+ protected Boolean daoHasRole(LdapName dn) {
+ return daoGetRole(dn) != null;
}
@Override
- public Role getRole(String name) {
+ protected DirectoryUser daoGetRole(LdapName name) {
try {
Attributes attrs = initialLdapContext.getAttributes(name);
+ if (attrs.size() == 0)
+ return null;
LdifUser res;
if (attrs.get("objectClass").contains("groupOfNames"))
- res = new LdifGroup(this, new LdapName(name), attrs);
+ res = new LdifGroup(this, name, attrs);
else if (attrs.get("objectClass").contains("inetOrgPerson"))
- res = new LdifUser(this, new LdapName(name), attrs);
+ res = new LdifUser(this, name, attrs);
else
throw new UserDirectoryException("Unsupported LDAP type for "
+ name);
}
@Override
- public Role[] getRoles(String filter) throws InvalidSyntaxException {
+ protected List<DirectoryUser> doGetRoles(Filter f) {
+ // TODO Auto-generated method stub
try {
- String searchFilter = filter;
- if (searchFilter == null)
- searchFilter = "(|(objectClass=inetOrgPerson)(objectClass=groupOfNames))";
+ String searchFilter = f != null ? f.toString()
+ : "(|(objectClass=inetOrgPerson)(objectClass=groupOfNames))";
SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration<SearchResult> results = initialLdapContext
.search(searchBase, searchFilter, searchControls);
- ArrayList<Role> res = new ArrayList<Role>();
+ ArrayList<DirectoryUser> res = new ArrayList<DirectoryUser>();
while (results.hasMoreElements()) {
SearchResult searchResult = results.next();
Attributes attrs = searchResult.getAttributes();
+ searchResult.getName());
res.add(role);
}
- return res.toArray(new Role[res.size()]);
+ return res;
} catch (Exception e) {
- throw new UserDirectoryException("Cannot get roles for filter "
- + filter, e);
+ throw new UserDirectoryException(
+ "Cannot get roles for filter " + f, e);
}
}
+ @Override
+ protected void doGetUser(String key, String value,
+ List<DirectoryUser> collectedUsers) {
+ try {
+ String searchFilter = "(&(objectClass=inetOrgPerson)(" + key + "="
+ + value + "))";
+
+ SearchControls searchControls = new SearchControls();
+ searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+
+ String searchBase = baseDn;
+ NamingEnumeration<SearchResult> results = initialLdapContext
+ .search(searchBase, searchFilter, searchControls);
+
+ SearchResult searchResult = null;
+ if (results.hasMoreElements()) {
+ searchResult = (SearchResult) results.nextElement();
+ if (results.hasMoreElements())
+ searchResult = null;
+ }
+ if (searchResult != null)
+ collectedUsers.add(new LdifUser(this, toDn(searchBase,
+ searchResult), searchResult.getAttributes()));
+ } catch (Exception e) {
+ throw new UserDirectoryException("Cannot get user with " + key
+ + "=" + value, e);
+ }
+
+ }
+
@Override
public User getUser(String key, String value) {
if (key == null) {
}
}
- @Override
- public Authorization getAuthorization(User user) {
- LdifUser u = (LdifUser) user;
- // populateDirectMemberOf(u);
- return new LdifAuthorization(u, getAllRoles(u));
- }
-
private LdapName toDn(String baseDn, Binding binding)
throws InvalidNameException {
return new LdapName(binding.isRelative() ? binding.getName() + ","
// }
@Override
- protected List<? extends Group> getDirectGroups(User user) {
- List<Group> directGroups = new ArrayList<Group>();
+ protected List<DirectoryGroup> getDirectGroups(User user) {
+ List<DirectoryGroup> directGroups = new ArrayList<DirectoryGroup>();
try {
String searchFilter = "(&(objectClass=groupOfNames)(member="
+ user.getName() + "))";
// TODO configure group search base
return baseDn;
}
+
+ @Override
+ protected void prepare(WorkingCopy wc) {
+ try {
+ initialLdapContext.reconnect(initialLdapContext
+ .getConnectControls());
+ // delete
+ for (LdapName dn : wc.getDeletedUsers().keySet()) {
+ if (!entryExists(dn))
+ throw new UserDirectoryException("User to delete no found "
+ + dn);
+ }
+ // add
+ for (LdapName dn : wc.getNewUsers().keySet()) {
+ if (!entryExists(dn))
+ throw new UserDirectoryException("User to create found "
+ + dn);
+ }
+ // modify
+ for (LdapName dn : wc.getModifiedUsers().keySet()) {
+ if (!entryExists(dn))
+ throw new UserDirectoryException("User to modify no found "
+ + dn);
+ }
+ } catch (NamingException e) {
+ throw new UserDirectoryException("Cannot prepare LDAP", e);
+ }
+ }
+
+ private boolean entryExists(LdapName dn) throws NamingException {
+ return initialLdapContext.getAttributes(dn).size() != 0;
+ }
+
+ @Override
+ protected void commit(WorkingCopy wc) {
+ try {
+ // delete
+ for (LdapName dn : wc.getDeletedUsers().keySet()) {
+ initialLdapContext.destroySubcontext(dn);
+ }
+ // add
+ for (LdapName dn : wc.getNewUsers().keySet()) {
+ DirectoryUser user = wc.getNewUsers().get(dn);
+ initialLdapContext.createSubcontext(dn, user.getAttributes());
+ }
+ // modify
+ for (LdapName dn : wc.getModifiedUsers().keySet()) {
+ Attributes modifiedAttrs = wc.getModifiedUsers().get(dn);
+ initialLdapContext.modifyAttributes(dn,
+ DirContext.REPLACE_ATTRIBUTE, modifiedAttrs);
+ }
+ } catch (NamingException e) {
+ throw new UserDirectoryException("Cannot commit LDAP", e);
+ }
+ }
+
+ @Override
+ protected void rollback(WorkingCopy wc) {
+ // TODO Auto-generated method stub
+ super.rollback(wc);
+ }
+
}
import javax.naming.directory.BasicAttribute;
import javax.naming.ldap.LdapName;
+import org.argeo.osgi.useradmin.AbstractUserDirectory.WorkingCopy;
+
class LdifUser implements DirectoryUser {
private final AbstractUserDirectory userAdmin;
private final boolean frozen;
private Attributes publishedAttributes;
- private Attributes modifiedAttributes = null;
private final AttributeDictionary properties;
private final AttributeDictionary credentials;
@Override
public synchronized Attributes getAttributes() {
- return isEditing() ? modifiedAttributes : publishedAttributes;
+ return isEditing() ? getModifiedAttributes() : publishedAttributes;
+ }
+
+ /** Should only be called from working copy thread. */
+ private synchronized Attributes getModifiedAttributes() {
+ assert getWc() != null;
+ return getWc().getAttributes(getDn());
}
- protected synchronized boolean isEditing() {
- return userAdmin.isEditing() && modifiedAttributes != null;
+ private synchronized boolean isEditing() {
+ return getWc() != null && getModifiedAttributes() != null;
+ }
+
+ private synchronized WorkingCopy getWc() {
+ return userAdmin.getWorkingCopy();
}
protected synchronized void startEditing() {
throw new UserDirectoryException("Cannot edit frozen view");
if (getUserAdmin().isReadOnly())
throw new UserDirectoryException("User directory is read-only");
- assert modifiedAttributes == null;
- modifiedAttributes = (Attributes) publishedAttributes.clone();
+ assert getModifiedAttributes() == null;
+ getWc().startEditing(this);
+ // modifiedAttributes = (Attributes) publishedAttributes.clone();
}
- protected synchronized void stopEditing(boolean apply) {
- assert modifiedAttributes != null;
- if (apply)
- publishedAttributes = modifiedAttributes;
- modifiedAttributes = null;
+ public synchronized void publishAttributes(Attributes modifiedAttributes) {
+ publishedAttributes = modifiedAttributes;
}
+ // protected synchronized void stopEditing(boolean apply) {
+ // assert getModifiedAttributes() != null;
+ // if (apply)
+ // publishedAttributes = getModifiedAttributes();
+ // // modifiedAttributes = null;
+ // }
+
public DirectoryUser getPublished() {
return new LdifUser(userAdmin, dn, publishedAttributes, true);
}
throw new IllegalArgumentException("Key " + key + " excluded");
try {
- Attribute attribute = modifiedAttributes.get(key.toString());
+ Attribute attribute = getModifiedAttributes().get(
+ key.toString());
attribute = new BasicAttribute(key.toString());
attribute.add(value);
- Attribute previousAttribute = modifiedAttributes.put(attribute);
+ Attribute previousAttribute = getModifiedAttributes().put(
+ attribute);
if (previousAttribute != null)
return previousAttribute.get();
else
throw new IllegalArgumentException("Key " + key + " excluded");
try {
- Attribute attr = modifiedAttributes.remove(key.toString());
+ Attribute attr = getModifiedAttributes().remove(key.toString());
if (attr != null)
return attr.get();
else
import javax.naming.InvalidNameException;
import javax.naming.NamingEnumeration;
import javax.naming.directory.Attributes;
-import javax.naming.directory.BasicAttributes;
import javax.naming.ldap.LdapName;
-import javax.naming.ldap.Rdn;
-import javax.transaction.xa.XAException;
-import javax.transaction.xa.XAResource;
-import javax.transaction.xa.Xid;
import org.apache.commons.io.IOUtils;
import org.osgi.framework.Filter;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.service.useradmin.Authorization;
import org.osgi.service.useradmin.Role;
import org.osgi.service.useradmin.User;
/** User admin implementation using LDIF file(s) as backend. */
public class LdifUserAdmin extends AbstractUserDirectory {
- SortedMap<LdapName, LdifUser> users = new TreeMap<LdapName, LdifUser>();
- SortedMap<LdapName, LdifGroup> groups = new TreeMap<LdapName, LdifGroup>();
+ SortedMap<LdapName, DirectoryUser> users = new TreeMap<LdapName, DirectoryUser>();
+ SortedMap<LdapName, DirectoryGroup> groups = new TreeMap<LdapName, DirectoryGroup>();
- private Map<String, Map<String, LdifUser>> userIndexes = new LinkedHashMap<String, Map<String, LdifUser>>();
+ private Map<String, Map<String, DirectoryUser>> userIndexes = new LinkedHashMap<String, Map<String, DirectoryUser>>();
// private Map<LdapName, List<LdifGroup>> directMemberOf = new
// TreeMap<LdapName, List<LdifGroup>>();
- private XaRes xaRes = new XaRes();
public LdifUserAdmin(String uri) {
this(uri, readOnlyDefault(uri));
protected void load(InputStream in) {
try {
+ users.clear();
+ groups.clear();
+
LdifParser ldifParser = new LdifParser();
SortedMap<LdapName, Attributes> allEntries = ldifParser.read(in);
for (LdapName key : allEntries.keySet()) {
// indexes
for (String attr : getIndexedUserProperties())
- userIndexes.put(attr, new TreeMap<String, LdifUser>());
+ userIndexes.put(attr, new TreeMap<String, DirectoryUser>());
- for (LdifUser user : users.values()) {
+ for (DirectoryUser user : users.values()) {
Dictionary<String, Object> properties = user.getProperties();
for (String attr : getIndexedUserProperties()) {
Object value = properties.get(attr);
if (value != null) {
- LdifUser otherUser = userIndexes.get(attr).put(
+ DirectoryUser otherUser = userIndexes.get(attr).put(
value.toString(), user);
if (otherUser != null)
throw new UserDirectoryException("User " + user
groups = null;
}
- @Override
- public Role getRole(String name) {
- LdapName key;
- try {
- key = new LdapName(name);
- } catch (InvalidNameException e) {
- // TODO implements default base DN
- throw new IllegalArgumentException("Badly formatted role name: "
- + name, e);
- }
-
+ protected DirectoryUser daoGetRole(LdapName key) {
if (groups.containsKey(key))
return groups.get(key);
if (users.containsKey(key))
return null;
}
- @Override
- public Authorization getAuthorization(User user) {
- return new LdifAuthorization((LdifUser) user,
- getAllRoles((LdifUser) user));
+ protected Boolean daoHasRole(LdapName dn) {
+ return users.containsKey(dn) || groups.containsKey(dn);
}
- @Override
- public Role createRole(String name, int type) {
- try {
- LdapName dn = new LdapName(name);
- if (users.containsKey(dn) || groups.containsKey(dn))
- throw new UserDirectoryException("Already a role " + name);
-
- BasicAttributes attrs = new BasicAttributes();
- attrs.put("dn", dn.toString());
- Rdn nameRdn = dn.getRdn(dn.size() - 1);
- // TODO deal with multiple attr RDN
- attrs.put(nameRdn.getType(), nameRdn.getValue());
- LdifUser newRole;
- if (type == Role.USER) {
- newRole = new LdifUser(this, dn, attrs);
- users.put(dn, newRole);
- } else if (type == Role.GROUP) {
- newRole = new LdifGroup(this, dn, attrs);
- groups.put(dn, (LdifGroup) newRole);
- } else
- throw new UserDirectoryException("Unsupported type " + type);
- return newRole;
- } catch (InvalidNameException e) {
- throw new UserDirectoryException("Cannot create role " + name, e);
- }
- }
-
- @Override
- public boolean removeRole(String name) {
- try {
- LdapName dn = new LdapName(name);
- LdifUser role = null;
- if (users.containsKey(dn))
- role = users.remove(dn);
- else if (groups.containsKey(dn))
- role = groups.remove(dn);
- else
- throw new UserDirectoryException("There is no role " + name);
- if (role == null)
- return false;
- for (LdifGroup group : getDirectGroups(role)) {
- // group.directMembers.remove(role);
- group.getAttributes().get(getMemberAttributeId())
- .remove(dn.toString());
- }
- if (role instanceof LdifGroup) {
- LdifGroup group = (LdifGroup) role;
- // for (Role user : group.directMembers) {
- // if (user instanceof LdifUser)
- // directMemberOf.get(((LdifUser) user).getDn()).remove(
- // group);
- // }
- }
- return true;
- } catch (InvalidNameException e) {
- throw new UserDirectoryException("Cannot create role " + name, e);
- }
- }
+ // @Override
+ // public boolean removeRole(String name) {
+ // LdapName dn = toDn(name);
+ // LdifUser role = null;
+ // if (users.containsKey(dn))
+ // role = users.remove(dn);
+ // else if (groups.containsKey(dn))
+ // role = groups.remove(dn);
+ // else
+ // throw new UserDirectoryException("There is no role " + name);
+ // if (role == null)
+ // return false;
+ // for (LdifGroup group : getDirectGroups(role)) {
+ // group.getAttributes().get(getMemberAttributeId())
+ // .remove(dn.toString());
+ // }
+ // return true;
+ // }
- @Override
- public Role[] getRoles(String filter) throws InvalidSyntaxException {
- ArrayList<Role> res = new ArrayList<Role>();
- if (filter == null) {
+ protected List<DirectoryUser> doGetRoles(Filter f) {
+ ArrayList<DirectoryUser> res = new ArrayList<DirectoryUser>();
+ if (f == null) {
res.addAll(users.values());
res.addAll(groups.values());
} else {
- Filter f = FrameworkUtil.createFilter(filter);
- for (LdifUser user : users.values())
+ // Filter f = FrameworkUtil.createFilter(filter);
+ for (DirectoryUser user : users.values())
if (f.match(user.getProperties()))
res.add(user);
- for (LdifUser group : groups.values())
+ for (DirectoryUser group : groups.values())
if (f.match(group.getProperties()))
res.add(group);
}
- return res.toArray(new Role[res.size()]);
+ return res;
}
- @Override
- public User getUser(String key, String value) {
- // TODO check value null or empty
- if (key != null) {
- if (!userIndexes.containsKey(key))
- return null;
- return userIndexes.get(key).get(value);
- }
-
- // Try all indexes
- List<LdifUser> collectedUsers = new ArrayList<LdifUser>(
- getIndexedUserProperties().size());
- // try dn
- LdifUser user = null;
- try {
- user = (LdifUser) getRole(value);
- if (user != null)
- collectedUsers.add(user);
- } catch (Exception e) {
- // silent
- }
- for (String attr : userIndexes.keySet()) {
- user = userIndexes.get(attr).get(value);
- if (user != null)
- collectedUsers.add(user);
- }
-
- if (collectedUsers.size() == 1)
- return collectedUsers.get(0);
- return null;
- // throw new UnsupportedOperationException();
+ protected void doGetUser(String key, String value,
+ List<DirectoryUser> collectedUsers) {
+ assert key != null;
+ DirectoryUser user = userIndexes.get(key).get(value);
+ if (user != null)
+ collectedUsers.add(user);
}
- // protected void loadMembers(LdifGroup group) {
- // group.directMembers = new ArrayList<Role>();
- // for (LdapName ldapName : group.getMemberNames()) {
- // LdifUser role = null;
- // if (groups.containsKey(ldapName))
- // role = groups.get(ldapName);
- // else if (users.containsKey(ldapName))
- // role = users.get(ldapName);
- // else {
- // if (getExternalRoles() != null)
- // role = (LdifUser) getExternalRoles().getRole(
- // ldapName.toString());
- // if (role == null)
- // throw new ArgeoUserAdminException("No role found for "
- // + ldapName);
- // }
- // // role.directMemberOf.add(group);
- // // if (!directMemberOf.containsKey(role.getDn()))
- // // directMemberOf.put(role.getDn(), new ArrayList<LdifGroup>());
- // // directMemberOf.get(role.getDn()).add(group);
- // group.directMembers.add(role);
- // }
- // }
-
@Override
- protected List<LdifGroup> getDirectGroups(User user) {
+ protected List<DirectoryGroup> getDirectGroups(User user) {
LdapName dn;
if (user instanceof LdifUser)
dn = ((LdifUser) user).getDn();
+ user.getName(), e);
}
- List<LdifGroup> directGroups = new ArrayList<LdifGroup>();
+ List<DirectoryGroup> directGroups = new ArrayList<DirectoryGroup>();
for (LdapName name : groups.keySet()) {
- LdifGroup group = groups.get(name);
+ DirectoryGroup group = groups.get(name);
if (group.getMemberNames().contains(dn))
directGroups.add(group);
}
return directGroups;
- // if (directMemberOf.containsKey(dn))
- // return Collections.unmodifiableList(directMemberOf.get(dn));
- // else
- // return Collections.EMPTY_LIST;
}
@Override
- public XAResource getXAResource() {
- return xaRes;
- }
-
- private class XaRes implements XAResource {
-
- @Override
- public void commit(Xid xid, boolean onePhase) throws XAException {
- save();
- }
-
- @Override
- public void end(Xid xid, int flags) throws XAException {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void forget(Xid xid) throws XAException {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public int getTransactionTimeout() throws XAException {
- // TODO Auto-generated method stub
- return 0;
- }
-
- @Override
- public boolean isSameRM(XAResource xares) throws XAException {
- // TODO Auto-generated method stub
- return false;
- }
-
- @Override
- public int prepare(Xid xid) throws XAException {
- // TODO Auto-generated method stub
- return 0;
- }
-
- @Override
- public Xid[] recover(int flag) throws XAException {
- // TODO Auto-generated method stub
- return null;
+ protected void prepare(WorkingCopy wc) {
+ // delete
+ for (LdapName dn : wc.getDeletedUsers().keySet()) {
+ if (users.containsKey(dn))
+ users.remove(dn);
+ else if (groups.containsKey(dn))
+ groups.remove(dn);
+ else
+ throw new UserDirectoryException("User to delete no found "
+ + dn);
}
-
- @Override
- public void rollback(Xid xid) throws XAException {
- // TODO Auto-generated method stub
-
+ // add
+ for (LdapName dn : wc.getNewUsers().keySet()) {
+ DirectoryUser user = wc.getNewUsers().get(dn);
+ if (Role.USER == user.getType())
+ users.put(dn, user);
+ else if (Role.GROUP == user.getType())
+ groups.put(dn, (DirectoryGroup) user);
+ else
+ throw new UserDirectoryException("Unsupported role type "
+ + user.getType() + " for new user " + dn);
}
-
- @Override
- public boolean setTransactionTimeout(int seconds) throws XAException {
- // TODO Auto-generated method stub
- return false;
+ // modify
+ for (LdapName dn : wc.getModifiedUsers().keySet()) {
+ Attributes modifiedAttrs = wc.getModifiedUsers().get(dn);
+ DirectoryUser user;
+ if (users.containsKey(dn))
+ user = users.get(dn);
+ else if (groups.containsKey(dn))
+ user = groups.get(dn);
+ else
+ throw new UserDirectoryException("User to modify no found "
+ + dn);
+ user.publishAttributes(modifiedAttrs);
}
+ }
- @Override
- public void start(Xid xid, int flags) throws XAException {
- // TODO Auto-generated method stub
-
- }
+ @Override
+ protected void commit(WorkingCopy wc) {
+ save();
+ }
+ @Override
+ protected void rollback(WorkingCopy wc) {
+ init();
}
}