diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d02e3e6..a8ca9db 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,16 +26,16 @@ jobs: 10.0.x - name: Restore - run: dotnet restore DapperIdentity.sln + run: dotnet restore DapperIdentity.slnx - name: Build - run: dotnet build DapperIdentity.sln -c Release --no-restore + run: dotnet build DapperIdentity.slnx -c Release --no-restore - name: Test - run: dotnet test DapperIdentity.sln -c Release --no-build --verbosity normal + run: dotnet test DapperIdentity.slnx -c Release --no-build --verbosity normal - name: Pack - run: dotnet pack DapperIdentity.sln -c Release --no-build -o artifacts + run: dotnet pack DapperIdentity.slnx -c Release --no-build -o artifacts # The five packages only work as a set: packing turns each ProjectReference into a dependency # on the referenced project's PackageId at its Version. A project that lost its PackageId or diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 4950e36..5b5a2f4 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -40,7 +40,7 @@ jobs: # Autobuild guesses, and guesses badly on a multi-targeted library that needs two SDKs. # Build it explicitly instead. - name: Build - run: dotnet build DapperIdentity.sln -c Release + run: dotnet build DapperIdentity.slnx -c Release - name: Analyze uses: github/codeql-action/analyze@v4 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index fbe366c..e15c99a 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -70,16 +70,16 @@ jobs: done - name: Restore - run: dotnet restore DapperIdentity.sln + run: dotnet restore DapperIdentity.slnx - name: Build - run: dotnet build DapperIdentity.sln -c Release --no-restore + run: dotnet build DapperIdentity.slnx -c Release --no-restore - name: Test - run: dotnet test DapperIdentity.sln -c Release --no-build + run: dotnet test DapperIdentity.slnx -c Release --no-build - name: Pack - run: dotnet pack DapperIdentity.sln -c Release --no-build -o artifacts + run: dotnet pack DapperIdentity.slnx -c Release --no-build -o artifacts - name: Verify package set, contents and inter-package dependencies shell: bash diff --git a/DapperIdentity.Stores/SignIn/SignInReporting.cs b/DapperIdentity.Stores/SignIn/SignInReporting.cs index 3d08509..cdffc9d 100644 --- a/DapperIdentity.Stores/SignIn/SignInReporting.cs +++ b/DapperIdentity.Stores/SignIn/SignInReporting.cs @@ -152,8 +152,9 @@ public static string[] Values(SignInAttempt attempt, string ip, string appName, { ArgumentNullException.ThrowIfNull(attempt); var user = UserNameFor(attempt, userNames); + // A success carries no name, so its sentence names none - "for ''" read like a bug. var sentence = attempt.Succeeded - ? $"Sign-in succeeded for '{user}' from {ip} on {appName} ({attempt.Path})." + ? $"Sign-in succeeded from {ip} on {appName} ({attempt.Path})." : $"Sign-in failed for '{user}' from {ip} on {appName} ({attempt.Path}): {attempt.Reason}."; return [sentence, Version, ip, user, attempt.Succeeded ? "" : attempt.Reason.ToString(), appName, attempt.Path]; } @@ -250,12 +251,22 @@ public void Report(SignInAttempt attempt) // Information, not Warning: on Windows ASP.NET Core's default event-log logger takes Warning and // up, so a Warning here would add a second Application-log entry per attempt during an attack. - _logger.LogInformation( - new EventId(attempt.Succeeded ? SignInEventFormat.SucceededId : SignInEventFormat.FailedId, - attempt.Succeeded ? "SignInSucceeded" : "SignInFailed"), - "Sign-in {Outcome} for {UserName} from {ClientIp} on {App} via {Path} ({Reason})", - attempt.Succeeded ? "succeeded" : "failed", SignInEventFormat.UserNameFor(attempt, _userNames), attempt.ClientIp, _appName, attempt.Path, - attempt.Succeeded ? "-" : attempt.Reason.ToString()); + // Two templates, not one with blanks: a success has no name or reason to show, and an empty "for from" or a + // "(-)" in the log reads like something failed to fill in. The shared properties keep the same names. + if (attempt.Succeeded) + { + _logger.LogInformation( + new EventId(SignInEventFormat.SucceededId, "SignInSucceeded"), + "Sign-in succeeded from {ClientIp} on {App} via {Path}", + attempt.ClientIp, _appName, attempt.Path); + } + else + { + _logger.LogInformation( + new EventId(SignInEventFormat.FailedId, "SignInFailed"), + "Sign-in failed for {UserName} from {ClientIp} on {App} via {Path} ({Reason})", + SignInEventFormat.UserNameFor(attempt, _userNames), attempt.ClientIp, _appName, attempt.Path, attempt.Reason.ToString()); + } // No address, nothing to ban: PortGuardian has no use for the event. if (_eventLogOff || attempt.ClientIp is null) diff --git a/DapperIdentity.Tests/SignInReportingTests.cs b/DapperIdentity.Tests/SignInReportingTests.cs index a54b50e..a244366 100644 --- a/DapperIdentity.Tests/SignInReportingTests.cs +++ b/DapperIdentity.Tests/SignInReportingTests.cs @@ -211,6 +211,21 @@ public void Every_attempt_is_logged_at_information_under_its_own_category() Assert.DoesNotContain("bob ", entry.Message, StringComparison.Ordinal); } + [Fact] + public void A_success_is_logged_without_an_empty_name_or_reason() + { + var logs = new CapturingLogs(); + var written = new List(); + var reporter = Reporter(logs, (_, _, values) => written.Add(values)); + + reporter.Report(SignInAttempt.Success(From("203.0.113.9"), "bob", "cookie")); + + var entry = Assert.Single(logs.Entries); + Assert.Equal((LogLevel.Information, 1001), (entry.Level, entry.EventId.Id)); + Assert.Equal("Sign-in succeeded from 203.0.113.9 on TestApp via cookie", entry.Message); + Assert.Equal("Sign-in succeeded from 203.0.113.9 on TestApp (cookie).", Assert.Single(written)[0]); + } + [Fact] public void An_attempt_without_an_address_is_logged_but_not_written_as_an_event() { diff --git a/DapperIdentity.sln b/DapperIdentity.sln deleted file mode 100644 index f1883c3..0000000 --- a/DapperIdentity.sln +++ /dev/null @@ -1,107 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.10.12201.205 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DapperIdentity.Abstractions", "DapperIdentity.Abstractions\DapperIdentity.Abstractions.csproj", "{E1C673CD-7494-4BA9-AAB3-F0B40969AF50}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DapperIdentity.Stores", "DapperIdentity.Stores\DapperIdentity.Stores.csproj", "{C7980EEE-5F0F-4396-AC39-D4DF47FADC0B}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DapperIdentity.Jwt.Client", "DapperIdentity.Jwt.Client\DapperIdentity.Jwt.Client.csproj", "{D6A15952-72EC-4A65-856F-29ADFCDD901B}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DapperIdentity.Jwt.Server", "DapperIdentity.Jwt.Server\DapperIdentity.Jwt.Server.csproj", "{865AFC9D-288D-45E2-BAC7-A7357313D929}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DapperIdentity.Cookies.Server", "DapperIdentity.Cookies.Server\DapperIdentity.Cookies.Server.csproj", "{3B26001E-FB9E-49E1-88F2-70C859D29F57}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DapperIdentity.Tests", "DapperIdentity.Tests\DapperIdentity.Tests.csproj", "{C8476948-3315-49AF-854A-020EA87E8173}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E1C673CD-7494-4BA9-AAB3-F0B40969AF50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E1C673CD-7494-4BA9-AAB3-F0B40969AF50}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E1C673CD-7494-4BA9-AAB3-F0B40969AF50}.Debug|x64.ActiveCfg = Debug|Any CPU - {E1C673CD-7494-4BA9-AAB3-F0B40969AF50}.Debug|x64.Build.0 = Debug|Any CPU - {E1C673CD-7494-4BA9-AAB3-F0B40969AF50}.Debug|x86.ActiveCfg = Debug|Any CPU - {E1C673CD-7494-4BA9-AAB3-F0B40969AF50}.Debug|x86.Build.0 = Debug|Any CPU - {E1C673CD-7494-4BA9-AAB3-F0B40969AF50}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E1C673CD-7494-4BA9-AAB3-F0B40969AF50}.Release|Any CPU.Build.0 = Release|Any CPU - {E1C673CD-7494-4BA9-AAB3-F0B40969AF50}.Release|x64.ActiveCfg = Release|Any CPU - {E1C673CD-7494-4BA9-AAB3-F0B40969AF50}.Release|x64.Build.0 = Release|Any CPU - {E1C673CD-7494-4BA9-AAB3-F0B40969AF50}.Release|x86.ActiveCfg = Release|Any CPU - {E1C673CD-7494-4BA9-AAB3-F0B40969AF50}.Release|x86.Build.0 = Release|Any CPU - {C7980EEE-5F0F-4396-AC39-D4DF47FADC0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C7980EEE-5F0F-4396-AC39-D4DF47FADC0B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C7980EEE-5F0F-4396-AC39-D4DF47FADC0B}.Debug|x64.ActiveCfg = Debug|Any CPU - {C7980EEE-5F0F-4396-AC39-D4DF47FADC0B}.Debug|x64.Build.0 = Debug|Any CPU - {C7980EEE-5F0F-4396-AC39-D4DF47FADC0B}.Debug|x86.ActiveCfg = Debug|Any CPU - {C7980EEE-5F0F-4396-AC39-D4DF47FADC0B}.Debug|x86.Build.0 = Debug|Any CPU - {C7980EEE-5F0F-4396-AC39-D4DF47FADC0B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C7980EEE-5F0F-4396-AC39-D4DF47FADC0B}.Release|Any CPU.Build.0 = Release|Any CPU - {C7980EEE-5F0F-4396-AC39-D4DF47FADC0B}.Release|x64.ActiveCfg = Release|Any CPU - {C7980EEE-5F0F-4396-AC39-D4DF47FADC0B}.Release|x64.Build.0 = Release|Any CPU - {C7980EEE-5F0F-4396-AC39-D4DF47FADC0B}.Release|x86.ActiveCfg = Release|Any CPU - {C7980EEE-5F0F-4396-AC39-D4DF47FADC0B}.Release|x86.Build.0 = Release|Any CPU - {D6A15952-72EC-4A65-856F-29ADFCDD901B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D6A15952-72EC-4A65-856F-29ADFCDD901B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D6A15952-72EC-4A65-856F-29ADFCDD901B}.Debug|x64.ActiveCfg = Debug|Any CPU - {D6A15952-72EC-4A65-856F-29ADFCDD901B}.Debug|x64.Build.0 = Debug|Any CPU - {D6A15952-72EC-4A65-856F-29ADFCDD901B}.Debug|x86.ActiveCfg = Debug|Any CPU - {D6A15952-72EC-4A65-856F-29ADFCDD901B}.Debug|x86.Build.0 = Debug|Any CPU - {D6A15952-72EC-4A65-856F-29ADFCDD901B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D6A15952-72EC-4A65-856F-29ADFCDD901B}.Release|Any CPU.Build.0 = Release|Any CPU - {D6A15952-72EC-4A65-856F-29ADFCDD901B}.Release|x64.ActiveCfg = Release|Any CPU - {D6A15952-72EC-4A65-856F-29ADFCDD901B}.Release|x64.Build.0 = Release|Any CPU - {D6A15952-72EC-4A65-856F-29ADFCDD901B}.Release|x86.ActiveCfg = Release|Any CPU - {D6A15952-72EC-4A65-856F-29ADFCDD901B}.Release|x86.Build.0 = Release|Any CPU - {865AFC9D-288D-45E2-BAC7-A7357313D929}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {865AFC9D-288D-45E2-BAC7-A7357313D929}.Debug|Any CPU.Build.0 = Debug|Any CPU - {865AFC9D-288D-45E2-BAC7-A7357313D929}.Debug|x64.ActiveCfg = Debug|Any CPU - {865AFC9D-288D-45E2-BAC7-A7357313D929}.Debug|x64.Build.0 = Debug|Any CPU - {865AFC9D-288D-45E2-BAC7-A7357313D929}.Debug|x86.ActiveCfg = Debug|Any CPU - {865AFC9D-288D-45E2-BAC7-A7357313D929}.Debug|x86.Build.0 = Debug|Any CPU - {865AFC9D-288D-45E2-BAC7-A7357313D929}.Release|Any CPU.ActiveCfg = Release|Any CPU - {865AFC9D-288D-45E2-BAC7-A7357313D929}.Release|Any CPU.Build.0 = Release|Any CPU - {865AFC9D-288D-45E2-BAC7-A7357313D929}.Release|x64.ActiveCfg = Release|Any CPU - {865AFC9D-288D-45E2-BAC7-A7357313D929}.Release|x64.Build.0 = Release|Any CPU - {865AFC9D-288D-45E2-BAC7-A7357313D929}.Release|x86.ActiveCfg = Release|Any CPU - {865AFC9D-288D-45E2-BAC7-A7357313D929}.Release|x86.Build.0 = Release|Any CPU - {3B26001E-FB9E-49E1-88F2-70C859D29F57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3B26001E-FB9E-49E1-88F2-70C859D29F57}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3B26001E-FB9E-49E1-88F2-70C859D29F57}.Debug|x64.ActiveCfg = Debug|Any CPU - {3B26001E-FB9E-49E1-88F2-70C859D29F57}.Debug|x64.Build.0 = Debug|Any CPU - {3B26001E-FB9E-49E1-88F2-70C859D29F57}.Debug|x86.ActiveCfg = Debug|Any CPU - {3B26001E-FB9E-49E1-88F2-70C859D29F57}.Debug|x86.Build.0 = Debug|Any CPU - {3B26001E-FB9E-49E1-88F2-70C859D29F57}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3B26001E-FB9E-49E1-88F2-70C859D29F57}.Release|Any CPU.Build.0 = Release|Any CPU - {3B26001E-FB9E-49E1-88F2-70C859D29F57}.Release|x64.ActiveCfg = Release|Any CPU - {3B26001E-FB9E-49E1-88F2-70C859D29F57}.Release|x64.Build.0 = Release|Any CPU - {3B26001E-FB9E-49E1-88F2-70C859D29F57}.Release|x86.ActiveCfg = Release|Any CPU - {3B26001E-FB9E-49E1-88F2-70C859D29F57}.Release|x86.Build.0 = Release|Any CPU - {C8476948-3315-49AF-854A-020EA87E8173}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C8476948-3315-49AF-854A-020EA87E8173}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C8476948-3315-49AF-854A-020EA87E8173}.Debug|x64.ActiveCfg = Debug|Any CPU - {C8476948-3315-49AF-854A-020EA87E8173}.Debug|x64.Build.0 = Debug|Any CPU - {C8476948-3315-49AF-854A-020EA87E8173}.Debug|x86.ActiveCfg = Debug|Any CPU - {C8476948-3315-49AF-854A-020EA87E8173}.Debug|x86.Build.0 = Debug|Any CPU - {C8476948-3315-49AF-854A-020EA87E8173}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C8476948-3315-49AF-854A-020EA87E8173}.Release|Any CPU.Build.0 = Release|Any CPU - {C8476948-3315-49AF-854A-020EA87E8173}.Release|x64.ActiveCfg = Release|Any CPU - {C8476948-3315-49AF-854A-020EA87E8173}.Release|x64.Build.0 = Release|Any CPU - {C8476948-3315-49AF-854A-020EA87E8173}.Release|x86.ActiveCfg = Release|Any CPU - {C8476948-3315-49AF-854A-020EA87E8173}.Release|x86.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {14E3224F-7CF3-4B33-947D-DFD49A49C3A2} - EndGlobalSection -EndGlobal diff --git a/DapperIdentity.slnx b/DapperIdentity.slnx new file mode 100644 index 0000000..1e3efa1 --- /dev/null +++ b/DapperIdentity.slnx @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/Directory.Build.props b/Directory.Build.props index b1e0c69..32c5d39 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -9,7 +9,7 @@ ProjectReference into a package dependency on the referenced project's Version, so the five ids only ever resolve correctly against each other when they move together. publish.yml reads this line and refuses to run if the requested version differs. --> - 0.10.0 + 0.10.1 CPE CPE