diff --git a/Linguard/Cli.Test/AddClientCommandShould.cs b/Linguard/Cli.Test/AddClientCommandShould.cs index 6f0f6f5..49351ba 100644 --- a/Linguard/Cli.Test/AddClientCommandShould.cs +++ b/Linguard/Cli.Test/AddClientCommandShould.cs @@ -25,7 +25,7 @@ public async Task CreateRandomClient() { var commandName = command.GetAttribute().Name!; var app = Utils.BuildTestApp(command); var iface = GenerateInterface(app.ConfigurationManager); - app.ConfigurationManager.Configuration.GetModule()!.Interfaces.Add(iface); + app.ConfigurationManager.Configuration.Wireguard.Interfaces.Add(iface); var commandLine = $"{commandName} --interface {iface.Name}"; @@ -41,7 +41,7 @@ public async Task CreateClientWithName() { var commandName = command.GetAttribute().Name!; var app = Utils.BuildTestApp(command); var iface = GenerateInterface(app.ConfigurationManager); - app.ConfigurationManager.Configuration.GetModule()!.Interfaces.Add(iface); + app.ConfigurationManager.Configuration.Wireguard.Interfaces.Add(iface); const string name = "bob"; var commandLine = $"{commandName} --interface {iface.Name} --name {name}"; @@ -59,7 +59,7 @@ public async Task CreateClientWithEndpoint() { var commandName = command.GetAttribute().Name!; var app = Utils.BuildTestApp(command); var iface = GenerateInterface(app.ConfigurationManager); - app.ConfigurationManager.Configuration.GetModule()!.Interfaces.Add(iface); + app.ConfigurationManager.Configuration.Wireguard.Interfaces.Add(iface); const string endpoint = "vpn.example2.com"; var commandLine = $"{commandName} --interface {iface.Name} --endpoint {endpoint}"; @@ -77,7 +77,7 @@ public async Task CreateClientWithIPs() { var commandName = command.GetAttribute().Name!; var app = Utils.BuildTestApp(command); var iface = GenerateInterface(app.ConfigurationManager); - app.ConfigurationManager.Configuration.GetModule()!.Interfaces.Add(iface); + app.ConfigurationManager.Configuration.Wireguard.Interfaces.Add(iface); var faker = new Faker(); var ipv4 = $"{faker.Internet.Ip()}/24"; @@ -99,7 +99,7 @@ public async Task CreateClientWithDns() { var commandName = command.GetAttribute().Name!; var app = Utils.BuildTestApp(command); var iface = GenerateInterface(app.ConfigurationManager); - app.ConfigurationManager.Configuration.GetModule()!.Interfaces.Add(iface); + app.ConfigurationManager.Configuration.Wireguard.Interfaces.Add(iface); var dns = "my.dns.com"; var commandLine = $"{commandName} --interface {iface.Name} --dns1 {dns}"; @@ -119,7 +119,7 @@ public async Task CreateClientWithTwoDns() { var commandName = command.GetAttribute().Name!; var app = Utils.BuildTestApp(command); var iface = GenerateInterface(app.ConfigurationManager); - app.ConfigurationManager.Configuration.GetModule()!.Interfaces.Add(iface); + app.ConfigurationManager.Configuration.Wireguard.Interfaces.Add(iface); var dns1 = "my.dns.com"; var dns2 = "8.8.8.8"; @@ -140,7 +140,7 @@ public async Task CreateClientWithAllowedIPs() { var commandName = command.GetAttribute().Name!; var app = Utils.BuildTestApp(command); var iface = GenerateInterface(app.ConfigurationManager); - app.ConfigurationManager.Configuration.GetModule()!.Interfaces.Add(iface); + app.ConfigurationManager.Configuration.Wireguard.Interfaces.Add(iface); const string allowedIPs = "10.7.1.2/24 10.7.1.3/24 10.8.1.3/24"; var commandLine = $"{commandName} --interface {iface.Name} --allowedIps {allowedIPs}"; diff --git a/Linguard/Cli.Test/AddInterfaceCommandShould.cs b/Linguard/Cli.Test/AddInterfaceCommandShould.cs index 68c28de..5e58ac9 100644 --- a/Linguard/Cli.Test/AddInterfaceCommandShould.cs +++ b/Linguard/Cli.Test/AddInterfaceCommandShould.cs @@ -22,7 +22,7 @@ public async Task CreateInterfaceWithNoArguments() { await app.App.RunAsync(commandLine); var errors = app.Error.GetString(); errors.Should().BeEmpty(); - app.ConfigurationManager.Configuration.GetModule()!.Interfaces.Should().NotBeEmpty(); + app.ConfigurationManager.Configuration.Wireguard.Interfaces.Should().NotBeEmpty(); } [Fact] @@ -37,7 +37,7 @@ public async Task CreateTwoInterfacesWithNoArguments() { await app.App.RunAsync(commandLine); var errors = app.Error.GetString(); errors.Should().BeEmpty(); - app.ConfigurationManager.Configuration.GetModule()!.Interfaces.Should().HaveCount(2); + app.ConfigurationManager.Configuration.Wireguard.Interfaces.Should().HaveCount(2); } [Fact] @@ -51,7 +51,7 @@ public async Task CreateInterfaceWithDefinedName() { await app.App.RunAsync(commandLine); var errors = app.Error.GetString(); errors.Should().BeEmpty(); - app.ConfigurationManager.Configuration.GetModule()!.Interfaces.Should().NotBeEmpty(); + app.ConfigurationManager.Configuration.Wireguard.Interfaces.Should().NotBeEmpty(); } [Fact] @@ -66,6 +66,6 @@ public async Task CreateInterfaceWithDefinedGateway() { await app.App.RunAsync(commandLine); var errors = app.Error.GetString(); errors.Should().BeEmpty(); - app.ConfigurationManager.Configuration.GetModule()!.Interfaces.Should().NotBeEmpty(); + app.ConfigurationManager.Configuration.Wireguard.Interfaces.Should().NotBeEmpty(); } } \ No newline at end of file diff --git a/Linguard/Cli.Test/AddInterfaceCommandShouldNot.cs b/Linguard/Cli.Test/AddInterfaceCommandShouldNot.cs index 38acbce..f3bd25c 100644 --- a/Linguard/Cli.Test/AddInterfaceCommandShouldNot.cs +++ b/Linguard/Cli.Test/AddInterfaceCommandShouldNot.cs @@ -38,7 +38,7 @@ public async Task AddTwoInterfacesWithTheSameName() { await app.App.RunAsync(commandLine); var errors = app.Error.GetString(); errors.Should().Contain(Validation.InterfaceNameAlreadyInUse); - app.ConfigurationManager.Configuration.GetModule()!.Interfaces.Should().HaveCount(1); + app.ConfigurationManager.Configuration.Wireguard.Interfaces.Should().HaveCount(1); } [Fact] diff --git a/Linguard/Cli.Test/ListClientsCommandShould.cs b/Linguard/Cli.Test/ListClientsCommandShould.cs index b9ab3de..2a2bb6a 100644 --- a/Linguard/Cli.Test/ListClientsCommandShould.cs +++ b/Linguard/Cli.Test/ListClientsCommandShould.cs @@ -24,7 +24,7 @@ public async Task ListOnePeer() { var commandName = command.GetAttribute().Name!; var app = Utils.BuildTestApp(command); var iface = GenerateInterface(app.ConfigurationManager); - app.ConfigurationManager.Configuration.GetModule()!.Interfaces.Add(iface); + app.ConfigurationManager.Configuration.Wireguard.Interfaces.Add(iface); var peer = GeneratePeer(app.ConfigurationManager, iface); iface.Clients.Add(peer); @@ -45,9 +45,9 @@ public async Task ListPeersForSpecificInterface() { var app = Utils.BuildTestApp(command); var iface1 = GenerateInterface(app.ConfigurationManager); - app.ConfigurationManager.Configuration.GetModule()!.Interfaces.Add(iface1); + app.ConfigurationManager.Configuration.Wireguard.Interfaces.Add(iface1); var iface2 = GenerateInterface(app.ConfigurationManager); - app.ConfigurationManager.Configuration.GetModule()!.Interfaces.Add(iface2); + app.ConfigurationManager.Configuration.Wireguard.Interfaces.Add(iface2); var peer = GeneratePeer(app.ConfigurationManager, iface1); iface1.Clients.Add(peer); iface2.Clients.Add(GeneratePeer(app.ConfigurationManager, iface2)); diff --git a/Linguard/Cli.Test/ListInterfacesCommandShould.cs b/Linguard/Cli.Test/ListInterfacesCommandShould.cs index 2e19a2b..a56560b 100644 --- a/Linguard/Cli.Test/ListInterfacesCommandShould.cs +++ b/Linguard/Cli.Test/ListInterfacesCommandShould.cs @@ -24,7 +24,7 @@ public async Task ListOneInterface() { var commandName = command.GetAttribute().Name!; var app = Utils.BuildTestApp(command); var iface = GenerateInterface(app.ConfigurationManager); - app.ConfigurationManager.Configuration.GetModule()!.Interfaces.Add(iface); + app.ConfigurationManager.Configuration.Wireguard.Interfaces.Add(iface); var commandLine = $"{commandName}"; diff --git a/Linguard/Cli.Test/ShowInterfaceCommandShould.cs b/Linguard/Cli.Test/ShowInterfaceCommandShould.cs index dc770c8..c2b9d1a 100644 --- a/Linguard/Cli.Test/ShowInterfaceCommandShould.cs +++ b/Linguard/Cli.Test/ShowInterfaceCommandShould.cs @@ -24,7 +24,7 @@ public async Task ShowInterface() { var commandName = command.GetAttribute().Name!; var app = Utils.BuildTestApp(command); var iface = GenerateInterface(app.ConfigurationManager); - app.ConfigurationManager.Configuration.GetModule()!.Interfaces.Add(iface); + app.ConfigurationManager.Configuration.Wireguard.Interfaces.Add(iface); var commandLine = $"{commandName} --name {iface.Name}"; diff --git a/Linguard/Cli/Cli.csproj b/Linguard/Cli/Cli.csproj index 01e7d56..53c335e 100644 --- a/Linguard/Cli/Cli.csproj +++ b/Linguard/Cli/Cli.csproj @@ -18,7 +18,7 @@ - + diff --git a/Linguard/Cli/Commands/AddClientCommand.cs b/Linguard/Cli/Commands/AddClientCommand.cs index a08f16b..b2b2cb2 100644 --- a/Linguard/Cli/Commands/AddClientCommand.cs +++ b/Linguard/Cli/Commands/AddClientCommand.cs @@ -28,8 +28,8 @@ public AddClientCommand(IConfigurationManager configurationManager, IClientGener private readonly ILogger _logger; private readonly IClientGenerator _generator; private readonly IConfigurationManager _configurationManager; - private IWireguardConfiguration Configuration - => _configurationManager.Configuration.GetModule()!; + private IWireguardOptions Options + => _configurationManager.Configuration.Wireguard; [CommandOption("name", Description = "Name of the peer.")] public string? Name { get; set; } @@ -78,7 +78,7 @@ public ValueTask ExecuteAsync(IConsole console) { console.Error.WriteLine(Validation.InterfaceNotFound); return ValueTask.CompletedTask; } - var iface = Configuration.Interfaces.SingleOrDefault(i => i.Name.Equals(Interface)); + var iface = Options.Interfaces.SingleOrDefault(i => i.Name.Equals(Interface)); if (iface == default) { console.Error.WriteLine(Validation.InterfaceNotFound); return ValueTask.CompletedTask; diff --git a/Linguard/Cli/Commands/AddInterfaceCommand.cs b/Linguard/Cli/Commands/AddInterfaceCommand.cs index cfa8248..56e29dc 100644 --- a/Linguard/Cli/Commands/AddInterfaceCommand.cs +++ b/Linguard/Cli/Commands/AddInterfaceCommand.cs @@ -71,7 +71,7 @@ public virtual ValueTask ExecuteAsync(IConsole console) { if (!Validate(iface, console)) { return ValueTask.CompletedTask; } - Configuration.GetModule()!.Interfaces.Add(iface); + Configuration.Wireguard.Interfaces.Add(iface); ConfigurationManager.Save(); var msg = $"Added interface '{iface.Name}'."; Logger.LogInformation(msg); diff --git a/Linguard/Cli/Commands/EditClientCommand.cs b/Linguard/Cli/Commands/EditClientCommand.cs index 50ec90d..24fb46e 100644 --- a/Linguard/Cli/Commands/EditClientCommand.cs +++ b/Linguard/Cli/Commands/EditClientCommand.cs @@ -25,8 +25,8 @@ public EditClientCommand(IConfigurationManager configurationManager, private readonly AbstractValidator _validator; private readonly ILogger _logger; private readonly IConfigurationManager _configurationManager; - private IWireguardConfiguration Configuration - => _configurationManager.Configuration.GetModule()!; + private IWireguardOptions Options + => _configurationManager.Configuration.Wireguard; [CommandOption("name", Description = "Current name of the client.", IsRequired = true)] public string Name { get; set; } @@ -77,7 +77,7 @@ private IWireguardConfiguration Configuration public string? NewInterface { get; set; } public ValueTask ExecuteAsync(IConsole console) { - var iface = Configuration.Interfaces.SingleOrDefault(i => i.Name.Equals(Interface)); + var iface = Options.Interfaces.SingleOrDefault(i => i.Name.Equals(Interface)); if (iface == default) { console.Error.WriteLine(Validation.InterfaceNotFound); return ValueTask.CompletedTask; @@ -120,7 +120,7 @@ protected void ApplyParametersSetByUser(Client client) { if (AllowedIPs != default) client.AllowedIPs = AllowedIPs; if (Endpoint != default) client.Endpoint = Endpoint; if (NewInterface == default || NewInterface.Equals(Interface)) return; - var iface = Configuration.Interfaces + var iface = Options.Interfaces .SingleOrDefault(i => i.Name.Equals(NewInterface)); if (iface == default) { throw new ArgumentException( diff --git a/Linguard/Cli/Commands/EditInterfaceCommand.cs b/Linguard/Cli/Commands/EditInterfaceCommand.cs index 133003f..68a64d9 100644 --- a/Linguard/Cli/Commands/EditInterfaceCommand.cs +++ b/Linguard/Cli/Commands/EditInterfaceCommand.cs @@ -18,7 +18,7 @@ public EditInterfaceCommand(IConfigurationManager configurationManager, ILogger } public override ValueTask ExecuteAsync(IConsole console) { - var iface = Configuration.GetModule()! + var iface = Configuration.Wireguard .Interfaces.SingleOrDefault(i => i.Name.Equals(Name)); if (iface == default) { Logger.LogError($"No interface named '{Name}' was found."); diff --git a/Linguard/Cli/Commands/ListClientsCommand.cs b/Linguard/Cli/Commands/ListClientsCommand.cs index be9ee98..267b1b9 100644 --- a/Linguard/Cli/Commands/ListClientsCommand.cs +++ b/Linguard/Cli/Commands/ListClientsCommand.cs @@ -16,8 +16,8 @@ public ListClientsCommand(IConfigurationManager configurationManager) { } private readonly IConfigurationManager _configurationManager; - private IWireguardConfiguration Configuration - => _configurationManager.Configuration.GetModule()!; + private IWireguardOptions Options + => _configurationManager.Configuration.Wireguard; [CommandOption("interface", Description = "Name of the client's interface.")] public string? Interface { get; set; } = default; @@ -25,7 +25,7 @@ private IWireguardConfiguration Configuration public ValueTask ExecuteAsync(IConsole console) { ICollection peers; if (Interface != default) { - var iface = Configuration.Interfaces.SingleOrDefault(i => i.Name.Equals(Interface)); + var iface = Options.Interfaces.SingleOrDefault(i => i.Name.Equals(Interface)); if (iface == default) { console.Error.WriteLine(Validation.InterfaceNotFound); return ValueTask.CompletedTask; @@ -33,7 +33,7 @@ public ValueTask ExecuteAsync(IConsole console) { peers = iface.Clients; } else { - peers = Configuration.Interfaces.SelectMany(i => i.Clients).ToList(); + peers = Options.Interfaces.SelectMany(i => i.Clients).ToList(); } if (!peers.Any()) { console.Output.WriteLine("There are no clients yet."); diff --git a/Linguard/Cli/Commands/ListInterfacesCommand.cs b/Linguard/Cli/Commands/ListInterfacesCommand.cs index 8fc0ca4..6245321 100644 --- a/Linguard/Cli/Commands/ListInterfacesCommand.cs +++ b/Linguard/Cli/Commands/ListInterfacesCommand.cs @@ -14,11 +14,11 @@ public ListInterfacesCommand(IConfigurationManager configurationManager) { } private readonly IConfigurationManager _configurationManager; - private IWireguardConfiguration Configuration - => _configurationManager.Configuration.GetModule()!; + private IWireguardOptions Options + => _configurationManager.Configuration.Wireguard; public ValueTask ExecuteAsync(IConsole console) { - var interfaces = Configuration.Interfaces; + var interfaces = Options.Interfaces; if (!interfaces.Any()) { console.Output.WriteLine("There are no interfaces yet."); return ValueTask.CompletedTask; diff --git a/Linguard/Cli/Commands/ShowClientCommand.cs b/Linguard/Cli/Commands/ShowClientCommand.cs index 69671d5..56274ba 100644 --- a/Linguard/Cli/Commands/ShowClientCommand.cs +++ b/Linguard/Cli/Commands/ShowClientCommand.cs @@ -23,7 +23,7 @@ public ShowClientCommand(IConfigurationManager configurationManager) { } public ValueTask ExecuteAsync(IConsole console) { - var peer = Configuration.GetModule()!.Interfaces + var peer = Configuration.Wireguard.Interfaces .SingleOrDefault(i => i.Name.Equals(Interface)) ?.Clients .SingleOrDefault(c => c.Name.Equals(Name)); diff --git a/Linguard/Cli/Commands/ShowInterfaceCommand.cs b/Linguard/Cli/Commands/ShowInterfaceCommand.cs index 1881e0b..12950b1 100644 --- a/Linguard/Cli/Commands/ShowInterfaceCommand.cs +++ b/Linguard/Cli/Commands/ShowInterfaceCommand.cs @@ -20,7 +20,7 @@ public ShowInterfacesCommand(IConfigurationManager configurationManager) { } public ValueTask ExecuteAsync(IConsole console) { - var iface = Configuration.GetModule()! + var iface = Configuration.Wireguard .Interfaces.SingleOrDefault(i => i.Name.Equals(Name)); if (iface == default) { console.Error.WriteLine(Validation.InterfaceNotFound); diff --git a/Linguard/Cli/DefaultYamlConfigurationSerializer.cs b/Linguard/Cli/DefaultYamlConfigurationSerializer.cs index 5c41065..4a959a3 100644 --- a/Linguard/Cli/DefaultYamlConfigurationSerializer.cs +++ b/Linguard/Cli/DefaultYamlConfigurationSerializer.cs @@ -3,7 +3,7 @@ using Linguard.Core.Configuration.Serialization; using Linguard.Core.Drivers.TrafficStorage; using Linguard.Core.Models.Wireguard; -using Linguard.Json; +using Linguard.Plugins.TrafficDrivers.Json; using Linguard.Yaml.Serialization; using YamlDotNet.Serialization.NamingConventions; using UriTypeConverter = Linguard.Yaml.Serialization.UriTypeConverter; @@ -18,8 +18,8 @@ public static class DefaultYamlConfigurationSerializer { .WithTypeConverter() .WithTypeConverter() .WithTypeMapping() - .WithTypeMapping() - .WithTypeMapping() + .WithTypeMapping() + .WithTypeMapping() .WithTypeMapping() .WithTypeMapping, HashSet>() .WithTypeMapping, HashSet>() diff --git a/Linguard/Core.Test/Mocks/DefaultConfiguration.cs b/Linguard/Core.Test/Mocks/DefaultConfiguration.cs index d81fadf..735fb3c 100644 --- a/Linguard/Core.Test/Mocks/DefaultConfiguration.cs +++ b/Linguard/Core.Test/Mocks/DefaultConfiguration.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Core.Test.Stubs; using Linguard.Core.Configuration; using Linguard.Core.Drivers.TrafficStorage; using Linguard.Core.Models.Wireguard; @@ -11,22 +12,15 @@ namespace Core.Test.Mocks; public sealed class DefaultConfiguration : Mock { public DefaultConfiguration() { - SetupProperty(c => c.Modules, new HashSet { - GetWireguardConfigurationMock().Object, - GetTrafficConfigurationMock().Object - }); + SetupProperty(o => o.Wireguard, GetWireguardConfigurationMock().Object); + SetupProperty(o => o.Traffic, GetTrafficConfigurationMock().Object); + SetupProperty(o => o.Plugins, GetPluginConfigurationMock().Object); Setup(o => o.Clone()).Returns(Object); - Setup(o => o.GetModule()) - .Returns(new InvocationFunc(invocation => { - var type = invocation.Method.GetGenericArguments()[0]; - return Object.Modules.SingleOrDefault(m - => m.GetType() == type || m.GetType().GetInterface(type.Name) != default); - })); } - private Mock GetWireguardConfigurationMock() { + private Mock GetWireguardConfigurationMock() { var interfaces = new HashSet(); - var wireguardConfiguration = new Mock() + var mock = new Mock() .SetupProperty(c => c.Interfaces, interfaces) .SetupProperty(c => c.Endpoint, new Uri("vpn.example.com", UriKind.RelativeOrAbsolute)) @@ -36,20 +30,26 @@ private Mock GetWireguardConfigurationMock() { .SetupProperty(c => c.PrimaryDns, new Uri("8.8.8.8", UriKind.RelativeOrAbsolute)) .SetupProperty(c => c.SecondaryDns, default); - wireguardConfiguration.Setup(o => o.GetInterface(It.IsAny())) + mock.Setup(o => o.GetInterface(It.IsAny())) .Returns(c => interfaces.SingleOrDefault(i => i.Clients.Contains(c)) ); - wireguardConfiguration.Setup(o => o.GetInterface(It.IsAny())) + mock.Setup(o => o.GetInterface(It.IsAny())) .Returns(pubkey => interfaces.SingleOrDefault(i => i.Clients.Any(c => c.PublicKey == pubkey)) ); - return wireguardConfiguration; + return mock; + } + + private Mock GetPluginConfigurationMock() { + var mock = new Mock(); + return mock; } - private Mock GetTrafficConfigurationMock() { - var mock = new Mock() - .SetupProperty(c => c.StorageDriver, new Mock().Object); + private Mock GetTrafficConfigurationMock() { + var mock = new Mock() + .SetupProperty(c => c.StorageDriver, new TrafficStorageDriverStub()) + .SetupProperty(c => c.Enabled, true); return mock; } } \ No newline at end of file diff --git a/Linguard/Core.Test/Mocks/DefaultConfigurationManager.cs b/Linguard/Core.Test/Mocks/DefaultConfigurationManager.cs index ecce47c..6efa137 100644 --- a/Linguard/Core.Test/Mocks/DefaultConfigurationManager.cs +++ b/Linguard/Core.Test/Mocks/DefaultConfigurationManager.cs @@ -7,6 +7,5 @@ namespace Core.Test.Mocks; public class DefaultConfigurationManager : Mock { public DefaultConfigurationManager() { SetupProperty(c => c.Configuration, new DefaultConfiguration().Object); - SetupProperty(c => c.WorkingDirectory, new Mock().Object); } } \ No newline at end of file diff --git a/Linguard/Core.Test/Mocks/WireguardServiceMock.cs b/Linguard/Core.Test/Mocks/WireguardServiceMock.cs index 9dd53e4..e7e513f 100644 --- a/Linguard/Core.Test/Mocks/WireguardServiceMock.cs +++ b/Linguard/Core.Test/Mocks/WireguardServiceMock.cs @@ -44,7 +44,7 @@ public WireguardServiceMock(IConfigurationManager manager, ISystemWrapper system Setup(o => o.GetTrafficData()) .Returns(() => { var data = new List(); - foreach (var iface in manager.Configuration.GetModule()!.Interfaces) { + foreach (var iface in manager.Configuration.Wireguard.Interfaces) { data.AddRange(Object.GetTrafficData(iface)); } return data; diff --git a/Linguard/Core.Test/Stubs/TrafficStorageDriverStub.cs b/Linguard/Core.Test/Stubs/TrafficStorageDriverStub.cs index 65a1b04..68188b9 100644 --- a/Linguard/Core.Test/Stubs/TrafficStorageDriverStub.cs +++ b/Linguard/Core.Test/Stubs/TrafficStorageDriverStub.cs @@ -30,7 +30,7 @@ public void Save(IEnumerable data) { } public IEnumerable Load() { - var interfaces = _configurationManager.Configuration.GetModule()!.Interfaces; + var interfaces = _configurationManager.Configuration.Wireguard.Interfaces; var data = new List(); var entries = (int) TimeSpan.FromDays(2).TotalHours; var timestampBase = DateTime.Now - TimeSpan.FromHours(entries); diff --git a/Linguard/Core.Test/WireguardConfigParserShould.cs b/Linguard/Core.Test/WireguardConfigParserShould.cs index 371a707..022acdc 100644 --- a/Linguard/Core.Test/WireguardConfigParserShould.cs +++ b/Linguard/Core.Test/WireguardConfigParserShould.cs @@ -21,10 +21,10 @@ public class WireguardConfigParserShould { private static readonly Faker Faker = new (); private static readonly IWireguardService WireguardService = new WireguardServiceMock(Manager, System, Faker).Object; private static readonly IWireguardConfigParser Parser = new WireguardConfigParser(Manager, WireguardService, Faker); - private IWireguardConfiguration Configuration => Manager.Configuration.GetModule()!; + private IWireguardOptions Options => Manager.Configuration.Wireguard; public WireguardConfigParserShould() { - Configuration.Interfaces.Add(new Interface { + Options.Interfaces.Add(new Interface { Name = "wg0", PublicKey = "server-pubkey", PrivateKey = "server-privkey", @@ -55,7 +55,7 @@ public WireguardConfigParserShould() { } public void ParseInterface() { - var iface = Configuration.Interfaces.First(); + var iface = Options.Interfaces.First(); var config = WireguardUtils.GenerateWireguardConfiguration(iface); var output = Parser.Parse(config); output.Should().Be(iface); @@ -87,7 +87,7 @@ public void ParseClient() { IPAddressCidr.Parse("0.0.0.0/0"), IPAddressCidr.Parse("::/0"), }); - Configuration.Interfaces + Options.Interfaces .Single(i => i.Name == "wg0") .Clients .First() diff --git a/Linguard/Core.Test/WireguardDumpParserShould.cs b/Linguard/Core.Test/WireguardDumpParserShould.cs index 29a7fc5..d6a1e06 100644 --- a/Linguard/Core.Test/WireguardDumpParserShould.cs +++ b/Linguard/Core.Test/WireguardDumpParserShould.cs @@ -15,8 +15,8 @@ namespace Core.Test; public class WireguardDumpParserShould { private static readonly Mock ConfigurationManagerMock = new DefaultConfigurationManager(); - private static IWireguardConfiguration Configuration - => ConfigurationManagerMock.Object.Configuration.GetModule()!; + private static IWireguardOptions Options + => ConfigurationManagerMock.Object.Configuration.Wireguard; public WireguardDumpParserShould() { var iface = new Interface { @@ -37,7 +37,7 @@ public WireguardDumpParserShould() { OnDown = new HashSet(), Gateway = new NetworkInterfaceMock("a").Object }; - Configuration.Interfaces.Add(iface); + Options.Interfaces.Add(iface); } @@ -49,18 +49,18 @@ public void ExtractEmptyData() { wg1 jUd41n3XYa3yXBzyBvWqlLhYgRef5RiBD7jwo70U+Rw= (none) (none) 10.7.1.0/24 0 0 0 off"; var data = - WireguardDumpParser.GetTrafficData(sampleData, Configuration.Interfaces.First()); + WireguardDumpParser.GetTrafficData(sampleData, Options.Interfaces.First()); data.Should().HaveCount(3); var clients = data.Where(e => e.Peer is Client); clients.Should().HaveCount(2); var interfaces = data.Where(e => e.Peer is Interface); interfaces.Should().HaveCount(1); - clients.Select(e => e.Peer).Should().BeEquivalentTo(Configuration.Interfaces.First().Clients); + clients.Select(e => e.Peer).Should().BeEquivalentTo(Options.Interfaces.First().Clients); clients.First().ReceivedData.Should().Be(default); clients.First().SentData.Should().Be(default); clients.Last().ReceivedData.Should().Be(default); clients.Last().SentData.Should().Be(default); - interfaces.Single().Peer.Should().Be(Configuration.Interfaces.First()); + interfaces.Single().Peer.Should().Be(Options.Interfaces.First()); interfaces.Single().ReceivedData.Bytes.Should().Be(clients.Sum(e => e.ReceivedData.Bytes)); interfaces.Single().SentData.Bytes.Should().Be(clients.Sum(e => e.SentData.Bytes)); } @@ -73,18 +73,18 @@ public void ExtractDataFromSingleInterface() { wg1 jUd41n3XYa3yXBzyBvWqlLhYgRef5RiBD7jwo70U+Rw= (none) 172.19.0.7:51823 10.0.0.3/32 1609974495 1403752 19462368 off"; var data = - WireguardDumpParser.GetTrafficData(sampleData, Configuration.Interfaces.First()); + WireguardDumpParser.GetTrafficData(sampleData, Options.Interfaces.First()); data.Should().HaveCount(3); var clients = data.Where(e => e.Peer is Client); clients.Should().HaveCount(2); var interfaces = data.Where(e => e.Peer is Interface); interfaces.Should().HaveCount(1); - clients.Select(e => e.Peer).Should().BeEquivalentTo(Configuration.Interfaces.First().Clients); + clients.Select(e => e.Peer).Should().BeEquivalentTo(Options.Interfaces.First().Clients); clients.First().ReceivedData.Bytes.Should().Be(3481633); clients.First().SentData.Bytes.Should().Be(33460136); clients.Last().ReceivedData.Bytes.Should().Be(1403752); clients.Last().SentData.Bytes.Should().Be(19462368); - interfaces.Single().Peer.Should().Be(Configuration.Interfaces.First()); + interfaces.Single().Peer.Should().Be(Options.Interfaces.First()); interfaces.Single().ReceivedData.Bytes.Should().Be(clients.Sum(e => e.ReceivedData.Bytes)); interfaces.Single().SentData.Bytes.Should().Be(clients.Sum(e => e.SentData.Bytes)); } diff --git a/Linguard/Core/Configuration/AuthenticationOptions.cs b/Linguard/Core/Configuration/AuthenticationOptions.cs new file mode 100644 index 0000000..52d6843 --- /dev/null +++ b/Linguard/Core/Configuration/AuthenticationOptions.cs @@ -0,0 +1,9 @@ +namespace Linguard.Core.Configuration; + +public class AuthenticationOptions : IAuthenticationOptions { + public string DataSource { get; set; } + + public object Clone() { + return MemberwiseClone(); + } +} \ No newline at end of file diff --git a/Linguard/Core/Configuration/ConfigurationBase.cs b/Linguard/Core/Configuration/ConfigurationBase.cs index 66d5405..7de51d4 100644 --- a/Linguard/Core/Configuration/ConfigurationBase.cs +++ b/Linguard/Core/Configuration/ConfigurationBase.cs @@ -1,38 +1,14 @@ -namespace Linguard.Core.Configuration; +using Linguard.Core.Utils; -public class ConfigurationBase : IConfiguration { - public ConfigurationBase() { - Modules = new HashSet(); - } - - public virtual object Clone() { - var clone = new ConfigurationBase(); - foreach (var module in Modules) { - clone.Modules.Add((IConfigurationModule) module.Clone()); - } - return clone; - } - - public T? GetModule() where T : IConfigurationModule { - var type = typeof(T); - return (T?) Modules.SingleOrDefault(m - => m.GetType() == type || m.GetType().GetInterface(type.Name) != default); - } +namespace Linguard.Core.Configuration; - public ISet Modules { get; set; } - - protected bool Equals(ConfigurationBase other) { - return Modules.SequenceEqual(other.Modules); - } - - public override bool Equals(object? obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; - return Equals((ConfigurationBase)obj); - } +public class ConfigurationBase : IConfiguration { + public IWireguardOptions Wireguard { get; set; } + public ITrafficOptions Traffic { get; set; } + public IPluginOptions Plugins { get; set; } + public IAuthenticationOptions Authentication { get; set; } - public override int GetHashCode() { - return Modules.GetHashCode(); + public object Clone() { + return Cloning.Clone(this); } } \ No newline at end of file diff --git a/Linguard/Core/Configuration/IAuthenticationOptions.cs b/Linguard/Core/Configuration/IAuthenticationOptions.cs new file mode 100644 index 0000000..a8384aa --- /dev/null +++ b/Linguard/Core/Configuration/IAuthenticationOptions.cs @@ -0,0 +1,9 @@ +namespace Linguard.Core.Configuration; + +public interface IAuthenticationOptions : IOptionsModule { + /// + /// Indicate the data source used to validate and authenticate users. + /// + /// It may be a path to a file or a connection string, for instance. + string DataSource { get; set; } +} \ No newline at end of file diff --git a/Linguard/Core/Configuration/IConfiguration.cs b/Linguard/Core/Configuration/IConfiguration.cs index 677b808..005c3e5 100644 --- a/Linguard/Core/Configuration/IConfiguration.cs +++ b/Linguard/Core/Configuration/IConfiguration.cs @@ -4,6 +4,9 @@ /// Represents all configurable settings. /// public interface IConfiguration : ICloneable { - T? GetModule() where T : IConfigurationModule; - ISet Modules { get; set; } + IWireguardOptions Wireguard { get; set; } + ITrafficOptions Traffic { get; set; } + IPluginOptions Plugins { get; set; } + IAuthenticationOptions Authentication { get; set; } + } \ No newline at end of file diff --git a/Linguard/Core/Configuration/IConfigurationModule.cs b/Linguard/Core/Configuration/IOptionsModule.cs similarity index 77% rename from Linguard/Core/Configuration/IConfigurationModule.cs rename to Linguard/Core/Configuration/IOptionsModule.cs index 39dc28f..c79bbbe 100644 --- a/Linguard/Core/Configuration/IConfigurationModule.cs +++ b/Linguard/Core/Configuration/IOptionsModule.cs @@ -4,6 +4,5 @@ /// Represents a configuration module. /// /// All configuration modules should implement this interface. -public interface IConfigurationModule : ICloneable { - +public interface IOptionsModule : ICloneable { } \ No newline at end of file diff --git a/Linguard/Core/Configuration/IPluginOptions.cs b/Linguard/Core/Configuration/IPluginOptions.cs new file mode 100644 index 0000000..cddb981 --- /dev/null +++ b/Linguard/Core/Configuration/IPluginOptions.cs @@ -0,0 +1,8 @@ +namespace Linguard.Core.Configuration; + +public interface IPluginOptions : IOptionsModule { + /// + /// Directory containing all plugins. + /// + DirectoryInfo PluginsDirectory { get; set; } +} \ No newline at end of file diff --git a/Linguard/Core/Configuration/ITrafficConfiguration.cs b/Linguard/Core/Configuration/ITrafficConfiguration.cs deleted file mode 100644 index 7d43790..0000000 --- a/Linguard/Core/Configuration/ITrafficConfiguration.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Linguard.Core.Drivers.TrafficStorage; - -namespace Linguard.Core.Configuration; - -public interface ITrafficConfiguration : IConfigurationModule { - bool Enabled { get; set; } - ITrafficStorageDriver StorageDriver { get; set; } -} \ No newline at end of file diff --git a/Linguard/Core/Configuration/ITrafficOptions.cs b/Linguard/Core/Configuration/ITrafficOptions.cs new file mode 100644 index 0000000..d2595dc --- /dev/null +++ b/Linguard/Core/Configuration/ITrafficOptions.cs @@ -0,0 +1,14 @@ +using Linguard.Core.Drivers.TrafficStorage; + +namespace Linguard.Core.Configuration; + +public interface ITrafficOptions : IOptionsModule { + /// + /// Whether traffic data collection will be performed or not. + /// + bool Enabled { get; set; } + /// + /// Driver that handles traffic data collection. + /// + ITrafficStorageDriver StorageDriver { get; set; } +} \ No newline at end of file diff --git a/Linguard/Core/Configuration/IWireguardConfiguration.cs b/Linguard/Core/Configuration/IWireguardOptions.cs similarity index 71% rename from Linguard/Core/Configuration/IWireguardConfiguration.cs rename to Linguard/Core/Configuration/IWireguardOptions.cs index 1b61e3b..d578cd2 100644 --- a/Linguard/Core/Configuration/IWireguardConfiguration.cs +++ b/Linguard/Core/Configuration/IWireguardOptions.cs @@ -2,34 +2,35 @@ namespace Linguard.Core.Configuration; -public interface IWireguardConfiguration : IConfigurationModule { - public ISet Interfaces { get; set; } +public interface IWireguardOptions : IOptionsModule { + DirectoryInfo InterfacesDirectory { get; set; } + ISet Interfaces { get; set; } /// /// Path to the iptables binary file. /// - public string IptablesBin { get; set; } + string IptablesBin { get; set; } /// /// Path to the wg binary file. /// - public string WireguardBin { get; set; } + string WireguardBin { get; set; } /// /// Path to the wg-quick binary file. /// - public string WireguardQuickBin { get; set; } + string WireguardQuickBin { get; set; } /// /// Default primary DNS for all peers if none specified at interface level. /// - public Uri? PrimaryDns { get; set; } + Uri? PrimaryDns { get; set; } /// /// Default secondary DNS for all peers if none specified at interface level. /// - public Uri? SecondaryDns { get; set; } + Uri? SecondaryDns { get; set; } /// /// Default endpoint for all peers if none specified at interface level. /// - public Uri? Endpoint { get; set; } + Uri? Endpoint { get; set; } /// - /// Get the interface associated to the given client or default if none. + /// Get the interface associated to the given client or default if none found. /// /// /// @@ -41,4 +42,5 @@ public interface IWireguardConfiguration : IConfigurationModule { /// /// Interface? GetInterface(string publicKey); + FileInfo GetInterfaceConfigurationFile(Interface @interface); } \ No newline at end of file diff --git a/Linguard/Core/Configuration/IWorkingDirectory.cs b/Linguard/Core/Configuration/IWorkingDirectory.cs deleted file mode 100644 index bb9a108..0000000 --- a/Linguard/Core/Configuration/IWorkingDirectory.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Linguard.Core.Models.Wireguard; - -namespace Linguard.Core.Configuration; - -/// -/// The root directory for the application, used to store and read data. -/// -public interface IWorkingDirectory { - DirectoryInfo BaseDirectory { get; set; } - FileInfo GetInterfaceConfigurationFile(Interface @interface); - string CredentialsPath { get; } - DirectoryInfo PluginsDirectory { get; } -} \ No newline at end of file diff --git a/Linguard/Core/Configuration/PluginOptions.cs b/Linguard/Core/Configuration/PluginOptions.cs new file mode 100644 index 0000000..3fe7007 --- /dev/null +++ b/Linguard/Core/Configuration/PluginOptions.cs @@ -0,0 +1,8 @@ +namespace Linguard.Core.Configuration; +public class PluginOptions : IPluginOptions { + public DirectoryInfo PluginsDirectory { get; set; } + + public object Clone() { + return MemberwiseClone(); + } +} \ No newline at end of file diff --git a/Linguard/Core/Configuration/Serialization/ConfigurationSerializerBase.cs b/Linguard/Core/Configuration/Serialization/ConfigurationSerializerBase.cs new file mode 100644 index 0000000..ce9b2a2 --- /dev/null +++ b/Linguard/Core/Configuration/Serialization/ConfigurationSerializerBase.cs @@ -0,0 +1,22 @@ +namespace Linguard.Core.Configuration.Serialization; + +public abstract class ConfigurationSerializerBase : IConfigurationSerializer { + public abstract string Serialize(T configuration) where T : IConfiguration; + public T? Deserialize(string text) where T : IConfiguration { + LoadPlugins(text); + return DeserializeAfterPluginsLoaded(text); + } + /// + /// Since there may be configuration dependent of what plugins have been loaded, this method gets called before + /// actually mapping the whole configuration. + /// + /// + protected abstract void LoadPlugins(string text); + /// + /// Map the configuration to classes after all plugins have been loaded. + /// + /// + /// + /// + protected abstract T DeserializeAfterPluginsLoaded(string text) where T : IConfiguration; +} \ No newline at end of file diff --git a/Linguard/Core/Configuration/Serialization/IConfigurationSerializer.cs b/Linguard/Core/Configuration/Serialization/IConfigurationSerializer.cs index 9e0b5a4..f933799 100644 --- a/Linguard/Core/Configuration/Serialization/IConfigurationSerializer.cs +++ b/Linguard/Core/Configuration/Serialization/IConfigurationSerializer.cs @@ -5,5 +5,5 @@ /// public interface IConfigurationSerializer { string Serialize(T configuration) where T : IConfiguration; - T Deserialize(string text) where T : IConfiguration; + T? Deserialize(string text) where T : IConfiguration; } \ No newline at end of file diff --git a/Linguard/Core/Configuration/TrafficConfiguration.cs b/Linguard/Core/Configuration/TrafficOptions.cs similarity index 72% rename from Linguard/Core/Configuration/TrafficConfiguration.cs rename to Linguard/Core/Configuration/TrafficOptions.cs index 87ac448..f953d8f 100644 --- a/Linguard/Core/Configuration/TrafficConfiguration.cs +++ b/Linguard/Core/Configuration/TrafficOptions.cs @@ -2,11 +2,11 @@ namespace Linguard.Core.Configuration; -public class TrafficConfiguration : ITrafficConfiguration { +public class TrafficOptions : ITrafficOptions { public bool Enabled { get; set; } public ITrafficStorageDriver StorageDriver { get; set; } public object Clone() { - var clone = (ITrafficConfiguration) MemberwiseClone(); + var clone = (ITrafficOptions) MemberwiseClone(); clone.StorageDriver = (ITrafficStorageDriver) StorageDriver.Clone(); return clone; } diff --git a/Linguard/Core/Configuration/WireguardConfiguration.cs b/Linguard/Core/Configuration/WireguardOptions.cs similarity index 56% rename from Linguard/Core/Configuration/WireguardConfiguration.cs rename to Linguard/Core/Configuration/WireguardOptions.cs index 4a3aa87..31a042a 100644 --- a/Linguard/Core/Configuration/WireguardConfiguration.cs +++ b/Linguard/Core/Configuration/WireguardOptions.cs @@ -2,7 +2,9 @@ namespace Linguard.Core.Configuration; -public class WireguardConfiguration : IWireguardConfiguration { +public class WireguardOptions : IWireguardOptions { + private const string WireguardConfigurationFileExtension = "conf"; + public DirectoryInfo InterfacesDirectory { get; set; } public ISet Interfaces { get; set; } public string IptablesBin { get; set; } public string WireguardBin { get; set; } @@ -17,6 +19,11 @@ public class WireguardConfiguration : IWireguardConfiguration { .SingleOrDefault(i => i.Clients.Any(c => c.PublicKey == publicKey)); public object Clone() { - return MemberwiseClone(); + var clone = (IWireguardOptions) MemberwiseClone(); + clone.Interfaces = new HashSet(Interfaces); + return clone; } + public FileInfo GetInterfaceConfigurationFile(Interface @interface) => + new(Path.ChangeExtension(Path.Combine(InterfacesDirectory.FullName, @interface.Name), + WireguardConfigurationFileExtension)); } \ No newline at end of file diff --git a/Linguard/Core/Configuration/WorkingDirectory.cs b/Linguard/Core/Configuration/WorkingDirectory.cs deleted file mode 100644 index 7cc32ae..0000000 --- a/Linguard/Core/Configuration/WorkingDirectory.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Linguard.Core.Models.Wireguard; -using Linguard.Core.Utils; - -namespace Linguard.Core.Configuration; - -public sealed class WorkingDirectory : IWorkingDirectory { - - public DirectoryInfo BaseDirectory { get; set; } - private DirectoryInfo InterfacesDirectory => new(Path.Combine(BaseDirectory.FullName, "interfaces")); - - private const string WireguardConfigurationFileExtension = "conf"; - - public FileInfo GetInterfaceConfigurationFile(Interface @interface) => - new(Path.ChangeExtension(Path.Combine(InterfacesDirectory.FullName, @interface.Name), - WireguardConfigurationFileExtension)); - - public string CredentialsPath => Path.Combine(BaseDirectory.FullName, $"{AssemblyInfo.Product.ToLower()}.db"); - public DirectoryInfo PluginsDirectory => new(Path.Combine(BaseDirectory.FullName, "plugins")); -} \ No newline at end of file diff --git a/Linguard/Core/Drivers/TrafficStorage/ITrafficStorageDriver.cs b/Linguard/Core/Drivers/TrafficStorage/ITrafficStorageDriver.cs index 77d4519..b9608cd 100644 --- a/Linguard/Core/Drivers/TrafficStorage/ITrafficStorageDriver.cs +++ b/Linguard/Core/Drivers/TrafficStorage/ITrafficStorageDriver.cs @@ -4,9 +4,16 @@ namespace Linguard.Core.Drivers.TrafficStorage; public interface ITrafficStorageDriver : IPlugin { - TimeSpan CollectionInterval { get; set; } + /// + /// Additional settings of the driver. + /// + /// Custom plugins may find this useful. IDictionary AdditionalOptions { get; set; } /// + /// Determines how frequently should we collect traffic data. + /// + TimeSpan CollectionInterval { get; set; } + /// /// Store the given traffic data. /// /// diff --git a/Linguard/Core/Managers/ConfigurationManagerBase.cs b/Linguard/Core/Managers/ConfigurationManagerBase.cs index 2ebce5f..c7bab95 100644 --- a/Linguard/Core/Managers/ConfigurationManagerBase.cs +++ b/Linguard/Core/Managers/ConfigurationManagerBase.cs @@ -10,36 +10,51 @@ public abstract class ConfigurationManagerBase : IConfigurationManager { private readonly ISystemWrapper _systemWrapper; - protected ConfigurationManagerBase(IConfiguration configuration, IWorkingDirectory workingDirectory, - ISystemWrapper systemWrapper, IPluginEngine pluginEngine) { + protected ConfigurationManagerBase(IConfiguration configuration, ISystemWrapper systemWrapper, + IPluginEngine pluginEngine) { Configuration = configuration; - WorkingDirectory = workingDirectory; PluginEngine = pluginEngine; _systemWrapper = systemWrapper; } + public string ConfigurationSource { get; set; } public IConfiguration Configuration { get; set; } - public IWorkingDirectory WorkingDirectory { get; set; } public IPluginEngine PluginEngine { get; set; } public virtual void LoadDefaults() { + LoadAuthenticationDefaults(); LoadTrafficDefaults(); LoadWireguardDefaults(); + LoadPluginDefaults(); + } + + protected virtual void LoadAuthenticationDefaults() { + var configuration = new AuthenticationOptions { + DataSource = $"{AssemblyInfo.Product}.db" + }; + Configuration.Authentication = configuration; } + protected virtual void LoadPluginDefaults() { + var configuration = new PluginOptions { + PluginsDirectory = new DirectoryInfo(Path.Combine(Directory.GetCurrentDirectory(), "plugins")) + }; + Configuration.Plugins = configuration; + } + protected virtual void LoadTrafficDefaults() { - var configuration = new TrafficConfiguration { + var configuration = new TrafficOptions { Enabled = false }; - Configuration.Modules.Add(configuration); + Configuration.Traffic = configuration; } protected virtual void LoadWireguardDefaults() { - var configuration = new WireguardConfiguration { + var configuration = new WireguardOptions { Interfaces = new HashSet(), PrimaryDns = new("8.8.8.8", UriKind.RelativeOrAbsolute), SecondaryDns = new("8.8.4.4", UriKind.RelativeOrAbsolute) }; - Configuration.Modules.Add(configuration); + Configuration.Wireguard = configuration; var publicIp = Network.GetPublicIPAddress(); configuration.Endpoint = publicIp == default ? default diff --git a/Linguard/Core/Managers/DefaultFileConfigurationManager.cs b/Linguard/Core/Managers/DefaultFileConfigurationManager.cs deleted file mode 100644 index 7499962..0000000 --- a/Linguard/Core/Managers/DefaultFileConfigurationManager.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Linguard.Core.Configuration; -using Linguard.Core.Configuration.Serialization; -using Linguard.Core.OS; -using Linguard.Core.Plugins; -using Linguard.Core.Utils; - -namespace Linguard.Core.Managers; - -public abstract class DefaultFileConfigurationManager : FileConfigurationManager where T : IConfiguration { - - protected DefaultFileConfigurationManager(IConfiguration configuration, IWorkingDirectory workingDirectory, - ISystemWrapper systemWrapper, IConfigurationSerializer serializer, - IPluginEngine pluginEngine) - : base(configuration, workingDirectory, systemWrapper, serializer, pluginEngine) { - } - - private FileInfo? _configurationFile; - - protected sealed override FileInfo ConfigurationFile { - get { - if (_configurationFile != default) return _configurationFile; - var filename = Path.Combine(WorkingDirectory.BaseDirectory.FullName, AssemblyInfo.Product.ToLower()); - var tries = 0; - while (tries < SupportedExtensions.Length && _configurationFile is not { Exists: true }) { - var filepath = Path.ChangeExtension(filename, SupportedExtensions[tries]); - _configurationFile = new FileInfo(filepath); - tries++; - } - return _configurationFile!; - } - } -} \ No newline at end of file diff --git a/Linguard/Core/Managers/FileConfigurationManager.cs b/Linguard/Core/Managers/FileConfigurationManager.cs index 7f00de3..dc66ead 100644 --- a/Linguard/Core/Managers/FileConfigurationManager.cs +++ b/Linguard/Core/Managers/FileConfigurationManager.cs @@ -8,20 +8,17 @@ namespace Linguard.Core.Managers; public abstract class FileConfigurationManager : ConfigurationManagerBase where T : IConfiguration { - protected FileConfigurationManager(IConfiguration configuration, IWorkingDirectory workingDirectory, + protected FileConfigurationManager(IConfiguration configuration, ISystemWrapper systemWrapper, IConfigurationSerializer serializer, IPluginEngine pluginEngine) - : base(configuration, workingDirectory, systemWrapper, pluginEngine) { + : base(configuration, systemWrapper, pluginEngine) { Configuration = configuration; - WorkingDirectory = workingDirectory; Serializer = serializer; } - protected abstract FileInfo ConfigurationFile { get; } + private FileInfo ConfigurationFile => new(ConfigurationSource); private IConfigurationSerializer Serializer { get; } - public abstract string[] SupportedExtensions { get; } - public override void Load() { if (!ConfigurationFile.Exists) { throw new ConfigurationNotLoadedError( @@ -29,7 +26,8 @@ public override void Load() { ); } try { - Configuration = Serializer.Deserialize(File.ReadAllText(ConfigurationFile.FullName)); + var text = File.ReadAllText(ConfigurationFile.FullName); + Configuration = Serializer.Deserialize(text); } catch (Exception e) { throw new ConfigurationNotLoadedError( diff --git a/Linguard/Core/Managers/IConfigurationManager.cs b/Linguard/Core/Managers/IConfigurationManager.cs index 789c4f2..741b9a6 100644 --- a/Linguard/Core/Managers/IConfigurationManager.cs +++ b/Linguard/Core/Managers/IConfigurationManager.cs @@ -9,13 +9,14 @@ namespace Linguard.Core.Managers; /// public interface IConfigurationManager { /// - /// Configuration of the application. + /// Indicates where is the configuration. /// - IConfiguration Configuration { get; set; } + /// Could be a path to a file, a connection string, etc. + string ConfigurationSource { get; set; } /// - /// Working directory of the application. + /// Configuration of the application. /// - IWorkingDirectory WorkingDirectory { get; set; } + IConfiguration Configuration { get; set; } /// /// Manage plugins of the application. /// diff --git a/Linguard/Core/Managers/YamlConfigurationManager.cs b/Linguard/Core/Managers/YamlConfigurationManager.cs deleted file mode 100644 index 3594e92..0000000 --- a/Linguard/Core/Managers/YamlConfigurationManager.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Linguard.Core.Configuration; -using Linguard.Core.Configuration.Serialization; -using Linguard.Core.OS; -using Linguard.Core.Plugins; - -namespace Linguard.Core.Managers; - -public class YamlConfigurationManager : DefaultFileConfigurationManager where T : IConfiguration { - - public override string[] SupportedExtensions => new [] { - "yaml", "yml" - }; - - public YamlConfigurationManager(IConfiguration configuration, IWorkingDirectory workingDirectory, - ISystemWrapper systemWrapper, IConfigurationSerializer serializer, - IPluginEngine pluginEngine) - : base(configuration, workingDirectory, systemWrapper, serializer, pluginEngine) { - } -} \ No newline at end of file diff --git a/Linguard/Core/Models/Wireguard/Validators/ClientValidator.cs b/Linguard/Core/Models/Wireguard/Validators/ClientValidator.cs index 2034a1f..4f6c30d 100644 --- a/Linguard/Core/Models/Wireguard/Validators/ClientValidator.cs +++ b/Linguard/Core/Models/Wireguard/Validators/ClientValidator.cs @@ -7,14 +7,14 @@ namespace Linguard.Core.Models.Wireguard.Validators; public class ClientValidator : AbstractValidator { private readonly IConfigurationManager _configurationManager; - private IWireguardConfiguration Configuration => _configurationManager.Configuration.GetModule()!; + private IWireguardOptions Options => _configurationManager.Configuration.Wireguard; public ClientValidator(IConfigurationManager configurationManager) { _configurationManager = configurationManager; } public override ValidationResult Validate(ValidationContext context) { - SetNameRules(Configuration); + SetNameRules(Options); SetAllowedIPsRules(); SetIPv4Rules(); SetIpv6Rules(); @@ -26,26 +26,26 @@ public override ValidationResult Validate(ValidationContext context) { return base.Validate(context); } - private void SetPrivateKeyRules(IWireguardConfiguration configuration) { + private void SetPrivateKeyRules(IWireguardOptions options) { const string field = nameof(Client.PublicKey); RuleFor(c => c.PublicKey).NotEmpty() .WithMessage($"{field} {Validation.CannotBeEmpty}") .DependentRules(() => { RuleFor(c => c.PublicKey) - .Must(key => !configuration.Interfaces + .Must(key => !options.Interfaces .SelectMany(i => i.Clients) .Select(c => c.PublicKey).Contains(key)) .WithMessage($"{Validation.ClientPublicKeyAlreadyInUse}."); }); } - private void SetPublicKeyRules(IWireguardConfiguration configuration) { + private void SetPublicKeyRules(IWireguardOptions options) { const string field = nameof(Client.PrivateKey); RuleFor(c => c.PrivateKey).NotEmpty() .WithMessage($"{field} {Validation.CannotBeEmpty}") .DependentRules(() => { RuleFor(c => c.PrivateKey) - .Must(key => !configuration.Interfaces + .Must(key => !options.Interfaces .SelectMany(i => i.Clients) .Select(c => c.PrivateKey).Contains(key)) .WithMessage($"{Validation.ClientPrivateKeyAlreadyInUse}."); @@ -88,13 +88,13 @@ private void SetAllowedIPsRules() { .WithMessage($"{field} {Validation.CannotBeEmpty}"); } - private void SetNameRules(IWireguardConfiguration configuration) { + private void SetNameRules(IWireguardOptions options) { const string field = nameof(Client.Name); RuleFor(c => c.Name).NotEmpty() .WithMessage($"{field} {Validation.CannotBeEmpty}.") .DependentRules(() => { RuleFor(c => c.Name) - .Must((client, name) => !configuration.Interfaces + .Must((client, name) => !options.Interfaces .SelectMany(i => i.Clients) .Where(c => c.PublicKey != client.PublicKey) .Select(c => c.Name).Contains(name)) diff --git a/Linguard/Core/Models/Wireguard/Validators/InterfaceValidator.cs b/Linguard/Core/Models/Wireguard/Validators/InterfaceValidator.cs index 8e6afbf..4d57c82 100644 --- a/Linguard/Core/Models/Wireguard/Validators/InterfaceValidator.cs +++ b/Linguard/Core/Models/Wireguard/Validators/InterfaceValidator.cs @@ -14,7 +14,7 @@ public class InterfaceValidator : AbstractValidator { public const int MinNameLength = 2; private readonly IConfigurationManager _configurationManager; - private IWireguardConfiguration Configuration => _configurationManager.Configuration.GetModule()!; + private IWireguardOptions Options => _configurationManager.Configuration.Wireguard; private readonly ISystemWrapper _system; public InterfaceValidator(IConfigurationManager configurationManager, ISystemWrapper system) { @@ -23,10 +23,10 @@ public InterfaceValidator(IConfigurationManager configurationManager, ISystemWra } public override ValidationResult Validate(ValidationContext context) { - SetNameRules(Configuration); - SetPortRules(Configuration); - SetIpv4Rules(Configuration); - SetIpv6Rules(Configuration); + SetNameRules(Options); + SetPortRules(Options); + SetIpv4Rules(Options); + SetIpv6Rules(Options); SetOnUpRules(); SetOnDownRules(); SetGatewayRules(); @@ -35,13 +35,13 @@ public override ValidationResult Validate(ValidationContext context) return base.Validate(context); } - private void SetPrivateKeyRules(IWireguardConfiguration configuration) { + private void SetPrivateKeyRules(IWireguardOptions options) { const string field = nameof(Interface.PublicKey); RuleFor(i => i.PublicKey).NotEmpty() .WithMessage($"{field} {Validation.CannotBeEmpty}"); } - private void SetPublicKeyRules(IWireguardConfiguration configuration) { + private void SetPublicKeyRules(IWireguardOptions options) { const string field = nameof(Interface.PrivateKey); RuleFor(i => i.PrivateKey).NotEmpty() .WithMessage($"{field} {Validation.CannotBeEmpty}"); @@ -67,21 +67,21 @@ private void SetOnUpRules() { // Ignore } - private void SetIpv6Rules(IWireguardConfiguration configuration) { + private void SetIpv6Rules(IWireguardOptions options) { const string field = nameof(Interface.IPv6Address); RuleFor(i => i.IPv6Address).NotEmpty() .When(i => i.IPv4Address == default) .WithMessage($"{field} {Validation.CannotBeEmpty}"); } - private void SetIpv4Rules(IWireguardConfiguration configuration) { + private void SetIpv4Rules(IWireguardOptions options) { const string field = nameof(Interface.IPv4Address); RuleFor(i => i.IPv4Address).NotEmpty() .When(i => i.IPv6Address == default) .WithMessage($"{field} {Validation.CannotBeEmpty}"); } - private void SetPortRules(IWireguardConfiguration configuration) { + private void SetPortRules(IWireguardOptions options) { const string field = nameof(Interface.Port); RuleFor(i => i.Port).NotEmpty() .WithMessage($"{field} {Validation.CannotBeEmpty}") @@ -91,7 +91,7 @@ private void SetPortRules(IWireguardConfiguration configuration) { }); } - private void SetNameRules(IWireguardConfiguration configuration) { + private void SetNameRules(IWireguardOptions options) { const string field = nameof(Interface.Name); RuleFor(i => i.Name).NotEmpty() .WithMessage($"{field} {Validation.CannotBeEmpty}.") @@ -104,7 +104,7 @@ private void SetNameRules(IWireguardConfiguration configuration) { .WithMessage($"{field} {Validation.CharactersNotAllowed}: " + $"{Validation.CharactersAllowedForInterfaceName}."); RuleFor(i => i.Name) - .Must((iface, name) => !configuration.Interfaces + .Must((iface, name) => !options.Interfaces .Where(i => i.PublicKey != iface.PublicKey) .Select(i => i.Name).Contains(name)) .WithMessage($"{Validation.InterfaceNameAlreadyInUse}."); diff --git a/Linguard/Core/Plugins/IPluginEngine.cs b/Linguard/Core/Plugins/IPluginEngine.cs index 2a98a83..500266f 100644 --- a/Linguard/Core/Plugins/IPluginEngine.cs +++ b/Linguard/Core/Plugins/IPluginEngine.cs @@ -9,7 +9,7 @@ public interface IPluginEngine { /// Load plugins from the given directory. /// /// - /// /// The amount of plugins successfully loaded. - int LoadPlugins(DirectoryInfo pluginsDirectory, IConfigurationManager configurationManager); + int LoadPlugins(DirectoryInfo pluginsDirectory); + void InitializePlugins(IConfigurationManager configurationManager); } \ No newline at end of file diff --git a/Linguard/Core/Plugins/PluginEngine.cs b/Linguard/Core/Plugins/PluginEngine.cs index 5bf9f80..f7e2f49 100644 --- a/Linguard/Core/Plugins/PluginEngine.cs +++ b/Linguard/Core/Plugins/PluginEngine.cs @@ -11,15 +11,15 @@ public PluginEngine(ILogger logger) { _logger = logger; } - private readonly List _plugins = new(); - public IEnumerable Plugins => _plugins; + private readonly IDictionary _plugins = new Dictionary(); + public IEnumerable Plugins => _plugins.Values; - public int LoadPlugins(DirectoryInfo pluginsDirectory, IConfigurationManager configurationManager) { + public int LoadPlugins(DirectoryInfo directory) { var plugins = new List(); - foreach (var file in pluginsDirectory.EnumerateFiles()) { + foreach (var file in directory.EnumerateFiles()) { try { _logger.LogDebug($"Loading plugins from file {file.FullName}..."); - var p = LoadPlugins(file, configurationManager).ToList(); + var p = LoadPlugins(file).ToList(); _logger.LogDebug($"Loaded {p.Count} plugins from file {file.FullName}."); plugins.AddRange(p); } @@ -27,24 +27,32 @@ public int LoadPlugins(DirectoryInfo pluginsDirectory, IConfigurationManager con _logger.LogError(e, $"Unable to load plugins from file {file.FullName}."); } } - _plugins.AddRange(plugins); + foreach (var plugin in plugins) { + _plugins[plugin.GetType()] = plugin; + } return plugins.Count; } - private IEnumerable LoadPlugins(FileInfo file, IConfigurationManager configurationManager) { + public void InitializePlugins(IConfigurationManager configurationManager) { + foreach (var plugin in Plugins) { + _logger.LogTrace($"Initializing plugin {plugin.Name}..."); + plugin.Initialize(configurationManager); + _logger.LogTrace($"Plugin {plugin.Name} was initialized successfully."); + } + } + + private IEnumerable LoadPlugins(FileInfo file) { var context = new PluginLoadContext(file); var assemblyName = new AssemblyName(Path.GetFileNameWithoutExtension(file.FullName)); var assembly = context.LoadFromAssemblyName(assemblyName); var plugins = new List(); foreach (var type in assembly.ExportedTypes) { if (type.GetInterface(nameof(IPlugin)) == default) continue; + if (_plugins.ContainsKey(type)) continue; try { _logger.LogTrace($"Loading plugin {type.FullName} from assembly {assembly.FullName}..."); var plugin = (IPlugin)Activator.CreateInstance(type)!; _logger.LogTrace($"Plugin {plugin.Name} was loaded successfully."); - _logger.LogTrace($"Initializing plugin {plugin.Name}..."); - plugin.Initialize(configurationManager); - _logger.LogTrace($"Plugin {plugin.Name} was initialized successfully."); plugins.Add(plugin); } catch (Exception e) { diff --git a/Linguard/Core/Services/DefaultClientGenerator.cs b/Linguard/Core/Services/DefaultClientGenerator.cs index aeac0a0..5cad162 100644 --- a/Linguard/Core/Services/DefaultClientGenerator.cs +++ b/Linguard/Core/Services/DefaultClientGenerator.cs @@ -8,7 +8,7 @@ namespace Linguard.Core.Services; public class DefaultClientGenerator : IClientGenerator { - private IWireguardConfiguration Configuration => _configurationManager.Configuration.GetModule()!; + private IWireguardOptions Options => _configurationManager.Configuration.Wireguard; private readonly IConfigurationManager _configurationManager; private readonly IWireguardService _wireguard; @@ -49,14 +49,14 @@ public Client Generate(Interface iface) { return ips; }) .RuleFor(c => c.Endpoint, iface.Endpoint - ?? Configuration.Endpoint + ?? Options.Endpoint ?? new(Network.GetPublicIPAddress()?.ToString() ?? string.Empty, UriKind.RelativeOrAbsolute)) .RuleFor(c => c.PrimaryDns, iface.PrimaryDns - ?? Configuration.PrimaryDns + ?? Options.PrimaryDns ?? new Uri("8.8.8.8", UriKind.RelativeOrAbsolute)) .RuleFor(c => c.SecondaryDns, iface.SecondaryDns - ?? Configuration.SecondaryDns + ?? Options.SecondaryDns ?? new Uri("8.8.4.4", UriKind.RelativeOrAbsolute)) .Generate(); } diff --git a/Linguard/Core/Services/DefaultInterfaceGenerator.cs b/Linguard/Core/Services/DefaultInterfaceGenerator.cs index c71043a..db049b6 100644 --- a/Linguard/Core/Services/DefaultInterfaceGenerator.cs +++ b/Linguard/Core/Services/DefaultInterfaceGenerator.cs @@ -14,7 +14,7 @@ public class DefaultInterfaceGenerator : IInterfaceGenerator { private readonly ISystemWrapper _system; private readonly IConfigurationManager _configurationManager; private const int MaxTries = 100; - private IWireguardConfiguration Configuration => _configurationManager.Configuration.GetModule()!; + private IWireguardOptions Options => _configurationManager.Configuration.Wireguard; public DefaultInterfaceGenerator(IConfigurationManager configurationManager, IWireguardService wireguardService, ISystemWrapper system) { @@ -36,7 +36,7 @@ public Interface Generate() { .RuleFor(i => i.Name, () => { for (var tries = 0; tries < MaxTries; tries++) { var name = $"wg{tries}"; - if (!Configuration.Interfaces.Select(i => i.Name).Contains(name)) { + if (!Options.Interfaces.Select(i => i.Name).Contains(name)) { return name; } } @@ -45,16 +45,16 @@ public Interface Generate() { .RuleFor(i => i.Port, f => { for (var tries = 0; tries < MaxTries; tries++) { var port = f.Internet.Port(); - if (!Configuration.Interfaces.Select(i => i.Port).Contains(port)) { + if (!Options.Interfaces.Select(i => i.Port).Contains(port)) { return port; } } return default; }) .RuleFor(i => i.OnDown, - (_, i) => WireguardUtils.GenerateOnDownRules(Configuration.IptablesBin, i.Name, i.Gateway)) + (_, i) => WireguardUtils.GenerateOnDownRules(Options.IptablesBin, i.Name, i.Gateway)) .RuleFor(i => i.OnUp, - (_, i) => WireguardUtils.GenerateOnUpRules(Configuration.IptablesBin, i.Name, i.Gateway)) + (_, i) => WireguardUtils.GenerateOnUpRules(Options.IptablesBin, i.Name, i.Gateway)) .RuleFor(i => i.PrivateKey, _wireguardService.GeneratePrivateKey()) .RuleFor(i => i.PublicKey, (_, i) => _wireguardService.GeneratePublicKey(i.PrivateKey)) @@ -63,7 +63,7 @@ public Interface Generate() { var addr = f.Internet.IpAddress(); var ip = IPAddressCidr.Parse(addr, IPNetwork.Parse(addr.ToString()).Cidr); var canBeUsed = true; - foreach (var address in Configuration.Interfaces.Select(i => i.IPv4Address)) { + foreach (var address in Options.Interfaces.Select(i => i.IPv4Address)) { if (ip.Equals(address) || address.Contains(ip.IPAddress)) { canBeUsed = false; break; @@ -78,7 +78,7 @@ public Interface Generate() { var addr = f.Internet.Ipv6Address(); var ip = IPAddressCidr.Parse(addr, IPNetwork.Parse(addr.ToString()).Cidr); var canBeUsed = true; - foreach (var address in Configuration.Interfaces.Select(i => i.IPv6Address)) { + foreach (var address in Options.Interfaces.Select(i => i.IPv6Address)) { if (ip.Equals(address) || address.Contains(ip.IPAddress)) { canBeUsed = false; break; @@ -88,9 +88,9 @@ public Interface Generate() { } return default; }) - .RuleFor(i => i.Endpoint, Configuration.Endpoint) - .RuleFor(i => i.PrimaryDns, Configuration.PrimaryDns) - .RuleFor(i => i.SecondaryDns, Configuration.SecondaryDns) + .RuleFor(i => i.Endpoint, Options.Endpoint) + .RuleFor(i => i.PrimaryDns, Options.PrimaryDns) + .RuleFor(i => i.SecondaryDns, Options.SecondaryDns) .RuleFor(i => i.Clients, new HashSet()) .Generate(); } diff --git a/Linguard/Core/Services/TrafficStorageService.cs b/Linguard/Core/Services/TrafficStorageService.cs index ba6ce8f..be24818 100644 --- a/Linguard/Core/Services/TrafficStorageService.cs +++ b/Linguard/Core/Services/TrafficStorageService.cs @@ -7,7 +7,7 @@ namespace Linguard.Core.Services; public class TrafficStorageService : ITrafficStorageService { - private ITrafficConfiguration Configuration => _configurationManager.Configuration.GetModule()!; + private ITrafficOptions Options => _configurationManager.Configuration.Traffic; private readonly IConfigurationManager _configurationManager; private readonly IWireguardService _wireguardService; private readonly Timer _timer; @@ -19,7 +19,7 @@ public TrafficStorageService(IConfigurationManager configurationManager, _timer = new Timer { Enabled = false, AutoReset = true, - Interval = Configuration.StorageDriver.CollectionInterval.TotalMilliseconds + Interval = Options.StorageDriver.CollectionInterval.TotalMilliseconds }; _timer.Elapsed += (_, _) => { CollectData(); @@ -27,15 +27,15 @@ public TrafficStorageService(IConfigurationManager configurationManager, } private void CollectData() { var data = _wireguardService.GetTrafficData(); - Configuration.StorageDriver.Save(data); + Options.StorageDriver.Save(data); } public void RefreshConfiguration() { - _timer.Enabled = Configuration.Enabled; - _timer.Interval = Configuration.StorageDriver.CollectionInterval.TotalMilliseconds; + _timer.Enabled = Options.Enabled; + _timer.Interval = Options.StorageDriver.CollectionInterval.TotalMilliseconds; } public IEnumerable LoadData() { - return Configuration.StorageDriver.Load(); + return Options.StorageDriver.Load(); } } \ No newline at end of file diff --git a/Linguard/Core/Services/WireguardConfigParser.cs b/Linguard/Core/Services/WireguardConfigParser.cs index da826bf..01489f7 100644 --- a/Linguard/Core/Services/WireguardConfigParser.cs +++ b/Linguard/Core/Services/WireguardConfigParser.cs @@ -21,7 +21,7 @@ public WireguardConfigParser(IConfigurationManager configurationManager, _faker = faker; } - private IWireguardConfiguration Configuration => _configurationManager.Configuration.GetModule()!; + private IWireguardOptions Options => _configurationManager.Configuration.Wireguard; /// /// Valid sections in a Wireguard configuration file. @@ -222,7 +222,7 @@ private Client ParseClient(IEnumerable lines) { break; case WireguardPeerConfigurationOption.PublicKey: var ifacePublicKey = setting.Value; - iface = Configuration.Interfaces + iface = Options.Interfaces .SingleOrDefault(i => i.PublicKey == ifacePublicKey); if (iface == default) { throw new WireguardConfigurationParsingError( @@ -256,7 +256,7 @@ private Client ParseClient(IEnumerable lines) { } if (client.Endpoint == default) { client.Endpoint = iface.Endpoint ?? - Configuration.Endpoint + Options.Endpoint ?? throw new WireguardConfigurationParsingError("No endpoint provided!"); } if (client.AllowedIPs == default) { diff --git a/Linguard/Core/Services/WireguardService.cs b/Linguard/Core/Services/WireguardService.cs index 9b6b643..69bb876 100644 --- a/Linguard/Core/Services/WireguardService.cs +++ b/Linguard/Core/Services/WireguardService.cs @@ -17,62 +17,62 @@ public WireguardService(IConfigurationManager configurationManager, ISystemWrapp _systemWrapper = systemWrapper; } - private IWireguardConfiguration Configuration => _configurationManager.Configuration.GetModule()!; + private IWireguardOptions Options => _configurationManager.Configuration.Wireguard; public void StartInterface(Interface iface) { if (_systemWrapper.IsInterfaceUp(iface)) return; - var filepath = _configurationManager.WorkingDirectory.GetInterfaceConfigurationFile(iface).FullName; + var filepath = _configurationManager.Configuration.Wireguard.GetInterfaceConfigurationFile(iface).FullName; _systemWrapper.WriteAllText(filepath, WireguardUtils.GenerateWireguardConfiguration(iface)); var result = _systemWrapper - .RunCommand($"sudo {Configuration.WireguardQuickBin} up {iface.Name}"); + .RunCommand($"sudo {Options.WireguardQuickBin} up {iface.Name}"); if (!result.Success) throw new WireguardException(result.Stderr); } public void StopInterface(Interface iface) { if (_systemWrapper.IsInterfaceDown(iface)) return; var result = _systemWrapper - .RunCommand($"sudo {Configuration.WireguardQuickBin} down {iface.Name}"); + .RunCommand($"sudo {Options.WireguardQuickBin} down {iface.Name}"); if (!result.Success) throw new WireguardException(result.Stderr); } public void AddClient(Interface iface, Client client) { - var cmd = $"sudo {Configuration.WireguardQuickBin} set {iface.Name} peer {client.PublicKey} " + + var cmd = $"sudo {Options.WireguardQuickBin} set {iface.Name} peer {client.PublicKey} " + $"allowed-ips {string.Join(",", client.AllowedIPs)}"; var result = _systemWrapper.RunCommand(cmd); if (!result.Success) throw new WireguardException(result.Stderr); } public void RemoveClient(Client client) { - var iface = Configuration.GetInterface(client); - var cmd = $"sudo {Configuration.WireguardQuickBin} set {iface.Name} peer {client.PublicKey} remove"; + var iface = Options.GetInterface(client); + var cmd = $"sudo {Options.WireguardQuickBin} set {iface.Name} peer {client.PublicKey} remove"; var result = _systemWrapper.RunCommand(cmd); if (!result.Success) throw new WireguardException(result.Stderr); } public void RemoveInterface(Interface iface) { StopInterface(iface); - var file = _configurationManager.WorkingDirectory.GetInterfaceConfigurationFile(iface); + var file = _configurationManager.Configuration.Wireguard.GetInterfaceConfigurationFile(iface); if (!file.Exists) return; file.Delete(); } public string GeneratePrivateKey() { var result = _systemWrapper - .RunCommand($"sudo {Configuration.WireguardBin} genkey"); + .RunCommand($"sudo {Options.WireguardBin} genkey"); if (!result.Success) throw new WireguardException(result.Stderr); return result.Stdout; } public string GeneratePublicKey(string privateKey) { var result = _systemWrapper - .RunCommand($"echo {privateKey} | sudo {Configuration.WireguardBin} pubkey"); + .RunCommand($"echo {privateKey} | sudo {Options.WireguardBin} pubkey"); if (!result.Success) throw new WireguardException(result.Stderr); return result.Stdout; } public DateTime GetLastHandshake(Client client) { var rawData = _systemWrapper - .RunCommand($"{Configuration.WireguardBin} show {Configuration.GetInterface(client).Name} dump") + .RunCommand($"{Options.WireguardBin} show {Options.GetInterface(client).Name} dump") .Stdout; try { return WireguardDumpParser.GetLastHandshake(rawData, client); @@ -84,20 +84,20 @@ public DateTime GetLastHandshake(Client client) { public IEnumerable GetTrafficData() { var data = new List(); - foreach (var iface in Configuration.Interfaces) { + foreach (var iface in Options.Interfaces) { data.AddRange(GetTrafficData(iface)); } return data; } public TrafficData? GetTrafficData(Client client) { - var data = GetTrafficData(Configuration.GetInterface(client)); + var data = GetTrafficData(Options.GetInterface(client)); return data.SingleOrDefault(e => e.Peer.Equals(client)); } public IEnumerable GetTrafficData(Interface iface) { var rawData = _systemWrapper - .RunCommand($"{Configuration.WireguardBin} show {iface.Name} dump") + .RunCommand($"{Options.WireguardBin} show {iface.Name} dump") .Stdout; return string.IsNullOrEmpty(rawData) ? Enumerable.Empty() diff --git a/Linguard/Json.Test/Json.Test.csproj b/Linguard/Json.Test/Json.Test.csproj index 9b6174b..dfe7068 100644 --- a/Linguard/Json.Test/Json.Test.csproj +++ b/Linguard/Json.Test/Json.Test.csproj @@ -8,7 +8,6 @@ - @@ -23,7 +22,14 @@ + + + + Always + + + diff --git a/Linguard/Json.Test/JsonSerializerOptionsBuilder.cs b/Linguard/Json.Test/JsonSerializerOptionsBuilder.cs new file mode 100644 index 0000000..0ed1515 --- /dev/null +++ b/Linguard/Json.Test/JsonSerializerOptionsBuilder.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.Text.Json; +using Linguard.Core; +using Linguard.Core.Configuration; +using Linguard.Core.Models.Wireguard; +using Linguard.Core.OS; +using Linguard.Core.Plugins; +using Linguard.Json.Converters; + +namespace Json.Test; + +public class JsonSerializerOptionsBuilder { + private readonly ISystemWrapper _systemWrapper; + private readonly IPluginEngine _pluginEngine; + + public JsonSerializerOptionsBuilder(ISystemWrapper systemWrapper, IPluginEngine pluginEngine) { + _systemWrapper = systemWrapper; + _pluginEngine = pluginEngine; + } + + public JsonSerializerOptions Build() { + return new JsonSerializerOptions { + Converters = { + new TypeMappingConverter(), + new TypeMappingConverter(), + new TypeMappingConverter(), + new TypeMappingConverter(), + new TypeMappingConverter, HashSet>(), + new TypeMappingConverter, HashSet>(), + new TypeMappingConverter, HashSet>(), + new TypeMappingConverter, HashSet>(), + new TypeMappingConverter, HashSet>(), + new IPAddressCidrConverter(), + new NetworkInterfaceConverter(_systemWrapper), + new UriConverter(), + new RuleConverter(), + new TrafficStorageDriverConverter(_pluginEngine), + new ConfigurationModuleConverter(), + new PluginConverter() + } + }; + } +} \ No newline at end of file diff --git a/Linguard/Json.Test/JsonSerializerShould.cs b/Linguard/Json.Test/JsonSerializerShould.cs index 19a5ca5..df52dfd 100644 --- a/Linguard/Json.Test/JsonSerializerShould.cs +++ b/Linguard/Json.Test/JsonSerializerShould.cs @@ -1,134 +1,159 @@ using System; using System.Collections.Generic; -using System.Linq; +using System.IO; using System.Text.Json; -using Bogus; -using ByteSizeLib; using Core.Test.Mocks; +using Core.Test.Stubs; using FluentAssertions; +using Json.Test.Stubs; +using Linguard.Core; using Linguard.Core.Configuration; -using Linguard.Core.Drivers.TrafficStorage; -using Linguard.Core.Managers; -using Linguard.Core.Models; using Linguard.Core.Models.Wireguard; -using Linguard.Json; using Xunit; namespace Json.Test; public class JsonSerializerShould { - private static readonly IConfigurationManager ConfigurationManager - = new DefaultConfigurationManager().Object; - private static readonly ITrafficStorageDriver TrafficStorageDriver - = new TrafficStorageDriver(); - - private Faker _faker = new(); - - private static IWireguardConfiguration WireguardConfiguration => - ConfigurationManager.Configuration.GetModule()!; - - private const string DateTimeFormat = "yyyy-MM-dd HH:mm:ss"; - - private JsonSerializerOptions SerializerOptions => - TrafficDataSerializerOptions.Build(WireguardConfiguration, DateTimeFormat); - - public JsonSerializerShould() { - ConfigurationManager.Configuration.GetModule()!.StorageDriver = TrafficStorageDriver; - - WireguardConfiguration.Interfaces.Add(new Interface { - PublicKey = _faker.Random.String2(20), - Clients = new HashSet { - new() { - PublicKey = _faker.Random.String2(20), - } - } - }); - WireguardConfiguration.Interfaces.Add(new Interface { - PublicKey = _faker.Random.String2(20), - Clients = new HashSet { - new() { - PublicKey = _faker.Random.String2(20), - } - } - }); - } - - private static string ToJson(ITrafficData data) { - return - "{" + - @$"""Peer"":""{data.Peer.PublicKey}""," + - @$"""SentData"":{data.SentData.Bytes}," + - @$"""ReceivedData"":{data.ReceivedData.Bytes}," + - @$"""TimeStamp"":""{data.TimeStamp.ToString(DateTimeFormat)}""" + - "}"; - } - [Fact] - public void SerializeTrafficData() { - ITrafficData data = new TrafficData { - Peer = WireguardConfiguration.Interfaces.First(), - ReceivedData = ByteSize.FromMegaBytes(1), - SentData = ByteSize.FromMegaBytes(2), - TimeStamp = DateTime.UnixEpoch + public void SerializeTrafficConfiguration() { + var config = new TrafficOptions { + Enabled = false, + StorageDriver = new TrafficStorageDriverStub() }; - var expected = ToJson(data); - var output = JsonSerializer.Serialize(data, SerializerOptions); - output.Should().Be(expected); + const string expected = + "{\"Enabled\":false,\"StorageDriver\":{\"Name\":\"Stub driver\"," + + "\"Description\":\"This is a stub driver\",\"CollectionInterval\":\"01:00:00\"," + + "\"AdditionalOptions\":{\"Fake\":\"Option\"}}}"; + var options = new JsonSerializerOptionsBuilder(new SystemMock().Object, new PluginEngineMock().Object).Build(); + var output = JsonSerializer.Serialize(config, options); + output.Trim().Should().Be(expected.Trim()); } - + [Fact] - public void SerializeTrafficDataList() { - IEnumerable data = new List { - new TrafficData { - Peer = WireguardConfiguration.Interfaces.First(), - ReceivedData = ByteSize.FromMegaBytes(1), - SentData = ByteSize.FromMegaBytes(2), - TimeStamp = DateTime.UnixEpoch + public void SerializeConfiguration() { + var json = File.ReadAllText("expected.json"); + var traffic = new TrafficOptions { + Enabled = false, + StorageDriver = new TrafficStorageDriverStub() + }; + var auth = new AuthenticationOptions { + DataSource = "auth.db" + }; + var wireguard = new WireguardOptions { + Interfaces = new HashSet { + new() { + PublicKey = "ifacePubkey", + Name = "wg1", + IPv4Address = IPAddressCidr.Parse("1.1.1.1/24"), + Gateway = new NetworkInterfaceMock("eth0").Object, + Clients = new HashSet { + new() { + Endpoint = new Uri("vpn.example.com", UriKind.RelativeOrAbsolute), + Name = "peer1", + IPv4Address = IPAddressCidr.Parse("1.1.1.2/30"), + AllowedIPs = new HashSet { + IPAddressCidr.Parse("1.1.1.0/24"), IPAddressCidr.Parse("1.1.2.0/24") + }, + PublicKey = "00000000-0000-0000-0000-000000000000" + }, + new() { + Endpoint = new Uri("192.168.0.1", UriKind.RelativeOrAbsolute), + Name = "peer2", + IPv4Address = IPAddressCidr.Parse("1.1.1.3/30"), + AllowedIPs = new HashSet { + IPAddressCidr.Parse("1.1.1.0/24"), IPAddressCidr.Parse("1.1.2.0/24") + }, + PublicKey = "00000000-0000-0000-0000-000000000001" + }, + new() { + Name = "peer3", + IPv4Address = IPAddressCidr.Parse("1.1.1.4/30"), + PublicKey = "00000000-0000-0000-0000-000000000002" + } + }, + OnUp = new HashSet { + "iptables fake rule 1", "iptables fake rule 2" + } + } }, - new TrafficData { - Peer = WireguardConfiguration.Interfaces.Last(), - ReceivedData = ByteSize.FromMegaBytes(2), - SentData = ByteSize.FromMegaBytes(1), - TimeStamp = DateTime.UnixEpoch - } + IptablesBin = "iptables", + WireguardBin = "wg", + WireguardQuickBin = "wg-quick" }; - var expected = $"[{string.Join(",", data.Select(ToJson))}]"; - var output = JsonSerializer.Serialize(data, SerializerOptions); - output.Should().Be(expected); - } - - [Fact] - public void DeserializeTrafficDataAsConcreteType() { - ITrafficData expected = new TrafficData { - Peer = WireguardConfiguration.Interfaces.First(), - ReceivedData = ByteSize.FromMegaBytes(1), - SentData = ByteSize.FromMegaBytes(2), - TimeStamp = DateTime.UnixEpoch + var plugins = new PluginOptions(); + var config = new ConfigurationBase { + Traffic = traffic, + Wireguard = wireguard, + Plugins = plugins, + Authentication = auth }; - var data = ToJson(expected); - var output = JsonSerializer.Deserialize(data, SerializerOptions); - output.Should().Be(expected); + var serializer = new JsonConfigurationSerializerStub(new SystemMock().Object, new PluginEngineMock().Object); + var output = serializer.Serialize(config); + output.Trim().Should().Be(json.Trim()); } [Fact] - public void DeserializeTrafficDataList() { - IEnumerable expected = new List { - new TrafficData { - Peer = WireguardConfiguration.Interfaces.First(), - ReceivedData = ByteSize.FromMegaBytes(1), - SentData = ByteSize.FromMegaBytes(2), - TimeStamp = DateTime.UnixEpoch + public void DeserializeConfiguration() { + var json = File.ReadAllText("expected.json"); + var traffic = new TrafficOptions { + Enabled = false, + StorageDriver = new TrafficStorageDriverStub() + }; + var auth = new AuthenticationOptions { + DataSource = "auth.db" + }; + var wireguard = new WireguardOptions { + Interfaces = new HashSet { + new() { + PublicKey = "ifacePubkey", + Name = "wg1", + IPv4Address = IPAddressCidr.Parse("1.1.1.1/24"), + Gateway = new NetworkInterfaceMock("eth0").Object, + Clients = new HashSet { + new() { + Endpoint = new Uri("vpn.example.com", UriKind.RelativeOrAbsolute), + Name = "peer1", + IPv4Address = IPAddressCidr.Parse("1.1.1.2/30"), + AllowedIPs = new HashSet { + IPAddressCidr.Parse("1.1.1.0/24"), IPAddressCidr.Parse("1.1.2.0/24") + }, + PublicKey = "00000000-0000-0000-0000-000000000000" + }, + new() { + Endpoint = new Uri("192.168.0.1", UriKind.RelativeOrAbsolute), + Name = "peer2", + IPv4Address = IPAddressCidr.Parse("1.1.1.3/30"), + AllowedIPs = new HashSet { + IPAddressCidr.Parse("1.1.1.0/24"), IPAddressCidr.Parse("1.1.2.0/24") + }, + PublicKey = "00000000-0000-0000-0000-000000000001" + }, + new() { + Name = "peer3", + IPv4Address = IPAddressCidr.Parse("1.1.1.4/30"), + PublicKey = "00000000-0000-0000-0000-000000000002" + } + }, + OnUp = new HashSet { + "iptables fake rule 1", "iptables fake rule 2" + } + } }, - new TrafficData { - Peer = WireguardConfiguration.Interfaces.Last(), - ReceivedData = ByteSize.FromMegaBytes(2), - SentData = ByteSize.FromMegaBytes(1), - TimeStamp = DateTime.UnixEpoch - } + IptablesBin = "iptables", + WireguardBin = "wg", + WireguardQuickBin = "wg-quick" + }; + var plugins = new PluginOptions(); + var config = new ConfigurationBase { + Traffic = traffic, + Wireguard = wireguard, + Plugins = plugins, + Authentication = auth }; - var data = $"[{string.Join(",", expected.Select(ToJson))}]"; - var output = JsonSerializer.Deserialize>(data, SerializerOptions); - output.Should().BeEquivalentTo(expected); + var serializer = new JsonConfigurationSerializerStub(new SystemMock().Object, new PluginEngineMock().Object); + var output = serializer.Deserialize(json); + output.Should().BeEquivalentTo(config); } + } \ No newline at end of file diff --git a/Linguard/Json.Test/Stubs/JsonConfigurationSerializerStub.cs b/Linguard/Json.Test/Stubs/JsonConfigurationSerializerStub.cs new file mode 100644 index 0000000..f9b27b6 --- /dev/null +++ b/Linguard/Json.Test/Stubs/JsonConfigurationSerializerStub.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; +using Linguard.Core; +using Linguard.Core.Configuration; +using Linguard.Core.Models.Wireguard; +using Linguard.Core.OS; +using Linguard.Core.Plugins; +using Linguard.Json; +using Linguard.Json.Converters; + +namespace Json.Test.Stubs; + +public class JsonConfigurationSerializerStub : JsonConfigurationSerializer { + + public JsonConfigurationSerializerStub(ISystemWrapper systemWrapper, IPluginEngine pluginEngine) : base(new JsonSerializerOptions { + Converters = { + new TypeMappingConverter(), + new TypeMappingConverter(), + new TypeMappingConverter(), + new TypeMappingConverter(), + new TypeMappingConverter(), + new TypeMappingConverter, HashSet>(), + new TypeMappingConverter, HashSet>(), + new TypeMappingConverter, HashSet>(), + new TypeMappingConverter, HashSet>(), + new TypeMappingConverter, Dictionary>(), + new IPAddressCidrConverter(), + new NetworkInterfaceConverter(systemWrapper), + new UriConverter(), + new RuleConverter(), + new TrafficStorageDriverConverter(pluginEngine), + new ConfigurationModuleConverter(), + new PluginConverter(), + new DirectoryInfoConverter() + }, + WriteIndented = true + }, pluginEngine) + { } +} \ No newline at end of file diff --git a/Linguard/Json.Test/expected.json b/Linguard/Json.Test/expected.json new file mode 100644 index 0000000..fd4b8ca --- /dev/null +++ b/Linguard/Json.Test/expected.json @@ -0,0 +1,96 @@ +{ + "Wireguard": { + "InterfacesDirectory": null, + "Interfaces": [ + { + "Gateway": "eth0", + "Port": 0, + "Auto": false, + "Clients": [ + { + "AllowedIPs": [ + "1.1.1.0/24", + "1.1.2.0/24" + ], + "Nat": false, + "PrimaryDns": null, + "SecondaryDns": null, + "Endpoint": "vpn.example.com", + "PublicKey": "00000000-0000-0000-0000-000000000000", + "PrivateKey": null, + "IPv4Address": "1.1.1.2/30", + "IPv6Address": null, + "Name": "peer1", + "Description": null + }, + { + "AllowedIPs": [ + "1.1.1.0/24", + "1.1.2.0/24" + ], + "Nat": false, + "PrimaryDns": null, + "SecondaryDns": null, + "Endpoint": "192.168.0.1", + "PublicKey": "00000000-0000-0000-0000-000000000001", + "PrivateKey": null, + "IPv4Address": "1.1.1.3/30", + "IPv6Address": null, + "Name": "peer2", + "Description": null + }, + { + "AllowedIPs": null, + "Nat": false, + "PrimaryDns": null, + "SecondaryDns": null, + "Endpoint": null, + "PublicKey": "00000000-0000-0000-0000-000000000002", + "PrivateKey": null, + "IPv4Address": "1.1.1.4/30", + "IPv6Address": null, + "Name": "peer3", + "Description": null + } + ], + "OnUp": [ + "iptables fake rule 1", + "iptables fake rule 2" + ], + "OnDown": null, + "PrimaryDns": null, + "SecondaryDns": null, + "Endpoint": null, + "PublicKey": "ifacePubkey", + "PrivateKey": null, + "IPv4Address": "1.1.1.1/24", + "IPv6Address": null, + "Name": "wg1", + "Description": null + } + ], + "IptablesBin": "iptables", + "WireguardBin": "wg", + "WireguardQuickBin": "wg-quick", + "PrimaryDns": null, + "SecondaryDns": null, + "Endpoint": null + }, + "Traffic": { + "Enabled": false, + "StorageDriver": { + "Name": "Stub driver", + "Description": "This is a stub driver", + "CollectionInterval": "01:00:00", + "AdditionalOptions": { + "Fake": "Option" + } + } + }, + "Plugins": { + "PluginsDirectory": null + }, + "Authentication": { + "DataSource": "auth.db" + } +} \ No newline at end of file diff --git a/Linguard/Json/Converters/ConfigurationModuleConverter.cs b/Linguard/Json/Converters/ConfigurationModuleConverter.cs new file mode 100644 index 0000000..0b6b51d --- /dev/null +++ b/Linguard/Json/Converters/ConfigurationModuleConverter.cs @@ -0,0 +1,18 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Linguard.Core.Configuration; + +namespace Linguard.Json.Converters; + +public class ConfigurationModuleConverter : JsonConverter { + public override IOptionsModule? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + var newOptions = new JsonSerializerOptions(options); + newOptions.Converters.Remove(this); + var module = JsonSerializer.Deserialize(ref reader, typeToConvert, newOptions) as IOptionsModule; + return module; + } + + public override void Write(Utf8JsonWriter writer, IOptionsModule value, JsonSerializerOptions options) { + JsonSerializer.Serialize(writer, value, value.GetType(), options); + } +} \ No newline at end of file diff --git a/Linguard/Json/Converters/DirectoryInfoConverter.cs b/Linguard/Json/Converters/DirectoryInfoConverter.cs new file mode 100644 index 0000000..12a6d27 --- /dev/null +++ b/Linguard/Json/Converters/DirectoryInfoConverter.cs @@ -0,0 +1,15 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Linguard.Json.Converters; + +public class DirectoryInfoConverter : JsonConverter { + public override DirectoryInfo? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + var value = reader.GetString(); + return value != default ? new DirectoryInfo(value) : default; + } + + public override void Write(Utf8JsonWriter writer, DirectoryInfo value, JsonSerializerOptions options) { + writer.WriteStringValue(value.FullName); + } +} \ No newline at end of file diff --git a/Linguard/Json/Converters/IPAddressCidrConverter.cs b/Linguard/Json/Converters/IPAddressCidrConverter.cs new file mode 100644 index 0000000..5576bd7 --- /dev/null +++ b/Linguard/Json/Converters/IPAddressCidrConverter.cs @@ -0,0 +1,17 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Linguard.Core; + +namespace Linguard.Json.Converters; + +public class IPAddressCidrConverter : JsonConverter { + + public override IPAddressCidr? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + var value = reader.GetString(); + return value != default ? IPAddressCidr.Parse(value) : default; + } + + public override void Write(Utf8JsonWriter writer, IPAddressCidr? value, JsonSerializerOptions options) { + writer.WriteStringValue(value?.ToString() ?? string.Empty); + } +} \ No newline at end of file diff --git a/Linguard/Json/Converters/NetworkInterfaceConverter.cs b/Linguard/Json/Converters/NetworkInterfaceConverter.cs new file mode 100644 index 0000000..3af657f --- /dev/null +++ b/Linguard/Json/Converters/NetworkInterfaceConverter.cs @@ -0,0 +1,27 @@ +using System.Net.NetworkInformation; +using System.Text.Json; +using System.Text.Json.Serialization; +using Linguard.Core.OS; + +namespace Linguard.Json.Converters; + +public class NetworkInterfaceConverter : JsonConverter { + + private readonly ISystemWrapper _systemWrapper; + + public NetworkInterfaceConverter(ISystemWrapper systemWrapper) { + _systemWrapper = systemWrapper; + } + + public override NetworkInterface? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + var value = reader.GetString(); + return value != default + ? _systemWrapper.NetworkInterfaces + .SingleOrDefault(i => i.Name.Equals(value, StringComparison.InvariantCultureIgnoreCase)) + : default; + } + + public override void Write(Utf8JsonWriter writer, NetworkInterface value, JsonSerializerOptions options) { + writer.WriteStringValue(value.Name); + } +} \ No newline at end of file diff --git a/Linguard/Json/Converters/PluginConverter.cs b/Linguard/Json/Converters/PluginConverter.cs new file mode 100644 index 0000000..f656ef5 --- /dev/null +++ b/Linguard/Json/Converters/PluginConverter.cs @@ -0,0 +1,16 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Linguard.Core.Plugins; + +namespace Linguard.Json.Converters; + +public class PluginConverter : JsonConverter { + public override IPlugin? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + var value = reader.GetString()!; + return JsonSerializer.Deserialize(value, typeToConvert, options) as IPlugin; + } + + public override void Write(Utf8JsonWriter writer, IPlugin value, JsonSerializerOptions options) { + JsonSerializer.Serialize(writer, value, value.GetType(), options); + } +} \ No newline at end of file diff --git a/Linguard/Json/Converters/RuleConverter.cs b/Linguard/Json/Converters/RuleConverter.cs new file mode 100644 index 0000000..6331811 --- /dev/null +++ b/Linguard/Json/Converters/RuleConverter.cs @@ -0,0 +1,17 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Linguard.Core.Models.Wireguard; + +namespace Linguard.Json.Converters; + +public class RuleConverter : JsonConverter { + + public override Rule? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + var value = reader.GetString(); + return value ?? string.Empty; + } + + public override void Write(Utf8JsonWriter writer, Rule value, JsonSerializerOptions options) { + writer.WriteStringValue(value.Command); + } +} \ No newline at end of file diff --git a/Linguard/Json/Converters/TrafficStorageDriverConverter.cs b/Linguard/Json/Converters/TrafficStorageDriverConverter.cs new file mode 100644 index 0000000..c2c7648 --- /dev/null +++ b/Linguard/Json/Converters/TrafficStorageDriverConverter.cs @@ -0,0 +1,68 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Linguard.Core.Drivers.TrafficStorage; +using Linguard.Core.Plugins; + +namespace Linguard.Json.Converters; + +public class TrafficStorageDriverConverter : JsonConverter { + private readonly IPluginEngine _pluginEngine; + + public TrafficStorageDriverConverter(IPluginEngine pluginEngine) { + _pluginEngine = pluginEngine; + } + + public override ITrafficStorageDriver? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + var properties = new Dictionary(); + while (reader.TokenType is not JsonTokenType.EndObject) { + reader.Read(); + var property = reader.GetString()!; + reader.Read(); + switch (property) { + case nameof(ITrafficStorageDriver.Name): + var name = reader.GetString()!; + properties[nameof(ITrafficStorageDriver.Name)] = name; + break; + case nameof(ITrafficStorageDriver.Description): + break; + case nameof(ITrafficStorageDriver.CollectionInterval): + var value = reader.GetString()!; + var interval = TimeSpan.Parse(value); + properties[nameof(ITrafficStorageDriver.CollectionInterval)] = interval; + break; + case nameof(ITrafficStorageDriver.AdditionalOptions): + ParseDictionary(ref reader, properties, nameof(ITrafficStorageDriver.AdditionalOptions)); + break; + } + } + reader.Read(); + var driverName = (string) properties[nameof(ITrafficStorageDriver.Name)]; + var driver = _pluginEngine.Plugins + .OfType() + .SingleOrDefault(p => p.Name.Equals(driverName)); + if (driver == default) { + throw new JsonException($"No instance of {nameof(ITrafficStorageDriver)} " + + $"named '{driverName}' was found. Maybe you forgot to add a plugin?"); + } + driver.CollectionInterval = (TimeSpan) properties[nameof(ITrafficStorageDriver.CollectionInterval)]; + driver.AdditionalOptions = (IDictionary) properties[nameof(ITrafficStorageDriver.AdditionalOptions)]; + return driver; + } + + private void ParseDictionary(ref Utf8JsonReader reader, IDictionary properties, string name) { + var dict = new Dictionary(); + reader.Read(); + while (reader.TokenType is not JsonTokenType.EndObject) { + var key = reader.GetString()!; + reader.Read(); + var value = reader.GetString(); + dict[key] = value ?? string.Empty; + reader.Read(); + } + properties[name] = dict; + } + + public override void Write(Utf8JsonWriter writer, ITrafficStorageDriver value, JsonSerializerOptions options) { + JsonSerializer.Serialize(writer, value, value.GetType(), options); + } +} \ No newline at end of file diff --git a/Linguard/Json/Converters/UriConverter.cs b/Linguard/Json/Converters/UriConverter.cs new file mode 100644 index 0000000..40a2d53 --- /dev/null +++ b/Linguard/Json/Converters/UriConverter.cs @@ -0,0 +1,16 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Linguard.Json.Converters; + +public class UriConverter : JsonConverter { + + public override Uri? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + var value = reader.GetString(); + return value == default ? default : new Uri(value, UriKind.RelativeOrAbsolute); + } + + public override void Write(Utf8JsonWriter writer, Uri value, JsonSerializerOptions options) { + writer.WriteStringValue(value.ToString()); + } +} \ No newline at end of file diff --git a/Linguard/Json/Json.csproj b/Linguard/Json/Json.csproj index 01a77c7..12902b9 100644 --- a/Linguard/Json/Json.csproj +++ b/Linguard/Json/Json.csproj @@ -19,9 +19,4 @@ - - - - - diff --git a/Linguard/Json/JsonConfigurationManager.cs b/Linguard/Json/JsonConfigurationManager.cs new file mode 100644 index 0000000..f380b46 --- /dev/null +++ b/Linguard/Json/JsonConfigurationManager.cs @@ -0,0 +1,13 @@ +using Linguard.Core.Configuration; +using Linguard.Core.Managers; +using Linguard.Core.OS; +using Linguard.Core.Plugins; + +namespace Linguard.Json; + +public class JsonConfigurationManager : FileConfigurationManager where T : IConfiguration { + public JsonConfigurationManager(IConfiguration configuration, ISystemWrapper systemWrapper, + JsonConfigurationSerializer serializer, IPluginEngine pluginEngine) + : base(configuration, systemWrapper, serializer, pluginEngine) { + } +} \ No newline at end of file diff --git a/Linguard/Json/JsonConfigurationSerializer.cs b/Linguard/Json/JsonConfigurationSerializer.cs new file mode 100644 index 0000000..c4d20ac --- /dev/null +++ b/Linguard/Json/JsonConfigurationSerializer.cs @@ -0,0 +1,32 @@ +using System.Text.Json; +using Linguard.Core.Configuration; +using Linguard.Core.Configuration.Serialization; +using Linguard.Core.Plugins; + +namespace Linguard.Json; + +public abstract class JsonConfigurationSerializer : ConfigurationSerializerBase { + + private readonly JsonSerializerOptions _serializerOptions; + private readonly IPluginEngine _pluginEngine; + + protected JsonConfigurationSerializer(JsonSerializerOptions options, IPluginEngine pluginEngine) { + _serializerOptions = options; + _pluginEngine = pluginEngine; + } + + public override string Serialize(T configuration) { + return JsonSerializer.Serialize(configuration, _serializerOptions); + } + + protected override T DeserializeAfterPluginsLoaded(string text) { + return JsonSerializer.Deserialize(text, _serializerOptions); + } + + protected override void LoadPlugins(string text) { + var dictionary = JsonSerializer.Deserialize>(text); + var plugins = dictionary["Plugins"].ToString(); + var pluginConfiguration = JsonSerializer.Deserialize(plugins, _serializerOptions); + _pluginEngine.LoadPlugins(pluginConfiguration.PluginsDirectory); + } +} \ No newline at end of file diff --git a/Linguard/Linguard.Plugins.TrafficDrivers.Json.Test/JsonSerializerShould.cs b/Linguard/Linguard.Plugins.TrafficDrivers.Json.Test/JsonSerializerShould.cs new file mode 100644 index 0000000..8b54399 --- /dev/null +++ b/Linguard/Linguard.Plugins.TrafficDrivers.Json.Test/JsonSerializerShould.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using Bogus; +using ByteSizeLib; +using Core.Test.Mocks; +using FluentAssertions; +using Linguard.Core.Configuration; +using Linguard.Core.Drivers.TrafficStorage; +using Linguard.Core.Managers; +using Linguard.Core.Models; +using Linguard.Core.Models.Wireguard; +using Xunit; + +namespace Linguard.Plugins.TrafficDrivers.Json.Test; + +public class JsonSerializerShould { + + private static readonly IConfigurationManager ConfigurationManager + = new DefaultConfigurationManager().Object; + private static readonly ITrafficStorageDriver TrafficStorageDriver + = new TrafficStorageDriver(); + + private Faker _faker = new(); + + private static IWireguardOptions WireguardOptions => + ConfigurationManager.Configuration.Wireguard; + + private const string DateTimeFormat = "yyyy-MM-dd HH:mm:ss"; + + private JsonSerializerOptions SerializerOptions => + TrafficDataSerializerOptions.Build(WireguardOptions, DateTimeFormat); + + public JsonSerializerShould() { + ConfigurationManager.Configuration.Traffic.StorageDriver = TrafficStorageDriver; + + WireguardOptions.Interfaces.Add(new Interface { + PublicKey = _faker.Random.String2(20), + Clients = new HashSet { + new() { + PublicKey = _faker.Random.String2(20), + } + } + }); + WireguardOptions.Interfaces.Add(new Interface { + PublicKey = _faker.Random.String2(20), + Clients = new HashSet { + new() { + PublicKey = _faker.Random.String2(20), + } + } + }); + } + + private static string ToJson(ITrafficData data) { + return + "{" + + @$"""Peer"":""{data.Peer.PublicKey}""," + + @$"""SentData"":{data.SentData.Bytes}," + + @$"""ReceivedData"":{data.ReceivedData.Bytes}," + + @$"""TimeStamp"":""{data.TimeStamp.ToString(DateTimeFormat)}""" + + "}"; + } + + [Fact] + public void SerializeTrafficData() { + ITrafficData data = new TrafficData { + Peer = WireguardOptions.Interfaces.First(), + ReceivedData = ByteSize.FromMegaBytes(1), + SentData = ByteSize.FromMegaBytes(2), + TimeStamp = DateTime.UnixEpoch + }; + var expected = ToJson(data); + var output = JsonSerializer.Serialize(data, SerializerOptions); + output.Should().Be(expected); + } + + [Fact] + public void SerializeTrafficDataList() { + IEnumerable data = new List { + new TrafficData { + Peer = WireguardOptions.Interfaces.First(), + ReceivedData = ByteSize.FromMegaBytes(1), + SentData = ByteSize.FromMegaBytes(2), + TimeStamp = DateTime.UnixEpoch + }, + new TrafficData { + Peer = WireguardOptions.Interfaces.Last(), + ReceivedData = ByteSize.FromMegaBytes(2), + SentData = ByteSize.FromMegaBytes(1), + TimeStamp = DateTime.UnixEpoch + } + }; + var expected = $"[{string.Join(",", data.Select(ToJson))}]"; + var output = JsonSerializer.Serialize(data, SerializerOptions); + output.Should().Be(expected); + } + + [Fact] + public void DeserializeTrafficDataAsConcreteType() { + ITrafficData expected = new TrafficData { + Peer = WireguardOptions.Interfaces.First(), + ReceivedData = ByteSize.FromMegaBytes(1), + SentData = ByteSize.FromMegaBytes(2), + TimeStamp = DateTime.UnixEpoch + }; + var data = ToJson(expected); + var output = JsonSerializer.Deserialize(data, SerializerOptions); + output.Should().Be(expected); + } + + [Fact] + public void DeserializeTrafficDataList() { + IEnumerable expected = new List { + new TrafficData { + Peer = WireguardOptions.Interfaces.First(), + ReceivedData = ByteSize.FromMegaBytes(1), + SentData = ByteSize.FromMegaBytes(2), + TimeStamp = DateTime.UnixEpoch + }, + new TrafficData { + Peer = WireguardOptions.Interfaces.Last(), + ReceivedData = ByteSize.FromMegaBytes(2), + SentData = ByteSize.FromMegaBytes(1), + TimeStamp = DateTime.UnixEpoch + } + }; + var data = $"[{string.Join(",", expected.Select(ToJson))}]"; + var output = JsonSerializer.Deserialize>(data, SerializerOptions); + output.Should().BeEquivalentTo(expected); + } +} \ No newline at end of file diff --git a/Linguard/Yaml.Test/Yaml.Test.csproj b/Linguard/Linguard.Plugins.TrafficDrivers.Json.Test/Linguard.Plugins.TrafficDrivers.Json.Test.csproj similarity index 79% rename from Linguard/Yaml.Test/Yaml.Test.csproj rename to Linguard/Linguard.Plugins.TrafficDrivers.Json.Test/Linguard.Plugins.TrafficDrivers.Json.Test.csproj index 11fbff5..95efdbf 100644 --- a/Linguard/Yaml.Test/Yaml.Test.csproj +++ b/Linguard/Linguard.Plugins.TrafficDrivers.Json.Test/Linguard.Plugins.TrafficDrivers.Json.Test.csproj @@ -5,9 +5,12 @@ enable false + + Linguard.Plugins.TrafficDrivers.Json.Test + @@ -22,9 +25,7 @@ - - - + diff --git a/Linguard/Linguard.sln b/Linguard/Linguard.sln index f5c71c0..a85a297 100644 --- a/Linguard/Linguard.sln +++ b/Linguard/Linguard.sln @@ -14,15 +14,15 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Web", "Web\Web.csproj", "{5 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebMock", "WebMock\WebMock.csproj", "{F5C5CEA6-068A-4E40-B889-04F52101A996}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yaml", "Yaml\Yaml.csproj", "{CD7EDB7B-738A-4353-8821-293A47AB9625}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Web.Test", "Web.Test\Web.Test.csproj", "{B56900D7-75AF-41A7-8D90-9AD5E7E0E62E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yaml.Test", "Yaml.Test\Yaml.Test.csproj", "{071598D1-9366-4CEE-B0BC-E7CAB4A1FA17}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plugins.TrafficDrivers.Json", "Plugins.TrafficDrivers.Json\Plugins.TrafficDrivers.Json.csproj", "{B72039ED-CCF6-42B9-9801-F3CE5AED64F6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Linguard.Plugins.TrafficDrivers.Json.Test", "Linguard.Plugins.TrafficDrivers.Json.Test\Linguard.Plugins.TrafficDrivers.Json.Test.csproj", "{AD5EE406-8C91-4594-B71B-8D62FC5D62ED}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Json", "Json\Json.csproj", "{B72039ED-CCF6-42B9-9801-F3CE5AED64F6}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Json", "Json\Json.csproj", "{84DFEE3B-59CA-402A-AD2C-E57F93C2F3E6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Json.Test", "Json.Test\Json.Test.csproj", "{AD5EE406-8C91-4594-B71B-8D62FC5D62ED}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Json.Test", "Json.Test\Json.Test.csproj", "{10BA4361-D2C7-47F2-BB4B-1963701BFC6C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -58,18 +58,10 @@ Global {F5C5CEA6-068A-4E40-B889-04F52101A996}.Debug|Any CPU.Build.0 = Debug|Any CPU {F5C5CEA6-068A-4E40-B889-04F52101A996}.Release|Any CPU.ActiveCfg = Release|Any CPU {F5C5CEA6-068A-4E40-B889-04F52101A996}.Release|Any CPU.Build.0 = Release|Any CPU - {CD7EDB7B-738A-4353-8821-293A47AB9625}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CD7EDB7B-738A-4353-8821-293A47AB9625}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CD7EDB7B-738A-4353-8821-293A47AB9625}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CD7EDB7B-738A-4353-8821-293A47AB9625}.Release|Any CPU.Build.0 = Release|Any CPU {B56900D7-75AF-41A7-8D90-9AD5E7E0E62E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B56900D7-75AF-41A7-8D90-9AD5E7E0E62E}.Debug|Any CPU.Build.0 = Debug|Any CPU {B56900D7-75AF-41A7-8D90-9AD5E7E0E62E}.Release|Any CPU.ActiveCfg = Release|Any CPU {B56900D7-75AF-41A7-8D90-9AD5E7E0E62E}.Release|Any CPU.Build.0 = Release|Any CPU - {071598D1-9366-4CEE-B0BC-E7CAB4A1FA17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {071598D1-9366-4CEE-B0BC-E7CAB4A1FA17}.Debug|Any CPU.Build.0 = Debug|Any CPU - {071598D1-9366-4CEE-B0BC-E7CAB4A1FA17}.Release|Any CPU.ActiveCfg = Release|Any CPU - {071598D1-9366-4CEE-B0BC-E7CAB4A1FA17}.Release|Any CPU.Build.0 = Release|Any CPU {B72039ED-CCF6-42B9-9801-F3CE5AED64F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B72039ED-CCF6-42B9-9801-F3CE5AED64F6}.Debug|Any CPU.Build.0 = Debug|Any CPU {B72039ED-CCF6-42B9-9801-F3CE5AED64F6}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -78,5 +70,13 @@ Global {AD5EE406-8C91-4594-B71B-8D62FC5D62ED}.Debug|Any CPU.Build.0 = Debug|Any CPU {AD5EE406-8C91-4594-B71B-8D62FC5D62ED}.Release|Any CPU.ActiveCfg = Release|Any CPU {AD5EE406-8C91-4594-B71B-8D62FC5D62ED}.Release|Any CPU.Build.0 = Release|Any CPU + {84DFEE3B-59CA-402A-AD2C-E57F93C2F3E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {84DFEE3B-59CA-402A-AD2C-E57F93C2F3E6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {84DFEE3B-59CA-402A-AD2C-E57F93C2F3E6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {84DFEE3B-59CA-402A-AD2C-E57F93C2F3E6}.Release|Any CPU.Build.0 = Release|Any CPU + {10BA4361-D2C7-47F2-BB4B-1963701BFC6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {10BA4361-D2C7-47F2-BB4B-1963701BFC6C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {10BA4361-D2C7-47F2-BB4B-1963701BFC6C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {10BA4361-D2C7-47F2-BB4B-1963701BFC6C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/Linguard/Json/Converters/WireguardPeerConverter.cs b/Linguard/Plugins.TrafficDrivers.Json/Converters/WireguardPeerConverter.cs similarity index 60% rename from Linguard/Json/Converters/WireguardPeerConverter.cs rename to Linguard/Plugins.TrafficDrivers.Json/Converters/WireguardPeerConverter.cs index 7ba874e..8de9681 100644 --- a/Linguard/Json/Converters/WireguardPeerConverter.cs +++ b/Linguard/Plugins.TrafficDrivers.Json/Converters/WireguardPeerConverter.cs @@ -3,19 +3,19 @@ using Linguard.Core.Configuration; using Linguard.Core.Models.Wireguard; -namespace Linguard.Json.Converters; +namespace Linguard.Plugins.TrafficDrivers.Json.Converters; public class WireguardPeerConverter : JsonConverter { - private readonly IWireguardConfiguration _configuration; + private readonly IWireguardOptions _options; - public WireguardPeerConverter(IWireguardConfiguration configuration) { - _configuration = configuration; + public WireguardPeerConverter(IWireguardOptions options) { + _options = options; } public override IWireguardPeer? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var publicKey = reader.GetString(); - var peer = _configuration.GetInterface(publicKey) - ?? _configuration.Interfaces.SingleOrDefault(p => p.PublicKey.Equals(publicKey)); + var peer = _options.GetInterface(publicKey) + ?? _options.Interfaces.SingleOrDefault(p => p.PublicKey.Equals(publicKey)); return peer; } diff --git a/Linguard/Plugins.TrafficDrivers.Json/Plugins.TrafficDrivers.Json.csproj b/Linguard/Plugins.TrafficDrivers.Json/Plugins.TrafficDrivers.Json.csproj new file mode 100644 index 0000000..fa3f010 --- /dev/null +++ b/Linguard/Plugins.TrafficDrivers.Json/Plugins.TrafficDrivers.Json.csproj @@ -0,0 +1,28 @@ + + + + net6.0 + enable + enable + Linguard.Plugins.TrafficDrivers.Json + Linguard.Plugins.TrafficDrivers.Json + Linguard.Plugins.TrafficDrivers.Json + 2.0.0 + Linguard.Plugins.TrafficDrivers.Json + José Antonio Mazón San Bartolomé + José Antonio Mazón San Bartolomé + Linguard + 2.0.0 + + + + + + + + + + + + + diff --git a/Linguard/Json/TrafficDataSerializerOptions.cs b/Linguard/Plugins.TrafficDrivers.Json/TrafficDataSerializerOptions.cs similarity index 63% rename from Linguard/Json/TrafficDataSerializerOptions.cs rename to Linguard/Plugins.TrafficDrivers.Json/TrafficDataSerializerOptions.cs index 29bc94c..77e820b 100644 --- a/Linguard/Json/TrafficDataSerializerOptions.cs +++ b/Linguard/Plugins.TrafficDrivers.Json/TrafficDataSerializerOptions.cs @@ -2,15 +2,16 @@ using Linguard.Core.Configuration; using Linguard.Core.Models; using Linguard.Json.Converters; +using Linguard.Plugins.TrafficDrivers.Json.Converters; -namespace Linguard.Json; +namespace Linguard.Plugins.TrafficDrivers.Json; public static class TrafficDataSerializerOptions { - public static JsonSerializerOptions Build(IWireguardConfiguration wireguardConfiguration, + public static JsonSerializerOptions Build(IWireguardOptions wireguardOptions, string timestampFormat) => new() { Converters = { new ByteSizeConverter(), - new WireguardPeerConverter(wireguardConfiguration), + new WireguardPeerConverter(wireguardOptions), new DateTimeConverter(timestampFormat), new TypeMappingConverter() } diff --git a/Linguard/Json/TrafficStorageDriver.cs b/Linguard/Plugins.TrafficDrivers.Json/TrafficStorageDriver.cs similarity index 80% rename from Linguard/Json/TrafficStorageDriver.cs rename to Linguard/Plugins.TrafficDrivers.Json/TrafficStorageDriver.cs index 78d3b48..20f1aac 100644 --- a/Linguard/Json/TrafficStorageDriver.cs +++ b/Linguard/Plugins.TrafficDrivers.Json/TrafficStorageDriver.cs @@ -3,7 +3,7 @@ using Linguard.Core.Drivers.TrafficStorage; using Linguard.Core.Models; -namespace Linguard.Json; +namespace Linguard.Plugins.TrafficDrivers.Json; public class TrafficStorageDriver : TrafficStorageDriverBase { @@ -11,13 +11,12 @@ public class TrafficStorageDriver : TrafficStorageDriverBase { private const string FileExtension = "json"; private const string DateTimeFormat = "yyyy-MM-dd HH:mm:ss"; - private IWireguardConfiguration WireguardConfiguration => - ConfigurationManager!.Configuration.GetModule()!; + private IWireguardOptions WireguardOptions => + ConfigurationManager!.Configuration.Wireguard; private JsonSerializerOptions SerializerOptions - => TrafficDataSerializerOptions.Build(WireguardConfiguration, DateTimeFormat); + => TrafficDataSerializerOptions.Build(WireguardOptions, DateTimeFormat); - private DirectoryInfo Directory => - new(Path.Combine(ConfigurationManager!.WorkingDirectory.BaseDirectory.FullName, "plugins")); + private DirectoryInfo Directory => ConfigurationManager!.Configuration.Plugins.PluginsDirectory; private FileInfo File => new(Path.ChangeExtension(Path.Combine(Directory.FullName, FileName), FileExtension)); diff --git a/Linguard/Web.Test/Mocks/DefaultConfiguration.cs b/Linguard/Web.Test/Mocks/DefaultConfiguration.cs index 8e22e73..10e0c62 100644 --- a/Linguard/Web.Test/Mocks/DefaultConfiguration.cs +++ b/Linguard/Web.Test/Mocks/DefaultConfiguration.cs @@ -6,29 +6,23 @@ using Linguard.Core.Models.Wireguard; using Linguard.Web.Configuration; using Moq; +using IConfiguration = Linguard.Web.Configuration.IConfiguration; namespace Web.Test.Mocks; public sealed class DefaultConfiguration : Mock { public DefaultConfiguration() { - SetupProperty(c => c.Modules, new HashSet { - GetWireguardConfigurationMock().Object, - GetTrafficConfigurationMock().Object, - GetWebConfigurationMock().Object - }); + SetupProperty(o => o.Wireguard, GetWireguardConfigurationMock().Object); + SetupProperty(o => o.Traffic, GetTrafficConfigurationMock().Object); + SetupProperty(o => o.Plugins, GetPluginConfigurationMock().Object); + SetupProperty(o => o.Web, GetWebConfigurationMock().Object); Setup(o => o.Clone()).Returns(Object); - Setup(o => o.GetModule()) - .Returns(new InvocationFunc(invocation => { - var type = invocation.Method.GetGenericArguments()[0]; - return Object.Modules.SingleOrDefault(m - => m.GetType() == type || m.GetType().GetInterface(type.Name) != default); - })); } - private Mock GetWireguardConfigurationMock() { + private Mock GetWireguardConfigurationMock() { var interfaces = new HashSet(); - var wireguardConfiguration = new Mock() + var mock = new Mock() .SetupProperty(c => c.Interfaces, interfaces) .SetupProperty(c => c.Endpoint, new Uri("vpn.example.com", UriKind.RelativeOrAbsolute)) @@ -38,23 +32,29 @@ private Mock GetWireguardConfigurationMock() { .SetupProperty(c => c.PrimaryDns, new Uri("8.8.8.8", UriKind.RelativeOrAbsolute)) .SetupProperty(c => c.SecondaryDns, default); - wireguardConfiguration.Setup(o => o.GetInterface(It.IsAny())) + mock.Setup(o => o.GetInterface(It.IsAny())) .Returns(c => interfaces.SingleOrDefault(i => i.Clients.Contains(c)) ); - wireguardConfiguration.Setup(o => o.GetInterface(It.IsAny())) + mock.Setup(o => o.GetInterface(It.IsAny())) .Returns(pubkey => interfaces.SingleOrDefault(i => i.Clients.Any(c => c.PublicKey == pubkey)) ); - return wireguardConfiguration; + return mock; } - private Mock GetWebConfigurationMock() { - return new Mock(); + private Mock GetWebConfigurationMock() { + var mock = new Mock(); + return mock; + } + + private Mock GetPluginConfigurationMock() { + var mock = new Mock(); + return mock; } - private Mock GetTrafficConfigurationMock() { - var mock = new Mock() + private Mock GetTrafficConfigurationMock() { + var mock = new Mock() .SetupProperty(c => c.StorageDriver, new TrafficStorageDriverStub()) .SetupProperty(c => c.Enabled, true); return mock; diff --git a/Linguard/Web.Test/Mocks/DefaultConfigurationManager.cs b/Linguard/Web.Test/Mocks/DefaultConfigurationManager.cs index cd427d9..12fd2e7 100644 --- a/Linguard/Web.Test/Mocks/DefaultConfigurationManager.cs +++ b/Linguard/Web.Test/Mocks/DefaultConfigurationManager.cs @@ -8,9 +8,8 @@ namespace Web.Test.Mocks; public class DefaultConfigurationManager : Mock { public DefaultConfigurationManager() { var configuration = new DefaultConfiguration().Object; - configuration.GetModule()!.StorageDriver.Initialize(Object); + configuration.Traffic.StorageDriver.Initialize(Object); SetupProperty(c => c.Configuration, configuration); - SetupProperty(c => c.WorkingDirectory, new Mock().Object); SetupProperty(c => c.PluginEngine, new PluginEngineMock().Object); } } \ No newline at end of file diff --git a/Linguard/Web/Auth/ApplicationDbContext.cs b/Linguard/Web/Auth/ApplicationDbContext.cs index 58c858b..b2f947d 100644 --- a/Linguard/Web/Auth/ApplicationDbContext.cs +++ b/Linguard/Web/Auth/ApplicationDbContext.cs @@ -16,7 +16,7 @@ public ApplicationDbContext(DbContextOptions options, ICon protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { base.OnConfiguring(optionsBuilder); var connectionString = new SqliteConnectionStringBuilder { - DataSource = _configurationManager.WorkingDirectory.CredentialsPath, + DataSource = _configurationManager.Configuration.Authentication.DataSource, Cache = SqliteCacheMode.Shared }.ConnectionString; optionsBuilder.UseSqlite(connectionString); diff --git a/Linguard/Web/Configuration/Configuration.cs b/Linguard/Web/Configuration/Configuration.cs new file mode 100644 index 0000000..61c2111 --- /dev/null +++ b/Linguard/Web/Configuration/Configuration.cs @@ -0,0 +1,16 @@ +using Linguard.Core.Configuration; +using Linguard.Core.Utils; + +namespace Linguard.Web.Configuration; + +public class Configuration : IConfiguration { + public IWireguardOptions Wireguard { get; set; } + public ITrafficOptions Traffic { get; set; } + public IPluginOptions Plugins { get; set; } + public IAuthenticationOptions Authentication { get; set; } + public IWebOptions Web { get; set; } + + public object Clone() { + return Cloning.Clone(this); + } +} diff --git a/Linguard/Web/Configuration/IConfiguration.cs b/Linguard/Web/Configuration/IConfiguration.cs new file mode 100644 index 0000000..1be658d --- /dev/null +++ b/Linguard/Web/Configuration/IConfiguration.cs @@ -0,0 +1,5 @@ +namespace Linguard.Web.Configuration; + +public interface IConfiguration : Core.Configuration.IConfiguration { + public IWebOptions Web { get; set; } +} \ No newline at end of file diff --git a/Linguard/Web/Configuration/IWebConfiguration.cs b/Linguard/Web/Configuration/IWebOptions.cs similarity index 78% rename from Linguard/Web/Configuration/IWebConfiguration.cs rename to Linguard/Web/Configuration/IWebOptions.cs index a2a3479..42360d1 100644 --- a/Linguard/Web/Configuration/IWebConfiguration.cs +++ b/Linguard/Web/Configuration/IWebOptions.cs @@ -5,7 +5,7 @@ namespace Linguard.Web.Configuration; /// /// Web related settings. /// -public interface IWebConfiguration : IConfigurationModule { +public interface IWebOptions : IOptionsModule { public int LoginAttempts { get; set; } public TimeSpan LoginBanTime { get; set; } } \ No newline at end of file diff --git a/Linguard/Web/Configuration/Serialization/ConfigurationSerializer.cs b/Linguard/Web/Configuration/Serialization/ConfigurationSerializer.cs index dc5a31e..707e547 100644 --- a/Linguard/Web/Configuration/Serialization/ConfigurationSerializer.cs +++ b/Linguard/Web/Configuration/Serialization/ConfigurationSerializer.cs @@ -30,25 +30,25 @@ public ConfigurationSerializer(IPluginEngine pluginEngine) { .WithTypeConverter() .WithTypeConverter(new TrafficStorageDriverConverter(_pluginEngine)) .WithTypeConverter() - .WithTypeMapping() - .WithTypeMapping() - .WithTypeMapping() - .WithTypeMapping() - .WithTagMapping("!Wireguard") - .WithTagMapping("!Traffic") - .WithTagMapping("!Web") + .WithTypeMapping() + .WithTypeMapping() + .WithTypeMapping() + .WithTypeMapping() + .WithTagMapping("!Wireguard") + .WithTagMapping("!Traffic") + .WithTagMapping("!Web") .WithTypeMapping, HashSet>() .WithTypeMapping, HashSet>() .WithTypeMapping, HashSet>() .WithTypeMapping, HashSet>() - .WithTypeMapping, HashSet>() + .WithTypeMapping, HashSet>() .Build(); - public string Serialize(T configuration) where T : IConfiguration { + public string Serialize(T configuration) where T : Core.Configuration.IConfiguration { return Serializer.Serialize(configuration); } - public T Deserialize(string text) where T : IConfiguration { + public T? Deserialize(string text) where T : Core.Configuration.IConfiguration { return Serializer.Deserialize(text); } } \ No newline at end of file diff --git a/Linguard/Web/Configuration/Serialization/JsonConfigurationSerializerWeb.cs b/Linguard/Web/Configuration/Serialization/JsonConfigurationSerializerWeb.cs new file mode 100644 index 0000000..66fd5e7 --- /dev/null +++ b/Linguard/Web/Configuration/Serialization/JsonConfigurationSerializerWeb.cs @@ -0,0 +1,40 @@ +using System.Text.Json; +using Linguard.Core; +using Linguard.Core.Configuration; +using Linguard.Core.Models.Wireguard; +using Linguard.Core.OS; +using Linguard.Core.Plugins; +using Linguard.Json; +using Linguard.Json.Converters; + +namespace Linguard.Web.Configuration.Serialization; + +public class JsonConfigurationSerializerWeb : JsonConfigurationSerializer { + + public JsonConfigurationSerializerWeb(ISystemWrapper systemWrapper, IPluginEngine pluginEngine) + : base(new JsonSerializerOptions { + Converters = { + new TypeMappingConverter(), + new TypeMappingConverter(), + new TypeMappingConverter(), + new TypeMappingConverter(), + new TypeMappingConverter(), + new TypeMappingConverter(), + new TypeMappingConverter(), + new TypeMappingConverter, HashSet>(), + new TypeMappingConverter, HashSet>(), + new TypeMappingConverter, HashSet>(), + new TypeMappingConverter, HashSet>(), + new TypeMappingConverter, HashSet>(), + new IPAddressCidrConverter(), + new NetworkInterfaceConverter(systemWrapper), + new UriConverter(), + new RuleConverter(), + new TrafficStorageDriverConverter(pluginEngine), + new DirectoryInfoConverter() + }, + WriteIndented = true, + PropertyNameCaseInsensitive = true + }, pluginEngine) + { } +} \ No newline at end of file diff --git a/Linguard/Web/Configuration/WebConfigurationManager.cs b/Linguard/Web/Configuration/WebConfigurationManager.cs index 2caca31..97aaef2 100644 --- a/Linguard/Web/Configuration/WebConfigurationManager.cs +++ b/Linguard/Web/Configuration/WebConfigurationManager.cs @@ -1,24 +1,23 @@ -using Linguard.Core.Configuration; -using Linguard.Core.OS; +using Linguard.Core.OS; using Linguard.Core.Plugins; +using Linguard.Json; using Linguard.Web.Configuration.Serialization; -using Linguard.Yaml; -using IConfiguration = Linguard.Core.Configuration.IConfiguration; namespace Linguard.Web.Configuration; -public class WebConfigurationManager : YamlConfigurationManager, IConfigurationManager { - public WebConfigurationManager(IConfiguration configuration, IWorkingDirectory workingDirectory, +public class WebConfigurationManager : JsonConfigurationManager, IConfigurationManager { + public WebConfigurationManager(IConfiguration configuration, ISystemWrapper systemWrapper, IPluginEngine pluginEngine) - : base(configuration, workingDirectory, systemWrapper, new ConfigurationSerializer(pluginEngine), pluginEngine) { + : base(configuration, systemWrapper, new JsonConfigurationSerializerWeb(systemWrapper, pluginEngine), + pluginEngine) { } public override void LoadDefaults() { - var configuration = new WebConfiguration { + var configuration = new WebOptions { LoginAttempts = 10, LoginBanTime = TimeSpan.FromMinutes(1) }; - Configuration.Modules.Add(configuration); + ((Configuration) Configuration).Web = configuration; base.LoadDefaults(); } } \ No newline at end of file diff --git a/Linguard/Web/Configuration/WebConfiguration.cs b/Linguard/Web/Configuration/WebOptions.cs similarity index 79% rename from Linguard/Web/Configuration/WebConfiguration.cs rename to Linguard/Web/Configuration/WebOptions.cs index 6be8459..d950770 100644 --- a/Linguard/Web/Configuration/WebConfiguration.cs +++ b/Linguard/Web/Configuration/WebOptions.cs @@ -1,6 +1,6 @@ namespace Linguard.Web.Configuration; -public class WebConfiguration : IWebConfiguration { +public class WebOptions : IWebOptions { public int LoginAttempts { get; set; } public TimeSpan LoginBanTime { get; set; } diff --git a/Linguard/Web/Helpers/WebHelper.cs b/Linguard/Web/Helpers/WebHelper.cs index 47d3a55..1d49189 100644 --- a/Linguard/Web/Helpers/WebHelper.cs +++ b/Linguard/Web/Helpers/WebHelper.cs @@ -23,7 +23,7 @@ public WebHelper(IJSRuntime jsRuntime, IWireguardService wireguardService, private IJSRuntime JsRuntime { get; } private IWireguardService WireguardService { get; } private IConfigurationManager ConfigurationManager { get; } - private IWireguardConfiguration Configuration => ConfigurationManager.Configuration.GetModule()!; + private IWireguardOptions Options => ConfigurationManager.Configuration.Wireguard; private QRCodeGenerator QrCodeGenerator {get; } public async Task Download(string data, string filename) { @@ -61,11 +61,11 @@ public byte[] GetQrCode(IWireguardPeer peer) { private void RemoveClient(Client client) { WireguardService.RemoveClient(client); - Configuration.GetInterface(client)?.Clients.Remove(client); + Options.GetInterface(client)?.Clients.Remove(client); } private void RemoveInterface(Interface iface) { - Configuration.Interfaces.Remove(iface); + Options.Interfaces.Remove(iface); WireguardService.RemoveInterface(iface); } } \ No newline at end of file diff --git a/Linguard/Web/Pages/AddClient.razor b/Linguard/Web/Pages/AddClient.razor index b30c33d..35bc88a 100644 --- a/Linguard/Web/Pages/AddClient.razor +++ b/Linguard/Web/Pages/AddClient.razor @@ -46,7 +46,7 @@ private Client Client { get; set; } protected override void OnInitialized() { - Interface = Configuration.GetModule()!.Interfaces + Interface = Configuration.Wireguard.Interfaces .SingleOrDefault(i => i.PublicKey.Equals(InterfacePublicKey)); if (Interface == default) return; Client = _generator.Generate(Interface); diff --git a/Linguard/Web/Pages/AddInterface.razor b/Linguard/Web/Pages/AddInterface.razor index 5efa93b..9f815d7 100644 --- a/Linguard/Web/Pages/AddInterface.razor +++ b/Linguard/Web/Pages/AddInterface.razor @@ -53,7 +53,7 @@ void Submit(Interface iface) { var result = _validator.Validate(iface); if (result.IsValid) { - Configuration.GetModule()!.Interfaces.Add(iface); + Configuration.Wireguard.Interfaces.Add(iface); _configurationManager.Save(); _navigationManager.NavigateTo("wireguard"); return; diff --git a/Linguard/Web/Pages/Dashboard.razor b/Linguard/Web/Pages/Dashboard.razor index c63065a..37a67c9 100644 --- a/Linguard/Web/Pages/Dashboard.razor +++ b/Linguard/Web/Pages/Dashboard.razor @@ -79,7 +79,7 @@ } - else if (Configuration.Interfaces.Any()) { + else if (Options.Interfaces.Any()) {

There are no interfaces online!

} else { @@ -140,7 +140,7 @@ var trafficData = _wireguardService.GetTrafficData(client); }
- @{var iface = Configuration.GetInterface(client);} + @{var iface = Options.GetInterface(client);} @iface?.Name @@ -157,7 +157,7 @@ else if (_clients.Any()) {

There are no clients online!

} - else if (Configuration.Interfaces.Any()) { + else if (Options.Interfaces.Any()) {

There are no clients yet, why don't you add one? @@ -189,8 +189,8 @@ @code { const string Title = "Dashboard"; - IWireguardConfiguration Configuration => _configurationManager.Configuration.GetModule()!; - IEnumerable OnlineInterfaces => Configuration.Interfaces + IWireguardOptions Options => _configurationManager.Configuration.Wireguard; + IEnumerable OnlineInterfaces => Options.Interfaces .Where(i => _system.NetworkInterfaces.Any(iface => iface.Name.Equals(i.Name))) .OrderBy(i => i.Name); IEnumerable OnlineClients => OnlineInterfaces.SelectMany(i => i.Clients).OrderBy(c => c.Name); @@ -198,7 +198,7 @@ protected override async void OnInitialized() { await base.OnInitializedAsync(); - _clients = Configuration.Interfaces.SelectMany(i => i.Clients).OrderBy(c => c.Name); + _clients = Options.Interfaces.SelectMany(i => i.Clients).OrderBy(c => c.Name); // TODO: Set timer to update real time traffic data //await using var timer = new Timer(_ => InvokeAsync(StateHasChanged)); //timer.Change(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)); @@ -212,7 +212,7 @@

+ Data="Options.Interfaces.Select(i => i.Name)"/>
@@ -235,7 +235,7 @@ void AddClient() { var item = _addClientInterfaces.SelectedItem; if (item == default) return; - var iface = Configuration.Interfaces.Single(i => i.Name.Equals(item)); + var iface = Options.Interfaces.Single(i => i.Name.Equals(item)); _navigationManager.NavigateTo($"/{nameof(Interface).ToLower()}/{iface.PublicKey}/add-{nameof(Client).ToLower()}"); } diff --git a/Linguard/Web/Pages/EditClient.razor b/Linguard/Web/Pages/EditClient.razor index c62c86f..2a88829 100644 --- a/Linguard/Web/Pages/EditClient.razor +++ b/Linguard/Web/Pages/EditClient.razor @@ -58,7 +58,7 @@ protected override void OnInitialized() { base.OnInitialized(); - _interface = Configuration.GetModule()!.GetInterface(PublicKey); + _interface = Configuration.Wireguard.GetInterface(PublicKey); if (_interface == default) return; _client = _interface.Clients.SingleOrDefault(c => c.PublicKey == PublicKey); } diff --git a/Linguard/Web/Pages/EditInterface.razor b/Linguard/Web/Pages/EditInterface.razor index f4ab07c..ed0bd15 100644 --- a/Linguard/Web/Pages/EditInterface.razor +++ b/Linguard/Web/Pages/EditInterface.razor @@ -118,19 +118,19 @@ protected override void OnInitialized() { base.OnInitialized(); - _iface = Configuration.GetModule()!.Interfaces + _iface = Configuration.Wireguard.Interfaces .SingleOrDefault(i => i.PublicKey == PublicKey)?.Clone() as Interface; } void Submit(Interface iface) { var result = _validator.Validate(iface); if (result.IsValid) { - var originalIface = Configuration.GetModule()!.Interfaces + var originalIface = Configuration.Wireguard.Interfaces .SingleOrDefault(i => i.PublicKey == PublicKey); - var interfaces = Configuration.GetModule()!.Interfaces + var interfaces = Configuration.Wireguard.Interfaces .Replace(originalIface, iface) .ToHashSet(); - Configuration.GetModule()!.Interfaces = interfaces; + Configuration.Wireguard.Interfaces = interfaces; _configurationManager.Save(); _notificationService.Notify(NotificationSeverity.Success, "Interface saved!"); return; diff --git a/Linguard/Web/Pages/Settings.razor b/Linguard/Web/Pages/Settings.razor index b8c4d0e..da5db9a 100644 --- a/Linguard/Web/Pages/Settings.razor +++ b/Linguard/Web/Pages/Settings.razor @@ -2,7 +2,6 @@ @using Linguard.Core.Utils @using Linguard.Web.Configuration @using Linguard.Web.Helpers -@using Linguard.Core.Configuration @using Linguard.Core.Services @($"{AssemblyInfo.Product} | {Title}") @@ -14,14 +13,20 @@ - + - + - + + @* + + + + + *@
diff --git a/Linguard/Web/Pages/Setup.razor b/Linguard/Web/Pages/Setup.razor index a74affa..0535705 100644 --- a/Linguard/Web/Pages/Setup.razor +++ b/Linguard/Web/Pages/Setup.razor @@ -5,7 +5,6 @@ @using Linguard.Web.Helpers @using Linguard.Web.Services @using Linguard.Web.Shared.Layouts -@using Linguard.Core.Configuration @($"{AssemblyInfo.Product} | {Title}") @@ -15,16 +14,24 @@

Web

- +

Wireguard

- +

Traffic

- +
+ @* +

Authentication

+ +
+ +

Plugins

+ +
*@
@@ -135,7 +135,7 @@ bool _stoppingAll; protected override void OnInitialized() { - _wireguardInterfaces = Configuration.GetModule()!.Interfaces + _wireguardInterfaces = Configuration.Wireguard.Interfaces .OrderBy(i => i.Name) .ToList(); _clients = _wireguardInterfaces @@ -174,7 +174,7 @@ void StopAllInterfaces() { _stoppingAll = true; - foreach (var iface in Configuration.GetModule()!.Interfaces) { + foreach (var iface in Configuration.Wireguard.Interfaces) { StopInterface(iface); } _stoppingAll = false; @@ -182,7 +182,7 @@ void StartAllInterfaces() { _startingAll = true; - foreach (var iface in Configuration.GetModule()!.Interfaces) { + foreach (var iface in Configuration.Wireguard.Interfaces) { StartInterface(iface); } _startingAll = false; diff --git a/Linguard/Web/Pages/_Host.cshtml.cs b/Linguard/Web/Pages/_Host.cshtml.cs index a044402..2f4c94f 100644 --- a/Linguard/Web/Pages/_Host.cshtml.cs +++ b/Linguard/Web/Pages/_Host.cshtml.cs @@ -1,17 +1,9 @@ using Linguard.Core.Models; -using Linguard.Web.Configuration; using Microsoft.AspNetCore.Mvc.RazorPages; namespace Linguard.Web.Pages; public class HostModel : PageModel { - private readonly IConfigurationManager _configurationManager; - - public HostModel(IConfigurationManager configurationManager) { - _configurationManager = configurationManager; - } - - private IWebConfiguration Configuration => _configurationManager.Configuration.GetModule()!; public static string Stylesheet => $"_content/Radzen.Blazor/css/{Style.Default}.css"; } \ No newline at end of file diff --git a/Linguard/Web/Program.cs b/Linguard/Web/Program.cs index 3791861..65080ee 100644 --- a/Linguard/Web/Program.cs +++ b/Linguard/Web/Program.cs @@ -1,5 +1,4 @@ using FluentValidation; -using Linguard.Core.Configuration; using Linguard.Web.Configuration; using Linguard.Core.Models.Wireguard; using Linguard.Core.Models.Wireguard.Validators; @@ -29,14 +28,17 @@ builder.Services.AddSingleton(provider => provider.GetRequiredService()); builder.Services.AddTransient(); -builder.Services.AddTransient(); -builder.Services.AddTransient(); +builder.Services.AddTransient(); +builder.Services.AddTransient(provider + => provider.GetRequiredService()); builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient, InterfaceValidator>(); builder.Services.AddTransient, ClientValidator>(); +builder.Services.AddScoped(_ => builder.Configuration); + #endregion #region Web services diff --git a/Linguard/Web/Services/AuthenticationService.cs b/Linguard/Web/Services/AuthenticationService.cs index 74c1b1b..c237b9e 100644 --- a/Linguard/Web/Services/AuthenticationService.cs +++ b/Linguard/Web/Services/AuthenticationService.cs @@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Options; using Microsoft.JSInterop; +using IConfiguration = Linguard.Web.Configuration.IConfiguration; using ICredentials = Auth.Models.ICredentials; namespace Linguard.Web.Services; @@ -29,7 +30,11 @@ public class AuthenticationService : IAuthenticationService { private readonly IAuthenticationCookieFormat _cookieFormat = AuthenticationCookieFormat.Default; private const string JsNamespace = "authFunctions"; private static readonly TimeSpan AuthCookieExpireTimeSpan = TimeSpan.FromHours(2); - private IWebConfiguration WebConfiguration => _configurationManager.Configuration.GetModule()!; + + private IConfiguration Configuration + => (IConfiguration) _configurationManager.Configuration; + + private IWebOptions WebOptions => Configuration.Web; public AuthenticationService(ILogger logger, UserManager userManager, SignInManager signInManager, AuthenticationStateProvider authenticationStateProvider, @@ -56,11 +61,11 @@ public TimeSpan BannedFor { if (clientIp == default || !_loginBansByIpAddress.TryGetValue(clientIp, out var stopwatch)) { return TimeSpan.Zero; } - if (stopwatch.Elapsed > WebConfiguration.LoginBanTime) { + if (stopwatch.Elapsed > WebOptions.LoginBanTime) { _loginBansByIpAddress.TryRemove(clientIp, out _); return TimeSpan.Zero; } - return WebConfiguration.LoginBanTime - stopwatch.Elapsed; + return WebOptions.LoginBanTime - stopwatch.Elapsed; } } @@ -100,7 +105,7 @@ private void AddLoginAttempt() { if (!_loginAttemptsByIpAddress.TryUpdate(clientIp, attempt, attempts)) { _loginAttemptsByIpAddress.TryAdd(clientIp, attempt); } - if (attempt < WebConfiguration.LoginAttempts) return; + if (attempt < WebOptions.LoginAttempts) return; _loginBansByIpAddress.TryAdd(clientIp, Stopwatch.StartNew()); _loginAttemptsByIpAddress.TryRemove(clientIp, out _); } diff --git a/Linguard/Web/Services/LifetimeService.cs b/Linguard/Web/Services/LifetimeService.cs index 1530348..ccfe0b9 100644 --- a/Linguard/Web/Services/LifetimeService.cs +++ b/Linguard/Web/Services/LifetimeService.cs @@ -14,16 +14,14 @@ public class LifetimeService : ILifetimeService { #region Fields and properties - private static readonly string WorkingDirectoryEnvironmentVariable = $"{AssemblyInfo.Product}Workdir"; - private readonly ILogger _logger; private readonly ISystemWrapper _system; private readonly IWireguardService _wireguardService; private readonly IConfigurationManager _configurationManager; private readonly IWebService _webService; private readonly IServiceScope _scope; - private IWireguardConfiguration? Configuration - => _configurationManager.Configuration.GetModule(); + private IWireguardOptions? Configuration + => _configurationManager.Configuration.Wireguard; #endregion @@ -39,11 +37,10 @@ public LifetimeService(IConfigurationManager configurationManager, IWireguardSer public Task OnAppStarted() { _logger.LogInformation($"{AssemblyInfo.Product} v{AssemblyInfo.Version.ProductVersion} is booting up..."); - _configurationManager.WorkingDirectory.BaseDirectory = GetWorkingDirectory(); //await Task.WhenAll(new Task(InitializeDatabases), new Task(LoadPlugins)); - InitializeDatabases(); - LoadPlugins(); LoadConfiguration(); + InitializeDatabases(); + InitializePlugins(); StartInterfaces(); _logger.LogInformation($"{AssemblyInfo.Product} is ready."); return Task.CompletedTask; @@ -69,16 +66,21 @@ private void InitializeDatabases() { _logger.LogDebug("Databases initialized."); } - private void LoadPlugins() { - _logger.LogDebug("Loading plugins..."); - var plugins = _configurationManager.PluginEngine - .LoadPlugins(_configurationManager.WorkingDirectory.PluginsDirectory, _configurationManager); - _logger.LogDebug($"{plugins} plugins were loaded."); + private void InitializePlugins() { + _logger.LogDebug("Initializing plugins..."); + _configurationManager.PluginEngine.InitializePlugins(_configurationManager); + _logger.LogDebug("Plugins initialized."); } private void LoadConfiguration() { try { _logger.LogDebug("Loading configuration..."); + var manager = _scope.ServiceProvider.GetService(); + var configurationSource = manager + .GetSection(AssemblyInfo.Product) + .GetSection(nameof(IConfigurationManager.ConfigurationSource)) + .Value; + _configurationManager.ConfigurationSource = configurationSource; _configurationManager.Load(); _webService.IsSetupNeeded = false; _logger.LogDebug("Configuration loaded."); @@ -88,26 +90,6 @@ private void LoadConfiguration() { _configurationManager.LoadDefaults(); } } - - private DirectoryInfo GetWorkingDirectory() { - _logger.LogDebug("Setting up working directory..."); - DirectoryInfo workingDirectory; - var useCurrentDirectory = false; - if (Environment.GetEnvironmentVariables()[WorkingDirectoryEnvironmentVariable] is string workdir) { - workingDirectory = new DirectoryInfo(workdir); - } - else { - workingDirectory = new DirectoryInfo(Path.GetFullPath(".")); - useCurrentDirectory = true; - } - if (useCurrentDirectory) { - _logger.LogWarning("No working directory specified through environment variable " + - $"'{WorkingDirectoryEnvironmentVariable}'."); - } - _logger.LogDebug($"Using '{workingDirectory.FullName}' as working directory..."); - workingDirectory.Create(); - return workingDirectory; - } private void StartInterfaces() { var interfaces = Configuration.Interfaces diff --git a/Linguard/Web/Shared/ClientConfigurationForm.razor b/Linguard/Web/Shared/ClientConfigurationForm.razor index 2b20d14..96ba4b5 100644 --- a/Linguard/Web/Shared/ClientConfigurationForm.razor +++ b/Linguard/Web/Shared/ClientConfigurationForm.razor @@ -46,9 +46,9 @@
Interface = Configuration.Wireguard.Interfaces .SingleOrDefault(i => i.Name.Equals(o))">
@@ -264,7 +264,7 @@ protected override void OnInitialized() { base.OnInitialized(); if (Interface == default) { - Interface = Configuration.GetModule()!.GetInterface(Client); + Interface = Configuration.Wireguard.GetInterface(Client); } } diff --git a/Linguard/Web/Shared/TrafficChartForMultiplePeers.razor b/Linguard/Web/Shared/TrafficChartForMultiplePeers.razor index 1c8451f..1e67844 100644 --- a/Linguard/Web/Shared/TrafficChartForMultiplePeers.razor +++ b/Linguard/Web/Shared/TrafficChartForMultiplePeers.razor @@ -28,7 +28,7 @@

History

- @if (!Configuration.GetModule()!.Enabled) { + @if (!Configuration.Traffic.Enabled) {

It looks like traffic data storage is disabled. Enable it to get more statistics. diff --git a/Linguard/Web/Shared/TrafficInformation.razor b/Linguard/Web/Shared/TrafficInformation.razor index 3445496..95d5bc6 100644 --- a/Linguard/Web/Shared/TrafficInformation.razor +++ b/Linguard/Web/Shared/TrafficInformation.razor @@ -30,7 +30,7 @@

History

- @if (!Configuration.GetModule()!.Enabled) { + @if (!Configuration.Traffic.Enabled) {

It looks like traffic data storage is disabled. Enable it to get more statistics. diff --git a/Linguard/Web/Shared/TrafficSettings.razor b/Linguard/Web/Shared/TrafficSettings.razor index 38efe43..9e44509 100644 --- a/Linguard/Web/Shared/TrafficSettings.razor +++ b/Linguard/Web/Shared/TrafficSettings.razor @@ -13,12 +13,12 @@

- +
-@if (Configuration.StorageDriver == default) { +@if (Options.StorageDriver == default) { There are no traffic drivers currently available. return; } @@ -27,8 +27,8 @@
Every + Value="@((int)Options.StorageDriver.CollectionInterval.TotalMinutes)" + ValueChanged="i => Options.StorageDriver.CollectionInterval = TimeSpan.FromMinutes(i)"/> minutes
@@ -46,13 +46,13 @@ Data="@(_configurationManager.PluginEngine.Plugins .Where(p => p is ITrafficStorageDriver) .Select(d => d.Name))" - Value="@Configuration.StorageDriver.Name" + Value="@Options.StorageDriver.Name" ValueChanged="OnChangedTrafficDriver" />
- @Configuration.StorageDriver.Description + @Options.StorageDriver.Description
@@ -132,7 +132,7 @@ } [Parameter] - public ITrafficConfiguration Configuration { get; set; } + public ITrafficOptions Options { get; set; } private ITrafficStorageDriver? _storageDriver; @@ -140,17 +140,17 @@ protected override void OnInitialized() { base.OnInitialized(); - if (Configuration.StorageDriver != default) { + if (Options.StorageDriver != default) { _additionalOptions = GetAdditionalOptions().ToHashSet(); return; } - Configuration.StorageDriver = (_configurationManager.PluginEngine.Plugins + Options.StorageDriver = (_configurationManager.PluginEngine.Plugins .FirstOrDefault(p => p is ITrafficStorageDriver) as ITrafficStorageDriver)!; _additionalOptions = GetAdditionalOptions().ToHashSet(); } private IEnumerable
@@ -27,8 +27,8 @@
+ Value="@((int) Options.LoginBanTime.TotalSeconds)" + ValueChanged="i => Options.LoginBanTime = TimeSpan.FromSeconds(i)"/> seconds
@@ -39,5 +39,5 @@ @code { [Parameter] - public IWebConfiguration Configuration { get; set; } + public IWebOptions Options { get; set; } } \ No newline at end of file diff --git a/Linguard/Web/Shared/WireguardSettings.razor b/Linguard/Web/Shared/WireguardSettings.razor index 7e63fca..761190b 100644 --- a/Linguard/Web/Shared/WireguardSettings.razor +++ b/Linguard/Web/Shared/WireguardSettings.razor @@ -12,9 +12,9 @@
- + ValueChanged="s => Options.Endpoint = new Uri(s, UriKind.RelativeOrAbsolute)"/>
@@ -26,9 +26,9 @@
- + ValueChanged="s => Options.PrimaryDns = new Uri(s, UriKind.RelativeOrAbsolute)"/>
@@ -40,9 +40,9 @@
- + ValueChanged="s => Options.SecondaryDns = new Uri(s, UriKind.RelativeOrAbsolute)"/>
@@ -57,7 +57,7 @@
-
@@ -70,7 +70,7 @@
-
@@ -83,7 +83,7 @@
-
@@ -93,5 +93,5 @@ @code { [Parameter] - public IWireguardConfiguration Configuration { get; set; } + public IWireguardOptions Options { get; set; } } \ No newline at end of file diff --git a/Linguard/Web/Utils/Configuration.cs b/Linguard/Web/Utils/Configuration.cs new file mode 100644 index 0000000..ed27e97 --- /dev/null +++ b/Linguard/Web/Utils/Configuration.cs @@ -0,0 +1,17 @@ +using System.Configuration; +using System.Text.Json; +using ConfigurationManager = Microsoft.Extensions.Configuration.ConfigurationManager; + +namespace Linguard.Web.Utils; + +public static class ConfigurationManagerExtensions { + public static void SaveConfiguration(this ConfigurationManager manager) { + var settingsPath = manager.GetFileProvider().GetFileInfo("appsettings.json").PhysicalPath; + // TODO complete + // IWebHostEnvironment to know if production + // var json = JsonSerializer.Serialize(manager, new JsonSerializerOptions { + // WriteIndented = true + // }); + // File.WriteAllText(filepath, json); + } +} \ No newline at end of file diff --git a/Linguard/Web/Web.csproj b/Linguard/Web/Web.csproj index 61a72e2..4414b6d 100644 --- a/Linguard/Web/Web.csproj +++ b/Linguard/Web/Web.csproj @@ -30,6 +30,7 @@ + diff --git a/Linguard/Web/appsettings.Development.json b/Linguard/Web/appsettings.Development.json index d71f3af..10d756c 100644 --- a/Linguard/Web/appsettings.Development.json +++ b/Linguard/Web/appsettings.Development.json @@ -35,5 +35,8 @@ "writeTo": "linguard" } ] + }, + "Linguard": { + "ConfigurationSource": "linguard.json" } } diff --git a/Linguard/Web/appsettings.json b/Linguard/Web/appsettings.json index 2363ba4..07ede88 100644 --- a/Linguard/Web/appsettings.json +++ b/Linguard/Web/appsettings.json @@ -36,5 +36,8 @@ "writeTo": "linguard" } ] + }, + "Linguard": { + "ConfigurationSource": "linguard.json" } } diff --git a/Linguard/WebMock/Program.cs b/Linguard/WebMock/Program.cs index 1915603..49e6a20 100644 --- a/Linguard/WebMock/Program.cs +++ b/Linguard/WebMock/Program.cs @@ -1,7 +1,6 @@ using Bogus; using Core.Test.Mocks; using FluentValidation; -using Linguard.Core.Configuration; using Linguard.Core.Models.Wireguard; using Linguard.Core.Models.Wireguard.Validators; using Linguard.Core.Services; @@ -15,7 +14,6 @@ using Microsoft.AspNetCore.Components.Server; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; -using NLog.Web; using QRCoder; using Radzen; using WebMock; @@ -45,7 +43,6 @@ builder.Services.AddSingleton(provider => provider.GetRequiredService()); -builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient, InterfaceValidator>(); @@ -56,6 +53,7 @@ builder.Services.AddTransient, InterfaceValidator>(); builder.Services.AddTransient, ClientValidator>(); +builder.Services.AddScoped(_ => builder.Configuration); #endregion #region Web services diff --git a/Linguard/WebMock/appsettings.Development.json b/Linguard/WebMock/appsettings.Development.json index d71f3af..10d756c 100644 --- a/Linguard/WebMock/appsettings.Development.json +++ b/Linguard/WebMock/appsettings.Development.json @@ -35,5 +35,8 @@ "writeTo": "linguard" } ] + }, + "Linguard": { + "ConfigurationSource": "linguard.json" } } diff --git a/Linguard/WebMock/appsettings.json b/Linguard/WebMock/appsettings.json index 63daba7..10893d6 100644 --- a/Linguard/WebMock/appsettings.json +++ b/Linguard/WebMock/appsettings.json @@ -36,6 +36,9 @@ "writeTo": "linguard" } ] + }, + "Linguard": { + "ConfigurationSource": "linguard.json" } } diff --git a/Linguard/Yaml.Test/Mocks/YamlConfigurationSerializerMock.cs b/Linguard/Yaml.Test/Mocks/YamlConfigurationSerializerMock.cs deleted file mode 100644 index b94806e..0000000 --- a/Linguard/Yaml.Test/Mocks/YamlConfigurationSerializerMock.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.Collections.Generic; -using Core.Test.Mocks; -using Linguard.Core; -using Linguard.Core.Configuration; -using Linguard.Core.Configuration.Serialization; -using Linguard.Core.Drivers.TrafficStorage; -using Linguard.Core.Models.Wireguard; -using Linguard.Core.Plugins; -using Linguard.Json; -using Linguard.Web.Configuration; -using Linguard.Yaml.Serialization; -using Moq; -using YamlDotNet.Serialization.NamingConventions; - -namespace Yaml.Test.Mocks; - -public static class YamlConfigurationSerializerMock { - - public static IConfigurationSerializer Instance => - new YamlConfigurationSerializerBuilder() - .WithNamingConvention(PascalCaseNamingConvention.Instance) - .WithTypeConverter() - .WithTypeConverter() - .WithTypeConverter() - .WithTypeConverter(new TrafficStorageDriverConverter(new PluginEngineMock().Object)) - .WithTypeConverter() - .WithTypeMapping() - .WithTypeMapping() - .WithTagMapping("!Wireguard") - .WithTypeMapping() - .WithTagMapping("!Web") - .WithTypeMapping() - .WithTagMapping("!Traffic") - .WithTypeMapping, HashSet>() - .WithTypeMapping, HashSet>() - .WithTypeMapping, HashSet>() - .WithTypeMapping, HashSet>() - .WithTypeMapping, HashSet>() - .Build(); -} \ No newline at end of file diff --git a/Linguard/Yaml.Test/YamlConfigurationSerializerShould.cs b/Linguard/Yaml.Test/YamlConfigurationSerializerShould.cs deleted file mode 100644 index e696a34..0000000 --- a/Linguard/Yaml.Test/YamlConfigurationSerializerShould.cs +++ /dev/null @@ -1,170 +0,0 @@ -using System; -using System.Collections.Generic; -using Core.Test.Mocks; -using Core.Test.Stubs; -using FluentAssertions; -using Linguard.Core; -using Linguard.Core.Configuration; -using Linguard.Core.Models.Wireguard; -using Microsoft.Extensions.Logging; -using Xunit; -using YamlConfigurationSerializerMock = Yaml.Test.Mocks.YamlConfigurationSerializerMock; - -namespace Yaml.Test; - -public class YamlConfigurationSerializerShould { - - #region Yaml string - - const string yaml = @"Modules: -- !Traffic - Enabled: false - StorageDriver: - Name: Stub driver - Description: This is a stub driver - CollectionInterval: 01:00:00 - AdditionalOptions: - Fake: Option -- !Wireguard - Interfaces: - - Gateway: eth0 - Port: 0 - Auto: false - Clients: - - AllowedIPs: - - 1.1.1.0/24 - - 1.1.2.0/24 - Nat: false - PrimaryDns: '' - SecondaryDns: '' - Endpoint: vpn.example.com - PublicKey: 00000000-0000-0000-0000-000000000000 - PrivateKey: - IPv4Address: 1.1.1.2/30 - IPv6Address: '' - Name: peer1 - Description: - - AllowedIPs: - - 1.1.1.0/24 - - 1.1.2.0/24 - Nat: false - PrimaryDns: '' - SecondaryDns: '' - Endpoint: 192.168.0.1 - PublicKey: 00000000-0000-0000-0000-000000000001 - PrivateKey: - IPv4Address: 1.1.1.3/30 - IPv6Address: '' - Name: peer2 - Description: - - AllowedIPs: - Nat: false - PrimaryDns: '' - SecondaryDns: '' - Endpoint: '' - PublicKey: 00000000-0000-0000-0000-000000000002 - PrivateKey: - IPv4Address: 1.1.1.4/30 - IPv6Address: '' - Name: peer3 - Description: - OnUp: - - iptables fake rule 1 - - iptables fake rule 2 - OnDown: - PrimaryDns: '' - SecondaryDns: '' - Endpoint: '' - PublicKey: ifacePubkey - PrivateKey: - IPv4Address: 1.1.1.1/24 - IPv6Address: '' - Name: wg1 - Description: - IptablesBin: iptables - WireguardBin: wg - WireguardQuickBin: wg-quick - PrimaryDns: '' - SecondaryDns: '' - Endpoint: ''"; - #endregion - - [Fact] - public void Deserialize() { - var serializer = YamlConfigurationSerializerMock.Instance; - var config = serializer.Deserialize(yaml); - config.Modules.Should().ContainItemsAssignableTo(); - config.Modules.Should().ContainItemsAssignableTo(); - } - - [Fact] - public void Serialize() { - var config = new ConfigurationBase { - Modules = { - new TrafficConfiguration { - Enabled = false, - StorageDriver = new TrafficStorageDriverStub() - }, - new WireguardConfiguration { - Interfaces = new HashSet {new() { - PublicKey = "ifacePubkey", - Name = "wg1", - IPv4Address = IPAddressCidr.Parse("1.1.1.1/24"), - Gateway = new NetworkInterfaceMock("eth0").Object, - Clients = new HashSet { - new() { - Endpoint = new Uri("vpn.example.com", UriKind.RelativeOrAbsolute), - Name = "peer1", - IPv4Address = IPAddressCidr.Parse("1.1.1.2/30"), - AllowedIPs = new HashSet { - IPAddressCidr.Parse("1.1.1.0/24"), IPAddressCidr.Parse("1.1.2.0/24") - }, - PublicKey = "00000000-0000-0000-0000-000000000000" - }, - new() { - Endpoint = new Uri("192.168.0.1", UriKind.RelativeOrAbsolute), - Name = "peer2", - IPv4Address = IPAddressCidr.Parse("1.1.1.3/30"), - AllowedIPs = new HashSet { - IPAddressCidr.Parse("1.1.1.0/24"), IPAddressCidr.Parse("1.1.2.0/24") - }, - PublicKey = "00000000-0000-0000-0000-000000000001" - }, - new() { - Name = "peer3", - IPv4Address = IPAddressCidr.Parse("1.1.1.4/30"), - PublicKey = "00000000-0000-0000-0000-000000000002" - } - }, - OnUp = new HashSet { - "iptables fake rule 1", "iptables fake rule 2" - } - }}, - IptablesBin = "iptables", - WireguardBin = "wg", - WireguardQuickBin = "wg-quick" - } - } - }; - var serializer = YamlConfigurationSerializerMock.Instance; - var output = serializer.Serialize(config); - output.Trim().Should().Be(yaml.Trim()); - } - - [Fact] - public void SerializeEmpty() { - var yaml = @"Modules: []"; - var serializer = YamlConfigurationSerializerMock.Instance; - var output = serializer.Serialize(new ConfigurationBase()); - output.Trim().Should().Be(yaml); - } - - [Fact] - public void DeserializeEmpty() { - var yaml = @"Modules: []"; - var serializer = YamlConfigurationSerializerMock.Instance; - var output = serializer.Deserialize(yaml); - var config = new ConfigurationBase(); - output.Should().Be(config); - } -} \ No newline at end of file diff --git a/Linguard/Yaml/Serialization/IPAddressCidrTypeConverter.cs b/Linguard/Yaml/Serialization/IPAddressCidrTypeConverter.cs deleted file mode 100644 index d34318a..0000000 --- a/Linguard/Yaml/Serialization/IPAddressCidrTypeConverter.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Linguard.Core; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; -using YamlDotNet.Serialization; - -namespace Linguard.Yaml.Serialization; - -public class IPAddressCidrTypeConverter : IYamlTypeConverter { - - public bool Accepts(Type type) { - return type == typeof(IPAddressCidr); - } - - public object? ReadYaml(IParser parser, Type type) { - var value = (parser.Current as Scalar)?.Value; - parser.MoveNext(); - return string.IsNullOrEmpty(value) ? default : IPAddressCidr.Parse(value); - } - - public void WriteYaml(IEmitter emitter, object? value, Type type) { - var address = (value as IPAddressCidr)?.ToString(); - emitter.Emit(new Scalar(null, address ?? string.Empty)); - } -} \ No newline at end of file diff --git a/Linguard/Yaml/Serialization/NetworkInterfaceTypeConverter.cs b/Linguard/Yaml/Serialization/NetworkInterfaceTypeConverter.cs deleted file mode 100644 index 13dab4a..0000000 --- a/Linguard/Yaml/Serialization/NetworkInterfaceTypeConverter.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Net.NetworkInformation; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; -using YamlDotNet.Serialization; - -namespace Linguard.Yaml.Serialization; - -public class NetworkInterfaceTypeConverter : IYamlTypeConverter { - public bool Accepts(Type type) { - return type == typeof(NetworkInterface) || type.BaseType == typeof(NetworkInterface); - } - - public object? ReadYaml(IParser parser, Type type) { - var value = (parser.Current as Scalar)?.Value; - parser.MoveNext(); - return string.IsNullOrEmpty(value) - ? default - : NetworkInterface.GetAllNetworkInterfaces() - .First(i => i.Name == value); - } - - public void WriteYaml(IEmitter emitter, object? value, Type type) { - var @interface = (value as NetworkInterface)?.Name; - emitter.Emit(new Scalar(null, @interface ?? string.Empty)); - } -} \ No newline at end of file diff --git a/Linguard/Yaml/Serialization/RuleTypeConverter.cs b/Linguard/Yaml/Serialization/RuleTypeConverter.cs deleted file mode 100644 index 879e2c2..0000000 --- a/Linguard/Yaml/Serialization/RuleTypeConverter.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Linguard.Core.Models.Wireguard; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; -using YamlDotNet.Serialization; - -namespace Linguard.Yaml.Serialization; - -public class RuleTypeConverter : IYamlTypeConverter { - - public bool Accepts(Type type) { - return type == typeof(Rule); - } - - public object? ReadYaml(IParser parser, Type type) { - var value = parser.Consume().Value; - return value; - } - - public void WriteYaml(IEmitter emitter, object? value, Type type) { - emitter.Emit(new Scalar(null, value?.ToString() ?? string.Empty)); - } -} \ No newline at end of file diff --git a/Linguard/Yaml/Serialization/TrafficStorageDriverConverter.cs b/Linguard/Yaml/Serialization/TrafficStorageDriverConverter.cs deleted file mode 100644 index 863b578..0000000 --- a/Linguard/Yaml/Serialization/TrafficStorageDriverConverter.cs +++ /dev/null @@ -1,78 +0,0 @@ -using Linguard.Core.Drivers.TrafficStorage; -using Linguard.Core.Plugins; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; -using YamlDotNet.Serialization; - -namespace Linguard.Yaml.Serialization; - -public class TrafficStorageDriverConverter : IYamlTypeConverter { - - private readonly IPluginEngine _pluginEngine; - - public TrafficStorageDriverConverter(IPluginEngine pluginEngine) { - _pluginEngine = pluginEngine; - } - - public bool Accepts(Type type) { - return type == typeof(ITrafficStorageDriver) || type.GetInterface(nameof(ITrafficStorageDriver)) != default; - } - - public object? ReadYaml(IParser parser, Type type) { - parser.MoveNext(); - var properties = new Dictionary(); - while (parser.Current is not MappingEnd) { - var property = parser.Consume().Value; - switch (property) { - case nameof(ITrafficStorageDriver.Name): - var name = parser.Consume().Value; - properties[nameof(ITrafficStorageDriver.Name)] = name; - break; - case nameof(ITrafficStorageDriver.Description): - // Ignore - parser.MoveNext(); - break; - case nameof(ITrafficStorageDriver.CollectionInterval): - if (!double.TryParse(parser.Consume().Value, out var interval)) { - interval = TimeSpan.FromHours(1).TotalSeconds; - } - properties[nameof(ITrafficStorageDriver.CollectionInterval)] = TimeSpan.FromSeconds(interval); - break; - case nameof(ITrafficStorageDriver.AdditionalOptions): - ParseDictionary(parser, properties, nameof(ITrafficStorageDriver.AdditionalOptions)); - break; - } - } - parser.MoveNext(); - var driverName = (string) properties[nameof(ITrafficStorageDriver.Name)]; - var driver = _pluginEngine.Plugins - .OfType() - .SingleOrDefault(p => p.Name.Equals(driverName)); - if (driver == default) { - throw new YamlException($"No instance of {nameof(ITrafficStorageDriver)} " + - $"named '{driverName}' was found. Maybe you forgot to add a plugin?"); - } - driver.CollectionInterval = (TimeSpan) properties[nameof(ITrafficStorageDriver.CollectionInterval)]; - driver.AdditionalOptions = (IDictionary) properties[nameof(ITrafficStorageDriver.AdditionalOptions)]; - return driver; - } - - private void ParseDictionary(IParser parser, IDictionary properties, string name) { - var dict = new Dictionary(); - parser.MoveNext(); // Skip MappingStart - while (parser.Current is not MappingEnd) { - var key = parser.Consume().Value; - parser.TryConsume(out var value); - dict[key] = value?.Value ?? string.Empty; - } - parser.MoveNext(); - properties[name] = dict; - } - - public void WriteYaml(IEmitter emitter, object? value, Type type) { - // Use default serialization - var serializer = new SerializerBuilder() - .BuildValueSerializer(); - serializer.SerializeValue(emitter, value, type); - } -} \ No newline at end of file diff --git a/Linguard/Yaml/Serialization/UriTypeConverter.cs b/Linguard/Yaml/Serialization/UriTypeConverter.cs deleted file mode 100644 index 166a03c..0000000 --- a/Linguard/Yaml/Serialization/UriTypeConverter.cs +++ /dev/null @@ -1,23 +0,0 @@ -using YamlDotNet.Core; -using YamlDotNet.Core.Events; -using YamlDotNet.Serialization; - -namespace Linguard.Yaml.Serialization; - -public class UriTypeConverter : IYamlTypeConverter { - public bool Accepts(Type type) { - return type == typeof(Uri); - } - - public object? ReadYaml(IParser parser, Type type) { - var value = (parser.Current as Scalar)?.Value; - var uri = string.IsNullOrEmpty(value) ? default : new Uri(value, UriKind.RelativeOrAbsolute); - parser.MoveNext(); - return uri; - } - - public void WriteYaml(IEmitter emitter, object? value, Type type) { - var uri = (value as Uri)?.ToString(); - emitter.Emit(new Scalar(null, uri ?? string.Empty)); - } -} \ No newline at end of file diff --git a/Linguard/Yaml/Serialization/YamlConfigurationSerializer.cs b/Linguard/Yaml/Serialization/YamlConfigurationSerializer.cs deleted file mode 100644 index 5679ef9..0000000 --- a/Linguard/Yaml/Serialization/YamlConfigurationSerializer.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Linguard.Core.Configuration; -using Linguard.Core.Configuration.Serialization; -using YamlDotNet.Serialization; - -namespace Linguard.Yaml.Serialization; - -/// -/// Serializes and deserializes the configuration of the application using YAML. -/// -public class YamlConfigurationSerializer : IConfigurationSerializer { - - private readonly IDeserializer _deserializer; - private readonly ISerializer _serializer; - - public YamlConfigurationSerializer(ISerializer serializer, IDeserializer deserializer) { - _serializer = serializer; - _deserializer = deserializer; - } - - public string Serialize(T configuration) where T : IConfiguration { - return _serializer.Serialize(configuration); - } - - public T Deserialize(string text) where T : IConfiguration { - return _deserializer.Deserialize(text); - } -} \ No newline at end of file diff --git a/Linguard/Yaml/Serialization/YamlConfigurationSerializerBuilder.cs b/Linguard/Yaml/Serialization/YamlConfigurationSerializerBuilder.cs deleted file mode 100644 index 3639c17..0000000 --- a/Linguard/Yaml/Serialization/YamlConfigurationSerializerBuilder.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Linguard.Core.Configuration.Serialization; -using YamlDotNet.Core; -using YamlDotNet.Serialization; - -namespace Linguard.Yaml.Serialization; - -public class YamlConfigurationSerializerBuilder { - private readonly DeserializerBuilder _deserializerBuilder = new(); - private readonly SerializerBuilder _serializerBuilder = new(); - - public YamlConfigurationSerializerBuilder WithTagMapping(TagName tag) { - _deserializerBuilder.WithTagMapping(tag, typeof(T)); - _serializerBuilder.WithTagMapping(tag, typeof(T)); - return this; - } - - public YamlConfigurationSerializerBuilder WithNamingConvention(INamingConvention namingConvention) { - _deserializerBuilder.WithNamingConvention(namingConvention); - _serializerBuilder.WithNamingConvention(namingConvention); - return this; - } - - public YamlConfigurationSerializerBuilder WithTypeConverter(T converter) where T : IYamlTypeConverter { - _deserializerBuilder.WithTypeConverter(converter); - _serializerBuilder.WithTypeConverter(converter); - return this; - } - - public YamlConfigurationSerializerBuilder WithTypeConverter() where T : IYamlTypeConverter { - return WithTypeConverter(Activator.CreateInstance()); - } - - public YamlConfigurationSerializerBuilder WithTypeMapping() where TTo : TFrom { - _deserializerBuilder.WithTypeMapping(); - return this; - } - - public IConfigurationSerializer Build() { - return new YamlConfigurationSerializer(_serializerBuilder.Build(), _deserializerBuilder.Build()); - } -} \ No newline at end of file diff --git a/Linguard/Yaml/Yaml.csproj b/Linguard/Yaml/Yaml.csproj deleted file mode 100644 index d2ab403..0000000 --- a/Linguard/Yaml/Yaml.csproj +++ /dev/null @@ -1,31 +0,0 @@ - - - net6.0 - enable - enable - Linguard.Yaml - Linguard.Yaml - Linguard.Yaml - 2.0.0 - Linguard.Log - José Antonio Mazón San Bartolomé - José Antonio Mazón San Bartolomé - Linguard - 2.0.0 - - - - - - - - - - - - - C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App\6.0.3\Microsoft.Extensions.Logging.Abstractions.dll - - - - diff --git a/Linguard/Yaml/YamlConfigurationManager.cs b/Linguard/Yaml/YamlConfigurationManager.cs deleted file mode 100644 index a846f3b..0000000 --- a/Linguard/Yaml/YamlConfigurationManager.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Linguard.Core.Configuration; -using Linguard.Core.Configuration.Serialization; -using Linguard.Core.Managers; -using Linguard.Core.OS; -using Linguard.Core.Plugins; - -namespace Linguard.Yaml; - -public class YamlConfigurationManager : DefaultFileConfigurationManager where T : IConfiguration { - - public override string[] SupportedExtensions => new [] { - "yaml", "yml" - }; - - public YamlConfigurationManager(IConfiguration configuration, IWorkingDirectory workingDirectory, - ISystemWrapper systemWrapper, IConfigurationSerializer serializer, - IPluginEngine pluginEngine) - : base(configuration, workingDirectory, systemWrapper, serializer, pluginEngine) { - } -} \ No newline at end of file