Closed aavanesov closed 5 years ago
Implement IAccountDirectoryQuery
and add it as "singleton" service.
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.
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.
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);
}
}
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.
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.
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.
@fubar-coder Thank you, After changing the order and some fixes here and there it started working as expected.
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.