Skip to content

Commit

Permalink
Logout when connection gets closed
Browse files Browse the repository at this point in the history
Closes #93

Differences between this change and the PR:

- No changes to IFtpUser
  - No `AuthorityId`
- No changes to `IUnixFileSystemEntry`
  - No `PrincipalUser`
- No changes to `FtpServerOptions`
  - PASV configuration is already configurable through `PasvListenerOptions`
- LogOut became LogOutAsync in `IMembershipProviderAsync`
  • Loading branch information
fubar-coder committed Oct 8, 2021
1 parent d0887cd commit 6ff4349
Show file tree
Hide file tree
Showing 17 changed files with 239 additions and 30 deletions.
20 changes: 18 additions & 2 deletions samples/TestFtpServer/CustomMembershipProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// </copyright>

using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;

using FubarDev.FtpServer.AccountManagement;
Expand All @@ -12,10 +13,13 @@ namespace TestFtpServer
/// <summary>
/// Custom membership provider
/// </summary>
public class CustomMembershipProvider : IMembershipProvider
public class CustomMembershipProvider : IMembershipProviderAsync
{
/// <inheritdoc />
public Task<MemberValidationResult> ValidateUserAsync(string username, string password)
public Task<MemberValidationResult> ValidateUserAsync(
string username,
string password,
CancellationToken cancellationToken)
{
if (username != "tester" || password != "testing")
{
Expand All @@ -38,5 +42,17 @@ public Task<MemberValidationResult> ValidateUserAsync(string username, string pa
user));

}

/// <inheritdoc />
public Task LogOutAsync(ClaimsPrincipal principal, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}

/// <inheritdoc />
public Task<MemberValidationResult> ValidateUserAsync(string username, string password)
{
return ValidateUserAsync(username, password, CancellationToken.None);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

using System.Collections.Generic;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;

using FubarDev.FtpServer.AccountManagement.Anonymous;
Expand All @@ -16,7 +17,7 @@ namespace FubarDev.FtpServer.AccountManagement
/// <summary>
/// Allow any anonymous login.
/// </summary>
public class AnonymousMembershipProvider : IMembershipProvider
public class AnonymousMembershipProvider : IMembershipProviderAsync
{
private readonly IAnonymousPasswordValidator _anonymousPasswordValidator;

Expand Down Expand Up @@ -68,7 +69,10 @@ public static ClaimsPrincipal CreateAnonymousPrincipal(string? email)
}

/// <inheritdoc/>
public Task<MemberValidationResult> ValidateUserAsync(string username, string password)
public Task<MemberValidationResult> ValidateUserAsync(
string username,
string password,
CancellationToken cancellationToken)
{
if (string.Equals(username, "anonymous"))
{
Expand All @@ -83,5 +87,17 @@ public Task<MemberValidationResult> ValidateUserAsync(string username, string pa

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

/// <inheritdoc />
public Task LogOutAsync(ClaimsPrincipal principal, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}

/// <inheritdoc />
public Task<MemberValidationResult> ValidateUserAsync(string username, string password)
{
return ValidateUserAsync(username, password, CancellationToken.None);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// <author>Mark Junker</author>
//-----------------------------------------------------------------------

using System;
using System.Threading.Tasks;

namespace FubarDev.FtpServer.AccountManagement
Expand All @@ -23,6 +24,9 @@ public interface IMembershipProvider
/// <param name="username">The user name.</param>
/// <param name="password">The password.</param>
/// <returns>The result of the validation.</returns>
Task<MemberValidationResult> ValidateUserAsync(string username, string password);
[Obsolete("Use IMembershipProviderAsync.ValidateUserAsync")]
Task<MemberValidationResult> ValidateUserAsync(
string username,
string password);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// <copyright file="IMembershipProviderAsync.cs" company="Fubar Development Junker">
// Copyright (c) Fubar Development Junker. All rights reserved.
// </copyright>

using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;

namespace FubarDev.FtpServer.AccountManagement
{
/// <summary>
/// Membership provider interface.
/// </summary>
/// <remarks>
/// This interface must be implemented to allow the username/password authentication.
/// </remarks>
public interface IMembershipProviderAsync : IMembershipProvider
{
/// <summary>
/// Validates if the combination of <paramref name="username"/> and <paramref name="password"/> is valid.
/// </summary>
/// <param name="username">The user name.</param>
/// <param name="password">The password.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/>.</param>
/// <returns>The result of the validation.</returns>
Task<MemberValidationResult> ValidateUserAsync(
string username,
string password,
CancellationToken cancellationToken = default);

/// <summary>
/// Logout of the given <paramref name="principal"/>.
/// </summary>
/// <param name="principal">The principal to be logged out.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/>.</param>
/// <returns>The task.</returns>
Task LogOutAsync(
ClaimsPrincipal principal,
CancellationToken cancellationToken = default);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public Task AuthorizedAsync(IAccountInformation accountInformation, Cancellation
authInfoFeature.User = accountInformation.User;
#pragma warning restore 618
authInfoFeature.FtpUser = accountInformation.FtpUser;
authInfoFeature.MembershipProvider = accountInformation.MembershipProvider;

#pragma warning disable 618
connection.Data.IsAnonymous = accountInformation.User is IAnonymousFtpUser;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,20 @@ public override async Task<IFtpResponse> HandlePassAsync(string password, Cancel

foreach (var membershipProvider in _membershipProviders)
{
var validationResult = await membershipProvider
.ValidateUserAsync(_userName, password)
.ConfigureAwait(false);
#pragma warning disable 618
var validationResult = membershipProvider is IMembershipProviderAsync membershipProviderAsync
? await membershipProviderAsync
.ValidateUserAsync(_userName, password, cancellationToken)
.ConfigureAwait(false)
: await membershipProvider
.ValidateUserAsync(_userName, password)
.ConfigureAwait(false);
#pragma warning restore 618
if (validationResult.IsSuccess)
{
var accountInformation = new DefaultAccountInformation(validationResult.FtpUser);
var accountInformation = new DefaultAccountInformation(
validationResult.FtpUser,
membershipProvider);

foreach (var authorizationAction in _authorizationActions)
{
Expand Down Expand Up @@ -132,8 +140,11 @@ public UnauthenticatedUser(string name)

private class DefaultAccountInformation : IAccountInformation
{
public DefaultAccountInformation(ClaimsPrincipal user)
public DefaultAccountInformation(
ClaimsPrincipal user,
IMembershipProvider membershipProvider)
{
MembershipProvider = membershipProvider;
FtpUser = user;
#pragma warning disable 618
#pragma warning disable 612
Expand All @@ -148,6 +159,9 @@ public DefaultAccountInformation(ClaimsPrincipal user)

/// <inheritdoc />
public ClaimsPrincipal FtpUser { get; }

/// <inheritdoc />
public IMembershipProvider MembershipProvider { get; }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,12 @@

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Linq;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;

using FubarDev.FtpServer.Features;
using FubarDev.FtpServer.Features.Impl;
using FubarDev.FtpServer.FileSystem.Error;
using FubarDev.FtpServer.ServerCommands;

using JetBrains.Annotations;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

using System;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
#if !NETSTANDARD1_3
using System.Net.Sockets;
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,10 @@ public interface IAuthorizationInformationFeature
/// Gets or sets the current user.
/// </summary>
ClaimsPrincipal? FtpUser { get; set; }

/// <summary>
/// Gets or sets the membership provider that authenticated the <see cref="FtpUser"/>.
/// </summary>
IMembershipProvider? MembershipProvider { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,8 @@ internal class AuthorizationInformationFeature : IAuthorizationInformationFeatur

/// <inheritdoc />
public ClaimsPrincipal? FtpUser { get; set; }

/// <inheritdoc />
public IMembershipProvider? MembershipProvider { get; set; }
}
}
18 changes: 15 additions & 3 deletions src/FubarDev.FtpServer.Abstractions/FtpConnectionData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public FtpConnectionData(
}

/// <inheritdoc />
[Obsolete("User the IAuthorizationInformationFeature services to get the current status.")]
[Obsolete("Use the IAuthorizationInformationFeature services to get the current status.")]
public IFtpUser? User
{
get => _featureCollection.Get<IAuthorizationInformationFeature>().User;
Expand All @@ -67,7 +67,7 @@ public IFtpUser? User
}

/// <inheritdoc />
[Obsolete("User the IAuthorizationInformationFeature services to get the current status.")]
[Obsolete("Use the IAuthorizationInformationFeature services to get the current status.")]
public ClaimsPrincipal? FtpUser
{
get => _featureCollection.Get<IAuthorizationInformationFeature>().FtpUser;
Expand All @@ -79,11 +79,23 @@ public ClaimsPrincipal? FtpUser
}
}

/// <inheritdoc />
[Obsolete("Use the IAuthorizationInformationFeature services to get the current status.")]
public IMembershipProvider? MembershipProvider
{
get => _featureCollection.Get<IAuthorizationInformationFeature>().MembershipProvider;
set
{
var feature = _featureCollection.Get<IAuthorizationInformationFeature>();
feature.MembershipProvider = value;
}
}

/// <summary>
/// Gets or sets a value indicating whether the user with the <see cref="User"/>.
/// is logged in.
/// </summary>
[Obsolete("User the IFtpLoginStateMachine services to get the current status.")]
[Obsolete("Use the IFtpLoginStateMachine services to get the current status.")]
public bool IsLoggedIn { get; set; }

/// <summary>
Expand Down
5 changes: 5 additions & 0 deletions src/FubarDev.FtpServer.Abstractions/IAccountInformation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,10 @@ public interface IAccountInformation
/// Gets the current FTP user.
/// </summary>
ClaimsPrincipal FtpUser { get; }

/// <summary>
/// Gets the membership provider which authenticated the <see cref="FtpUser"/>.
/// </summary>
IMembershipProvider MembershipProvider { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
// </copyright>

using System;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;

using FubarDev.FtpServer.AccountManagement;
using FubarDev.FtpServer.Commands;
using FubarDev.FtpServer.DataConnection;
using FubarDev.FtpServer.Features;
Expand Down Expand Up @@ -49,6 +49,15 @@ public ReinCommandHandler(
/// <inheritdoc />
public override async Task<IFtpResponse?> Process(FtpCommand command, CancellationToken cancellationToken)
{
// User-Logout
var authorizationInformationFeature = Connection.Features.Get<IAuthorizationInformationFeature>();
var user = authorizationInformationFeature.FtpUser;
var membershipProvider = authorizationInformationFeature.MembershipProvider;
if (user != null && membershipProvider is IMembershipProviderAsync membershipProviderAsync)
{
await membershipProviderAsync.LogOutAsync(user, cancellationToken);
}

// Reset the login
var loginStateMachine = Connection.ConnectionServices.GetRequiredService<IFtpLoginStateMachine>();
loginStateMachine.Reset();
Expand Down Expand Up @@ -91,7 +100,11 @@ await secureConnectionFeature.CloseEncryptedControlStream(cancellationToken)
catch (Exception ex)
{
// Ignore exceptions
_logger?.LogWarning(ex, "Failed to dispose feature of type {featureType}: {errorMessage}", featureItem.Key, ex.Message);
_logger?.LogWarning(
ex,
"Failed to dispose feature of type {FeatureType}: {ErrorMessage}",
featureItem.Key,
ex.Message);
}

// Remove from features collection
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Linq;
using System.Net;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;

using FubarDev.FtpServer.AccountManagement;
Expand All @@ -22,7 +23,7 @@ namespace FubarDev.FtpServer.MembershipProvider.Pam
/// <summary>
/// The PAM membership provider.
/// </summary>
public class PamMembershipProvider : IMembershipProvider
public class PamMembershipProvider : IMembershipProviderAsync
{
private readonly IFtpConnectionAccessor _connectionAccessor;
private readonly IPamService _pamService;
Expand Down Expand Up @@ -80,7 +81,8 @@ public static ClaimsPrincipal CreateUnixPrincipal(UnixUserInfo userInfo)
/// <inheritdoc />
public Task<MemberValidationResult> ValidateUserAsync(
string username,
string password)
string password,
CancellationToken cancellationToken)
{
MemberValidationResult result;
var credentials = new NetworkCredential(username, password);
Expand Down Expand Up @@ -120,5 +122,17 @@ public Task<MemberValidationResult> ValidateUserAsync(

return Task.FromResult(result);
}

/// <inheritdoc />
public Task LogOutAsync(ClaimsPrincipal principal, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}

/// <inheritdoc />
public Task<MemberValidationResult> ValidateUserAsync(string username, string password)
{
return ValidateUserAsync(username, password, CancellationToken.None);
}
}
}
Loading

0 comments on commit 6ff4349

Please sign in to comment.