Set all required objectClass attributes when adding an entry to LDAP. Also consistent...
[pithos] / src / gr / ebs / gss / server / ejb / ExternalAPIBean.java
index 4adc790..0cb2971 100644 (file)
@@ -31,6 +31,7 @@ import gr.ebs.gss.server.domain.FileTag;
 import gr.ebs.gss.server.domain.FileUploadStatus;
 import gr.ebs.gss.server.domain.Folder;
 import gr.ebs.gss.server.domain.Group;
+import gr.ebs.gss.server.domain.Invitation;
 import gr.ebs.gss.server.domain.Nonce;
 import gr.ebs.gss.server.domain.Permission;
 import gr.ebs.gss.server.domain.User;
@@ -63,6 +64,7 @@ import java.util.StringTokenizer;
 
 import javax.ejb.EJB;
 import javax.ejb.EJBException;
+import javax.ejb.EJBTransactionRolledbackException;
 import javax.ejb.Stateless;
 import javax.ejb.TransactionAttribute;
 import javax.ejb.TransactionAttributeType;
@@ -74,10 +76,10 @@ import javax.jms.MessageProducer;
 import javax.jms.Queue;
 import javax.jms.QueueConnectionFactory;
 import javax.jms.Session;
-import javax.jws.WebMethod;
 import javax.naming.Context;
 import javax.naming.InitialContext;
 import javax.naming.NamingException;
+import javax.persistence.PersistenceException;
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
@@ -98,12 +100,19 @@ import org.apache.commons.httpclient.methods.StringRequestEntity;
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.hibernate.exception.ConstraintViolationException;
 import org.w3c.dom.DOMException;
 import org.w3c.dom.Document;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 import org.xml.sax.SAXException;
 
+import com.novell.ldap.LDAPAttribute;
+import com.novell.ldap.LDAPAttributeSet;
+import com.novell.ldap.LDAPConnection;
+import com.novell.ldap.LDAPEntry;
+import com.novell.ldap.LDAPException;
+
 /**
  * The concrete implementation of the ExternalAPI interface.
  *
@@ -139,6 +148,20 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
         */
        private static Random random = new Random();
 
+       /**
+        * Mark the folder and all of its parent folders as modified from the specified user.
+        */
+       private void touchParentFolders(Folder folder, User user, Date date) {
+               Folder f = folder;
+               while (f != null) {
+                       AuditInfo ai = f.getAuditInfo();
+                       ai.setModifiedBy(user);
+                       ai.setModificationDate(date);
+                       f.setAuditInfo(ai);
+                       f = f.getParent();
+               }
+       }
+
        @Override
        public FolderDTO getRootFolder(Long userId) throws ObjectNotFoundException {
                if (userId == null)
@@ -147,11 +170,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                return folder.getDTO();
        }
 
