diff --git a/src/Authentication/Controllers/LogoutController.cs b/src/Authentication/Controllers/LogoutController.cs index a95ecb3c..b8182d38 100644 --- a/src/Authentication/Controllers/LogoutController.cs +++ b/src/Authentication/Controllers/LogoutController.cs @@ -96,6 +96,8 @@ public ActionResult FrontchannelLogout() CookieOptions opt = new CookieOptions() { Domain = _generalSettings.HostName, Secure = true, HttpOnly = true }; Response.Cookies.Delete(_generalSettings.SblAuthCookieName, opt); Response.Cookies.Delete(_generalSettings.JwtCookieName, opt); + string tokenCookie = Request.Cookies[_generalSettings.JwtCookieName]; + EventlogHelper.CreateAuthenticationEvent(_featureManager, _eventLog, tokenCookie, AuthenticationEventType.Logout); return Ok(); } diff --git a/src/Authentication/Helpers/EventlogHelper.cs b/src/Authentication/Helpers/EventlogHelper.cs index e9c46bd5..3860b0b4 100644 --- a/src/Authentication/Helpers/EventlogHelper.cs +++ b/src/Authentication/Helpers/EventlogHelper.cs @@ -26,7 +26,7 @@ public static class EventlogHelper /// token in the authentication request /// authentication event type public async static Task CreateAuthenticationEvent(IFeatureManager featureManager, IEventLog eventLog, string jwtToken, AuthenticationEventType eventType) - { + { if (await featureManager.IsEnabledAsync(FeatureFlags.AuditLog)) { AuthenticationEvent authenticationEvent = MapAuthenticationEvent(jwtToken, eventType); diff --git a/test/Altinn.Platform.Authentication.Tests/Controllers/LogoutControllerTests.cs b/test/Altinn.Platform.Authentication.Tests/Controllers/LogoutControllerTests.cs index 9f7fd65a..063aa42d 100644 --- a/test/Altinn.Platform.Authentication.Tests/Controllers/LogoutControllerTests.cs +++ b/test/Altinn.Platform.Authentication.Tests/Controllers/LogoutControllerTests.cs @@ -25,6 +25,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; +using Microsoft.FeatureManagement; using Moq; using Xunit; @@ -139,11 +140,15 @@ public async Task Logout_LogedIn_RedirectToSBL_ExternalAuthenticationMethod() string issuer = "www.altinn.no"; claims.Add(new Claim("originaliss", "uidp", ClaimValueTypes.String, issuer)); claims.Add(new Claim("amr", AuthenticationMethod.BankID.ToString(), ClaimValueTypes.String, issuer)); - claims.Add(new Claim("acr", SecurityLevel.Sensitive.ToString(), ClaimValueTypes.String, issuer)); + claims.Add(new Claim("acr", "Level4", ClaimValueTypes.String, issuer)); string token = PrincipalUtil.GetToken(1337, claims); - HttpClient client = GetTestClient(_cookieDecryptionService.Object, _userProfileService.Object); + Mock eventQueue = new Mock(); + eventQueue.Setup(q => q.CreateAuthenticationEvent(It.IsAny())); + AuthenticationEvent expectedAuthenticationEvent = GetAuthenticationEvent(AuthenticationMethod.BankID, SecurityLevel.VerySensitive, null, AuthenticationEventType.Logout, "1337"); + + HttpClient client = GetTestClient(_cookieDecryptionService.Object, _userProfileService.Object, eventQueue.Object); HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, "/authentication/api/v1/logout"); SetupUtil.AddAuthCookie(requestMessage, token); @@ -159,6 +164,8 @@ public async Task Logout_LogedIn_RedirectToSBL_ExternalAuthenticationMethod() { Assert.Equal("https://idporten.azurewebsites.net/api/v1/logout", values.First()); } + + AssertAuthenticationEvent(eventQueue, expectedAuthenticationEvent, Moq.Times.Once()); } /// @@ -209,7 +216,16 @@ public async Task Logout_FrontChannelOK() string token = PrincipalUtil.GetToken(1337, claims); - HttpClient client = GetTestClient(_cookieDecryptionService.Object, _userProfileService.Object); + Mock eventQueue = new Mock(); + eventQueue.Setup(q => q.CreateAuthenticationEvent(It.IsAny())); + AuthenticationEvent expectedAuthenticationEvent = GetAuthenticationEvent(AuthenticationMethod.AltinnPIN, SecurityLevel.QuiteSensitive, null, AuthenticationEventType.Logout, "1337"); + + Mock featureManageMock = new Mock(); + featureManageMock + .Setup(m => m.IsEnabledAsync("AuditLog")) + .Returns(Task.FromResult(true)); + + HttpClient client = GetTestClient(_cookieDecryptionService.Object, _userProfileService.Object, eventQueue.Object, featureManageMock.Object); HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, "/authentication/api/v1/frontchannel_logout"); SetupUtil.AddAuthCookie(requestMessage, token); @@ -227,9 +243,54 @@ public async Task Logout_FrontChannelOK() Assert.Equal(".ASPXAUTH=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=localhost; path=/; secure; httponly", values.First()); Assert.Equal("AltinnStudioRuntime=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=localhost; path=/; secure; httponly", values.Last()); } + + AssertAuthenticationEvent(eventQueue, expectedAuthenticationEvent, Moq.Times.Once()); } - private HttpClient GetTestClient(ISblCookieDecryptionService cookieDecryptionService, IUserProfileService userProfileService, bool enableOidc = false, bool forceOidc = false, string defaultOidc = "altinn") + /// + /// Frontchannel logout with event log feature turned off + /// + [Fact] + public async Task Logout_FrontChannelOK_Auditlog_off() + { + List claims = new List(); + string issuer = "www.altinn.no"; + claims.Add(new Claim("originaliss", "uidp", ClaimValueTypes.String, issuer)); + + string token = PrincipalUtil.GetToken(1337, claims); + + Mock eventQueue = new Mock(); + eventQueue.Setup(q => q.CreateAuthenticationEvent(It.IsAny())); + AuthenticationEvent expectedAuthenticationEvent = GetAuthenticationEvent(AuthenticationMethod.AltinnPIN, SecurityLevel.QuiteSensitive, null, AuthenticationEventType.Logout, "1337"); + + Mock featureManageMock = new Mock(); + featureManageMock + .Setup(m => m.IsEnabledAsync("AuditLog")) + .Returns(Task.FromResult(false)); + + HttpClient client = GetTestClient(_cookieDecryptionService.Object, _userProfileService.Object, eventQueue.Object, featureManageMock.Object); + + HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, "/authentication/api/v1/frontchannel_logout"); + SetupUtil.AddAuthCookie(requestMessage, token); + + // Act + HttpResponseMessage response = await client.SendAsync(requestMessage); + + // Assert + Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode); + + IEnumerable values; + + if (response.Headers.TryGetValues("Set-Cookie", out values)) + { + Assert.Equal(".ASPXAUTH=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=localhost; path=/; secure; httponly", values.First()); + Assert.Equal("AltinnStudioRuntime=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=localhost; path=/; secure; httponly", values.Last()); + } + + AssertAuthenticationEvent(eventQueue, expectedAuthenticationEvent, Moq.Times.Never()); + } + + private HttpClient GetTestClient(ISblCookieDecryptionService cookieDecryptionService, IUserProfileService userProfileService, IEventLog eventLog = null, IFeatureManager featureManager = null, bool enableOidc = false, bool forceOidc = false, bool auditLog = true, string defaultOidc = "altinn") { HttpClient client = _factory.WithWebHostBuilder(builder => { @@ -261,6 +322,15 @@ private HttpClient GetTestClient(ISblCookieDecryptionService cookieDecryptionSer services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + if (featureManager != null) + { + services.AddSingleton(featureManager); + } + + if (eventLog != null) + { + services.AddSingleton(eventLog); + } }); }).CreateClient(new WebApplicationFactoryClientOptions { AllowAutoRedirect = false }); @@ -272,5 +342,22 @@ private static string GetConfigPath() string unitTestFolder = Path.GetDirectoryName(new Uri(typeof(AuthenticationControllerTests).Assembly.Location).LocalPath); return Path.Combine(unitTestFolder, $"../../../appsettings.json"); } + + private static AuthenticationEvent GetAuthenticationEvent(AuthenticationMethod authMethod, SecurityLevel authLevel, string orgNumber, AuthenticationEventType authEventType, string userId = null) + { + AuthenticationEvent authenticationEvent = new AuthenticationEvent(); + authenticationEvent.AuthenticationMethod = authMethod.ToString(); + authenticationEvent.AuthenticationLevel = authLevel.ToString(); + authenticationEvent.OrgNumber = orgNumber; + authenticationEvent.EventType = authEventType.ToString(); + authenticationEvent.UserId = userId; + + return authenticationEvent; + } + + private static void AssertAuthenticationEvent(Mock eventQueue, AuthenticationEvent expectedAuthenticationEvent, Moq.Times invocationsTime) + { + eventQueue.Verify(e => e.CreateAuthenticationEvent(It.Is(q => q.AuthenticationMethod == expectedAuthenticationEvent.AuthenticationMethod && q.AuthenticationLevel == expectedAuthenticationEvent.AuthenticationLevel && q.OrgNumber == expectedAuthenticationEvent.OrgNumber && q.UserId == expectedAuthenticationEvent.UserId && q.EventType == expectedAuthenticationEvent.EventType)), invocationsTime); + } } } diff --git a/test/Altinn.Platform.Authentication.Tests/appsettings.json b/test/Altinn.Platform.Authentication.Tests/appsettings.json index bb4bbf16..13e4e42b 100644 --- a/test/Altinn.Platform.Authentication.Tests/appsettings.json +++ b/test/Altinn.Platform.Authentication.Tests/appsettings.json @@ -36,7 +36,7 @@ "TokenEndpoint": "https://idporten.azurewebsites.net/api/token", "WellKnownConfigEndpoint": "https://idporten.azurewebsites.net/api/v1/openid/.well-known/openid-configuration", "ClientId": "345345s", - "ProviderClaims": [ "sub", "locale", "role"] + "ProviderClaims": [ "sub", "locale", "role" ] }, "uidp": { "Issuer": "https://uidp-qa.udir.no", @@ -51,5 +51,8 @@ "ProviderClaims": [ "locale", "urn:feide:role", "sub" ] } + }, + "FeatureManagement": { + "AuditLog": true } }