Skip to content
Open
  •  
  •  
  •  
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Multifactor.Radius.Adapter.v2.Application.Cache;

public interface IAuthenticatedClientCache
{
void SetCache(string? callingStationId, string userName, string clientName, TimeSpan lifetime);
bool TryHitCache(string? callingStationId, string userName, string clientName, TimeSpan lifetime);
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
namespace Multifactor.Radius.Adapter.v2.Services.Cache;
namespace Multifactor.Radius.Adapter.v2.Application.Cache;

public interface ICacheService
{
//TODO разделить на несколько
void Set<T>(string key, T value, DateTimeOffset expirationDate);
void Set<T>(string key, T value);
bool TryGetValue<T>(string key, out T? value);
void Remove(string key);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Multifactor.Radius.Adapter.v2.Core
namespace Multifactor.Radius.Adapter.v2.Application.Configuration.Models
{
public class ApplicationVariables
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Net;
using Multifactor.Radius.Adapter.v2.Application.Core.Models;
using Multifactor.Radius.Adapter.v2.Application.Core.Models.Enum;
using NetTools;

namespace Multifactor.Radius.Adapter.v2.Application.Configuration.Models;

public interface IClientConfiguration
{
public string Name { get; }

public string MultifactorNasIdentifier { get; }
public string MultifactorSharedSecret { get; }
public IReadOnlyList<string> SignUpGroups { get; }
public bool BypassSecondFactorWhenApiUnreachable { get; }
public AuthenticationSource FirstFactorAuthenticationSource { get; }
public IPEndPoint AdapterClientEndpoint { get; }

public IReadOnlyList<IPAddress?> RadiusClientIps { get; }
public string RadiusClientNasIdentifier { get; }
public string RadiusSharedSecret { get; }
public IReadOnlyList<IPEndPoint> NpsServerEndpoints { get; }
public TimeSpan NpsServerTimeout { get; }

public Privacy Privacy { get; }

public PreAuthMode? PreAuthenticationMethod { get; }
public TimeSpan AuthenticationCacheLifetime { get; }
public CredentialDelay? InvalidCredentialDelay { get; }
public string? CallingStationIdAttribute { get; }
public IReadOnlyList<IPAddressRange> IpWhiteList { get; }

public IReadOnlyList<ILdapServerConfiguration>? LdapServers { get; }
public IReadOnlyDictionary<string, IReadOnlyList<IRadiusReplyAttribute>>? ReplyAttributes { get; }
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Multifactor.Core.Ldap.Name;

namespace Multifactor.Radius.Adapter.v2.Application.Configuration.Models;

public interface ILdapServerConfiguration
{
public string ConnectionString { get; }
public string Username { get; }
public string Password { get; }
public int BindTimeoutSeconds{ get; }
public IReadOnlyList<DistinguishedName> AccessGroups { get; }
public IReadOnlyList<DistinguishedName> SecondFaGroups { get; }
public IReadOnlyList<DistinguishedName> SecondFaBypassGroups { get; }
public bool LoadNestedGroups { get; }
public IReadOnlyList<DistinguishedName> NestedGroupsBaseDns { get; }
public IReadOnlyList<DistinguishedName> AuthenticationCacheGroups { get; }
public IReadOnlyList<string> PhoneAttributes { get; }
public string IdentityAttribute { get; }
public bool RequiresUpn { get; }
public bool TrustedDomainsEnabled { get; }
public bool AlternativeSuffixesEnabled { get; }
public IReadOnlyList<string> IncludedDomains { get; }
public IReadOnlyList<string> ExcludedDomains { get; }
public IReadOnlyList<string> IncludedSuffixes { get; }
public IReadOnlyList<string> ExcludedSuffixes { get; }
public IReadOnlyList<string> BypassSecondFactorWhenApiUnreachableGroups { get; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Multifactor.Radius.Adapter.v2.Application.Configuration.Models;

public interface IRadiusReplyAttribute
{
public string Name { get; }
public object Value { get; }
public IReadOnlyList<string> UserGroupCondition { get; }
public IReadOnlyList<string> UserNameCondition { get; }
public bool Sufficient { get; }
public bool IsMemberOf => Name?.ToLower() == "memberof";
public bool FromLdap => !string.IsNullOrWhiteSpace(Name);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System.Net;

namespace Multifactor.Radius.Adapter.v2.Application.Configuration.Models;

public interface IRootConfiguration
{
IReadOnlyList<Uri> MultifactorApiUrls { get; }
string? MultifactorApiProxy { get; }
TimeSpan MultifactorApiTimeout { get; }
IPEndPoint? AdapterServerEndpoint { get; }
string LoggingLevel { get; }
string? LoggingFormat { get; }
bool SyslogUseTls { get; }
string? SyslogServer { get; }
string? SyslogFormat { get; }
string? SyslogFacility { get; }
string SyslogAppName { get; }
string? SyslogFramer { get; }
string? SyslogOutputTemplate { get; }

string? ConsoleLogOutputTemplate { get; }
string? FileLogOutputTemplate { get; }
int LogFileMaxSizeBytes { get; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System.Net;

namespace Multifactor.Radius.Adapter.v2.Application.Configuration.Models;

public class ServiceConfiguration
{
public required IRootConfiguration RootConfiguration { get; init; }
public required IReadOnlyList<IClientConfiguration> ClientsConfigurations { get; init; }
public bool isRootClientMode { get; init; }
public IClientConfiguration? GetClientConfiguration(string nasIdentifier)
{
if (isRootClientMode)
{
return ClientsConfigurations.FirstOrDefault();
}

return ClientsConfigurations.FirstOrDefault(config => config.RadiusClientNasIdentifier == nasIdentifier);
}
public IClientConfiguration? GetClientConfiguration(IPAddress ip)
{
if (isRootClientMode)
{
return ClientsConfigurations.FirstOrDefault();
}

return ClientsConfigurations.FirstOrDefault(config =>
config.RadiusClientIps != null && config.RadiusClientIps.Any() && config.RadiusClientIps.Contains(ip));

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
namespace Multifactor.Radius.Adapter.v2.Application.Core.Models
{
public record CredentialDelay(int Min, int Max);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Multifactor.Radius.Adapter.v2.Core.Auth
namespace Multifactor.Radius.Adapter.v2.Application.Core.Models.Enum
{
[Flags]
public enum AuthenticationSource
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это не конфиг, а Core

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
namespace Multifactor.Radius.Adapter.v2.Core.Auth.PreAuthMode
namespace Multifactor.Radius.Adapter.v2.Application.Core.Models.Enum
{
[Flags]
public enum PreAuthMode
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это не конфиг, а Core. Если бы тут был DTO для чтения из конфига - тогда ладно

{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
//Please see licence at
//https://github.com/MultifactorLab/multifactor-radius-adapter/blob/main/LICENSE.md

namespace Multifactor.Radius.Adapter.v2.Core.MultifactorApi.PrivacyMode;
namespace Multifactor.Radius.Adapter.v2.Application.Core.Models.Enum;

/// <summary>
/// User information disclosure mode
/// </summary>
[Flags]
public enum PrivacyMode
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
using Multifactor.Radius.Adapter.v2.Application.Core.Models.Enum;

namespace Multifactor.Radius.Adapter.v2.Application.Core.Models
{
public record Privacy(PrivacyMode PrivacyMode, string[] PrivacyFields);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using Multifactor.Core.Ldap.Schema;
using Multifactor.Radius.Adapter.v2.Application.Configuration.Models;
using Multifactor.Radius.Adapter.v2.Application.Features.Ldap.Models;
using Multifactor.Radius.Adapter.v2.Application.Features.LoadLdapForest.Models;
using Multifactor.Radius.Adapter.v2.Application.Features.Pipeline.Models;
using Multifactor.Radius.Adapter.v2.Application.Features.Pipeline.Models.Enum;
using Multifactor.Radius.Adapter.v2.Application.Features.Radius.Models;
using Multifactor.Radius.Adapter.v2.Application.Features.Radius.Models.Enums;

namespace Multifactor.Radius.Adapter.v2.Application.Core;

public class RadiusPipelineContext
{
public RadiusPacket RequestPacket { get; }
public IClientConfiguration ClientConfiguration { get; }
public ILdapServerConfiguration? LdapConfiguration { get; }
public UserPassphrase? Passphrase { get; set; }
public ILdapSchema? LdapSchema { get; set; }
public ILdapProfile? LdapProfile { get; set; }
public string MustChangePasswordDomain { get; set; }
public HashSet<string> UserGroups { get; set; } = [];
public IForestMetadata? ForestMetadata { get; set; }

public RadiusPacket? ResponsePacket { get; set; }
public ResponseInformation ResponseInformation { get; set; } = new();
public AuthenticationStatus FirstFactorStatus { get; set; }
public AuthenticationStatus SecondFactorStatus { get; set; }

public bool IsTerminated { get; private set; }
public bool ShouldSkipResponse { get; private set; }
public bool IsDomainAccount => RequestPacket.AccountType == AccountType.Domain;
public void Terminate() => IsTerminated = true;
public void SkipResponse() => ShouldSkipResponse = true;

public RadiusPipelineContext(
RadiusPacket requestPacket,
IClientConfiguration clientConfiguration,
ILdapServerConfiguration? ldapServerConfig = null)
{
RequestPacket = requestPacket;
ClientConfiguration = clientConfiguration;
LdapConfiguration = ldapServerConfig;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Multifactor.Radius.Adapter.v2.Application.Configuration.Models;
using Multifactor.Radius.Adapter.v2.Application.Features.Multifactor;
using Multifactor.Radius.Adapter.v2.Application.Features.Pipeline;
using Multifactor.Radius.Adapter.v2.Application.Features.Pipeline.AccessChallenge;
using Multifactor.Radius.Adapter.v2.Application.Features.Pipeline.FirstFactor;
using Multifactor.Radius.Adapter.v2.Application.Features.Pipeline.FirstFactor.BindNameFormat;
using Multifactor.Radius.Adapter.v2.Application.Features.Pipeline.Interfaces;
using Multifactor.Radius.Adapter.v2.Application.Features.Pipeline.Steps;
using Multifactor.Radius.Adapter.v2.Application.Features.Radius.Services;

namespace Multifactor.Radius.Adapter.v2.Application.Extensions;

public static class ApplicationExtensions
{
public static void AddApplicationVariables(this IServiceCollection services)
{
var appVars = new ApplicationVariables
{
AppPath = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory),
AppVersion = Assembly.GetExecutingAssembly().GetName().Version?.ToString(),
StartedAt = DateTime.Now
};
services.AddSingleton(appVars);
}

private static void AddLdapBindNameFormation(IServiceCollection services)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Предлагаю эти вещи раскидать по модулям, чтобы сделать честными фичами и избавиться от DI hell

{
services.AddSingleton<ILdapBindNameFormatterProvider, LdapBindNameFormatterProvider>();
services.AddTransient<ILdapBindNameFormatter, ActiveDirectoryFormatter>();
services.AddTransient<ILdapBindNameFormatter, FreeIpaFormatter>();
services.AddTransient<ILdapBindNameFormatter, MultiDirectoryFormatter>();
services.AddTransient<ILdapBindNameFormatter, OpenLdapFormatter>();
services.AddTransient<ILdapBindNameFormatter, SambaFormatter>();
}

public static void AddFirstFactor(this IServiceCollection services)
{
services.AddSingleton<IFirstFactorProcessorProvider, FirstFactorProcessorProvider>();
services.AddTransient<IFirstFactorProcessor, LdapFirstFactorProcessor>();
services.AddTransient<IFirstFactorProcessor, RadiusFirstFactorProcessor>();
services.AddTransient<IFirstFactorProcessor, NoneFirstFactorProcessor>();
}

public static void AddChallenge(this IServiceCollection services)
{
services.AddTransient<IChallengeProcessor, SecondFactorChallengeProcessor>();
services.AddTransient<IChallengeProcessor, ChangePasswordChallengeProcessor>();
services.AddSingleton<IChallengeProcessorProvider, ChallengeProcessorProvider>();
}

public static void AddPipelines(this IServiceCollection services)
{
services.AddSingleton<IPipelineProvider, RadiusPipelineProvider>();
services.AddSingleton<IRadiusPipelineFactory, RadiusPipelineFactory>();
}

public static void AddPipelineSteps(this IServiceCollection services)
{
services.AddTransient<StatusServerFilteringStep>();
services.AddTransient<AccessRequestFilteringStep>();
services.AddTransient<LdapSchemaLoadingStep>();
services.AddTransient<ProfileLoadingStep>();
services.AddTransient<LoadLdapForestStep>();
services.AddTransient<AccessGroupsCheckingStep>();
services.AddTransient<AccessChallengeStep>();
services.AddTransient<FirstFactorStep>();
services.AddTransient<SecondFactorStep>();
services.AddTransient<PreAuthCheckStep>();
services.AddTransient<PreAuthPostCheck>();
services.AddTransient<UserGroupLoadingStep>();
services.AddTransient<UserNameValidationStep>();
services.AddTransient<IpWhiteListStep>();
}

public static void AddAppServices(this IServiceCollection services)
{
services.AddTransient<MultifactorApiService>();
services.AddTransient<IRadiusPacketProcessor, RadiusPacketProcessor>();
AddLdapBindNameFormation(services);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Multifactor.Core.Ldap.Name;
using Multifactor.Core.Ldap.Schema;

namespace Multifactor.Radius.Adapter.v2.Application.Features.Ldap.Models;

public class ChangeUserPasswordRequest
{
public LdapConnectionData ConnectionData { get; set; }
public ILdapSchema LdapSchema { get; set; }
public DistinguishedName DistinguishedName { get; set; }
public string NewPassword { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Multifactor.Core.Ldap.Attributes;
using Multifactor.Core.Ldap.Name;
using Multifactor.Core.Ldap.Schema;
using Multifactor.Radius.Adapter.v2.Application.Features.Pipeline.Models;

namespace Multifactor.Radius.Adapter.v2.Application.Features.Ldap.Models;

public class FindUserRequest
{
public LdapConnectionData ConnectionData { get; set; }
public UserIdentity UserIdentity { get; set; }
public DistinguishedName SearchBase { get; set; }
public ILdapSchema LdapSchema { get; set; }
public LdapAttributeName[]? AttributeNames { get; set; }
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
using Multifactor.Core.Ldap.Attributes;
using Multifactor.Core.Ldap.Name;

namespace Multifactor.Radius.Adapter.v2.Core.Ldap;
namespace Multifactor.Radius.Adapter.v2.Application.Features.Ldap.Models;

public interface ILdapProfile
{
DistinguishedName Dn { get; }
string? Upn { get; }
string? Phone { get; }
string? Email { get; }
string? DisplayName { get; }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Multifactor.Radius.Adapter.v2.Application.Features.Ldap.Models;

public class LdapConnectionData
{
public string ConnectionString { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public int BindTimeoutInSeconds { get; set; }
}
Loading