FubarDevelopment / FtpServer

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

FtpServer custom ftp user v2.1.1 #30

Closed kyndeyrn closed 6 years ago

kyndeyrn commented 6 years ago

Hello,

I'm unable to get the custom FTP user to work.

Scenario: connect with a simple username+password to the ftp server.

It worked before with version 1.3.7, but with the newer version, I'm not sure how to implement this.

Would it be possible to write a small & easy example?

Best regards & Thanks, Kyndeyrn

fubar-coder commented 6 years ago

I added a CustomMembershipProvider.cs to samples/TestFtpServer.

This is what you should do:

  1. Copy the CustomMembershipProvider.cs from the sample application
  2. Remove the call to EnableAnonymousAuthentication
  3. Add a singleton for your CustomMembershipProvider with services.AddSingleton<IMembershipProvider, CustomMembershipProvider>()

Done :)

rosscpeterson commented 5 years ago

I'm confused slightly by this. I've done your steps above, but I am still left wondering how I add a user (a CustomerMembershipProvider) to the server.

I have tried doing this:

FtpUserMembershipProvider ftpUserMembershipProvider = new FtpUserMembershipProvider(ftpUID, ftpPID);
services.AddSingleton<IMembershipProvider, FtpUserMembershipProvider>().AddSingleton(ftpUserMembershipProvider);

But when I try to login, it fails with "Service not available."

ftp NodeCraft@172.16.209.207
Connected to 172.16.209.207.
220 FTP Server Ready
421 Service not available, remote server has closed connection.
ftp: Login failed

However, if I don't add a user and keep anonymous authentication enabled, my commands to get files failed with "530 Not Logged In."

ftp 172.16.209.207
Connected to 172.16.209.207.
220 FTP Server Ready
Name (172.16.209.207:rosscpeterson): 
331 User rosscpeterson logged in, needs password
Password: 
530 Username or password incorrect
ftp: Login failed
ftp> 
ftp> get filea
local: filea remote: filea
530 Not logged in.
530 Not logged in.
530 Not logged in.

What am I doing wrong? How can I add a user and ensure that the proper services are enabled?

fubar-coder commented 5 years ago

I cannot reproduce this problem, but I can give you some hints:

  1. Create a CustomMembershipProvider class (see below)
  2. Register it with services.AddSingleton<IMembershipProvider, CustomMembershipProvider>()
  3. Remove the call to EnableAnonymousAuthentication()

CustomMembershipProvider

private class CustomMembershipProvider : IMembershipProvider
{
    /// <inheritdoc />
    public Task<MemberValidationResult> ValidateUserAsync(string username, string password)
    {
        if (username == "tester" && password == "test")
        {
            var identity = new ClaimsIdentity();
            return Task.FromResult(
                new MemberValidationResult(
                    MemberValidationStatus.AuthenticatedUser,
                    new ClaimsPrincipal(identity)));
        }

        return Task.FromResult(new MemberValidationResult(MemberValidationStatus.InvalidLogin));
    }
}

The membership provider takes care of the validation of the user name and password and returns a ClaimsPrincipal instance in case of a successful validation. This is the point where you implement your own user/password validation.

FTP server configuration

services
    .AddFtpServer(opt => opt.UseSingleRoot().UseInMemoryFileSystem())
    .AddSingleton<IMembershipProvider, CustomMembershipProvider>();

EDIT: Added FTP server configuration

rosscpeterson commented 5 years ago

Thanks for the quick reply. One strange issue that seems to indicate a version mismatch:

The 2nd argument of the MemberValidationResult constructor in my code doesn't accept a ClaimsPrincipal, but wants a FubarDev.FtpServer.AccountManagement.IFtpUser.

Why does yours expect a ClaimsPrincipal while mine expects a IFtpUser?

I implemented my own IFtpUser class and I passed that in, but I'm having the same problem as before.

I'm using version 3.0.1 of the FubarDev Nuget packages:

fubar-coder commented 5 years ago

You're correct. 3.0.1 doesn't contain this constructor yet. I'll switch to ClaimsPrincipal for 3.1, but keep the old code, marked as obsolete.

I'm unable to reproduce the problem. Can you upload a sample (zip file) or give me access to a sample repository?

rosscpeterson commented 5 years ago

FubarDevSupportSample.zip

I'm attaching a .ZIP of my code that's been trimmed down to the relevant parts.

In my Xamarin.UWP app, I make a call from LaunchPage.xaml.cs to Setup (start) the FtpServer server. All of the code relevant to setting up and starting the server is in Connection.cs. FtpUser.cs and FtpUserMembershipProvider.cs is also provided.

I'm in a rush with my current project so I didn't have time to make sure ensure that this sample would compile as-is, but you should at least see what I'm doing -- and hopefully you'll see what I'm doing wrong.

fubar-coder commented 5 years ago

OK, I see at least on problem:

The FtpUserMembershipProvider class cannot be instantiated via dependency injection, because it's unable to provide the parameters for its constructors.

Have you tried using my sample code above? It should work out of the box with user name "tester" and password "test". When you replace the username with Connection.FTUID and the password with Connection.FTPID, then everything should start working immediately.

EDIT: Ooops, I forgot that the membership provider only works with master. Here's the correct example:

private class CustomMembershipProvider : IMembershipProvider
{
    /// <inheritdoc />
    public Task<MemberValidationResult> ValidateUserAsync(string username, string password)
    {
        if (username == "tester" && password == "test")
        {
            return Task.FromResult(
                new MemberValidationResult(
                    MemberValidationStatus.AuthenticatedUser,
                    new AuthenticatedFtpUser(username)));
        }

        return Task.FromResult(new MemberValidationResult(MemberValidationStatus.InvalidLogin));
    }

    private class AuthenticatedFtpUser : IFtpUser
    {
        public AuthenticatedFtpUser(string name)
        {
            Name = name;
        }

        /// <inheritdoc />
        public string Name { get; }

        /// <inheritdoc />
        public bool IsInGroup(string groupName)
        {
            return groupName.Equals(Name, StringComparison.OrdinalIgnoreCase)
                || groupName.Equals("users", StringComparison.OrdinalIgnoreCase);
        }
    }
}
rosscpeterson commented 5 years ago

OK. I can log in now. Thank you very much for the help!

robokamran commented 4 years ago

Update: IFtpUser is obsolete, use sample code in here: https://github.com/FubarDevelopment/FtpServer/blob/master/samples/TestFtpServer/CustomMembershipProvider.cs

akanshSirohi commented 2 years ago

I want to generate a different password every time the FTP server start, I am not getting the point that how can I pass the different username and password...??? @fubar-coder

robokamran commented 2 years ago

@akanshSirohi, I think a good way would be to store the password in the DB each time the server starts, then validate the input password with the stored passwords in the DB (each time the validation is needed).