import javax.naming.directory.BasicAttributes;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
+import javax.transaction.xa.XAResource;
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;
import org.osgi.service.useradmin.UserAdmin;
/** Base class for a {@link UserDirectory}. */
-abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
+abstract class AbstractUserDirectory
+ implements UserAdmin, UserDirectory, WorkingCopyProcessor<DirectoryUserWorkingCopy>, XAResourceProvider {
static final String SHARED_STATE_USERNAME = "javax.security.auth.login.name";
static final String SHARED_STATE_PASSWORD = "javax.security.auth.login.password";
// Transaction
// private TransactionManager transactionManager;
private WorkControl transactionControl;
- private WcXaResource xaResource = new WcXaResource(this);
+ private WorkingCopyXaResource<DirectoryUserWorkingCopy> xaResource = new WorkingCopyXaResource<>(this);
private String forcedPassword;
return xaResource.wc() != null;
}
- protected UserDirectoryWorkingCopy getWorkingCopy() {
- UserDirectoryWorkingCopy wc = xaResource.wc();
+ protected DirectoryUserWorkingCopy getWorkingCopy() {
+ DirectoryUserWorkingCopy wc = xaResource.wc();
if (wc == null)
return null;
return wc;
}
protected DirectoryUser doGetRole(LdapName dn) {
- UserDirectoryWorkingCopy wc = getWorkingCopy();
+ DirectoryUserWorkingCopy wc = getWorkingCopy();
DirectoryUser user;
try {
user = daoGetRole(dn);
user = null;
}
if (wc != null) {
- if (user == null && wc.getNewUsers().containsKey(dn))
- user = wc.getNewUsers().get(dn);
- else if (wc.getDeletedUsers().containsKey(dn))
+ if (user == null && wc.getNewData().containsKey(dn))
+ user = wc.getNewData().get(dn);
+ else if (wc.getDeletedData().containsKey(dn))
user = null;
}
return user;
}
List<DirectoryUser> getRoles(LdapName searchBase, String filter, boolean deep) throws InvalidSyntaxException {
- UserDirectoryWorkingCopy wc = getWorkingCopy();
+ DirectoryUserWorkingCopy wc = getWorkingCopy();
Filter f = filter != null ? FrameworkUtil.createFilter(filter) : null;
List<DirectoryUser> res = doGetRoles(searchBase, f, deep);
if (wc != null) {
for (Iterator<DirectoryUser> it = res.iterator(); it.hasNext();) {
DirectoryUser user = it.next();
LdapName dn = user.getDn();
- if (wc.getDeletedUsers().containsKey(dn))
+ if (wc.getDeletedData().containsKey(dn))
it.remove();
}
- for (DirectoryUser user : wc.getNewUsers().values()) {
+ for (DirectoryUser user : wc.getNewData().values()) {
if (f == null || f.match(user.getProperties()))
res.add(user);
}
@Override
public Role createRole(String name, int type) {
checkEdit();
- UserDirectoryWorkingCopy wc = getWorkingCopy();
+ DirectoryUserWorkingCopy wc = getWorkingCopy();
LdapName dn = toLdapName(name);
- if ((daoHasRole(dn) && !wc.getDeletedUsers().containsKey(dn)) || wc.getNewUsers().containsKey(dn))
+ if ((daoHasRole(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());
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);
+ if (wc.getDeletedData().containsKey(dn)) {
+ wc.getDeletedData().remove(dn);
+ wc.getModifiedData().put(dn, attrs);
return getRole(name);
} else {
- wc.getModifiedUsers().put(dn, attrs);
+ wc.getModifiedData().put(dn, attrs);
DirectoryUser newRole = newRole(dn, type, attrs);
- wc.getNewUsers().put(dn, newRole);
+ wc.getNewData().put(dn, newRole);
return newRole;
}
}
@Override
public boolean removeRole(String name) {
checkEdit();
- UserDirectoryWorkingCopy wc = getWorkingCopy();
+ DirectoryUserWorkingCopy wc = getWorkingCopy();
LdapName dn = toLdapName(name);
boolean actuallyDeleted;
- if (daoHasRole(dn) || wc.getNewUsers().containsKey(dn)) {
+ if (daoHasRole(dn) || wc.getNewData().containsKey(dn)) {
DirectoryUser user = (DirectoryUser) getRole(name);
- wc.getDeletedUsers().put(dn, user);
+ wc.getDeletedData().put(dn, user);
actuallyDeleted = true;
} else {// just removing from groups (e.g. system roles)
actuallyDeleted = false;
return actuallyDeleted;
}
- // TRANSACTION
- protected void prepare(UserDirectoryWorkingCopy wc) {
-
- }
-
- protected void commit(UserDirectoryWorkingCopy wc) {
-
- }
-
- protected void rollback(UserDirectoryWorkingCopy wc) {
-
+ /*
+ * TRANSACTION
+ */
+ @Override
+ public DirectoryUserWorkingCopy newWorkingCopy() {
+ return new DirectoryUserWorkingCopy();
}
/*
this.transactionControl = transactionControl;
}
- public WcXaResource getXaResource() {
+ public XAResource getXaResource() {
return xaResource;
}
--- /dev/null
+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<DirectoryUser, Attributes, LdapName> {
+ @Override
+ protected LdapName getId(DirectoryUser user) {
+ return user.getDn();
+ }
+
+ @Override
+ protected Attributes cloneAttributes(DirectoryUser user) {
+ return (Attributes) user.getAttributes().clone();
+ }
+}
+++ /dev/null
-package org.argeo.osgi.useradmin;
-
-public interface FunctionalGroup {
-
-}
}
}
- synchronized void prepareChanges(UserDirectoryWorkingCopy wc) throws NamingException {
+ synchronized void prepareChanges(DirectoryUserWorkingCopy wc) throws NamingException {
// make sure connection will work
reconnect();
// delete
- for (LdapName dn : wc.getDeletedUsers().keySet()) {
+ for (LdapName dn : wc.getDeletedData().keySet()) {
if (!entryExists(dn))
throw new IllegalStateException("User to delete no found " + dn);
}
// add
- for (LdapName dn : wc.getNewUsers().keySet()) {
+ for (LdapName dn : wc.getNewData().keySet()) {
if (entryExists(dn))
throw new IllegalStateException("User to create found " + dn);
}
// modify
- for (LdapName dn : wc.getModifiedUsers().keySet()) {
- if (!wc.getNewUsers().containsKey(dn) && !entryExists(dn))
+ for (LdapName dn : wc.getModifiedData().keySet()) {
+ if (!wc.getNewData().containsKey(dn) && !entryExists(dn))
throw new IllegalStateException("User to modify not found " + dn);
}
}
}
- synchronized void commitChanges(UserDirectoryWorkingCopy wc) throws NamingException {
+ synchronized void commitChanges(DirectoryUserWorkingCopy wc) throws NamingException {
// delete
- for (LdapName dn : wc.getDeletedUsers().keySet()) {
+ for (LdapName dn : wc.getDeletedData().keySet()) {
getLdapContext().destroySubcontext(dn);
}
// add
- for (LdapName dn : wc.getNewUsers().keySet()) {
- DirectoryUser user = wc.getNewUsers().get(dn);
+ for (LdapName dn : wc.getNewData().keySet()) {
+ DirectoryUser user = wc.getNewData().get(dn);
getLdapContext().createSubcontext(dn, user.getAttributes());
}
// modify
- for (LdapName dn : wc.getModifiedUsers().keySet()) {
- Attributes modifiedAttrs = wc.getModifiedUsers().get(dn);
+ for (LdapName dn : wc.getModifiedData().keySet()) {
+ Attributes modifiedAttrs = wc.getModifiedData().get(dn);
getLdapContext().modifyAttributes(dn, DirContext.REPLACE_ATTRIBUTE, modifiedAttrs);
}
}
}
@Override
- protected void prepare(UserDirectoryWorkingCopy wc) {
+ public void prepare(DirectoryUserWorkingCopy wc) {
try {
ldapConnection.prepareChanges(wc);
} catch (NamingException e) {
}
@Override
- protected void commit(UserDirectoryWorkingCopy wc) {
+ public void commit(DirectoryUserWorkingCopy wc) {
try {
ldapConnection.commitChanges(wc);
} catch (NamingException e) {
}
@Override
- protected void rollback(UserDirectoryWorkingCopy wc) {
+ public void rollback(DirectoryUserWorkingCopy wc) {
// prepare not impacting
}
import javax.naming.directory.Attributes;
import javax.naming.ldap.LdapName;
+import org.argeo.util.directory.FunctionalGroup;
+import org.argeo.util.directory.Organization;
+import org.argeo.util.directory.SystemPermissions;
import org.osgi.service.useradmin.Role;
/** Directory group implementation */
import javax.naming.directory.BasicAttribute;
import javax.naming.ldap.LdapName;
+import org.argeo.util.directory.Person;
import org.argeo.util.naming.LdapAttrs;
import org.argeo.util.naming.LdapObjs;
import org.argeo.util.naming.SharedSecret;
/** Should only be called from working copy thread. */
private synchronized Attributes getModifiedAttributes() {
assert getWc() != null;
- return getWc().getAttributes(getDn());
+ return getWc().getModifiedData().get(getDn());
}
protected synchronized boolean isEditing() {
return getWc() != null && getModifiedAttributes() != null;
}
- private synchronized UserDirectoryWorkingCopy getWc() {
+ private synchronized DirectoryUserWorkingCopy getWc() {
return userAdmin.getWorkingCopy();
}
}
@Override
- protected void prepare(UserDirectoryWorkingCopy wc) {
+ public void prepare(DirectoryUserWorkingCopy wc) {
// delete
- for (LdapName dn : wc.getDeletedUsers().keySet()) {
+ for (LdapName dn : wc.getDeletedData().keySet()) {
if (users.containsKey(dn))
users.remove(dn);
else if (groups.containsKey(dn))
throw new IllegalStateException("User to delete not found " + dn);
}
// add
- for (LdapName dn : wc.getNewUsers().keySet()) {
- DirectoryUser user = wc.getNewUsers().get(dn);
+ for (LdapName dn : wc.getNewData().keySet()) {
+ DirectoryUser user = 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())
throw new IllegalStateException("Unsupported role type " + user.getType() + " for new user " + dn);
}
// modify
- for (LdapName dn : wc.getModifiedUsers().keySet()) {
- Attributes modifiedAttrs = wc.getModifiedUsers().get(dn);
+ for (LdapName dn : wc.getModifiedData().keySet()) {
+ Attributes modifiedAttrs = wc.getModifiedData().get(dn);
DirectoryUser user;
if (users.containsKey(dn))
user = users.get(dn);
}
@Override
- protected void commit(UserDirectoryWorkingCopy wc) {
+ public void commit(DirectoryUserWorkingCopy wc) {
save();
}
@Override
- protected void rollback(UserDirectoryWorkingCopy wc) {
+ public void rollback(DirectoryUserWorkingCopy wc) {
init();
}
+++ /dev/null
-package org.argeo.osgi.useradmin;
-
-public interface Organization {
-
-}
return new ArrayList<>();
}
+ public void prepare(DirectoryUserWorkingCopy wc) {
+
+ }
+
+ public void commit(DirectoryUserWorkingCopy wc) {
+
+ }
+
+ public void rollback(DirectoryUserWorkingCopy wc) {
+
+ }
+
+
}
+++ /dev/null
-package org.argeo.osgi.useradmin;
-
-public interface Person {
-
-}
+++ /dev/null
-package org.argeo.osgi.useradmin;
-
-public interface SystemPermissions {
-
-}
+++ /dev/null
-package org.argeo.osgi.useradmin;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.naming.directory.Attributes;
-import javax.naming.ldap.LdapName;
-import javax.transaction.xa.XAResource;
-
-/** {@link XAResource} for a user directory being edited. */
-class UserDirectoryWorkingCopy {
- // private final static Log log = LogFactory
- // .getLog(UserDirectoryWorkingCopy.class);
-
- 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>();
-
- void cleanUp() {
- // clean collections
- newUsers.clear();
- newUsers = null;
- modifiedUsers.clear();
- modifiedUsers = null;
- deletedUsers.clear();
- deletedUsers = 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 IllegalStateException("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;
- }
-}
class WcXaResource implements XAResource {
private final AbstractUserDirectory userDirectory;
- private Map<Xid, UserDirectoryWorkingCopy> workingCopies = new HashMap<Xid, UserDirectoryWorkingCopy>();
+ private Map<Xid, DirectoryUserWorkingCopy> workingCopies = new HashMap<Xid, DirectoryUserWorkingCopy>();
private Xid editingXid = null;
private int transactionTimeout = 0;
public synchronized void start(Xid xid, int flags) throws XAException {
if (editingXid != null)
throw new IllegalStateException("Already editing " + editingXid);
- UserDirectoryWorkingCopy wc = workingCopies.put(xid, new UserDirectoryWorkingCopy());
+ DirectoryUserWorkingCopy wc = workingCopies.put(xid, new DirectoryUserWorkingCopy());
if (wc != null)
throw new IllegalStateException("There is already a working copy for " + xid);
this.editingXid = xid;
checkXid(xid);
}
- private UserDirectoryWorkingCopy wc(Xid xid) {
+ private DirectoryUserWorkingCopy wc(Xid xid) {
return workingCopies.get(xid);
}
- synchronized UserDirectoryWorkingCopy wc() {
+ synchronized DirectoryUserWorkingCopy wc() {
if (editingXid == null)
return null;
- UserDirectoryWorkingCopy wc = workingCopies.get(editingXid);
+ DirectoryUserWorkingCopy wc = workingCopies.get(editingXid);
if (wc == null)
throw new IllegalStateException("No working copy found for " + editingXid);
return wc;
@Override
public int prepare(Xid xid) throws XAException {
checkXid(xid);
- UserDirectoryWorkingCopy wc = wc(xid);
+ DirectoryUserWorkingCopy wc = wc(xid);
if (wc.noModifications())
return XA_RDONLY;
try {
public void commit(Xid xid, boolean onePhase) throws XAException {
try {
checkXid(xid);
- UserDirectoryWorkingCopy wc = wc(xid);
+ DirectoryUserWorkingCopy wc = wc(xid);
if (wc.noModifications())
return;
if (onePhase)
--- /dev/null
+package org.argeo.util.directory;
+
+public interface FunctionalGroup {
+
+}
--- /dev/null
+package org.argeo.util.directory;
+
+public interface Organization {
+
+}
--- /dev/null
+package org.argeo.util.directory;
+
+public interface Person {
+
+}
--- /dev/null
+package org.argeo.util.directory;
+
+public interface SystemPermissions {
+
+}
--- /dev/null
+package org.argeo.util.transaction;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public abstract class AbstractWorkingCopy<DATA, ATTR, ID> implements WorkingCopy<DATA, ATTR, ID> {
+ private Map<ID, DATA> newData = new HashMap<ID, DATA>();
+ private Map<ID, ATTR> modifiedData = new HashMap<ID, ATTR>();
+ private Map<ID, DATA> deletedData = new HashMap<ID, DATA>();
+
+ protected abstract ID getId(DATA data);
+
+ protected abstract ATTR cloneAttributes(DATA data);
+
+ public void cleanUp() {
+ // clean collections
+ newData.clear();
+ newData = null;
+ modifiedData.clear();
+ modifiedData = null;
+ deletedData.clear();
+ deletedData = null;
+ }
+
+ public boolean noModifications() {
+ return newData.size() == 0 && modifiedData.size() == 0 && deletedData.size() == 0;
+ }
+
+ public void startEditing(DATA user) {
+ ID id = getId(user);
+ if (modifiedData.containsKey(id))
+ throw new IllegalStateException("Already editing " + id);
+ modifiedData.put(id, cloneAttributes(user));
+ }
+
+ public Map<ID, DATA> getNewData() {
+ return newData;
+ }
+
+ public Map<ID, DATA> getDeletedData() {
+ return deletedData;
+ }
+
+ public Map<ID, ATTR> getModifiedData() {
+ return modifiedData;
+ }
+
+}
--- /dev/null
+package org.argeo.util.transaction;
+
+import java.util.Map;
+
+public interface WorkingCopy<DATA, ATTR, ID> {
+ void startEditing(DATA user);
+
+ boolean noModifications();
+
+ void cleanUp();
+
+ Map<ID, DATA> getNewData();
+
+ Map<ID, DATA> getDeletedData();
+
+ Map<ID, ATTR> getModifiedData();
+
+}
--- /dev/null
+package org.argeo.util.transaction;
+
+public interface WorkingCopyProcessor<WC extends WorkingCopy<?, ?, ?>> {
+ void prepare(WC wc);
+
+ void commit(WC wc);
+
+ void rollback(WC wc);
+
+ WC newWorkingCopy();
+}
--- /dev/null
+package org.argeo.util.transaction;
+
+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. */
+public class WorkingCopyXaResource<WC extends WorkingCopy<?, ?, ?>> implements XAResource {
+ private final WorkingCopyProcessor<WC> processor;
+
+ private Map<Xid, WC> workingCopies = new HashMap<Xid, WC>();
+ private Xid editingXid = null;
+ private int transactionTimeout = 0;
+
+ public WorkingCopyXaResource(WorkingCopyProcessor<WC> processor) {
+ this.processor = processor;
+ }
+
+ @Override
+ public synchronized void start(Xid xid, int flags) throws XAException {
+ if (editingXid != null)
+ throw new IllegalStateException("Already editing " + editingXid);
+ WC wc = workingCopies.put(xid, processor.newWorkingCopy());
+ 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 WC wc(Xid xid) {
+ return workingCopies.get(xid);
+ }
+
+ public synchronized WC wc() {
+ if (editingXid == null)
+ return null;
+ WC 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);
+ WC wc = wc(xid);
+ if (wc.noModifications())
+ return XA_RDONLY;
+ try {
+ processor.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);
+ WC wc = wc(xid);
+ if (wc.noModifications())
+ return;
+ if (onePhase)
+ processor.prepare(wc);
+ processor.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);
+ processor.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);
+ }
+
+}
--- /dev/null
+package org.argeo.util.transaction;
+
+import javax.transaction.xa.XAResource;
+
+public interface XAResourceProvider {
+ XAResource getXaResource();
+}