-       /*
-        * (non-Javadoc)
-        *
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#getFolder(java.lang.Long)
-        */
+       @Override
        public FolderDTO getFolder(final Long userId, final Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
@@ -318,6 +337,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                auditInfo.setModifiedBy(creator);
                auditInfo.setModificationDate(now);
                folder.setAuditInfo(auditInfo);
+               touchParentFolders(folder, auditInfo.getModifiedBy(), auditInfo.getModificationDate());
 
                if (parent != null)
                        for (Permission p : parent.getPermissions()) {
@@ -368,6 +388,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                removeSubfolderFiles(folder);
                parent.removeSubfolder(folder);
                dao.delete(folder);
+               touchParentFolders(parent, user, new Date());
        }
 
        /**
@@ -408,30 +429,40 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
        }
 
        @Override
-       public FolderDTO modifyFolder(Long userId, Long folderId, String folderName)
-                       throws InsufficientPermissionsException, ObjectNotFoundException, DuplicateNameException {
+       public FolderDTO updateFolder(Long userId, Long folderId, String folderName,
+                               Set<PermissionDTO> permissions)
+                       throws InsufficientPermissionsException, ObjectNotFoundException,
+                       DuplicateNameException {
 
                // Validate.
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
                if (folderId == null)
                        throw new ObjectNotFoundException("No folder specified");
-               if (StringUtils.isEmpty(folderName))
-                       throw new ObjectNotFoundException("New folder name is empty");
 
                Folder folder = dao.getEntityById(Folder.class, folderId);
                User user = dao.getEntityById(User.class, userId);
-               if (!folder.hasWritePermission(user))
+               if (folderName != null && !folder.hasWritePermission(user))
+                       throw new InsufficientPermissionsException("You don't have the necessary permissions");
+               if(permissions != null && !permissions.isEmpty() && !folder.hasModifyACLPermission(user))
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
 
                Folder parent = folder.getParent();
-               if (parent != null)
-                       if (!folder.getName().equals(folderName) && dao.existsFolderOrFile(parent.getId(), folderName))
-                               throw new DuplicateNameException("A folder or file with the name '" + folderName + "' already exists at this level");
+               if (folderName != null) {
+                       if (parent != null)
+                               if (!folder.getName().equals(folderName) && dao.existsFolderOrFile(parent.getId(), folderName))
+                                       throw new DuplicateNameException("A folder or file with the name '" + folderName + "' already exists at this level");
+
+                       // Do the actual modification.
+                       folder.setName(folderName);
+               }
+               if (permissions != null)
+                       setFolderPermissions(user, folder, permissions);
 
-               // Do the actual modification.
-               folder.setName(folderName);
+               folder.getAuditInfo().setModificationDate(new Date());
+               folder.getAuditInfo().setModifiedBy(user);
                dao.update(folder);
+               touchParentFolders(folder, user, new Date());
                return folder.getDTO();
        }
 
@@ -599,6 +630,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                for (final FileBody body : file.getBodies())
                        deleteActualFile(body.getStoredFilePath());
                dao.delete(file);
+               touchParentFolders(parent, user, new Date());
                indexFile(fileId, true);
        }
 
@@ -610,12 +642,6 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                        logger.error("Could not delete file " + filePath);
        }
 
-       /*
-        * (non-Javadoc)
-        *
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#createTag(java.lang.Long,
-        *      java.lang.Long, java.lang.String)
-        */
        public void createTag(final Long userId, final Long fileHeaderId, final String tag) throws ObjectNotFoundException {
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
@@ -626,34 +652,55 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
 
                final User user = dao.getEntityById(User.class, userId);
                final FileHeader fh = dao.getEntityById(FileHeader.class, fileHeaderId);
+               final Folder parent = fh.getFolder();
+               if (parent == null)
+                       throw new ObjectNotFoundException("The specified file has no parent folder");
                user.addTag(fh, tag);
+               touchParentFolders(parent, user, new Date());
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#getUserTags(java.lang.Long)
-        */
-       @WebMethod(operationName = "getUserTags")
        public Set<String> getUserTags(final Long userId) throws ObjectNotFoundException {
                return dao.getUserTags(userId);
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#updateFile(java.lang.Long, java.lang.Long, java.lang.String, java.util.Set)
-        */
-       public void updateFile(Long userId, Long fileId, String name, String tagSet) throws ObjectNotFoundException, InsufficientPermissionsException {
+       public void updateFile(Long userId, Long fileId, String name,
+                               String tagSet, Date modificationDate, Boolean versioned,
+                               Boolean readForAll,     Set<PermissionDTO> permissions)
+                       throws DuplicateNameException, ObjectNotFoundException, InsufficientPermissionsException {
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
                if (fileId == null)
                        throw new ObjectNotFoundException("No file specified");
                FileHeader file = dao.getEntityById(FileHeader.class, fileId);
-               User user = dao.getEntityById(User.class, userId);
-               if (!file.hasWritePermission(user))
-                       throw new InsufficientPermissionsException("User " + user.getId() + " cannot update file " + file.getName() + "(" + file.getId() + ")");
+               final Folder parent = file.getFolder();
+               if (parent == null)
+                       throw new ObjectNotFoundException("The specified file has no parent folder");
 
-               if (name != null)
+               User user = dao.getEntityById(User.class, userId);
+               // Check permissions for modifying the file metadata.
+               if ((name != null || tagSet != null || modificationDate != null || versioned != null) && !file.hasWritePermission(user))
+                       throw new InsufficientPermissionsException("User " + user.getId() +     " cannot update file " + file.getName() + "(" + file.getId() + ")");
+               // Check permissions for making file public.
+               if (readForAll != null && !user.equals(file.getOwner()))
+                               throw new InsufficientPermissionsException("Only the owner can make a file public or not public");
+               // Check permissions for modifying the ACL.
+               if(permissions != null && !permissions.isEmpty() &&     !file.hasModifyACLPermission(user))
+                       throw new InsufficientPermissionsException("User " + user.getId() +     " cannot update the permissions on file " +     file.getName() + "(" + file.getId() + ")");
+
+               if (name != null) {
+                       // Do plain check for file already exists.
+                       // Extreme concurrency case should be caught by constraint violation later.
+                       if (dao.existsFolderOrFile(parent.getId(), name)) throw new DuplicateNameException("A file or folder with the name '" + name + "' already exists");
                        file.setName(name);
-               List<FileTag> tags = file.getFileTags();
+               }
 
+               if (modificationDate != null)
+                       file.getAuditInfo().setModificationDate(modificationDate);
+               else
+                       file.getAuditInfo().setModificationDate(new Date());
+               file.getAuditInfo().setModifiedBy(user);
+
+               List<FileTag> tags = file.getFileTags();
                if (tagSet != null) {
                        Iterator<FileTag> i = tags.iterator();
                        while (i.hasNext()) {
@@ -668,6 +715,32 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                        while (st.hasMoreTokens())
                                new FileTag(user, file, st.nextToken().trim());
                }
+               if (versioned != null && !file.isVersioned() == versioned) {
+                       if (file.isVersioned())
+                               removeOldVersions(userId, fileId);
+                       file.setVersioned(versioned);
+               }
+               if (readForAll != null && user.equals(file.getOwner()))
+                       file.setReadForAll(readForAll);
+               if (permissions != null && !permissions.isEmpty())
+                       setFilePermissions(file, permissions);
+
+               /*
+                * Force constraint violation to manifest itself here.
+                * This should cover extreme concurrency cases that the simple check
+                * above hasn't caught.
+                */
+               try {
+                       dao.flush();
+               }
+               catch (EJBTransactionRolledbackException e) {
+                       Throwable cause = e.getCause();
+                       if (cause instanceof PersistenceException && cause.getCause() instanceof ConstraintViolationException)
+                               throw new DuplicateNameException("A file or folder with the name '" + name + "' already exists");
+                       throw e;
+               }
+
+               touchParentFolders(parent, user, new Date());
 
                // Re-index the file if it was modified.
                if (name != null || tagSet != null)
@@ -1083,6 +1156,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
 
                file.setDeleted(true);
                dao.update(file);
+               touchParentFolders(parent, user, new Date());
        }
 
        @Override
@@ -1115,6 +1189,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                        throw new ObjectNotFoundException("No destination file name specified");
 
                FileHeader file = dao.getEntityById(FileHeader.class, fileId);
+               Folder source = file.getFolder();
                Folder destination = dao.getEntityById(Folder.class, destId);
 
                User owner = dao.getEntityById(User.class, userId);
@@ -1148,10 +1223,12 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                }
                // move the file to the destination folder
                file.setFolder(destination);
+               touchParentFolders(source, owner, new Date());
+               touchParentFolders(destination, owner, new Date());
        }
 
        @Override
-       public void moveFolderToPath(Long userId, Long ownerId, Long folderId, String dest) throws ObjectNotFoundException, DuplicateNameException, InsufficientPermissionsException, GSSIOException, QuotaExceededException {
+       public void moveFolderToPath(Long userId, Long ownerId, Long folderId, String dest) throws ObjectNotFoundException, InsufficientPermissionsException, QuotaExceededException {
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
                if (ownerId == null)
@@ -1169,16 +1246,62 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
        }
 
        @Override
-       public void moveFolder(Long userId, Long folderId, Long destId, String destName) throws ObjectNotFoundException, DuplicateNameException, InsufficientPermissionsException, GSSIOException, QuotaExceededException {
-               // TODO Simple Move and delete of original folder, in production
-               // scenario we must first check individual files and folders permissions
-               copyFolderStructure(userId, folderId, destId, destName);
-               deleteFolder(userId, folderId);
+       public void moveFolder(Long userId, Long folderId, Long destId, String destName)
+                       throws ObjectNotFoundException, InsufficientPermissionsException,
+                       QuotaExceededException {
+               Folder source = dao.getEntityById(Folder.class, folderId);
+               Folder destination = dao.getEntityById(Folder.class, destId);
+               User user = dao.getEntityById(User.class, userId);
+               User sourceOwner = source.getOwner();
+               User destinationOwner = destination.getOwner();
+               // Do not move trashed folders and contents.
+               if (source.isDeleted())
+                       return;
+               // Check permissions.
+               if (!destination.hasWritePermission(user)
+                               || !source.hasReadPermission(user)
+                               || !source.hasWritePermission(user))
+                       throw new InsufficientPermissionsException("You don't have the " +
+                                       "necessary permissions");
+               // Use the same timestamp for all subsequent modifications to make
+               // changes appear simultaneous.
+               Date now = new Date();
+               // If source and destination are not in the same user's namespace,
+               // change owners and check quota.
+               if (!sourceOwner.equals(destinationOwner)) {
+                       changeOwner(source, destinationOwner, user, now);
+                       if (getQuotaLeft(destinationOwner.getId()) < 0)
+                               throw new QuotaExceededException("Not enough free space " +
+                                               "available in destination folder");
+               }
+               // Perform the move.
+               Folder oldParent = source.getParent();
+               oldParent.removeSubfolder(source);
+               destination.addSubfolder(source);
+               // Mark the former parent and destination trees upwards as modified.
+               touchParentFolders(oldParent, user, now);
+               touchParentFolders(source, user, now);
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#getDeletedFiles(java.lang.Long)
+       /**
+        * Recursively change the owner of the specified folder and all of its
+        * contents to the specified owner. Also mark them all as modified with the
+        * specified modifier and modificationDate.
         */
+       private void changeOwner(Folder folder, User owner, User modifier, Date modificationDate) {
+               for (FileHeader file: folder.getFiles()) {
+                       file.setOwner(owner);
+                       file.getAuditInfo().setModificationDate(modificationDate);
+                       file.getAuditInfo().setModifiedBy(modifier);
+               }
+               for (Folder sub: folder.getSubfolders())
+                       changeOwner(sub, owner, modifier, modificationDate);
+               folder.setOwner(owner);
+               folder.getAuditInfo().setModificationDate(modificationDate);
+               folder.getAuditInfo().setModifiedBy(modifier);
+       }
+
+       @Override
        public List<FileHeaderDTO> getDeletedFiles(Long userId) throws ObjectNotFoundException {
                // Validate.
                if (userId == null)
@@ -1212,6 +1335,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
 
                file.setDeleted(false);
                dao.update(file);
+               touchParentFolders(parent, user, new Date());
        }
 
        @Override
@@ -1226,6 +1350,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
                folder.setDeleted(true);
                dao.update(folder);
+               touchParentFolders(folder, user, new Date());
                for (FileHeader file : folder.getFiles())
                        moveFileToTrash(userId, file.getId());
                for (Folder subFolder : folder.getSubfolders())
@@ -1251,6 +1376,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                for (Folder subFolder : folder.getSubfolders())
                        removeFolderFromTrash(userId, subFolder.getId());
                dao.update(folder);
+               touchParentFolders(folder, user, new Date());
        }
 
        @Override
@@ -1283,7 +1409,8 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
        }
 
        @Override
-       public User createUser(String username, String name, String mail) throws ObjectNotFoundException {
+       public User createUser(String username, String name, String mail,
+                               String idp, String idpid) throws ObjectNotFoundException {
                if (username == null)
                        throw new ObjectNotFoundException("No username specified");
                if (name == null)
@@ -1293,6 +1420,8 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                user.setUsername(username);
                user.setName(name);
                user.setEmail(mail);
+               user.setIdentityProvider(idp);
+               user.setIdentityProviderId(idpid);
                Date now = new Date();
                AuditInfo auditInfo = new AuditInfo();
                auditInfo.setCreationDate(now);
@@ -1319,17 +1448,6 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
        }
 
        @Override
-       public User updateUser(String username, String name, String mail) throws ObjectNotFoundException {
-               if (username == null)
-                       throw new ObjectNotFoundException("No username specified");
-
-               User user = dao.getUser(username);
-               user.setName(name);
-               user.setEmail(mail);
-               return user;
-       }
-
-       @Override
        public User findUser(String username) {
                if (username == null)
                        return null;
@@ -1371,19 +1489,17 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
 
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#setFolderPermissions(java.lang.Long, java.lang.Long, java.util.Set)
+       /**
+        * Set the provided permissions as the new permissions of the specified
+        * folder.
+        *
+        * @param user
+        * @param folder
+        * @param permissions
+        * @throws ObjectNotFoundException
+        * @throws InsufficientPermissionsException
         */
-       @Override
-       public void setFolderPermissions(Long userId, Long folderId, Set<PermissionDTO> permissions) throws ObjectNotFoundException, InsufficientPermissionsException {
-               if (userId == null)
-                       throw new ObjectNotFoundException("No user specified");
-               if (folderId == null)
-                       throw new ObjectNotFoundException("No folder specified");
-               User user = dao.getEntityById(User.class, userId);
-               Folder folder = dao.getEntityById(Folder.class, folderId);
-               if(!folder.hasModifyACLPermission(user))
-                       throw new InsufficientPermissionsException("You don't have the necessary permissions");
+       private void setFolderPermissions(User user, Folder folder, Set<PermissionDTO> permissions) throws ObjectNotFoundException, InsufficientPermissionsException {
                // Delete previous entries
                for (Permission perm: folder.getPermissions())
                        dao.delete(perm);
@@ -1396,10 +1512,14 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                        folder.addPermission(getPermission(dto));
                }
                dao.update(folder);
-               for (FileHeader fh : folder.getFiles())
-                       setFilePermissions(userId, fh.getId(), fh.isReadForAll(), permissions);
+               for (FileHeader file : folder.getFiles()) {
+                       setFilePermissions(file, permissions);
+                       Date now = new Date();
+                       file.getAuditInfo().setModificationDate(now);
+                       file.getAuditInfo().setModifiedBy(user);
+               }
                for (Folder sub : folder.getSubfolders())
-                       setFolderPermissions(userId, sub.getId(), permissions);
+                       setFolderPermissions(user, sub, permissions);
        }
 
        private Permission getPermission(PermissionDTO dto) throws ObjectNotFoundException {
@@ -1497,9 +1617,6 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
 
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#getUsersSharingFoldersForUser(java.lang.Long)
-        */
        @Override
        public List<UserDTO> getUsersSharingFoldersForUser(Long userId) throws ObjectNotFoundException {
                List<User> users = dao.getUsersSharingFoldersForUser(userId);
@@ -1513,9 +1630,6 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                return res;
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#getFilePermissions(java.lang.Long, java.lang.Long)
-        */
        @Override
        public Set<PermissionDTO> getFilePermissions(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
                if (userId == null)
@@ -1538,41 +1652,32 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                return result;
        }
 
-       @Override
-       public void setFilePermissions(Long userId, Long fileId, Boolean readForAll, Set<PermissionDTO> permissions) throws ObjectNotFoundException, InsufficientPermissionsException {
-               if (userId == null)
-                       throw new ObjectNotFoundException("No user specified");
-               if (fileId == null)
-                       throw new ObjectNotFoundException("No folder specified");
-
-               User user = dao.getEntityById(User.class, userId);
-               FileHeader file = dao.getEntityById(FileHeader.class, fileId);
-               if(!file.hasModifyACLPermission(user))
-                       throw new InsufficientPermissionsException("You don't have the necessary permissions");
-
-               if (readForAll != null)
-                       if (user.equals(file.getOwner()))
-                               file.setReadForAll(readForAll);
-                       else
-                               throw new InsufficientPermissionsException("Only the owner can change the read-for-all flag");
-
-               // Update the file if there was a change.
-               if (readForAll != null || permissions != null && !permissions.isEmpty()) {
-                       if (permissions != null && !permissions.isEmpty()) {
-                               // Delete previous entries
-                               for (Permission perm: file.getPermissions())
-                                       dao.delete(perm);
-                               file.getPermissions().clear();
-                               for (PermissionDTO dto : permissions) {
-                                       if (dto.getUser()!=null && dto.getUser().getId().equals(file.getOwner().getId()) && (!dto.hasRead() || !dto.hasWrite() || !dto.hasModifyACL()))
-                                               throw new InsufficientPermissionsException("Can't remove permissions from owner");
-                                       // Don't include 'empty' permission
-                                       if (!dto.getRead() && !dto.getWrite() && !dto.getModifyACL()) continue;
-                                       file.addPermission(getPermission(dto));
-                               }
+       /**
+        * Set the provided permissions as the new permissions of the specified
+        * file. This method sets the modification date/user attributes to the
+        * current values as a side effect.
+        *
+        * @param file
+        * @param permissions
+        * @throws ObjectNotFoundException
+        * @throws InsufficientPermissionsException
+        */
+       private void setFilePermissions(FileHeader file,
+                               Set<PermissionDTO> permissions)
+                       throws ObjectNotFoundException, InsufficientPermissionsException {
+               if (permissions != null && !permissions.isEmpty()) {
+                       // Delete previous entries.
+                       for (Permission perm: file.getPermissions())
+                               dao.delete(perm);
+                       file.getPermissions().clear();
+                       for (PermissionDTO dto : permissions) {
+                               if (dto.getUser()!=null && dto.getUser().getId().equals(file.getOwner().getId()) && (!dto.hasRead() || !dto.hasWrite() || !dto.hasModifyACL()))
+                                       throw new InsufficientPermissionsException("Can't remove permissions from owner");
+                               // Don't include 'empty' permission.
+                               if (!dto.getRead() && !dto.getWrite() && !dto.getModifyACL()) continue;
+                               file.addPermission(getPermission(dto));
                        }
-
-                       dao.update(file);
+                       dao.flush();
                }
        }
 
@@ -1835,6 +1940,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                        for (final FileBody body : file.getBodies())
                                filesToRemove.add(body.getStoredFilePath());
                        dao.delete(file);
+                       touchParentFolders(parent, user, new Date());
                }
                //then remove physical files if everything is ok
                for(String physicalFileName : filesToRemove)
@@ -1961,6 +2067,8 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                deleteActualFile(body.getStoredFilePath());
                header.getBodies().remove(body);
 
+               Folder parent = header.getFolder();
+               touchParentFolders(parent, user, new Date());
 
        }
 
@@ -2009,27 +2117,8 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                }
                header.getCurrentBody().setVersion(1);
 
-       }
-
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#toggleFileVersioning(java.lang.Long, java.lang.Long, boolean)
-        */
-       @Override
-       public void toggleFileVersioning(Long userId, Long fileId, boolean versioned) throws ObjectNotFoundException, InsufficientPermissionsException {
-               if (userId == null)
-                       throw new ObjectNotFoundException("No user specified");
-               if (fileId == null)
-                       throw new ObjectNotFoundException("No file specified");
-               User user = dao.getEntityById(User.class, userId);
-               FileHeader header = dao.getEntityById(FileHeader.class, fileId);
-               if(!header.hasWritePermission(user))
-                       throw new InsufficientPermissionsException("You don't have the necessary permissions");
-               if(!header.isVersioned() == versioned){
-                       if(header.isVersioned())
-                               removeOldVersions(userId, fileId);
-                       header.setVersioned(versioned);
-
-               }
+               Folder parent = header.getFolder();
+               touchParentFolders(parent, user, new Date());
        }
 
        /**
@@ -2243,6 +2332,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                } catch (FileNotFoundException e) {
                        throw new GSSIOException(e);
                }
+               touchParentFolders(parent, owner, new Date());
                dao.flush();
                indexFile(file.getId(), false);
 
@@ -2281,6 +2371,8 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                } catch (FileNotFoundException e) {
                        throw new GSSIOException(e);
                }
+               Folder parent = file.getFolder();
+               touchParentFolders(parent, owner, new Date());
 
                indexFile(fileId, false);
                return file.getDTO();
@@ -2375,6 +2467,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
 
                dao.flush();
                header.addBody(body);
+               header.setAuditInfo(auditInfo);
 
                dao.create(body);
        }
@@ -2531,13 +2624,6 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                return true;
        }
 
-       /**
-        * Reset WebDAV password for given user.
-        *
-        * @param userId
-        * @return the new password
-        * @throws ObjectNotFoundException
-        */
        @Override
        public String resetWebDAVPassword(Long userId) throws ObjectNotFoundException {
                if (userId == null)
@@ -2547,4 +2633,39 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                return user.getWebDAVPassword();
        }
 
+       @Override
+       public Invitation findInvite(String code) {
+               if (code == null)
+                       return null;
+               return dao.findInvite(code);
+       }
+
+       @Override
+       public void createLdapUser(String username, String firstname, String lastname, String email, String password) {
+               LDAPConnection lc = new LDAPConnection();
+        LDAPAttributeSet attributeSet = new LDAPAttributeSet();
+        attributeSet.add(new LDAPAttribute("objectClass", getConfiguration().getStringArray("objectClass")));
+        attributeSet.add(new LDAPAttribute("uid", username));
+        attributeSet.add(new LDAPAttribute("cn", new String[]{firstname + " " + lastname}));
+        attributeSet.add(new LDAPAttribute("sn", lastname));
+        attributeSet.add(new LDAPAttribute("givenName", firstname));
+        attributeSet.add(new LDAPAttribute("mail", email));
+        attributeSet.add(new LDAPAttribute("userPassword", password));
+        String dn = "uid=" + username + "," + getConfiguration().getString("baseDn");
+        LDAPEntry newEntry = new LDAPEntry(dn, attributeSet);
+        try {
+               lc.connect(getConfiguration().getString("ldapHost"), LDAPConnection.DEFAULT_PORT);
+               lc.bind(LDAPConnection.LDAP_V3, getConfiguration().getString("bindDn"),
+                               getConfiguration().getString("bindPassword").getBytes("UTF8"));
+               lc.add(newEntry);
+               logger.info("Successfully added LDAP account: " + dn);
+               lc.disconnect();
+        } catch(LDAPException e) {
+               throw new RuntimeException(e);
+        } catch(UnsupportedEncodingException e) {
+               throw new RuntimeException(e);
+        }
+
+       }
+
 }