diff --git a/src/SQLInsight.Agent/Setup/RepositorySetup.cs b/src/SQLInsight.Agent/Setup/RepositorySetup.cs index 78e2c1d..89f0912 100644 --- a/src/SQLInsight.Agent/Setup/RepositorySetup.cs +++ b/src/SQLInsight.Agent/Setup/RepositorySetup.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.Text.RegularExpressions; using Microsoft.Data.SqlClient; using SQLInsight.Connectivity; +using SQLInsight.Core.Configuration; using SQLInsight.Repository; namespace SQLInsight.Agent.Setup; @@ -146,7 +147,7 @@ public static partial class RepositorySetup return; } - var login = LoginFor(agentAccount, instance.Server, Environment.MachineName, Environment.UserDomainName); + var login = ServiceAccounts.LoginFor(agentAccount, instance.Server, Environment.MachineName, Environment.UserDomainName); var target = new SqlConnectionStringBuilder { DataSource = instance.Server, @@ -174,43 +175,13 @@ public static partial class RepositorySetup /// public static IReadOnlyList<(string Login, string Role)> RepositoryLogins(string agentAccount, string webAccount, string sqlServer) { - var agent = LoginFor(agentAccount, sqlServer, Environment.MachineName, Environment.UserDomainName); - var web = LoginFor(webAccount, sqlServer, Environment.MachineName, Environment.UserDomainName); + var agent = ServiceAccounts.LoginFor(agentAccount, sqlServer, Environment.MachineName, Environment.UserDomainName); + var web = ServiceAccounts.LoginFor(webAccount, sqlServer, Environment.MachineName, Environment.UserDomainName); return string.Equals(agent, web, StringComparison.OrdinalIgnoreCase) ? [(agent, "db_owner")] : [(agent, "db_owner"), (web, "db_datareader")]; } - /// - /// The Windows login a service account has on . Virtual and built-in machine accounts - /// (NT SERVICE\…, LocalSystem, NETWORK SERVICE) only exist locally; on another server they arrive as the computer - /// account DOMAIN\COMPUTER$. Domain accounts and gMSAs are the same everywhere. - /// - public static string LoginFor(string account, string sqlServer, string machineName, string domain) - { - ArgumentException.ThrowIfNullOrWhiteSpace(account); - var normalized = account.Trim(); - if (normalized.Equals("LocalSystem", StringComparison.OrdinalIgnoreCase)) - { - normalized = @"NT AUTHORITY\SYSTEM"; - } - - var machineIdentity = normalized.StartsWith(@"NT SERVICE\", StringComparison.OrdinalIgnoreCase) - || normalized.StartsWith(@"NT AUTHORITY\", StringComparison.OrdinalIgnoreCase); - return machineIdentity && !IsLocal(sqlServer, machineName) ? $@"{domain}\{machineName}$" : normalized; - } - - /// Whether a SQL Server data source (server, server\instance, tcp:server,port …) is this computer. - public static bool IsLocal(string sqlServer, string machineName) - { - ArgumentNullException.ThrowIfNull(sqlServer); - var host = ProtocolPrefix().Replace(sqlServer.Trim(), string.Empty).Split('\\', ',')[0].Trim(); - return host is "" or "." or "(local)" or "(localdb)" or "127.0.0.1" or "::1" - || host.Equals("localhost", StringComparison.OrdinalIgnoreCase) - || host.Equals(machineName, StringComparison.OrdinalIgnoreCase) - || host.StartsWith(machineName + ".", StringComparison.OrdinalIgnoreCase); - } - private static async Task EnsureLoginAsync(SqlConnection connection, string login) => await ScalarAsync(connection, """ IF SUSER_ID(@login) IS NULL @@ -282,9 +253,6 @@ public static partial class RepositorySetup return i >= 0 && i + 1 < args.Length ? args[i + 1] : null; } - [GeneratedRegex(@"^(tcp|np|lpc|admin):", RegexOptions.IgnoreCase)] - private static partial Regex ProtocolPrefix(); - [GeneratedRegex(@"SET @LoginName = N'[^']*';[^\r\n]*")] private static partial Regex LoginPlaceholder(); diff --git a/tests/SQLInsight.Agent.Tests/RepositorySetupTests.cs b/tests/SQLInsight.Agent.Tests/RepositorySetupTests.cs index 6a4e346..26cb292 100644 --- a/tests/SQLInsight.Agent.Tests/RepositorySetupTests.cs +++ b/tests/SQLInsight.Agent.Tests/RepositorySetupTests.cs @@ -1,4 +1,5 @@ using SQLInsight.Agent.Setup; +using SQLInsight.Core.Configuration; namespace SQLInsight.Agent.Tests; @@ -13,29 +14,29 @@ public sealed class RepositorySetupTests [InlineData("SQLMON01")] [InlineData(@"sqlmon01\SQLEXPRESS")] [InlineData("sqlmon01.kunde.local,1433")] - public void Recognises_this_computer(string server) => Assert.True(RepositorySetup.IsLocal(server, "SQLMON01")); + public void Recognises_this_computer(string server) => Assert.True(ServiceAccounts.IsLocal(server, "SQLMON01")); [Theory] [InlineData("sql02")] [InlineData(@"sql02\PROD")] [InlineData("sqlmon011")] [InlineData("tcp:10.0.0.5,1433")] - public void Recognises_other_computers(string server) => Assert.False(RepositorySetup.IsLocal(server, "SQLMON01")); + public void Recognises_other_computers(string server) => Assert.False(ServiceAccounts.IsLocal(server, "SQLMON01")); [Fact] public void Virtual_account_keeps_its_name_on_the_same_computer() => - Assert.Equal(@"NT SERVICE\SQLInsight Agent", RepositorySetup.LoginFor(@"NT SERVICE\SQLInsight Agent", @".\SQLEXPRESS", "SQLMON01", "KUNDE")); + Assert.Equal(@"NT SERVICE\SQLInsight Agent", ServiceAccounts.LoginFor(@"NT SERVICE\SQLInsight Agent", @".\SQLEXPRESS", "SQLMON01", "KUNDE")); [Theory] [InlineData(@"NT SERVICE\SQLInsight Agent")] [InlineData(@"NT AUTHORITY\NETWORK SERVICE")] [InlineData("LocalSystem")] public void Machine_accounts_arrive_as_the_computer_account_on_another_server(string account) => - Assert.Equal(@"KUNDE\SQLMON01$", RepositorySetup.LoginFor(account, "sql02", "SQLMON01", "KUNDE")); + Assert.Equal(@"KUNDE\SQLMON01$", ServiceAccounts.LoginFor(account, "sql02", "SQLMON01", "KUNDE")); [Fact] public void Domain_accounts_and_gmsas_are_the_same_everywhere() => - Assert.Equal(@"KUNDE\svc-sqlinsight$", RepositorySetup.LoginFor(@"KUNDE\svc-sqlinsight$", "sql02", "SQLMON01", "KUNDE")); + Assert.Equal(@"KUNDE\svc-sqlinsight$", ServiceAccounts.LoginFor(@"KUNDE\svc-sqlinsight$", "sql02", "SQLMON01", "KUNDE")); [Fact] public void Grant_script_takes_the_login_as_a_parameter()