Oh, sorry about that. I've coded for both Mina SFTP and Mina FTP, and the concepts are very similar (so similar, in fact, that it's a shame the two projects don't share a Filesystem API). You should be able to adapt the concept -- and possibly the code directly -- to Mina FTP.
On 18 October 2013 19:54, Gentian Hila <gh...@commercehub.com> wrote: > Taking a closer look at the example - it seems to be more geared toward > the SFTP (SSH) rather than FTP. > > > Genti > > -----Original Message----- > From: Gentian Hila [mailto:gh...@commercehub.com] > Sent: Wednesday, October 16, 2013 10:44 AM > To: ftpserver-users@mina.apache.org > Subject: RE: directory structure permissions & account locking > > That's great. All I was looking for was a way to approach the problem . > You've provided even more > > Thank you very much, > > Genti > > -----Original Message----- > From: John Hartnup [mailto:john.hart...@gmail.com] > Sent: Wednesday, October 16, 2013 10:28 AM > To: ftpserver-users@mina.apache.org > Subject: Re: directory structure permissions & account locking > > It looks like the mailing list strips attachments. Here's the source, > pasted in: > > Unfortunately there are some imports that I can't provide you, but you > should be able to knock up a substitute. > > ------------------------------------- > > package com.example.apachesftp.filesystem.delegating; > > import java.io.IOException; > import java.util.*; > import java.util.Map.Entry; > > import org.apache.sshd.common.Session; > import org.apache.sshd.server.FileSystemFactory; > import org.apache.sshd.server.FileSystemView; > > public class DelegatingFilesystemFactory implements FileSystemFactory { > > Map<String, FileSystemFactory> delegateFactories = new TreeMap<String, > FileSystemFactory>(); > > @Override > public FileSystemView createFileSystemView(final Session session) > throws IOException { > Map<String, FileSystemView> delegates = new TreeMap<String, > FileSystemView>(); > > for (Entry<String, FileSystemFactory> entry : delegateFactories > .entrySet()) { > delegates.put(entry.getKey(), > entry.getValue().createFileSystemView(session)); > } > > return new DelegatingFilesystemView(session, delegates); > } > > /** > * Provides a set of FileSystemFactories from which delegate > FileSystemViews > * are created. > * > * @param delegateFactories > * A map. The keys are the names of the "directories" used for > * these delegates; the values are the FileSystemFactories. > */ > public void setDelegateFactories( > final Map<String, FileSystemFactory> delegateFactories) { > this.delegateFactories = delegateFactories; > } > > } > > --------------------------------------- > > package com.example.apachesftp.filesystem.delegating; > > import java.io.*; > import java.util.*; > > import org.apache.sshd.server.FileSystemView; > import org.apache.sshd.server.SshFile; > > import com.example.apachesftp.filesystem.*; > > public class DelegatingSshFile implements DelegatableSshDirectory { > > public enum Disposition { > ROOT_OF_EVERYTHING, ROOT_OF_DELEGATE, IN_DELEGATE > } > > private final String root; > private final SshFile delegate; > private final Map<String, FileSystemView> delegates; private String > alias = null; private DelegatableSshDirectory parent = this; > > /* > * Some notes. > * > * If absolute path is empty or "/" then we are the root of the > delegating > * filesystem. > * > * If absolute path is exactly one element long, then we are at the > root of a > * delegate > * > * If the absolute path is 2 elements or more, then we are deeper into a > * delegate. > */ > public DelegatingSshFile(String path, > final Map<String, FileSystemView> delegates) { > > if (path.equals(".")) { > path = "/"; > } > > String[] parts = PathUtil.headAndTail(path); > this.root = parts[0]; > this.delegates = delegates; > > FileSystemView delegateFSV = delegates.get(root); > if (null != delegateFSV) { > this.delegate = delegateFSV.getFile(parts[1]); > } else { > this.delegate = new NonExistantSshFile(parts[1]); > } > } > > public DelegatingSshFile(final String root, final SshFile delegate, > final Map<String, FileSystemView> delegates) { > this.root = root; > this.delegate = delegate; > this.delegates = delegates; > } > > public Disposition getDisposition() { > > if (PathUtil.isBottom(root)) { > return Disposition.ROOT_OF_EVERYTHING; > } > if (delegate instanceof NonExistantSshFile) { > if (PathUtil.isBottom(delegate.getAbsolutePath())) { > return Disposition.ROOT_OF_EVERYTHING; > } else { > return Disposition.IN_DELEGATE; > } > } > if (PathUtil.isBottom(delegate.getAbsolutePath())) { > return Disposition.ROOT_OF_DELEGATE; > } > // else > return Disposition.IN_DELEGATE; > } > > @Override > public String getAbsolutePath() { > if (PathUtil.isBottom(root)) { > return "/"; > } else { > return PathUtil.joinPaths(root, delegate.getAbsolutePath()); > } > } > > @Override > public String getName() { > if(alias != null) { > return alias; > } else { > return getOriginalName(); > } > } > > private String getOriginalName() { > String name = delegate.getName(); > if (PathUtil.isBottom(name)) { > name = root; > } > name = PathUtil.deSlashFront(name); > > if (name.equals("")) { > name = "/"; > } > > return name; > } > > @Override > public boolean isDirectory() { > switch (getDisposition()) { > case ROOT_OF_EVERYTHING: > return true; > default: > return delegate.isDirectory(); > } > } > > @Override > public boolean isFile() { > switch (getDisposition()) { > case ROOT_OF_EVERYTHING: > return false; > default: > return delegate.isFile(); > } > } > > @Override > public boolean doesExist() { > switch (getDisposition()) { > case ROOT_OF_EVERYTHING: > if(root.equals("")) { > return true; > } else { > return delegates.keySet().contains(root); > } > default: > return delegate.doesExist(); > } > } > > @Override > public boolean isReadable() { > switch (getDisposition()) { > case ROOT_OF_EVERYTHING: > return true; > default: > return delegate.isReadable(); > } > } > > @Override > public boolean isWritable() { > switch (getDisposition()) { > case ROOT_OF_EVERYTHING: > return false; > default: > return delegate.isWritable(); > } > } > > @Override > public boolean isExecutable() { > switch (getDisposition()) { > case ROOT_OF_EVERYTHING: > return true; > default: > return delegate.isExecutable(); > } > } > > @Override > public boolean isRemovable() { > return delegate.isRemovable(); > } > > @Override > public SshFile getParentFile() { > switch (getDisposition()) { > case ROOT_OF_EVERYTHING: > return this; > case ROOT_OF_DELEGATE: > return new DelegatingSshFile("/", delegates); > default: > return new DelegatingSshFile(root, delegate.getParentFile(), > delegates); > } > } > > @Override > public long getLastModified() { > switch (getDisposition()) { > case ROOT_OF_EVERYTHING: > return 0; > default: > return delegate.getLastModified(); > } > } > > @Override > public boolean setLastModified(final long time) { > switch (getDisposition()) { > case ROOT_OF_EVERYTHING: > return false; > default: > return delegate.setLastModified(time); > } > } > > @Override > public long getSize() { > switch (getDisposition()) { > case ROOT_OF_EVERYTHING: > return delegates.size(); // plus . and .. > default: > return delegate.getSize(); > } > } > > @Override > public boolean mkdir() { > switch (getDisposition()) { > case ROOT_OF_EVERYTHING: > return false; > default: > return delegate.mkdir(); > } > } > > @Override > public boolean delete() { > switch (getDisposition()) { > case ROOT_OF_EVERYTHING: > return false; > default: > return delegate.delete(); > } > } > > @Override > public boolean create() throws IOException { > switch (getDisposition()) { > case ROOT_OF_EVERYTHING: > return false; > default: > return delegate.create(); > } > } > > @Override > public void truncate() throws IOException { > > } > > @Override > public boolean move(final SshFile destination) { > switch (getDisposition()) { > case ROOT_OF_EVERYTHING: > return false; > default: > return delegate.move(destination); > } > } > > @Override > public List<SshFile> listSshFiles() { > switch (getDisposition()) { > case ROOT_OF_EVERYTHING: > return listDelegates(); > default: > List<SshFile> files = delegate.listSshFiles(); > return delegatify(files); > } > } > > private List<SshFile> listDelegates() { > List<SshFile> l = new LinkedList<SshFile>(); > l.add(copyWithAlias(".")); > l.add(parent.copyWithAlias("..")); > Set<String> keys = this.delegates.keySet(); > for (String key : keys) { > l.add(new DelegatingSshFile(key, delegates)); > } > return l; > } > > private List<SshFile> delegatify(final List<SshFile> plainFiles) { > List<SshFile> l = new LinkedList<SshFile>(); > if(null != plainFiles) { > for (SshFile f : plainFiles) { > l.add(new DelegatingSshFile(root, f, delegates)); > } > } > return l; > } > > @Override > public OutputStream createOutputStream(final long offset) throws > IOException { > switch (getDisposition()) { > case ROOT_OF_EVERYTHING: > return null; > default: > return delegate.createOutputStream(offset); > } > > } > > @Override > public InputStream createInputStream(final long offset) throws > IOException { > return delegate.createInputStream(offset); > } > > @Override > public void handleClose() throws IOException { > switch (getDisposition()) { > case ROOT_OF_EVERYTHING: > break; > default: > delegate.handleClose(); > } > > } > > @Override > public void setAlias(String alias) { > this.alias = alias; > } > @Override > public DelegatingSshFile copyWithAlias(String alias) { DelegatingSshFile > copy = new DelegatingSshFile(root,delegate,delegates); > copy.setAlias(alias); > return copy; > } > > @Override > public void setParent(DelegatableSshDirectory parent) { > this.parent = parent; > } > > @Override > public String getOwner() { > switch (getDisposition()) { > case ROOT_OF_EVERYTHING: > return "system"; > default: > return delegate.getOwner(); > } > } > public SshFile getDelegate() { > return delegate; > } > } > > > > ------------------------ > > package com.example.apachesftp.filesystem.delegating; > > import java.util.Map; > > import org.apache.sshd.common.Session; > import org.apache.sshd.server.FileSystemView; > import org.apache.sshd.server.SshFile; > > public class DelegatingFilesystemView implements FileSystemView { > > private Map<String, FileSystemView> delegates; > > public DelegatingFilesystemView(final Session session, > final Map<String, FileSystemView> delegates) { > setDelegates(delegates); > } > > public void setDelegates(final Map<String, FileSystemView> delegates) { > this.delegates = delegates; > } > > @Override > public SshFile getFile(final String path) { > DelegatingSshFile f = new DelegatingSshFile( > PathUtil.cleanDots(path), delegates); > if(f instanceof DelegatableSshDirectory) { > DelegatingSshFile root = new DelegatingSshFile("/", delegates); > f.setParent(root); > } > return f; > } > > @Override > public SshFile getFile(final SshFile baseDir, final String file) { > String fullPath = PathUtil.joinPaths(baseDir.getAbsolutePath(), > file); > return getFile(fullPath); > } > > } > > > > > On 16 October 2013 15:20, Gentian Hila <gh...@commercehub.com> wrote: > > > Thank you very much, > > > > I will take a look around the interfaces you suggest. I did not get > > any attached files though. > > > > > > Genti > > > > From: John Hartnup [mailto:john.hart...@gmail.com] > > Sent: Wednesday, October 16, 2013 9:58 AM > > To: ftpserver-users@mina.apache.org > > Subject: Re: directory structure permissions & account locking > > > > The answer to any question like this is to customise the Filesystem > > classes to do what you want. > > > > I've attached some Java files (not warrantied for any particular > > purpose) for "DelegatingFilesystem" -- you can use one of these as > > the top of your filesystem hierarchy, with real, separately > > configured, filesystems as delegates, which appear as subdirectories. > > > > To grok these, you'll need to grok the FilesystemView, > > FilesystemViewFactory and SshFile interfaces - read the interfaces and > > they'll probably make sense. > > > > > > > > On 16 October 2013 14:50, Gentian Hila <gh...@commercehub.com<mailto: > > gh...@commercehub.com>> wrote: > > We have a use case where we would want to have two subdirectories > > under the user home directory incoming and outgoing. Each of them > > would have a hierarchical path. > > > > However, we want: > > > > User have read-only permissions for the outgoing directory and its > > children. > > User have read-write permissions for the incoming directory and its > > children. > > Users cannot change the structure of the directories ( I think we can > > achieve that by disabling MKDIR and RMD). > > Users cannot delete a file ( we can achieve that by disabling DELE > > command). > > > > However I do not see a way how to apply different permissions to > > different folders either through configuration or through extending > > the API source code yet. > > > > Is this possible at all? Has anybody attempted to do this? > > > > > > > > > > We also have a use case that basically requires that we lock an > > account after several failed logins. I did not see a mechanism for > > account locking and unlocking when this happens? > > > > Am I missing something? > > > > > > Genti > > > > > > > > -- > > "There is no way to peace; peace is the way" > > > > > > -- > "There is no way to peace; peace is the way" > -- "There is no way to peace; peace is the way"