From 836669694d7ece35a5fe3c6140f571074659dc79 Mon Sep 17 00:00:00 2001 From: jgauffin Date: Fri, 18 Oct 2019 07:32:35 +0200 Subject: [PATCH 01/11] Started on integration tests and finishing the whirelist --- .../Coderr.Server.Api.Client.csproj | 14 +- .../Coderr.Server.Api.csproj | 18 +- .../Modules}/Whitelists/Commands/AddDomain.cs | 9 +- .../Whitelists/Commands/RemoveEntry.cs | 2 +- .../Whitelists/Queries/GetWhitelistEntries.cs | 8 +- .../Queries/GetWhitelistEntriesResult.cs | 2 +- .../Queries/GetWhitelistEntriesResultItem.cs | 2 +- .../Coderr.Server.App.csproj | 1 + .../Whitelists/IWhitelistRepository.cs | 17 ++ .../Modules/Whitelists/IWhitelistService.cs | 27 ++ .../Modules/Whitelists/IpType.cs | 23 ++ .../Modules/Whitelists/WhitelistService.cs | 127 ++++++++++ .../Modules/Whitelists/WhitelistedDomain.cs | 33 +++ .../Modules/Whitelists/WhitelistedDomainIp.cs | 31 +++ .../Inbound/SaveReportHandler.cs | 1 + .../Inbound/Whitelist/AddDomainHandler.cs | 26 -- .../Inbound/Whitelist/WhitelistedDomain.cs | 16 -- .../Whitelist/WhitelistedDomainMapper.cs | 17 -- .../Coderr.Server.SqlServer.csproj | 5 + .../Modules/Whitelists/AddDomainHandler.cs | 60 +++++ .../Whitelists}/GetWhitelistEntriesHandler.cs | 8 +- .../Whitelists}/RemoveDomainHandler.cs | 5 +- .../Modules/Whitelists/WhitelistRepository.cs | 51 ++++ .../Whitelists/WhitelistedDomainIpMapper.cs | 22 ++ .../Whitelists/WhitelistedDomainMapper.cs | 16 ++ .../Schema/Coderr.v21.sql | 14 ++ .../components/home/navmenu/navmenu.ts | 10 +- .../manage/application/settings/settings.ts | 8 + .../application/settings/settings.vue.html | 7 +- .../components/manage/system/menu.vue.html | 8 +- .../manage/system/whitelist/home.ts | 14 +- .../manage/system/whitelist/home.vue.html | 64 ++--- .../applications/ApplicationService.ts | 11 +- .../Controllers/CqsController.cs | 9 +- .../Controllers/ReportReceiverController.cs | 40 ++- src/Server/Coderr.Server.Web/Program.cs | 1 + src/Server/Coderr.Server.Web/appsettings.json | 2 +- src/Server/Coderr.Server.Web/tsconfig.json | 2 +- .../Coderr.IntegrationTests.sln | 25 ++ .../ApplicationClient.cs | 52 ++++ .../Coderr.IntegrationTests.Core.csproj | 15 ++ .../Coderr.IntegrationTests/Program.cs | 24 ++ .../TestCases/IncidentLifecycle.cs | 23 ++ .../TestFailedException.cs | 12 + .../Tools/ActionExtensions.cs | 25 ++ .../Tools/IncidentWrapper.cs | 85 +++++++ .../Coderr.IntegrationTests/Tools/Reporter.cs | 97 ++++++++ .../Tools/ReportingApiClient.cs | 234 ++++++++++++++++++ .../Coderr.IntegrationTests/Tools/SqlTools.cs | 54 ++++ .../Tools/TestExtensions.cs | 21 ++ 50 files changed, 1235 insertions(+), 163 deletions(-) rename src/Server/{Coderr.Server.ReportAnalyzer.Abstractions/Inbound => Coderr.Server.Api/Modules}/Whitelists/Commands/AddDomain.cs (60%) rename src/Server/{Coderr.Server.ReportAnalyzer.Abstractions/Inbound => Coderr.Server.Api/Modules}/Whitelists/Commands/RemoveEntry.cs (75%) rename src/Server/{Coderr.Server.ReportAnalyzer.Abstractions/Inbound => Coderr.Server.Api/Modules}/Whitelists/Queries/GetWhitelistEntries.cs (71%) rename src/Server/{Coderr.Server.ReportAnalyzer.Abstractions/Inbound => Coderr.Server.Api/Modules}/Whitelists/Queries/GetWhitelistEntriesResult.cs (72%) rename src/Server/{Coderr.Server.ReportAnalyzer.Abstractions/Inbound => Coderr.Server.Api/Modules}/Whitelists/Queries/GetWhitelistEntriesResultItem.cs (77%) create mode 100644 src/Server/Coderr.Server.App/Modules/Whitelists/IWhitelistRepository.cs create mode 100644 src/Server/Coderr.Server.App/Modules/Whitelists/IWhitelistService.cs create mode 100644 src/Server/Coderr.Server.App/Modules/Whitelists/IpType.cs create mode 100644 src/Server/Coderr.Server.App/Modules/Whitelists/WhitelistService.cs create mode 100644 src/Server/Coderr.Server.App/Modules/Whitelists/WhitelistedDomain.cs create mode 100644 src/Server/Coderr.Server.App/Modules/Whitelists/WhitelistedDomainIp.cs delete mode 100644 src/Server/Coderr.Server.ReportAnalyzer/Inbound/Whitelist/AddDomainHandler.cs delete mode 100644 src/Server/Coderr.Server.ReportAnalyzer/Inbound/Whitelist/WhitelistedDomain.cs delete mode 100644 src/Server/Coderr.Server.ReportAnalyzer/Inbound/Whitelist/WhitelistedDomainMapper.cs create mode 100644 src/Server/Coderr.Server.SqlServer/Modules/Whitelists/AddDomainHandler.cs rename src/Server/{Coderr.Server.ReportAnalyzer/Inbound/Whitelist => Coderr.Server.SqlServer/Modules/Whitelists}/GetWhitelistEntriesHandler.cs (84%) rename src/Server/{Coderr.Server.ReportAnalyzer/Inbound/Whitelist => Coderr.Server.SqlServer/Modules/Whitelists}/RemoveDomainHandler.cs (79%) create mode 100644 src/Server/Coderr.Server.SqlServer/Modules/Whitelists/WhitelistRepository.cs create mode 100644 src/Server/Coderr.Server.SqlServer/Modules/Whitelists/WhitelistedDomainIpMapper.cs create mode 100644 src/Server/Coderr.Server.SqlServer/Modules/Whitelists/WhitelistedDomainMapper.cs create mode 100644 src/Server/Coderr.Server.SqlServer/Schema/Coderr.v21.sql create mode 100644 src/Tools/Coderr.IntegrationTests/Coderr.IntegrationTests.sln create mode 100644 src/Tools/Coderr.IntegrationTests/Coderr.IntegrationTests/ApplicationClient.cs create mode 100644 src/Tools/Coderr.IntegrationTests/Coderr.IntegrationTests/Coderr.IntegrationTests.Core.csproj create mode 100644 src/Tools/Coderr.IntegrationTests/Coderr.IntegrationTests/Program.cs create mode 100644 src/Tools/Coderr.IntegrationTests/Coderr.IntegrationTests/TestCases/IncidentLifecycle.cs create mode 100644 src/Tools/Coderr.IntegrationTests/Coderr.IntegrationTests/TestFailedException.cs create mode 100644 src/Tools/Coderr.IntegrationTests/Coderr.IntegrationTests/Tools/ActionExtensions.cs create mode 100644 src/Tools/Coderr.IntegrationTests/Coderr.IntegrationTests/Tools/IncidentWrapper.cs create mode 100644 src/Tools/Coderr.IntegrationTests/Coderr.IntegrationTests/Tools/Reporter.cs create mode 100644 src/Tools/Coderr.IntegrationTests/Coderr.IntegrationTests/Tools/ReportingApiClient.cs create mode 100644 src/Tools/Coderr.IntegrationTests/Coderr.IntegrationTests/Tools/SqlTools.cs create mode 100644 src/Tools/Coderr.IntegrationTests/Coderr.IntegrationTests/Tools/TestExtensions.cs diff --git a/src/Server/Coderr.Server.Api.Client/Coderr.Server.Api.Client.csproj b/src/Server/Coderr.Server.Api.Client/Coderr.Server.Api.Client.csproj index 812663f5..b55110aa 100644 --- a/src/Server/Coderr.Server.Api.Client/Coderr.Server.Api.Client.csproj +++ b/src/Server/Coderr.Server.Api.Client/Coderr.Server.Api.Client.csproj @@ -4,22 +4,22 @@ 1.1.0 true bin\$(Configuration)\$(TargetFramework)\Coderr.Server.Api.Client.xml + Coderr.Server.Api.Client + Coderr.Server.Api.Client Coderr.Server.Api.Client 1TCompany AB - API client for codeRR Server. + API client for Coderr Server. true Converted to vstudio 2017 csproj format Copyright 2017 © 1TCompany AB. All rights reserved. logger exceptions analysis .net-core netstandard - http://coderrapp.com/images/nuget_icon.png - https://github.com/coderr/coderr.server + https://coderr.io/images/nuget_icon.png + https://github.com/coderrio/coderr.server git - https://raw.githubusercontent.com/coderr/codeRR.Server/master/LICENSE - https://coderrapp.com - Coderr.Server.Api.Client - Coderr.Server.Api.Client + Apache-2.0 + https://coderr.io diff --git a/src/Server/Coderr.Server.Api/Coderr.Server.Api.csproj b/src/Server/Coderr.Server.Api/Coderr.Server.Api.csproj index 1100355d..77f745b9 100644 --- a/src/Server/Coderr.Server.Api/Coderr.Server.Api.csproj +++ b/src/Server/Coderr.Server.Api/Coderr.Server.Api.csproj @@ -5,21 +5,21 @@ Coderr.Server.Api Coderr.Server.Api true - bin\$(Configuration)\$(TargetFramework)\codeRR.Server.Api.xml + bin\$(Configuration)\$(TargetFramework)\Coderr.Server.Api.xml - - codeRR.Server.Api + + Coderr.Server.Api 1TCompany AB - CQRS API definition for codeRR Server. + API object definitions for Coderr Server, use the ApiClient package to communicate with the Coderr Server. true First release - Copyright 2017 © 1TCompany AB. All rights reserved. + Copyright 2019 © 1TCompany AB. All rights reserved. logger exceptions analysis .net-core netstandard - http://coderrapp.com/images/nuget_icon.png - https://github.com/coderr/coderr.server + https://coderr.io/images/nuget_icon.png + https://github.com/coderrio/coderr.server git - https://raw.githubusercontent.com/coderr/codeRR.Server/master/LICENSE - https://coderrapp.com + Apache-2.0 + https://coderr.io 1701;1702;1705;1591 diff --git a/src/Server/Coderr.Server.ReportAnalyzer.Abstractions/Inbound/Whitelists/Commands/AddDomain.cs b/src/Server/Coderr.Server.Api/Modules/Whitelists/Commands/AddDomain.cs similarity index 60% rename from src/Server/Coderr.Server.ReportAnalyzer.Abstractions/Inbound/Whitelists/Commands/AddDomain.cs rename to src/Server/Coderr.Server.Api/Modules/Whitelists/Commands/AddDomain.cs index 708b75d2..51d32b2d 100644 --- a/src/Server/Coderr.Server.ReportAnalyzer.Abstractions/Inbound/Whitelists/Commands/AddDomain.cs +++ b/src/Server/Coderr.Server.Api/Modules/Whitelists/Commands/AddDomain.cs @@ -1,4 +1,4 @@ -namespace Coderr.Server.ReportAnalyzer.Abstractions.Inbound.Whitelists.Commands +namespace Coderr.Server.Api.Modules.Whitelists.Commands { /// /// Add a domain that may post error reports without using a shared secret (javascript applications) @@ -8,12 +8,7 @@ public class AddDomain /// /// Application that the domain is allowed for. /// - /// - /// - /// ´null if allowed for all applications - /// - /// - public int? ApplicationId { get; set; } + public int ApplicationId { get; set; } /// /// For instance yourdomain.com. diff --git a/src/Server/Coderr.Server.ReportAnalyzer.Abstractions/Inbound/Whitelists/Commands/RemoveEntry.cs b/src/Server/Coderr.Server.Api/Modules/Whitelists/Commands/RemoveEntry.cs similarity index 75% rename from src/Server/Coderr.Server.ReportAnalyzer.Abstractions/Inbound/Whitelists/Commands/RemoveEntry.cs rename to src/Server/Coderr.Server.Api/Modules/Whitelists/Commands/RemoveEntry.cs index 5c586277..87d2a23d 100644 --- a/src/Server/Coderr.Server.ReportAnalyzer.Abstractions/Inbound/Whitelists/Commands/RemoveEntry.cs +++ b/src/Server/Coderr.Server.Api/Modules/Whitelists/Commands/RemoveEntry.cs @@ -1,4 +1,4 @@ -namespace Coderr.Server.ReportAnalyzer.Abstractions.Inbound.Whitelists.Commands +namespace Coderr.Server.Api.Modules.Whitelists.Commands { /// /// Remove a previously added white list entry diff --git a/src/Server/Coderr.Server.ReportAnalyzer.Abstractions/Inbound/Whitelists/Queries/GetWhitelistEntries.cs b/src/Server/Coderr.Server.Api/Modules/Whitelists/Queries/GetWhitelistEntries.cs similarity index 71% rename from src/Server/Coderr.Server.ReportAnalyzer.Abstractions/Inbound/Whitelists/Queries/GetWhitelistEntries.cs rename to src/Server/Coderr.Server.Api/Modules/Whitelists/Queries/GetWhitelistEntries.cs index 2da8271d..759c4c90 100644 --- a/src/Server/Coderr.Server.ReportAnalyzer.Abstractions/Inbound/Whitelists/Queries/GetWhitelistEntries.cs +++ b/src/Server/Coderr.Server.Api/Modules/Whitelists/Queries/GetWhitelistEntries.cs @@ -1,10 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Coderr.Server.Api; -using DotNetCqs; +using DotNetCqs; -namespace Coderr.Server.ReportAnalyzer.Abstractions.Inbound.Whitelists.Queries +namespace Coderr.Server.Api.Modules.Whitelists.Queries { /// /// Get whitelist either by application id or DomainName diff --git a/src/Server/Coderr.Server.ReportAnalyzer.Abstractions/Inbound/Whitelists/Queries/GetWhitelistEntriesResult.cs b/src/Server/Coderr.Server.Api/Modules/Whitelists/Queries/GetWhitelistEntriesResult.cs similarity index 72% rename from src/Server/Coderr.Server.ReportAnalyzer.Abstractions/Inbound/Whitelists/Queries/GetWhitelistEntriesResult.cs rename to src/Server/Coderr.Server.Api/Modules/Whitelists/Queries/GetWhitelistEntriesResult.cs index 6835fe74..b6e709ae 100644 --- a/src/Server/Coderr.Server.ReportAnalyzer.Abstractions/Inbound/Whitelists/Queries/GetWhitelistEntriesResult.cs +++ b/src/Server/Coderr.Server.Api/Modules/Whitelists/Queries/GetWhitelistEntriesResult.cs @@ -1,4 +1,4 @@ -namespace Coderr.Server.ReportAnalyzer.Abstractions.Inbound.Whitelists.Queries +namespace Coderr.Server.Api.Modules.Whitelists.Queries { /// /// Result for . diff --git a/src/Server/Coderr.Server.ReportAnalyzer.Abstractions/Inbound/Whitelists/Queries/GetWhitelistEntriesResultItem.cs b/src/Server/Coderr.Server.Api/Modules/Whitelists/Queries/GetWhitelistEntriesResultItem.cs similarity index 77% rename from src/Server/Coderr.Server.ReportAnalyzer.Abstractions/Inbound/Whitelists/Queries/GetWhitelistEntriesResultItem.cs rename to src/Server/Coderr.Server.Api/Modules/Whitelists/Queries/GetWhitelistEntriesResultItem.cs index a008500e..fc5b2175 100644 --- a/src/Server/Coderr.Server.ReportAnalyzer.Abstractions/Inbound/Whitelists/Queries/GetWhitelistEntriesResultItem.cs +++ b/src/Server/Coderr.Server.Api/Modules/Whitelists/Queries/GetWhitelistEntriesResultItem.cs @@ -1,4 +1,4 @@ -namespace Coderr.Server.ReportAnalyzer.Abstractions.Inbound.Whitelists.Queries +namespace Coderr.Server.Api.Modules.Whitelists.Queries { /// /// Entry for diff --git a/src/Server/Coderr.Server.App/Coderr.Server.App.csproj b/src/Server/Coderr.Server.App/Coderr.Server.App.csproj index 1456298e..23595412 100644 --- a/src/Server/Coderr.Server.App/Coderr.Server.App.csproj +++ b/src/Server/Coderr.Server.App/Coderr.Server.App.csproj @@ -21,6 +21,7 @@ NU1701 + diff --git a/src/Server/Coderr.Server.App/Modules/Whitelists/IWhitelistRepository.cs b/src/Server/Coderr.Server.App/Modules/Whitelists/IWhitelistRepository.cs new file mode 100644 index 00000000..2cf82f73 --- /dev/null +++ b/src/Server/Coderr.Server.App/Modules/Whitelists/IWhitelistRepository.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using System.Net; +using System.Threading.Tasks; + +namespace Coderr.Server.App.Modules.Whitelists +{ + /// + /// Whitelists is used for reports that don't use a shared secret + /// + public interface IWhitelistRepository + { + Task FindIp(int applicationId, IPAddress address); + Task> GetWhitelist(int applicationId); + + Task SaveIp(WhitelistedDomainIp entry); + } +} \ No newline at end of file diff --git a/src/Server/Coderr.Server.App/Modules/Whitelists/IWhitelistService.cs b/src/Server/Coderr.Server.App/Modules/Whitelists/IWhitelistService.cs new file mode 100644 index 00000000..eafd580a --- /dev/null +++ b/src/Server/Coderr.Server.App/Modules/Whitelists/IWhitelistService.cs @@ -0,0 +1,27 @@ +using System.Net; +using System.Threading.Tasks; + +namespace Coderr.Server.App.Modules.Whitelists +{ + /// + /// Used to validate origin of inbound requests when a shared secret is not used. + /// + public interface IWhitelistService + { + /// + /// Is domain white listed? + /// + /// AppKey used when receiving error reports. + /// IP address of the client reporting the error. + /// + Task Validate(string appKey, IPAddress remoteAddress); + + /// + /// Is domain white listed? + /// + /// Application that the error is reported for. + /// IP address of the client reporting the error. + /// + Task Validate(int applicationId, IPAddress remoteAddress); + } +} \ No newline at end of file diff --git a/src/Server/Coderr.Server.App/Modules/Whitelists/IpType.cs b/src/Server/Coderr.Server.App/Modules/Whitelists/IpType.cs new file mode 100644 index 00000000..1433a307 --- /dev/null +++ b/src/Server/Coderr.Server.App/Modules/Whitelists/IpType.cs @@ -0,0 +1,23 @@ +namespace Coderr.Server.App.Modules.Whitelists +{ + /// + /// Typ of stored IP record. + /// + public enum IpType + { + /// + /// Added when doing a lookup for the domain + /// + Lookup, + + /// + /// Manually specified by the user + /// + Specified, + + /// + /// We got a request from this IP and a lookup didn't match it. + /// + Denied + } +} \ No newline at end of file diff --git a/src/Server/Coderr.Server.App/Modules/Whitelists/WhitelistService.cs b/src/Server/Coderr.Server.App/Modules/Whitelists/WhitelistService.cs new file mode 100644 index 00000000..a9ac1442 --- /dev/null +++ b/src/Server/Coderr.Server.App/Modules/Whitelists/WhitelistService.cs @@ -0,0 +1,127 @@ +using System; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using Coderr.Server.Abstractions.Boot; +using Coderr.Server.Domain.Core.Applications; +using DnsClient; +using DnsClient.Protocol; + +namespace Coderr.Server.App.Modules.Whitelists +{ + /// + /// Used to validate origin of inbound requests when a shared secret is not used. + /// + [ContainerService] + public class WhitelistService : IWhitelistService + { + private readonly IWhitelistRepository _repository; + private readonly IApplicationRepository _applicationRepository; + + public WhitelistService(IWhitelistRepository repository, IApplicationRepository applicationRepository) + { + _repository = repository ?? throw new ArgumentNullException(nameof(repository)); + _applicationRepository = applicationRepository; + } + + public async Task Validate(string appKey, IPAddress remoteAddress) + { + var app = await _applicationRepository.GetByKeyAsync(appKey); + return await Validate(app.Id, remoteAddress); + } + + /// + /// Is domain white listed? + /// + /// Application that the error is reported for. + /// IP address of the client reporting the error. + /// + public async Task Validate(int applicationId, IPAddress remoteAddress) + { + var ipEntry = await _repository.FindIp(applicationId, remoteAddress); + if (ipEntry != null) return ipEntry.IpType != IpType.Denied; + + var domains = await _repository.GetWhitelist(applicationId); + + // Allow everything if no domains are listed. + if (!domains.Any()) + return true; + + foreach (var domain in domains) + { + // User stored an IP. + if (IPAddress.TryParse(domain.DomainName, out var ip)) + { + if (ip.Equals(remoteAddress)) + return true; + + continue; + } + + var found = await Lookup(domain, remoteAddress); + if (found) + return true; + } + + foreach (var domain in domains) + { + await _repository.SaveIp(new WhitelistedDomainIp + { + IpAddress = remoteAddress, + DomainId = domain.Id, + IpType = IpType.Denied, + StoredAtUtc = DateTime.UtcNow + }); + } + + return false; + } + + private async Task Lookup(WhitelistedDomain domain, IPAddress remoteAddress) + { + var found = false; + var lookup = new LookupClient(); + var result = await lookup.QueryAsync(domain.DomainName, QueryType.ANY); + foreach (var record in result.AllRecords) + { + switch (record) + { + case ARecord ipRecord: + if (domain.IpAddresses.Any(x => Equals(x.IpAddress, ipRecord.Address))) + continue; + + + if (remoteAddress.Equals(ipRecord.Address)) + found = true; + + await _repository.SaveIp(new WhitelistedDomainIp + { + DomainId = domain.Id, + IpAddress = ipRecord.Address, + IpType = IpType.Lookup, + StoredAtUtc = DateTime.UtcNow + }); + break; + + case AaaaRecord ip6Record: + if (domain.IpAddresses.Any(x => Equals(x.IpAddress, ip6Record.Address))) + continue; + + if (remoteAddress.Equals(ip6Record.Address)) + found = true; + + await _repository.SaveIp(new WhitelistedDomainIp + { + DomainId = domain.Id, + IpAddress = ip6Record.Address, + IpType = IpType.Lookup, + StoredAtUtc = DateTime.UtcNow + }); + break; + } + } + + return found; + } + } +} \ No newline at end of file diff --git a/src/Server/Coderr.Server.App/Modules/Whitelists/WhitelistedDomain.cs b/src/Server/Coderr.Server.App/Modules/Whitelists/WhitelistedDomain.cs new file mode 100644 index 00000000..2b1fbf88 --- /dev/null +++ b/src/Server/Coderr.Server.App/Modules/Whitelists/WhitelistedDomain.cs @@ -0,0 +1,33 @@ +namespace Coderr.Server.App.Modules.Whitelists +{ + /// + /// Domain that is allowed to report errors without + /// + public class WhitelistedDomain + { + /// + /// Application that the domain is allowed to report for. + /// + /// + /// + /// null = all applications. + /// + /// + public int? ApplicationId { get; set; } + + /// + /// Domain name, must be an exact match. Can also be an IP address + /// + public string DomainName { get; set; } + + /// + /// PK + /// + public int Id { get; set; } + + /// + /// Addresses that have been stored for this domain + /// + public WhitelistedDomainIp[] IpAddresses { get; set; } + } +} \ No newline at end of file diff --git a/src/Server/Coderr.Server.App/Modules/Whitelists/WhitelistedDomainIp.cs b/src/Server/Coderr.Server.App/Modules/Whitelists/WhitelistedDomainIp.cs new file mode 100644 index 00000000..f27f695a --- /dev/null +++ b/src/Server/Coderr.Server.App/Modules/Whitelists/WhitelistedDomainIp.cs @@ -0,0 +1,31 @@ +using System; +using System.Net; + +namespace Coderr.Server.App.Modules.Whitelists +{ + /// + /// IP address that we have looked up (DNS lookup) for the specified domain entry. + /// + public class WhitelistedDomainIp + { + /// + /// Domain that this entry is for. + /// + public int DomainId { get; set; } + + /// + /// Address that we found + /// + public IPAddress IpAddress { get; set; } + + /// + /// How this IP should be treated + /// + public IpType IpType { get; set; } + + /// + /// When this entry was stored. + /// + public DateTime StoredAtUtc { get; set; } + } +} \ No newline at end of file diff --git a/src/Server/Coderr.Server.ReportAnalyzer/Inbound/SaveReportHandler.cs b/src/Server/Coderr.Server.ReportAnalyzer/Inbound/SaveReportHandler.cs index 7c63b403..e0509ae9 100644 --- a/src/Server/Coderr.Server.ReportAnalyzer/Inbound/SaveReportHandler.cs +++ b/src/Server/Coderr.Server.ReportAnalyzer/Inbound/SaveReportHandler.cs @@ -22,6 +22,7 @@ namespace Coderr.Server.ReportAnalyzer.Inbound /// public class SaveReportHandler { + private readonly List> _filters = new List>(); private readonly ILog _logger = LogManager.GetLogger(typeof(SaveReportHandler)); private readonly IMessageQueue _queue; diff --git a/src/Server/Coderr.Server.ReportAnalyzer/Inbound/Whitelist/AddDomainHandler.cs b/src/Server/Coderr.Server.ReportAnalyzer/Inbound/Whitelist/AddDomainHandler.cs deleted file mode 100644 index 5aa504a0..00000000 --- a/src/Server/Coderr.Server.ReportAnalyzer/Inbound/Whitelist/AddDomainHandler.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; -using Coderr.Server.ReportAnalyzer.Abstractions.Inbound.Whitelists.Commands; -using DotNetCqs; -using Griffin.Data; -using Griffin.Data.Mapper; - -namespace Coderr.Server.ReportAnalyzer.Inbound.Whitelist -{ - class AddDomainHandler : IMessageHandler - { - IAdoNetUnitOfWork _uow; - - public AddDomainHandler(IAdoNetUnitOfWork uow) - { - _uow = uow; - } - - public async Task HandleAsync(IMessageContext context, AddDomain message) - { - await _uow.InsertAsync(new WhitelistedDomain(message.ApplicationId, message.DomainName)); - } - } -} diff --git a/src/Server/Coderr.Server.ReportAnalyzer/Inbound/Whitelist/WhitelistedDomain.cs b/src/Server/Coderr.Server.ReportAnalyzer/Inbound/Whitelist/WhitelistedDomain.cs deleted file mode 100644 index 174e3442..00000000 --- a/src/Server/Coderr.Server.ReportAnalyzer/Inbound/Whitelist/WhitelistedDomain.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Coderr.Server.ReportAnalyzer.Inbound.Whitelist -{ - public class WhitelistedDomain - { - public WhitelistedDomain(int? applicationId, string domainName) - { - ApplicationId = applicationId; - DomainName = domainName; - } - - public int Id { get; private set; } - - public int? ApplicationId { get; private set; } - public string DomainName { get; private set; } - } -} \ No newline at end of file diff --git a/src/Server/Coderr.Server.ReportAnalyzer/Inbound/Whitelist/WhitelistedDomainMapper.cs b/src/Server/Coderr.Server.ReportAnalyzer/Inbound/Whitelist/WhitelistedDomainMapper.cs deleted file mode 100644 index 9a2cd50c..00000000 --- a/src/Server/Coderr.Server.ReportAnalyzer/Inbound/Whitelist/WhitelistedDomainMapper.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Griffin.Data.Mapper; - -namespace Coderr.Server.ReportAnalyzer.Inbound.Whitelist -{ - class WhitelistedDomainMapper : CrudEntityMapper - { - public WhitelistedDomainMapper() : base("ReportingWhitelistedDomains") - { - Property(x => x.Id) - .PrimaryKey(true); - - } - } -} diff --git a/src/Server/Coderr.Server.SqlServer/Coderr.Server.SqlServer.csproj b/src/Server/Coderr.Server.SqlServer/Coderr.Server.SqlServer.csproj index f4b16ac6..adeabcec 100644 --- a/src/Server/Coderr.Server.SqlServer/Coderr.Server.SqlServer.csproj +++ b/src/Server/Coderr.Server.SqlServer/Coderr.Server.SqlServer.csproj @@ -5,6 +5,7 @@ Coderr.Server.SqlServer + @@ -26,4 +27,8 @@ + + + + diff --git a/src/Server/Coderr.Server.SqlServer/Modules/Whitelists/AddDomainHandler.cs b/src/Server/Coderr.Server.SqlServer/Modules/Whitelists/AddDomainHandler.cs new file mode 100644 index 00000000..894975cc --- /dev/null +++ b/src/Server/Coderr.Server.SqlServer/Modules/Whitelists/AddDomainHandler.cs @@ -0,0 +1,60 @@ +using System; +using System.Threading.Tasks; +using Coderr.Server.Api.Modules.Whitelists.Commands; +using Coderr.Server.App.Modules.Whitelists; +using DnsClient; +using DnsClient.Protocol; +using DotNetCqs; +using Griffin.Data; +using Griffin.Data.Mapper; +using log4net; + +namespace Coderr.Server.SqlServer.Modules.Whitelist +{ + internal class AddDomainHandler : IMessageHandler + { + private readonly ILog _logger = LogManager.GetLogger(typeof(AddDomainHandler)); + private readonly IAdoNetUnitOfWork _uow; + + public AddDomainHandler(IAdoNetUnitOfWork uow) + { + _uow = uow; + } + + public async Task HandleAsync(IMessageContext context, AddDomain message) + { + var entry = new WhitelistedDomain + { + ApplicationId = message.ApplicationId, + DomainName = message.DomainName + }; + await _uow.InsertAsync(entry); + var lookup = new LookupClient(); + var result = await lookup.QueryAsync(message.DomainName, QueryType.ANY); + foreach (var record in result.AllRecords) + { + switch (record) + { + case ARecord ipRecord: + await _uow.InsertAsync(new WhitelistedDomainIp + { + DomainId = entry.Id, + IpAddress = ipRecord.Address, + IpType = IpType.Lookup, + StoredAtUtc = DateTime.UtcNow + }); + break; + case AaaaRecord ip6Record: + await _uow.InsertAsync(new WhitelistedDomainIp + { + DomainId = entry.Id, + IpAddress = ip6Record.Address, + IpType = IpType.Lookup, + StoredAtUtc = DateTime.UtcNow + }); + break; + } + } + } + } +} \ No newline at end of file diff --git a/src/Server/Coderr.Server.ReportAnalyzer/Inbound/Whitelist/GetWhitelistEntriesHandler.cs b/src/Server/Coderr.Server.SqlServer/Modules/Whitelists/GetWhitelistEntriesHandler.cs similarity index 84% rename from src/Server/Coderr.Server.ReportAnalyzer/Inbound/Whitelist/GetWhitelistEntriesHandler.cs rename to src/Server/Coderr.Server.SqlServer/Modules/Whitelists/GetWhitelistEntriesHandler.cs index ffaa0376..1108d791 100644 --- a/src/Server/Coderr.Server.ReportAnalyzer/Inbound/Whitelist/GetWhitelistEntriesHandler.cs +++ b/src/Server/Coderr.Server.SqlServer/Modules/Whitelists/GetWhitelistEntriesHandler.cs @@ -1,15 +1,17 @@ using System.Collections.Generic; using System.Threading.Tasks; -using Coderr.Server.ReportAnalyzer.Abstractions.Inbound.Whitelists.Queries; +using Coderr.Server.Api.Modules.Whitelists.Queries; using DotNetCqs; using Griffin.Data; using Griffin.Data.Mapper; -namespace Coderr.Server.ReportAnalyzer.Inbound.Whitelist +namespace Coderr.Server.SqlServer.Modules.Whitelist { public class GetWhitelistEntriesHandler : IQueryHandler { private readonly IAdoNetUnitOfWork _unitOfWork; + private readonly IEntityMapper _mapper = + new MirrorMapper(); public GetWhitelistEntriesHandler(IAdoNetUnitOfWork unitOfWork) { @@ -20,7 +22,7 @@ public async Task HandleAsync(IMessageContext context { List entries; if (message.ApplicationId != null && !string.IsNullOrWhiteSpace(message.DomainName)) - entries = await _unitOfWork.ToListAsync( + entries = await _unitOfWork.ToListAsync(_mapper, "ApplicationId = @applicationId AND DomainName = @domainNAme", new {message.ApplicationId, message.DomainName}); else if (message.ApplicationId != null) diff --git a/src/Server/Coderr.Server.ReportAnalyzer/Inbound/Whitelist/RemoveDomainHandler.cs b/src/Server/Coderr.Server.SqlServer/Modules/Whitelists/RemoveDomainHandler.cs similarity index 79% rename from src/Server/Coderr.Server.ReportAnalyzer/Inbound/Whitelist/RemoveDomainHandler.cs rename to src/Server/Coderr.Server.SqlServer/Modules/Whitelists/RemoveDomainHandler.cs index f5ed054c..82607cae 100644 --- a/src/Server/Coderr.Server.ReportAnalyzer/Inbound/Whitelist/RemoveDomainHandler.cs +++ b/src/Server/Coderr.Server.SqlServer/Modules/Whitelists/RemoveDomainHandler.cs @@ -1,10 +1,11 @@ using System.Threading.Tasks; -using Coderr.Server.ReportAnalyzer.Abstractions.Inbound.Whitelists.Commands; +using Coderr.Server.Api.Modules.Whitelists.Commands; +using Coderr.Server.App.Modules.Whitelists; using DotNetCqs; using Griffin.Data; using Griffin.Data.Mapper; -namespace Coderr.Server.ReportAnalyzer.Inbound.Whitelist +namespace Coderr.Server.SqlServer.Modules.Whitelist { internal class RemoveDomainHandler : IMessageHandler { diff --git a/src/Server/Coderr.Server.SqlServer/Modules/Whitelists/WhitelistRepository.cs b/src/Server/Coderr.Server.SqlServer/Modules/Whitelists/WhitelistRepository.cs new file mode 100644 index 00000000..82937768 --- /dev/null +++ b/src/Server/Coderr.Server.SqlServer/Modules/Whitelists/WhitelistRepository.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using Coderr.Server.Abstractions.Boot; +using Coderr.Server.App.Modules.Whitelists; +using Griffin.Data; +using Griffin.Data.Mapper; + +namespace Coderr.Server.SqlServer.Modules.Whitelists +{ + [ContainerService] + public class WhitelistRepository : IWhitelistRepository + { + private readonly IAdoNetUnitOfWork _unitOfWork; + + public WhitelistRepository(IAdoNetUnitOfWork unitOfWork) + { + _unitOfWork = unitOfWork; + } + + public async Task FindIp(int applicationId, IPAddress address) + { + return await _unitOfWork.FirstOrDefaultAsync( + "ApplicationId = @applicationId AND IpAddress=@address", + new {applicationId, address = address.ToString()}); + } + + public async Task> GetWhitelist(int applicationId) + { + var domains = await _unitOfWork.ToListAsync("ApplicationId = @applicationId", + new {applicationId}); + + var ids = string.Join(", ", domains.Select(x => x.Id)); + var ips = await _unitOfWork.ToListAsync($"DomainId IN ({ids})"); + var ipsPerDomain = ips.GroupBy(x => x.DomainId); + foreach (var domainIps in ipsPerDomain) + { + var domain = domains.First(x => x.Id == domainIps.Key); + domain.IpAddresses = domainIps.ToArray(); + } + + return domains; + } + + public async Task SaveIp(WhitelistedDomainIp entry) + { + await _unitOfWork.InsertAsync(entry); + } + } +} \ No newline at end of file diff --git a/src/Server/Coderr.Server.SqlServer/Modules/Whitelists/WhitelistedDomainIpMapper.cs b/src/Server/Coderr.Server.SqlServer/Modules/Whitelists/WhitelistedDomainIpMapper.cs new file mode 100644 index 00000000..a44fc59c --- /dev/null +++ b/src/Server/Coderr.Server.SqlServer/Modules/Whitelists/WhitelistedDomainIpMapper.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; +using Coderr.Server.App.Modules.Whitelists; +using Griffin.Data.Mapper; + +namespace Coderr.Server.SqlServer.Modules.Whitelists +{ + class WhitelistedDomainIpMapper : CrudEntityMapper + { + public WhitelistedDomainIpMapper() : base("WhitelistedDomainIps") + { + Property(x => x.IpAddress) + .ToColumnValue(x => x.ToString()) + .ToPropertyValue(x => IPAddress.Parse((string)x)); + Property(x => x.IpType) + .ToColumnValue(x => (int) x) + .ToPropertyValue(x => (IpType) x); + } + } +} diff --git a/src/Server/Coderr.Server.SqlServer/Modules/Whitelists/WhitelistedDomainMapper.cs b/src/Server/Coderr.Server.SqlServer/Modules/Whitelists/WhitelistedDomainMapper.cs new file mode 100644 index 00000000..29327295 --- /dev/null +++ b/src/Server/Coderr.Server.SqlServer/Modules/Whitelists/WhitelistedDomainMapper.cs @@ -0,0 +1,16 @@ +using Coderr.Server.App.Modules.Whitelists; +using Griffin.Data.Mapper; + +namespace Coderr.Server.SqlServer.Modules.Whitelist +{ + class WhitelistedDomainMapper : CrudEntityMapper + { + public WhitelistedDomainMapper() : base("WhitelistedDomains") + { + Property(x => x.Id) + .PrimaryKey(true); + Property(x => x.IpAddresses) + .Ignore(); + } + } +} diff --git a/src/Server/Coderr.Server.SqlServer/Schema/Coderr.v21.sql b/src/Server/Coderr.Server.SqlServer/Schema/Coderr.v21.sql new file mode 100644 index 00000000..9d616c24 --- /dev/null +++ b/src/Server/Coderr.Server.SqlServer/Schema/Coderr.v21.sql @@ -0,0 +1,14 @@ +create table WhitelistedDomains +( + Id int not null identity primary key, + ApplicationId int not null constraint FK_WhitelistedDomains_Applications foreign key references Applications(Id) ON DELETE CASCADE, + DomainName varchar(255) not null +); + +create table WhitelistedDomainIps +( + Id int not null identity primary key, + DomainId int not null constraint FK_WhitelistedDomainIps_WhitelistedDomains foreign key references WhitelistedDomains(Id) ON DELETE CASCADE, + IpAddress varchar(36) not null, + IpType int not null +); diff --git a/src/Server/Coderr.Server.Web/ClientApp/components/home/navmenu/navmenu.ts b/src/Server/Coderr.Server.Web/ClientApp/components/home/navmenu/navmenu.ts index 5aa8d556..6c015bf2 100644 --- a/src/Server/Coderr.Server.Web/ClientApp/components/home/navmenu/navmenu.ts +++ b/src/Server/Coderr.Server.Web/ClientApp/components/home/navmenu/navmenu.ts @@ -1,7 +1,7 @@ import { PubSubService, MessageContext } from "../../../services/PubSub"; import * as MenuApi from "../../../services/menu/MenuApi"; import { AppRoot } from "../../../services/AppRoot"; -import { ApplicationSummary } from "../../../services/applications/ApplicationService"; +import { AppEvents } from "../../../services/applications/ApplicationService"; import Vue from 'vue'; import { Component, Watch } from 'vue-property-decorator'; import * as Router from "vue-router"; @@ -91,6 +91,12 @@ export default class NavMenuComponent extends Vue { var msg = ctx.message.body; this.changeApplication(msg.applicationId); }); + PubSubService.Instance.subscribe(AppEvents.Removed, ctx => { + this.myApplications = this.myApplications.filter(x => x.tag !== ctx.message.body); + if (this.currentApplicationId === ctx.message.body) { + this.changeApplication(null); + } + }); } mounted() { @@ -104,7 +110,7 @@ export default class NavMenuComponent extends Vue { } } - changeApplication(applicationId: number) { + changeApplication(applicationId: number|null) { if (applicationId == null) { this.updateCurrent(0); } else { diff --git a/src/Server/Coderr.Server.Web/ClientApp/components/manage/application/settings/settings.ts b/src/Server/Coderr.Server.Web/ClientApp/components/manage/application/settings/settings.ts index fc874b92..b8e29562 100644 --- a/src/Server/Coderr.Server.Web/ClientApp/components/manage/application/settings/settings.ts +++ b/src/Server/Coderr.Server.Web/ClientApp/components/manage/application/settings/settings.ts @@ -35,6 +35,14 @@ export default class ManageAppSettingsComponent extends Vue { }); } + deleteApp() { + var result = confirm("Do you really want to delete '" + this.applicationName + "'."); + if (result) { + AppRoot.Instance.applicationService.delete(this.applicationId); + AppRoot.notify("Application have been queued for deletion. Might take time depending on the number of incidents.", "fa-info", "success"); + this.$router.push('manageHome'); + } + } updateApp() { AppRoot.Instance.applicationService.update(this.applicationId, this.applicationName); AppRoot.notify('Application settings have been saved.'); diff --git a/src/Server/Coderr.Server.Web/ClientApp/components/manage/application/settings/settings.vue.html b/src/Server/Coderr.Server.Web/ClientApp/components/manage/application/settings/settings.vue.html index fdbf9ba1..a526b438 100644 --- a/src/Server/Coderr.Server.Web/ClientApp/components/manage/application/settings/settings.vue.html +++ b/src/Server/Coderr.Server.Web/ClientApp/components/manage/application/settings/settings.vue.html @@ -3,7 +3,9 @@
-
Application settings
+
+ Application settings +
@@ -13,10 +15,11 @@
+
- Create new application +
diff --git a/src/Server/Coderr.Server.Web/ClientApp/components/manage/system/menu.vue.html b/src/Server/Coderr.Server.Web/ClientApp/components/manage/system/menu.vue.html index eb04d7d3..d736beb6 100644 --- a/src/Server/Coderr.Server.Web/ClientApp/components/manage/system/menu.vue.html +++ b/src/Server/Coderr.Server.Web/ClientApp/components/manage/system/menu.vue.html @@ -8,12 +8,12 @@ Api keys - + + Billing + -->