-
Notifications
You must be signed in to change notification settings - Fork 6
Refactoring Radius 2.0 #129
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
d898a5c
467842f
a42e35b
b487323
689237d
3fa2cb7
3ec4959
f1401ca
10ad7dc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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,5 +1,6 @@ | ||
| namespace Multifactor.Radius.Adapter.v2.Core.Auth.PreAuthMode | ||
| namespace Multifactor.Radius.Adapter.v2.Application.Configuration.Models.Enum | ||
| { | ||
| [Flags] | ||
| public enum PreAuthMode | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Это не конфиг, а Core. Если бы тут был DTO для чтения из конфига - тогда ладно |
||
| { | ||
| /// <summary> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| using System.Net; | ||
| using Multifactor.Radius.Adapter.v2.Application.Configuration.Models.Enum; | ||
| using NetTools; | ||
|
|
||
| namespace Multifactor.Radius.Adapter.v2.Application.Configuration.Models; | ||
|
|
||
| public interface IClientConfiguration | ||
| { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Почему некоторые сеттеры используются, а остальные - нет? Зачем в интерфейсе допускать сеттер? Нашел только один вызов - в тесте. Вывод: сеттер надо убрать |
||
| public string Name { get; set; } | ||
|
|
||
| public string MultifactorNasIdentifier { get; set; } | ||
| public string MultifactorSharedSecret { get; set; } | ||
| public IReadOnlyList<string> SignUpGroups { get; set; } | ||
| public bool BypassSecondFactorWhenApiUnreachable { get; set; } | ||
| public AuthenticationSource FirstFactorAuthenticationSource { get; set; } | ||
| public IPEndPoint AdapterClientEndpoint { get; set; } | ||
|
|
||
| public IPAddress? RadiusClientIp { get; set; } | ||
| public string RadiusClientNasIdentifier { get; set; } | ||
| public string RadiusSharedSecret { get; set; } | ||
| public IPEndPoint[] NpsServerEndpoints { get; set; } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Раз уж это интерфейс, все свойства должн быть немутабельными. Массив мутабелен. Лучше заменить на IReadOnlyList, например |
||
| public TimeSpan NpsServerTimeout { get; set; } | ||
|
|
||
| public (PrivacyMode PrivacyMode, string[] PrivacyFields) Privacy { get; set; } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Неудачная сигнатура, лучше сделать record. Кроме того, массив можно мутировать снаружи |
||
|
|
||
| public PreAuthMode? PreAuthenticationMethod { get; set; } | ||
| public TimeSpan AuthenticationCacheLifetime { get; set; } | ||
| public (int min, int max)? InvalidCredentialDelay { get; set; } | ||
| public string? CallingStationIdAttribute { get; set; } //TODO not used | ||
| public IReadOnlyList<IPAddressRange> IpWhiteList { get; set; } | ||
|
|
||
| public IReadOnlyList<ILdapServerConfiguration>? LdapServers { get; set; } | ||
| public IReadOnlyDictionary<string, IRadiusReplyAttribute[]>? ReplyAttributes { get; set; } | ||
| } | ||
| 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; init; } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. А тут вообще иниты... зачем? |
||
| public string Username { get; init; } | ||
| public string Password { get; init; } | ||
| public int BindTimeoutSeconds{ get; init; } | ||
| public IReadOnlyList<DistinguishedName> AccessGroups { get; init; } | ||
| public IReadOnlyList<DistinguishedName> SecondFaGroups { get; init; } | ||
| public IReadOnlyList<DistinguishedName> SecondFaBypassGroups { get; init; } | ||
| public bool LoadNestedGroups { get; init; } | ||
| public IReadOnlyList<DistinguishedName> NestedGroupsBaseDns { get; init; } | ||
| public IReadOnlyList<DistinguishedName> AuthenticationCacheGroups { get; init; } | ||
| public IReadOnlyList<string> PhoneAttributes { get; init; } | ||
| public string IdentityAttribute { get; init; } | ||
| public bool RequiresUpn { get; init; } | ||
| public bool TrustedDomainsEnabled { get; init; } | ||
| public bool AlternativeSuffixesEnabled { get; init; } | ||
| public IReadOnlyList<string> IncludedDomains { get; init; }//TODO not used | ||
| public IReadOnlyList<string> ExcludedDomains { get; init; }//TODO not used | ||
| public IReadOnlyList<string> IncludedSuffixes { get; init; } | ||
| public IReadOnlyList<string> ExcludedSuffixes { get; init; } | ||
| public IReadOnlyList<string> BypassSecondFactorWhenApiUnreachableGroups { get; init; } | ||
| } | ||
| 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 | ||
| { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. опять сеттеры |
||
| public string Name { get; set; } | ||
| public object Value { get; set; } | ||
| public IReadOnlyList<string> UserGroupCondition { get; set; } | ||
| public IReadOnlyList<string> UserNameCondition { get; set; } | ||
| public bool Sufficient { get; set; } | ||
| 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,25 @@ | ||
| using System.Net; | ||
|
|
||
| namespace Multifactor.Radius.Adapter.v2.Application.Configuration.Models; | ||
|
|
||
| public interface IRootConfiguration | ||
| { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. зачем тут везде сеттеры? Это же интерфейс. Иначе зачем интерфейс? |
||
|
|
||
| IReadOnlyList<Uri> MultifactorApiUrls { get; set; } | ||
| string? MultifactorApiProxy { get; set; } | ||
| TimeSpan MultifactorApiTimeout { get; set; } | ||
| IPEndPoint? AdapterServerEndpoint { get; set; } | ||
| string LoggingLevel { get; set; } | ||
| string? LoggingFormat { get; set; } | ||
| bool SyslogUseTls { get; set; } | ||
| string? SyslogServer { get; set; } | ||
| string? SyslogFormat { get; set; } | ||
| string? SyslogFacility { get; set; } | ||
| string SyslogAppName { get; set; } | ||
| string? SyslogFramer { get; set; } | ||
| string? SyslogOutputTemplate { get; set; } | ||
|
|
||
| string? ConsoleLogOutputTemplate { get; set; } | ||
| string? FileLogOutputTemplate { get; set; } | ||
| int LogFileMaxSizeBytes { get; set; } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| using System.Net; | ||
|
|
||
| namespace Multifactor.Radius.Adapter.v2.Application.Configuration.Models; | ||
|
|
||
| public class ServiceConfiguration | ||
| { | ||
| public required IRootConfiguration RootConfiguration { get; set; } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. зачем set? init + required |
||
| public required IReadOnlyList<IClientConfiguration> ClientsConfigurations { get; set; } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. set не нужен |
||
| public IClientConfiguration? GetClientConfiguration(string nasIdentifier) => ClientsConfigurations.FirstOrDefault(config => config.RadiusClientNasIdentifier == nasIdentifier); | ||
| public IClientConfiguration? GetClientConfiguration(IPAddress ip) | ||
| { | ||
| if (SingleClientMode) | ||
| { | ||
| return ClientsConfigurations.FirstOrDefault(); | ||
| } | ||
|
|
||
| return ClientsConfigurations.FirstOrDefault(config => | ||
| config.RadiusClientIp != null && config.RadiusClientIp.Equals(ip)); | ||
|
|
||
| } | ||
| public bool SingleClientMode { get; set; } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Аналогично. Получился мутабельный снаружи объект и хрупкий код |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| 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) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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<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 |
|---|---|---|
| @@ -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; } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| using Multifactor.Core.Ldap.Name; | ||
| using Multifactor.Core.Ldap.Schema; | ||
|
|
||
| namespace Multifactor.Radius.Adapter.v2.Application.Features.Ldap.Models; | ||
|
|
||
| public class LoadUserGroupRequest | ||
| { | ||
| public LdapConnectionData ConnectionData { get; set; } | ||
| public ILdapSchema LdapSchema { get; set; } | ||
| public DistinguishedName UserDN { get; set; } | ||
| public DistinguishedName? SearchBase { get; set; } | ||
| public int Limit { get; set; } = int.MaxValue; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Это не конфиг, а Core