From 2c570522cdd727b03f2aa49e6743ff761622511c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Antonio=20Maz=C3=B3n=20San=20Bartolom=C3=A9?= Date: Sat, 5 Mar 2022 17:17:05 +0100 Subject: [PATCH] #115 refactor: use type mappings instead of tag mappings by default + Added configuration empty tests for serialization --- Linguard/Cli/Commands/AddInterfaceCommand.cs | 4 +- Linguard/Core.Test/InterfaceShould.cs | 10 +-- .../Mocks/YamlConfigurationSerializerMock.cs | 21 ++++-- .../Core.Test/WireguardDumpParserShould.cs | 2 +- .../YamlConfigurationSerializerShould.cs | 65 ++++++++++++++----- Linguard/Core/Configuration/Configuration.cs | 26 ++++++-- .../Configuration/IWireguardConfiguration.cs | 2 +- .../DefaultYamlConfigurationSerializer.cs | 17 +++-- .../YamlConfigurationSerializerBuilder.cs | 5 ++ .../Configuration/WireguardConfiguration.cs | 2 +- .../Core/Managers/ConfigurationManagerBase.cs | 3 +- .../Core/Managers/FileConfigurationManager.cs | 1 - Linguard/Core/Models/Wireguard/Client.cs | 2 +- Linguard/Core/Models/Wireguard/Interface.cs | 6 +- Linguard/Web/Program.cs | 2 +- 15 files changed, 116 insertions(+), 52 deletions(-) diff --git a/Linguard/Cli/Commands/AddInterfaceCommand.cs b/Linguard/Cli/Commands/AddInterfaceCommand.cs index 2a35482..d3f7ab8 100644 --- a/Linguard/Cli/Commands/AddInterfaceCommand.cs +++ b/Linguard/Cli/Commands/AddInterfaceCommand.cs @@ -54,10 +54,10 @@ public AddInterfaceCommand(IConfigurationManager configurationManager, ILogger l public bool? Auto { get; set; } = default; [CommandOption("onUp", Description = "Commands to execute right after the interface is brought up.")] - public ICollection? OnUp { get; set; } = default; + public ISet? OnUp { get; set; } = default; [CommandOption("onDown", Description = "Commands to execute right after the interface is brought down.")] - public ICollection? OnDown { get; set; } = default; + public ISet? OnDown { get; set; } = default; [CommandOption("pubkey", Description = "The peer's public key.")] public string? PublicKey { get; set; } = default; diff --git a/Linguard/Core.Test/InterfaceShould.cs b/Linguard/Core.Test/InterfaceShould.cs index 4576170..ba8f2ba 100644 --- a/Linguard/Core.Test/InterfaceShould.cs +++ b/Linguard/Core.Test/InterfaceShould.cs @@ -46,19 +46,19 @@ public void CreateValidWireguardConfiguration() { IPv6Address = IPAddressCidr.Parse("47cc:ec62:b8b4:d4c0:9c90:4c5c:1df5:a13f/64"), PublicKey = "c892a52a-1fad-4564-af83-641744cd4dc3", PrivateKey = "16116afc-3068-4ff5-88e0-0662ef57641a", - OnUp = new Rule[] { + OnUp = new HashSet { "/usr/sbin/iptables -I FORWARD -i wg0 -j ACCEPT", "/usr/sbin/iptables -I FORWARD -o wg0 -j ACCEPT", "/usr/sbin/iptables -t nat -I POSTROUTING -o eth0 -j MASQUERADE" }, - OnDown = new Rule[] { + OnDown = new HashSet { "/usr/sbin/iptables -D FORWARD -i wg0 -j ACCEPT", "/usr/sbin/iptables -D FORWARD -o wg0 -j ACCEPT", "/usr/sbin/iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE" }, Gateway = _system.NetworkInterfaces.First(), - Clients = new[] { - new Client { + Clients = new HashSet { + new() { Endpoint = new Uri("vpn.example.com", UriKind.RelativeOrAbsolute), Name = "peer1", Nat = true, @@ -69,7 +69,7 @@ public void CreateValidWireguardConfiguration() { AllowedIPs = new HashSet { IPAddressCidr.Parse("1.1.2.0/24") }, PrimaryDns = new Uri("8.8.8.8", UriKind.RelativeOrAbsolute) }, - new Client { + new() { Endpoint = new Uri("vpn2.example.com", UriKind.RelativeOrAbsolute), Name = "peer2", IPv4Address = IPAddressCidr.Parse("1.1.1.2/30"), diff --git a/Linguard/Core.Test/Mocks/YamlConfigurationSerializerMock.cs b/Linguard/Core.Test/Mocks/YamlConfigurationSerializerMock.cs index 9e27a21..f4d76e0 100644 --- a/Linguard/Core.Test/Mocks/YamlConfigurationSerializerMock.cs +++ b/Linguard/Core.Test/Mocks/YamlConfigurationSerializerMock.cs @@ -1,6 +1,9 @@ -using Linguard.Core.Configuration; +using System.Collections.Generic; +using Linguard.Core; +using Linguard.Core.Configuration; using Linguard.Core.Configuration.Serialization; using Linguard.Core.Drivers.TrafficStorage; +using Linguard.Core.Models.Wireguard; using YamlDotNet.Serialization.NamingConventions; namespace Core.Test.Mocks; @@ -13,11 +16,15 @@ public static class YamlConfigurationSerializerMock { .WithTypeConverter() .WithTypeConverter() .WithTypeConverter() - .WithTagMapping("!Configuration") - .WithTagMapping("!Wireguard") - .WithTagMapping("!Logging") - .WithTagMapping("!Web") - .WithTagMapping("!Traffic") - .WithTagMapping("!Json") + .WithTypeMapping() + .WithTypeMapping() + .WithTypeMapping() + .WithTypeMapping() + .WithTypeMapping() + .WithTypeMapping() + .WithTypeMapping, HashSet>() + .WithTypeMapping, HashSet>() + .WithTypeMapping, HashSet>() + .WithTypeMapping, HashSet>() .Build(); } \ No newline at end of file diff --git a/Linguard/Core.Test/WireguardDumpParserShould.cs b/Linguard/Core.Test/WireguardDumpParserShould.cs index 6d03e5a..36839cc 100644 --- a/Linguard/Core.Test/WireguardDumpParserShould.cs +++ b/Linguard/Core.Test/WireguardDumpParserShould.cs @@ -18,7 +18,7 @@ public class WireguardDumpParserShould { public WireguardDumpParserShould() { var iface = new Interface { Name = "wg1", - Clients = new List { + Clients = new HashSet { new() { PublicKey = "fE/wdxzl0klVp/IR8UcaoGUMjqaWi3jAd7KzHKFS6Ds=" }, diff --git a/Linguard/Core.Test/YamlConfigurationSerializerShould.cs b/Linguard/Core.Test/YamlConfigurationSerializerShould.cs index 60aba35..3c10ee1 100644 --- a/Linguard/Core.Test/YamlConfigurationSerializerShould.cs +++ b/Linguard/Core.Test/YamlConfigurationSerializerShould.cs @@ -16,8 +16,7 @@ public class YamlConfigurationSerializerShould { #region Yaml string - const string yaml = @"!Configuration -Wireguard: !Wireguard + const string yaml = @"Wireguard: Interfaces: - Gateway: eth0 Port: 0 @@ -44,27 +43,27 @@ public class YamlConfigurationSerializerShould { PrimaryDns: '' SecondaryDns: '' Endpoint: 192.168.0.1 - Id: 00000000-0000-0000-0000-000000000000 + Id: 00000000-0000-0000-0000-000000000001 PublicKey: PrivateKey: IPv4Address: 1.1.1.3/30 IPv6Address: '' Name: peer2 Description: - - AllowedIPs: [] + - AllowedIPs: Nat: false PrimaryDns: '' SecondaryDns: '' Endpoint: '' - Id: 00000000-0000-0000-0000-000000000000 + Id: 00000000-0000-0000-0000-000000000002 PublicKey: PrivateKey: IPv4Address: 1.1.1.4/30 IPv6Address: '' Name: peer3 Description: - OnUp: [] - OnDown: [] + OnUp: + OnDown: PrimaryDns: '' SecondaryDns: '' Endpoint: '' @@ -81,16 +80,16 @@ public class YamlConfigurationSerializerShould { PrimaryDns: '' SecondaryDns: '' Endpoint: '' -Logging: !Logging +Logging: Level: Debug Overwrite: false -Web: !Web +Web: LoginAttempts: 10 SecretKey: '' Style: Default -Traffic: !Traffic +Traffic: Enabled: false - StorageDriver: !Json + StorageDriver: TimestampFormat: aaa "; #endregion @@ -128,22 +127,29 @@ public void Serialize() { Name = "wg1", IPv4Address = IPAddressCidr.Parse("1.1.1.1/24"), Gateway = new NetworkInterfaceMock("eth0").Object, - Clients = new []{ - new Client { + 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")}, + AllowedIPs = new HashSet { + IPAddressCidr.Parse("1.1.1.0/24"), IPAddressCidr.Parse("1.1.2.0/24") + }, + Id = Guid.Parse("00000000-0000-0000-0000-000000000000") }, - new Client { + 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")} + AllowedIPs = new HashSet { + IPAddressCidr.Parse("1.1.1.0/24"), IPAddressCidr.Parse("1.1.2.0/24") + }, + Id = Guid.Parse("00000000-0000-0000-0000-000000000001") }, - new Client { + new() { Name = "peer3", IPv4Address = IPAddressCidr.Parse("1.1.1.4/30"), + Id = Guid.Parse("00000000-0000-0000-0000-000000000002") } } }}, @@ -156,4 +162,29 @@ public void Serialize() { var output = serializer.Serialize(config); output.Trim().Should().Be(yaml.Trim()); } + + [Fact] + public void SerializeEmpty() { + const string yaml = @"Wireguard: +Logging: +Web: +Traffic: +"; + var serializer = YamlConfigurationSerializerMock.Instance; + var output = serializer.Serialize(new Configuration()); + output.Should().Be(yaml); + } + + [Fact] + public void DeserializeEmpty() { + const string yaml = @"Wireguard: +Logging: +Web: +Traffic: +"; + var serializer = YamlConfigurationSerializerMock.Instance; + var output = serializer.Deserialize(yaml); + var config = new Configuration(); + output.Should().Be(config); + } } \ No newline at end of file diff --git a/Linguard/Core/Configuration/Configuration.cs b/Linguard/Core/Configuration/Configuration.cs index 6edbc1d..e908b45 100644 --- a/Linguard/Core/Configuration/Configuration.cs +++ b/Linguard/Core/Configuration/Configuration.cs @@ -3,12 +3,30 @@ namespace Linguard.Core.Configuration; public class Configuration : IConfiguration { - public IWireguardConfiguration Wireguard { get; set; } = new WireguardConfiguration(); - public ILoggingConfiguration Logging { get; set; } = new LoggingConfiguration(); - public IWebConfiguration Web { get; set; } = new WebConfiguration(); - public ITrafficConfiguration Traffic { get; set; } = new TrafficConfiguration(); + public IWireguardConfiguration? Wireguard { get; set; } + public ILoggingConfiguration? Logging { get; set; } + public IWebConfiguration? Web { get; set; } + public ITrafficConfiguration? Traffic { get; set; } public object Clone() { return Cloning.Clone(this); } + + protected bool Equals(Configuration other) { + return Equals(Wireguard, other.Wireguard) + && Equals(Logging, other.Logging) + && Equals(Web, other.Web) + && Equals(Traffic, other.Traffic); + } + + public override bool Equals(object? obj) { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((Configuration)obj); + } + + public override int GetHashCode() { + return HashCode.Combine(Wireguard, Logging, Web, Traffic); + } } \ No newline at end of file diff --git a/Linguard/Core/Configuration/IWireguardConfiguration.cs b/Linguard/Core/Configuration/IWireguardConfiguration.cs index 730aa61..de826ec 100644 --- a/Linguard/Core/Configuration/IWireguardConfiguration.cs +++ b/Linguard/Core/Configuration/IWireguardConfiguration.cs @@ -3,7 +3,7 @@ namespace Linguard.Core.Configuration; public interface IWireguardConfiguration : ICloneable { - public HashSet Interfaces { get; set; } + public ISet Interfaces { get; set; } /// /// Path to the iptables binary file. /// diff --git a/Linguard/Core/Configuration/Serialization/DefaultYamlConfigurationSerializer.cs b/Linguard/Core/Configuration/Serialization/DefaultYamlConfigurationSerializer.cs index 43457cf..d3801fc 100644 --- a/Linguard/Core/Configuration/Serialization/DefaultYamlConfigurationSerializer.cs +++ b/Linguard/Core/Configuration/Serialization/DefaultYamlConfigurationSerializer.cs @@ -1,4 +1,5 @@ using Linguard.Core.Drivers.TrafficStorage; +using Linguard.Core.Models.Wireguard; using YamlDotNet.Serialization.NamingConventions; namespace Linguard.Core.Configuration.Serialization; @@ -11,11 +12,15 @@ public static class DefaultYamlConfigurationSerializer { .WithTypeConverter() .WithTypeConverter() .WithTypeConverter() - .WithTagMapping("!Configuration") - .WithTagMapping("!Wireguard") - .WithTagMapping("!Logging") - .WithTagMapping("!Web") - .WithTagMapping("!Traffic") - .WithTagMapping("!Json") + .WithTypeMapping() + .WithTypeMapping() + .WithTypeMapping() + .WithTypeMapping() + .WithTypeMapping() + .WithTypeMapping() + .WithTypeMapping, HashSet>() + .WithTypeMapping, HashSet>() + .WithTypeMapping, HashSet>() + .WithTypeMapping, HashSet>() .Build(); } \ No newline at end of file diff --git a/Linguard/Core/Configuration/Serialization/YamlConfigurationSerializerBuilder.cs b/Linguard/Core/Configuration/Serialization/YamlConfigurationSerializerBuilder.cs index 53bb148..6dba313 100644 --- a/Linguard/Core/Configuration/Serialization/YamlConfigurationSerializerBuilder.cs +++ b/Linguard/Core/Configuration/Serialization/YamlConfigurationSerializerBuilder.cs @@ -29,6 +29,11 @@ public YamlConfigurationSerializerBuilder WithTypeConverter() where T : IYaml return WithTypeConverter(Activator.CreateInstance()); } + public YamlConfigurationSerializerBuilder WithTypeMapping() where TTo : TFrom { + _deserializerBuilder.WithTypeMapping(); + return this; + } + public YamlConfigurationSerializer Build() { return new YamlConfigurationSerializer(_serializerBuilder.Build(), _deserializerBuilder.Build()); } diff --git a/Linguard/Core/Configuration/WireguardConfiguration.cs b/Linguard/Core/Configuration/WireguardConfiguration.cs index d1f44a2..37c532e 100644 --- a/Linguard/Core/Configuration/WireguardConfiguration.cs +++ b/Linguard/Core/Configuration/WireguardConfiguration.cs @@ -3,7 +3,7 @@ namespace Linguard.Core.Configuration; public class WireguardConfiguration : IWireguardConfiguration { - public HashSet Interfaces { get; set; } + public ISet Interfaces { get; set; } public string IptablesBin { get; set; } public string WireguardBin { get; set; } public string WireguardQuickBin { get; set; } diff --git a/Linguard/Core/Managers/ConfigurationManagerBase.cs b/Linguard/Core/Managers/ConfigurationManagerBase.cs index 4163a61..6a12a95 100644 --- a/Linguard/Core/Managers/ConfigurationManagerBase.cs +++ b/Linguard/Core/Managers/ConfigurationManagerBase.cs @@ -43,14 +43,13 @@ private void LoadTrafficDefaults() { Configuration.Traffic.StorageDriver = new JsonTrafficStorageDriver(); } private void LoadWireguardDefaults() { - Configuration.Wireguard.Interfaces = new HashSet(); Configuration.Wireguard.IptablesBin = _systemWrapper .RunCommand("whereis iptables | tr ' ' '\n' | grep bin").Stdout; Configuration.Wireguard.WireguardBin = _systemWrapper .RunCommand("whereis wg | tr ' ' '\n' | grep bin").Stdout; Configuration.Wireguard.WireguardQuickBin = _systemWrapper .RunCommand("whereis wg-quick | tr ' ' '\n' | grep bin").Stdout; - Configuration.Wireguard.Interfaces = new(); + Configuration.Wireguard.Interfaces = new HashSet(); Configuration.Wireguard.PrimaryDns = new("8.8.8.8", UriKind.RelativeOrAbsolute); Configuration.Wireguard.SecondaryDns = new("8.8.4.4", UriKind.RelativeOrAbsolute); var publicIp = Network.GetPublicIPAddress(); diff --git a/Linguard/Core/Managers/FileConfigurationManager.cs b/Linguard/Core/Managers/FileConfigurationManager.cs index 4b368fd..4a05028 100644 --- a/Linguard/Core/Managers/FileConfigurationManager.cs +++ b/Linguard/Core/Managers/FileConfigurationManager.cs @@ -25,7 +25,6 @@ public override void Load() { $"Configuration file '{ConfigurationFile.FullName}' does not exist." ); } - try { Configuration = Serializer.Deserialize(File.ReadAllText(ConfigurationFile.FullName)); } diff --git a/Linguard/Core/Models/Wireguard/Client.cs b/Linguard/Core/Models/Wireguard/Client.cs index 47ad2cf..2336553 100644 --- a/Linguard/Core/Models/Wireguard/Client.cs +++ b/Linguard/Core/Models/Wireguard/Client.cs @@ -1,7 +1,7 @@ namespace Linguard.Core.Models.Wireguard; public class Client : WireguardPeerBase, ICloneable { - public HashSet AllowedIPs { get; set; } = new(); + public ISet AllowedIPs { get; set; } public bool Nat { get; set; } public Uri PrimaryDns { get; set; } public Uri? SecondaryDns { get; set; } diff --git a/Linguard/Core/Models/Wireguard/Interface.cs b/Linguard/Core/Models/Wireguard/Interface.cs index 4520c25..22f6618 100644 --- a/Linguard/Core/Models/Wireguard/Interface.cs +++ b/Linguard/Core/Models/Wireguard/Interface.cs @@ -6,9 +6,9 @@ public class Interface : WireguardPeerBase, ICloneable { public NetworkInterface Gateway { get; set; } public int Port { get; set; } public bool Auto { get; set; } - public ICollection Clients { get; set; } = new List(); - public ICollection OnUp { get; set; } = new List(); - public ICollection OnDown { get; set; } = new List(); + public ISet Clients { get; set; } + public ISet OnUp { get; set; } + public ISet OnDown { get; set; } /// /// Default primary DNS for all peers if none specified. /// diff --git a/Linguard/Web/Program.cs b/Linguard/Web/Program.cs index 20f5509..02bd713 100644 --- a/Linguard/Web/Program.cs +++ b/Linguard/Web/Program.cs @@ -24,7 +24,7 @@ builder.Services.AddTransient(); builder.Services.AddSingleton(DefaultYamlConfigurationSerializer.Instance); builder.Services.AddTransient(); -builder.Services.AddTransient(); +builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient();