FubarDevelopment / FtpServer

Portable FTP server written in .NET
http://fubardevelopment.github.io/FtpServer/
MIT License
474 stars 161 forks source link

Custom root folders for users #74

Closed aavanesov closed 5 years ago

aavanesov commented 5 years ago

Is there a way to define flexible root folders on the level of users? I found a way to use root folder per user with UseRootPerUser() but is is not providing enough flexiibilty for my case.

fubar-coder commented 5 years ago

Implement IAccountDirectoryQuery and add it as "singleton" service.

aavanesov commented 5 years ago

Thank you for your clarifications. But unfortunatelly, it looks like GetDirectories of IAccountDirectoryQuery is setting the relative path starting from the root path. Is there a way to override the root path completly for the user? For example, map users to folders on different drives.

fubar-coder commented 5 years ago

The root path returned by IAccountDirectoryQuery might be relative to the file system root, but it might also be an absolute path, due to the way Path.Combine works.

aavanesov commented 5 years ago

For example, this set up will not change the drive to "C:\" for the user, but will create a sub folders in D:\ drive like "d:\Temp\ftpserver\Temp\ftpserver\":

services.Configure<DotNetFileSystemOptions>(opt =>  opt.RootPath = @"d:\Temp\ftpserver\");

// ...

public class FtpAccountDirectoryQuery : IAccountDirectoryQuery
{
    public IAccountDirectories GetDirectories(IAccountInformation accountInformation)
    {
        return new GenericAccountDirectories(@"c:\Temp\ftpserver\" + accountInformation.User.Name);
    }
}
fubar-coder commented 5 years ago

Umm, no. The Users root directory will be c:\Temp\ftpserver\<username> and all directories he will create are in (or below) this directory, regardless of the DotNetFileSystemOptions configuration. BTW: You can differentiate between "normal" and "anonymous" FTP users by testing if accountInformation.User is of type IAnonymousFtpUser.

So, if your code returns new GenericAccountDirectories(null) for anonymous user, then those users will use d:\Temp\ftpserver, while authenticated users might be using c:\Temp\ftpserver\<username> as root directory.

Maciejszuchta commented 4 years ago

I am wondering if this is correct configuration to use IAccountDirectoryQuery

 public class AccountDirectoryQuery : IAccountDirectoryQuery
    {
        private readonly IProjectsQueryHub _projectsQueryHub;
        private readonly IConfiguration _configuration;

        public AccountDirectoryQuery(IProjectsQueryHub projectsHub, IConfiguration configuration)
        {
            _projectsQueryHub = projectsHub;
            _configuration = configuration;
        }

        public IAccountDirectories GetDirectories(IAccountInformation accountInformation)
        {
            var adminUser = _configuration.GetSection("Settings")["FtpLogin"];
            var login = accountInformation.User.Name;
            var rootPath = _configuration.GetSection("Settings")["DataPath"];

            if (accountInformation.User.Name != adminUser)
            {
                var project = _projectsQueryHub.GetSingle(login).WaitResult();
                if (project != null)
                {

                    return new GenericAccountDirectories(Path.Combine("Upload", login));
                }
            }
            return new GenericAccountDirectories(rootPath);
        }
    }

and in startup:

services.Configure<DotNetFileSystemOptions>(opt =>
            {
                opt.RootPath = DataFolderPath;
            });

 services.AddFtpServer(builder => builder
                .UseDotNetFileSystem()
                .UseRootPerUser(opt => { opt.UserRootDirectory = "Upload"; })
            );

services.AddSingleton<IAccountDirectoryQuery, AccountDirectoryQuery>();

However, if I set a breakpoint in GetDirectories method it never hits. Also connecting with custom user name doesn't correctly set the root folder for this user.

I am really confused about how to configure the option so when the user is connecting its root directory is set to "Upload/userName" and when the Administrator connects root is set to a directory above Upload.

fubar-coder commented 4 years ago

That the breakpoint doesn't get called means that services.AddSingleton<IAccountDirectoryQuery, AccountDirectoryQuery>(); gets executed before the services.AddFtpServer line. The UseRootPerUser adds its own IAccountDirectoryQuery implementation to the service collection.

Maciejszuchta commented 4 years ago

@fubar-coder Thank you, After changing the order and some fixes here and there it started working as expected.