From 13730e9b7c8794ff5914e3fe02db76c6c129e525 Mon Sep 17 00:00:00 2001 From: acn-sbuad Date: Thu, 12 Oct 2023 13:09:11 +0200 Subject: [PATCH 01/14] including pem file --- .gitignore | 1 - test/Altinn.Notifications.Tests/ttd-org.pem | 19 +++++++++++++++++++ test/Altinn.Notifications.Tests/ttd-org.pfx | Bin 0 -> 2389 bytes 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 test/Altinn.Notifications.Tests/ttd-org.pem create mode 100644 test/Altinn.Notifications.Tests/ttd-org.pfx diff --git a/.gitignore b/.gitignore index 04d68e97..5adf7963 100644 --- a/.gitignore +++ b/.gitignore @@ -227,7 +227,6 @@ ClientBin/ *.dbmdl *.dbproj.schemaview *.jfm -*.pfx *.publishsettings orleans.codegen.cs diff --git a/test/Altinn.Notifications.Tests/ttd-org.pem b/test/Altinn.Notifications.Tests/ttd-org.pem new file mode 100644 index 00000000..f2a42d40 --- /dev/null +++ b/test/Altinn.Notifications.Tests/ttd-org.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDAzCCAeugAwIBAgIJANTdO8o3I8x5MA0GCSqGSIb3DQEBCwUAMA4xDDAKBgNV +BAMTA3R0ZDAeFw0yMDA1MjUxMjIxMzdaFw0zMDA1MjQxMjIxMzdaMA4xDDAKBgNV +BAMTA3R0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMcfTsXwwLyC +UkIz06eadWJvG3yrzT+ZB2Oy/WPaZosDnPcnZvCDueN+oy0zTx5TyH5gCi1FvzX2 +7G2eZEKwQaRPv0yuM+McHy1rXxMSOlH/ebP9KJj3FDMUgZl1DCAjJxSAANdTwdrq +ydVg1Crp37AQx8IIEjnBhXsfQh1uPGt1XwgeNyjl00IejxvQOPzd1CofYWwODVtQ +l3PKn1SEgOGcB6wuHNRlnZPCIelQmqxWkcEZiu/NU+kst3NspVUQG2Jf2AF8UWgC +rnrhMQR0Ra1Vi7bWpu6QIKYkN9q0NRHeRSsELOvTh1FgDySYJtNd2xDRSf6IvOiu +tSipl1NZlV0CAwEAAaNkMGIwIAYDVR0OAQH/BBYEFIwq/KbSMzLETdo9NNxj0rz4 +qMqVMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgWgMCAGA1UdJQEB/wQWMBQG +CCsGAQUFBwMBBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOCAQEAE56UmH5gEYbe +1kVw7nrfH0R9FyVZGeQQWBn4/6Ifn+eMS9mxqe0Lq74Ue1zEzvRhRRqWYi9JlKNf +7QQNrc+DzCceIa1U6cMXgXKuXquVHLmRfqvKHbWHJfIkaY8Mlfy++77UmbkvIzly +T1HVhKKp6Xx0r5koa6frBh4Xo/vKBlEyQxWLWF0RPGpGErnYIosJ41M3Po3nw3lY +f7lmH47cdXatcntj2Ho/b2wGi9+W29teVCDfHn2/0oqc7K0EOY9c2ODLjUvQyPZR +OD2yykpyh9x/YeYHFDYdLDJ76/kIdxN43kLU4/hTrh9tMb1PZF+/4DshpAlRoQuL +o8I8avQm/A== +-----END CERTIFICATE----- diff --git a/test/Altinn.Notifications.Tests/ttd-org.pfx b/test/Altinn.Notifications.Tests/ttd-org.pfx new file mode 100644 index 0000000000000000000000000000000000000000..6da835fbb6111808b9cbfe7fabea97a609ae4f0c GIT binary patch literal 2389 zcmZuxc{o&k8$M^|%rFcMMk>r$iV1NHvcDorWt~vjvSqK-7`#TrBzc=;PfC(0B@9wZ zve#=#QI<#<5oK3-8JhHszP_%$>-+wA&h@+R`?;Urb)Dy&8)jfA2oMP~Fk)zoO5(1> zB@s{n%x7Q_5JPYkW(cmp3{>iWNvL>;fr{bNQTz%=3;%Rsg+N3;1Db^y&?GE_7Wxm( zA1(?BhS-MF$(CMB1VW$-VnD55mDdSga}qE;*T2yKO!K*CEuw zguO|llDUo}`n6drbfq`bS(dnU*%8sH+-VuZ%0yKeS60RYI`_Gj=U?(^*)}wN>Tl-qg_*qNzuDR5DT+m^ z81Zy}H;;=D>ns+Y5_0M&eQ0&|P8$$?r>OS!UxDMNc%9nbikI)dNT|{_@IL035K-oq zJ-vb&^AeGNp6bGVQn35h;DYwZJ8Z~XDbY7o--_FL_t}Tt;?li$7H72W9pGf85bhVw zx;#vZyq-Bw8hb4~qaE0SqwyZw2Atpb=Qf*zzR=$z}{GM4nBaWrh<@^zBm>NCTeN~FD(lFCSrP-}hV`Sq_F z)noP#NpX8f`rYRpgKuBdC}~b|-QD%1o1mWG!f`la&&$dF#-K*v)>l{vmfKGketMIC zt}E9hYduNr1uFw3@ruwGRj3^oYq;Bxx9z~LIzux>0Txm|a*3?^HFXA+L04x9SF%6R zf_t|%m{&v@^5ox|&-UdricP&Xp#ygq_-zhdsaZ!96MZ(xK1J9yIryNsfirKvupxn~ zv;YRXmm!04n^Rn3B%OeY9u2$GuXyH0_~2cl>zB5zWlGBrEs`#)NQq$; zBP*PUqiOi}k*OxL33~!}NJmPWWEPh9*h_sJS*^_&BORkgn#4_xvk(<}+Y2@XiW@ws zo=8$uvpUo#-i(2zOt-3^6vWC3x>OSr$|UbroKC+qQ67iQU9pHZ*jy;Fb8hX%e( z-VsouFk-jN{&2(K^n6zcAWNsn?))RKL4n9zY62)}UjQGoXLT2~v*7KN>*pI@gmeb%{WmGNb2 z(!Mxlc>ltK%@yyr)f}wd?i@dFGi~qEI} zUtF^_X|Tkvbi_z%q9F(b008ztV^q-~1Tu8RKurgN9bgLt0lokg2nE9U&n*WUYq0smmmCpzK;2nNABlt58@i7%G#bC;C;=TB9S#~A#OnV9##2`ba4ryW4*)@hx#mz&C)qU^BAcu zE*szD4s7IpFCbq1aL#(5qG(3AxDFrf{`mQVx5cIlJ{r3-S)MbHNNMM(4rPLTsnbNW zR6Lb^usBPmIxV)iUH0CYEuS|BrC_sobzkdxa;h~H^{Muh-h_gpi4(N498$>5DKtTf z^|c*MA@7uA9`Fz|3bqul?pI)9MjdtkNdC|qb>L*RKk@vV@Q6}sv~NG%B=j(v$+<8% zc+g3XLx`z=c{X>t6w*&|?;n1eY~9tmNYOLC)4qp#=FLg(aRK~xSv7L>)Usft#GgZE zJ)>MlHt~pNt^Fa-`e8%)>ln&4VJ54|D0o4Mrz!dWe_~yj2n(Q5s-j55CI~-6-`6>L pD?>houHI3Uk96pSl$RmAVrJ*fxa<|(6U@55y2zBJ%Z3vs{{qsZ>!APu literal 0 HcmV?d00001 From 59ffa90499bbf8088e868f5a62747104f3755887 Mon Sep 17 00:00:00 2001 From: acn-sbuad Date: Thu, 12 Oct 2023 13:09:24 +0200 Subject: [PATCH 02/14] introducing .pfx to readme again --- .gitignore | 1 + .../Altinn.Notifications.csproj | 2 + .../CreateScopeOrAccessTokenRequirement.cs | 28 ++++++ .../Configuration/AuthorizationConstants.cs | 18 ++++ .../EmailNotificationOrdersController.cs | 7 +- .../Controllers/OrdersController.cs | 8 +- .../Extensions/ClaimsPrincipalExtensions.cs | 28 ------ .../Middleware/OrgExtractorMiddleware.cs | 94 +++++++++++++++++++ src/Altinn.Notifications/Program.cs | 36 ++++++- .../EmailNotificationOrdersControllerTests.cs | 72 ++++++++++++-- .../PostTests.cs | 12 ++- .../OrdersController/GetByIdTests.cs | 2 + .../OrdersController/GetBySendersRefTests.cs | 2 + .../OrdersController/GetWithStatusById.cs | 2 + .../OrdersController/OrdersControllerTests.cs | 2 + .../Altinn.Notifications.Tests.csproj | 6 ++ .../Mocks/Authentication/JwtTokenMock.cs | 15 ++- .../PublicSigningKeyProviderMock.cs | 32 +++++++ .../Notifications/Utils/PrincipalUtil.cs | 16 ++++ 19 files changed, 329 insertions(+), 54 deletions(-) create mode 100644 src/Altinn.Notifications/Authorization/CreateScopeOrAccessTokenRequirement.cs create mode 100644 src/Altinn.Notifications/Configuration/AuthorizationConstants.cs delete mode 100644 src/Altinn.Notifications/Extensions/ClaimsPrincipalExtensions.cs create mode 100644 src/Altinn.Notifications/Middleware/OrgExtractorMiddleware.cs create mode 100644 test/Altinn.Notifications.Tests/Notifications/Mocks/Authentication/PublicSigningKeyProviderMock.cs diff --git a/.gitignore b/.gitignore index 5adf7963..04d68e97 100644 --- a/.gitignore +++ b/.gitignore @@ -227,6 +227,7 @@ ClientBin/ *.dbmdl *.dbproj.schemaview *.jfm +*.pfx *.publishsettings orleans.codegen.cs diff --git a/src/Altinn.Notifications/Altinn.Notifications.csproj b/src/Altinn.Notifications/Altinn.Notifications.csproj index 0e5c1db0..38276bd3 100644 --- a/src/Altinn.Notifications/Altinn.Notifications.csproj +++ b/src/Altinn.Notifications/Altinn.Notifications.csproj @@ -9,6 +9,8 @@ + + diff --git a/src/Altinn.Notifications/Authorization/CreateScopeOrAccessTokenRequirement.cs b/src/Altinn.Notifications/Authorization/CreateScopeOrAccessTokenRequirement.cs new file mode 100644 index 00000000..65adb866 --- /dev/null +++ b/src/Altinn.Notifications/Authorization/CreateScopeOrAccessTokenRequirement.cs @@ -0,0 +1,28 @@ +using Altinn.Common.AccessToken; +using Altinn.Common.PEP.Authorization; + +namespace Altinn.Notifications.Authorization; + +/// +/// This requirement was created to allow access if either Scope or AccessToken verification is successful. +/// It inherits from both and which +/// will trigger both and . If any of them +/// indicate success, authorization will succeed. +/// +public class CreateScopeOrAccessTokenRequirement : IAccessTokenRequirement, IScopeAccessRequirement +{ + /// + /// Initializes a new instance of the class with the given scope. + /// + public CreateScopeOrAccessTokenRequirement(string scope) + { + ApprovedIssuers = Array.Empty(); + Scope = new string[] { scope }; + } + + /// + public string[] ApprovedIssuers { get; set; } + + /// + public string[] Scope { get; set; } +} diff --git a/src/Altinn.Notifications/Configuration/AuthorizationConstants.cs b/src/Altinn.Notifications/Configuration/AuthorizationConstants.cs new file mode 100644 index 00000000..9f5ed09c --- /dev/null +++ b/src/Altinn.Notifications/Configuration/AuthorizationConstants.cs @@ -0,0 +1,18 @@ +namespace Altinn.Notifications.Configuration +{ + /// + /// Constants related to authorization of notifications + /// + public static class AuthorizationConstants + { + /// + /// Id for the policy requiring create scope or access platform access token + /// + public const string POLICY_CREATE_SCOPE_OR_PLATFORM_ACCESS = "CreateScopeOrPlatfomAccessToken"; + + /// + /// Scope for allowing access to creating notifications + /// + public const string SCOPE_NOTIFICATIONS_CREATE = "altinn:notifications.create"; + } +} diff --git a/src/Altinn.Notifications/Controllers/EmailNotificationOrdersController.cs b/src/Altinn.Notifications/Controllers/EmailNotificationOrdersController.cs index 93a499aa..ffecadf7 100644 --- a/src/Altinn.Notifications/Controllers/EmailNotificationOrdersController.cs +++ b/src/Altinn.Notifications/Controllers/EmailNotificationOrdersController.cs @@ -1,4 +1,5 @@ -using Altinn.Notifications.Core.Models; +using Altinn.Notifications.Configuration; +using Altinn.Notifications.Core.Models; using Altinn.Notifications.Core.Models.Orders; using Altinn.Notifications.Core.Services.Interfaces; using Altinn.Notifications.Extensions; @@ -21,7 +22,6 @@ namespace Altinn.Notifications.Controllers; /// [Route("notifications/api/v1/orders/email")] [ApiController] -[Authorize] [SwaggerResponse(401, "Caller is unauthorized")] [SwaggerResponse(403, "Caller is not authorized to access the requested resource")] @@ -45,6 +45,7 @@ public EmailNotificationOrdersController(IValidator /// The id of the registered notification order + [Authorize(Policy = AuthorizationConstants.POLICY_CREATE_SCOPE_OR_PLATFORM_ACCESS)] [HttpPost] [Consumes("application/json")] [Produces("application/json")] @@ -60,7 +61,7 @@ public async Task> Post(EmailNotificationOrderRequestEx return ValidationProblem(ModelState); } - string? creator = User.GetOrg(); + string? creator = HttpContext.Items["Org"] as string; if (creator == null) { diff --git a/src/Altinn.Notifications/Controllers/OrdersController.cs b/src/Altinn.Notifications/Controllers/OrdersController.cs index b8ccaf3d..4a387e45 100644 --- a/src/Altinn.Notifications/Controllers/OrdersController.cs +++ b/src/Altinn.Notifications/Controllers/OrdersController.cs @@ -1,5 +1,4 @@ using Altinn.Notifications.Core.Services.Interfaces; -using Altinn.Notifications.Extensions; using Altinn.Notifications.Mappers; using Altinn.Notifications.Models; @@ -43,7 +42,8 @@ public OrdersController(IGetOrderService getOrderService) [SwaggerResponse(404, "No order with the provided id was found")] public async Task> GetById([FromRoute] Guid id) { - string? expectedCreator = User.GetOrg(); + string? expectedCreator = HttpContext.Items["Org"] as string; + if (expectedCreator == null) { return Forbid(); @@ -69,7 +69,7 @@ public async Task> GetById([FromRoute] Guid i [SwaggerResponse(200, "The list of notification orders matching the provided senders ref was retrieved successfully", typeof(NotificationOrderListExt))] public async Task> GetBySendersRef([FromQuery, BindRequired] string sendersReference) { - string? expectedCreator = User.GetOrg(); + string? expectedCreator = HttpContext.Items["Org"] as string; if (expectedCreator == null) { return Forbid(); @@ -97,7 +97,7 @@ public async Task> GetBySendersRef([FromQ [SwaggerResponse(404, "No order with the provided id was found")] public async Task> GetWithStatusById([FromRoute] Guid id) { - string? expectedCreator = User.GetOrg(); + string? expectedCreator = HttpContext.Items["Org"] as string; if (expectedCreator == null) { return Forbid(); diff --git a/src/Altinn.Notifications/Extensions/ClaimsPrincipalExtensions.cs b/src/Altinn.Notifications/Extensions/ClaimsPrincipalExtensions.cs deleted file mode 100644 index c4440aaf..00000000 --- a/src/Altinn.Notifications/Extensions/ClaimsPrincipalExtensions.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Security.Claims; - -using AltinnCore.Authentication.Constants; - -namespace Altinn.Notifications.Extensions; - -/// -/// Extensions for claimsprincial -/// -public static class ClaimsPrincipalExtensions -{ - /// - /// Get the org identifier string or null if it is not an org. - /// - public static string? GetOrg(this ClaimsPrincipal user) - { - if (user.HasClaim(c => c.Type == AltinnCoreClaimTypes.Org)) - { - Claim? orgClaim = user.FindFirst(c => c.Type == AltinnCoreClaimTypes.Org); - if (orgClaim != null) - { - return orgClaim.Value; - } - } - - return null; - } -} diff --git a/src/Altinn.Notifications/Middleware/OrgExtractorMiddleware.cs b/src/Altinn.Notifications/Middleware/OrgExtractorMiddleware.cs new file mode 100644 index 00000000..f60226a3 --- /dev/null +++ b/src/Altinn.Notifications/Middleware/OrgExtractorMiddleware.cs @@ -0,0 +1,94 @@ +using AltinnCore.Authentication.Constants; + +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; + +namespace Altinn.Notifications.Middleware; + +/// +/// Middleware for extracting org information in an HTTP request +/// from either the issuer of PlatformAccessToken header or as +/// an org claim in the bearer token. +/// +public class OrgExtractorMiddleware +{ + private readonly RequestDelegate _next; + + /// + /// Initializes a new instance of the class. + /// + public OrgExtractorMiddleware(RequestDelegate next) + { + _next = next; + } + + /// + /// Retrieve org claim and save in httpContext as Creator item. + /// + public async Task InvokeAsync(HttpContext context) + { + if (ShouldApplyMiddleware(context.Request.Path)) + { + string? org = GetOrgFromHttpContext(context); + + if (org != null) + { + context.Items["Org"] = org; + } + } + + await _next(context); + } + + private static string? GetOrgFromHttpContext(HttpContext context) + { + string? accessToken = context.Request.Headers["PlatformAccessToken"]; + if (!string.IsNullOrEmpty(accessToken)) + { + return GetIssuerOfAccessToken(accessToken); + } + + return GetOrgFromClaim(context.User); + } + + private static string GetIssuerOfAccessToken(string accessToken) + { + JwtSecurityTokenHandler validator = new(); + JwtSecurityToken jwt = validator.ReadJwtToken(accessToken); + return jwt.Issuer; + } + + private static string? GetOrgFromClaim(ClaimsPrincipal user) + { + if (user.HasClaim(c => c.Type == AltinnCoreClaimTypes.Org)) + { + Claim? orgClaim = user.FindFirst(c => c.Type == AltinnCoreClaimTypes.Org); + if (orgClaim != null) + { + return orgClaim.Value; + } + } + + return null; + } + + private static bool ShouldApplyMiddleware(string path) + { + return !path.Contains("/token"); + } +} + +/// +/// Static class for middleware registration +/// +public static class OrgExtractorMiddlewareExtensions +{ + /// + /// Registers the in the application + /// + public static IApplicationBuilder UseOrgExtractor( + this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } +} diff --git a/src/Altinn.Notifications/Program.cs b/src/Altinn.Notifications/Program.cs index 3619c3b1..8cf28fc5 100644 --- a/src/Altinn.Notifications/Program.cs +++ b/src/Altinn.Notifications/Program.cs @@ -3,11 +3,16 @@ using System.Text.Json; using System.Text.Json.Serialization; +using Altinn.Common.AccessToken; +using Altinn.Common.AccessToken.Services; +using Altinn.Common.PEP.Authorization; +using Altinn.Notifications.Authorization; using Altinn.Notifications.Configuration; using Altinn.Notifications.Core.Extensions; using Altinn.Notifications.Extensions; using Altinn.Notifications.Health; using Altinn.Notifications.Integrations.Extensions; +using Altinn.Notifications.Middleware; using Altinn.Notifications.Models; using Altinn.Notifications.Persistence.Extensions; using Altinn.Notifications.Validators; @@ -23,6 +28,7 @@ using Microsoft.ApplicationInsights.Channel; using Microsoft.ApplicationInsights.Extensibility; using Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel; +using Microsoft.AspNetCore.Authorization; using Microsoft.IdentityModel.Tokens; using Swashbuckle.AspNetCore.Filters; @@ -45,11 +51,12 @@ builder.Services.AddSwaggerGen(c => { IncludeXmlComments(c); - c.EnableAnnotations(); + c.EnableAnnotations(); c.OperationFilter(); }); var app = builder.Build(); + app.SetUpPostgreSql(builder.Environment.IsDevelopment(), builder.Configuration); // Configure the HTTP request pipeline. @@ -65,6 +72,8 @@ app.MapHealthChecks("/health"); +app.UseOrgExtractor(); + app.Run(); void ConfigureSetupLogging() @@ -144,7 +153,6 @@ void ConfigureServices(IServiceCollection services, IConfiguration config) } GeneralSettings generalSettings = config.GetSection("GeneralSettings").Get(); - services.Configure(config.GetSection("GeneralSettings")); services.AddAuthentication(JwtCookieDefaults.AuthenticationScheme) .AddJwtCookie(JwtCookieDefaults.AuthenticationScheme, options => @@ -167,6 +175,8 @@ void ConfigureServices(IServiceCollection services, IConfiguration config) } }); + AddAuthorizationRulesAndHandlers(services, config); + ResourceLinkExtensions.Initialize(generalSettings.BaseUri); AddInputModelValidators(services); services.AddCoreServices(config); @@ -174,10 +184,30 @@ void ConfigureServices(IServiceCollection services, IConfiguration config) services.AddKafkaServices(config); services.AddKafkaHealthChecks(config); - services.AddPostgresRepositories(config); + services.AddPostgresRepositories(config); services.AddPostgresHealthChecks(config); } +void AddAuthorizationRulesAndHandlers(IServiceCollection services, IConfiguration config) +{ + services.AddAuthorization(options => + { + options.AddPolicy(AuthorizationConstants.POLICY_CREATE_SCOPE_OR_PLATFORM_ACCESS, policy => + { + policy.Requirements.Add(new CreateScopeOrAccessTokenRequirement(AuthorizationConstants.SCOPE_NOTIFICATIONS_CREATE)); + }); + }); + + services.AddTransient(); + + // services required for access token handler + services.AddMemoryCache(); + services.AddSingleton(); + services.AddSingleton(); + services.Configure(config.GetSection("kvSetting")); + services.AddSingleton(); +} + async Task SetConfigurationProviders(ConfigurationManager config) { string basePath = Directory.GetParent(Directory.GetCurrentDirectory()).FullName; diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/EmailNotificationOrdersControllerTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/EmailNotificationOrdersControllerTests.cs index d60958ac..74670432 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/EmailNotificationOrdersControllerTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/EmailNotificationOrdersControllerTests.cs @@ -3,6 +3,7 @@ using System.Text; using System.Text.Json; +using Altinn.Common.AccessToken.Services; using Altinn.Notifications.Configuration; using Altinn.Notifications.Core.Enums; using Altinn.Notifications.Core.Models; @@ -87,11 +88,26 @@ public async Task Post_MissingBearerToken_Unauthorized() Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } + [Fact] + public async Task Post_InvalidScopeInToken_Forbidden() + { + // Arrange + HttpClient client = GetTestClient(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:dummmy.scope")); + HttpRequestMessage httpRequestMessage = new(HttpMethod.Post, _basePath); + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + + // Assert + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + } + [Fact] public async Task Post_EmptyBody_BadRequest() { HttpClient client = GetTestClient(); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd")); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:notifications.create")); HttpRequestMessage httpRequestMessage = new(HttpMethod.Post, _basePath) { @@ -117,7 +133,7 @@ public async Task Post_ValidationReturnsError_BadRequest() .Returns(new ValidationResult(new List { new ValidationFailure("SomeProperty", "SomeError") })); HttpClient client = GetTestClient(validator.Object); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd")); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:notifications.create")); HttpRequestMessage httpRequestMessage = new(HttpMethod.Post, _basePath) { @@ -162,7 +178,7 @@ public async Task Post_ServiceReturnsError_ServerError() .ReturnsAsync((null, new ServiceError(500))); HttpClient client = GetTestClient(orderService: serviceMock.Object); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd")); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:notifications.create")); HttpRequestMessage httpRequestMessage = new(HttpMethod.Post, _basePath) { @@ -178,7 +194,7 @@ public async Task Post_ServiceReturnsError_ServerError() } [Fact] - public async Task Post_ServiceReturnsOrder_Accepted() + public async Task Post_ValidScope_ServiceReturnsOrder_Accepted() { // Arrange string expectedFromAddress = "sender@domain.com"; @@ -197,7 +213,7 @@ public async Task Post_ServiceReturnsOrder_Accepted() .ReturnsAsync((_order, null)); HttpClient client = GetTestClient(orderService: serviceMock.Object); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd")); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:notifications.create")); HttpRequestMessage httpRequestMessage = new(HttpMethod.Post, _basePath) { @@ -207,10 +223,51 @@ public async Task Post_ServiceReturnsOrder_Accepted() // Act HttpResponseMessage response = await client.SendAsync(httpRequestMessage); string respoonseString = await response.Content.ReadAsStringAsync(); - OrderIdExt? orderIdObjectExt = JsonSerializer.Deserialize(respoonseString); // Assert Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); + OrderIdExt? orderIdObjectExt = JsonSerializer.Deserialize(respoonseString); + Assert.NotNull(orderIdObjectExt); + Assert.Equal(_order.Id, orderIdObjectExt.OrderId); + Assert.Equal("http://localhost:5090/notifications/api/v1/orders/" + _order.Id, response.Headers?.Location?.ToString()); + + serviceMock.VerifyAll(); + } + + [Fact] + public async Task Post_ValidAccessToken_ServiceReturnsOrder_Accepted() + { + // Arrange + string expectedFromAddress = "sender@domain.com"; + + Mock serviceMock = new(); + serviceMock.Setup(s => s.RegisterEmailNotificationOrder(It.IsAny())) + .Callback(orderRequest => + { + var emailTemplate = orderRequest.Templates + .OfType() + .FirstOrDefault(); + + Assert.NotNull(emailTemplate); + Assert.Equal(expectedFromAddress, emailTemplate.FromAddress); + }) + .ReturnsAsync((_order, null)); + + HttpClient client = GetTestClient(orderService: serviceMock.Object); + + HttpRequestMessage httpRequestMessage = new(HttpMethod.Post, _basePath) + { + Content = new StringContent(_orderRequestExt.Serialize(), Encoding.UTF8, "application/json") + }; + httpRequestMessage.Headers.Add("PlatformAccessToken", PrincipalUtil.GetAccessToken("ttd", "apps-test")); + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + string respoonseString = await response.Content.ReadAsStringAsync(); + + // Assert + Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); + OrderIdExt? orderIdObjectExt = JsonSerializer.Deserialize(respoonseString); Assert.NotNull(orderIdObjectExt); Assert.Equal(_order.Id, orderIdObjectExt.OrderId); Assert.Equal("http://localhost:5090/notifications/api/v1/orders/" + _order.Id, response.Headers?.Location?.ToString()); @@ -237,7 +294,7 @@ public async Task Post_OrderWithoutFromAddress_StringEmptyUsedAsServiceInput_Acc .ReturnsAsync((_order, null)); HttpClient client = GetTestClient(orderService: serviceMock.Object); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd")); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:notifications.create")); EmailNotificationOrderRequestExt request = new() { @@ -301,6 +358,7 @@ private HttpClient GetTestClient(IValidator? v // Set up mock authentication so that not well known endpoint is used services.AddSingleton, JwtCookiePostConfigureOptionsStub>(); + services.AddSingleton(); }); }).CreateClient(); diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/PostTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/PostTests.cs index 9eb836e7..58970521 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/PostTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/PostTests.cs @@ -3,6 +3,7 @@ using System.Text; using System.Text.Json; +using Altinn.Common.AccessToken.Services; using Altinn.Notifications.Controllers; using Altinn.Notifications.Core.Enums; using Altinn.Notifications.IntegrationTests.Utils; @@ -67,7 +68,7 @@ public async Task Post_ServiceReturnsOrderWIthId_Accepted() { // Arrange HttpClient client = GetTestClient(); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd")); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:notifications.create")); HttpRequestMessage httpRequestMessage = new(HttpMethod.Post, _basePath) { @@ -77,10 +78,10 @@ public async Task Post_ServiceReturnsOrderWIthId_Accepted() // Act HttpResponseMessage response = await client.SendAsync(httpRequestMessage); string respoonseString = await response.Content.ReadAsStringAsync(); - OrderIdExt? orderIdObjectExt = JsonSerializer.Deserialize(respoonseString); // Assert Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); + OrderIdExt? orderIdObjectExt = JsonSerializer.Deserialize(respoonseString); Assert.NotNull(orderIdObjectExt); Assert.Equal("http://localhost:5090/notifications/api/v1/orders/" + orderIdObjectExt.OrderId, response.Headers?.Location?.ToString()); } @@ -90,7 +91,7 @@ public async Task Post_OrderWithoutSendersRef_Accepted() { // Arrange HttpClient client = GetTestClient(); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd")); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:notifications.create")); HttpRequestMessage httpRequestMessage = new(HttpMethod.Post, _basePath) { @@ -100,10 +101,10 @@ public async Task Post_OrderWithoutSendersRef_Accepted() // Act HttpResponseMessage response = await client.SendAsync(httpRequestMessage); string respoonseString = await response.Content.ReadAsStringAsync(); - OrderIdExt? orderIdObjectExt = JsonSerializer.Deserialize(respoonseString); // Assert Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); + OrderIdExt? orderIdObjectExt = JsonSerializer.Deserialize(respoonseString); Assert.NotNull(orderIdObjectExt); Assert.Equal("http://localhost:5090/notifications/api/v1/orders/" + orderIdObjectExt.OrderId, response.Headers?.Location?.ToString()); } @@ -129,8 +130,9 @@ private HttpClient GetTestClient() builder.ConfigureTestServices(services => { - // Set up mock authentication so that not well known endpoint is used + // Set up mock authentication services.AddSingleton, JwtCookiePostConfigureOptionsStub>(); + services.AddSingleton(); }); }).CreateClient(); diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetByIdTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetByIdTests.cs index b6c8dea8..a4b75721 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetByIdTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetByIdTests.cs @@ -3,6 +3,7 @@ using System.Text.Json; using System.Text.Json.Serialization; +using Altinn.Common.AccessToken.Services; using Altinn.Notifications.Core.Models.Orders; using Altinn.Notifications.IntegrationTests.Utils; using Altinn.Notifications.Mappers; @@ -122,6 +123,7 @@ private HttpClient GetTestClient() { // Set up mock authentication so that not well known endpoint is used services.AddSingleton, JwtCookiePostConfigureOptionsStub>(); + services.AddSingleton(); }); }).CreateClient(); diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetBySendersRefTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetBySendersRefTests.cs index 4e296b13..23506414 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetBySendersRefTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetBySendersRefTests.cs @@ -3,6 +3,7 @@ using System.Text.Json; using System.Text.Json.Serialization; +using Altinn.Common.AccessToken.Services; using Altinn.Notifications.Core.Models.Orders; using Altinn.Notifications.IntegrationTests.Utils; using Altinn.Notifications.Models; @@ -131,6 +132,7 @@ private HttpClient GetTestClient() { // Set up mock authentication so that not well known endpoint is used services.AddSingleton, JwtCookiePostConfigureOptionsStub>(); + services.AddSingleton(); }); }).CreateClient(); diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetWithStatusById.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetWithStatusById.cs index d9aea92b..424b36f4 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetWithStatusById.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetWithStatusById.cs @@ -3,6 +3,7 @@ using System.Text.Json; using System.Text.Json.Serialization; +using Altinn.Common.AccessToken.Services; using Altinn.Notifications.Core.Models.Orders; using Altinn.Notifications.IntegrationTests.Utils; using Altinn.Notifications.Models; @@ -166,6 +167,7 @@ private HttpClient GetTestClient() { // Set up mock authentication so that not well known endpoint is used services.AddSingleton, JwtCookiePostConfigureOptionsStub>(); + services.AddSingleton(); }); }).CreateClient(); diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/OrdersControllerTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/OrdersControllerTests.cs index f2f00233..3118c39b 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/OrdersControllerTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/OrdersControllerTests.cs @@ -1,6 +1,7 @@ using System.Net; using System.Net.Http.Headers; +using Altinn.Common.AccessToken.Services; using Altinn.Notifications.Core.Enums; using Altinn.Notifications.Core.Models; using Altinn.Notifications.Core.Models.NotificationTemplate; @@ -207,6 +208,7 @@ private HttpClient GetTestClient(IGetOrderService? orderService = null) // Set up mock authentication so that not well known endpoint is used services.AddSingleton, JwtCookiePostConfigureOptionsStub>(); + services.AddSingleton(); }); }).CreateClient(); diff --git a/test/Altinn.Notifications.Tests/Altinn.Notifications.Tests.csproj b/test/Altinn.Notifications.Tests/Altinn.Notifications.Tests.csproj index cbc93649..df7fe583 100644 --- a/test/Altinn.Notifications.Tests/Altinn.Notifications.Tests.csproj +++ b/test/Altinn.Notifications.Tests/Altinn.Notifications.Tests.csproj @@ -56,6 +56,12 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + diff --git a/test/Altinn.Notifications.Tests/Notifications/Mocks/Authentication/JwtTokenMock.cs b/test/Altinn.Notifications.Tests/Notifications/Mocks/Authentication/JwtTokenMock.cs index e790ef01..e181c04f 100644 --- a/test/Altinn.Notifications.Tests/Notifications/Mocks/Authentication/JwtTokenMock.cs +++ b/test/Altinn.Notifications.Tests/Notifications/Mocks/Authentication/JwtTokenMock.cs @@ -17,16 +17,16 @@ public static class JwtTokenMock /// Generates a token with a self signed certificate included in the integration test project. /// /// A new token. - public static string GenerateToken(ClaimsPrincipal principal, TimeSpan tokenExipry) + public static string GenerateToken(ClaimsPrincipal principal, TimeSpan tokenExipry, string issuer = "UnitTest") { JwtSecurityTokenHandler tokenHandler = new(); SecurityTokenDescriptor tokenDescriptor = new() { Subject = new ClaimsIdentity(principal.Identity), Expires = DateTime.UtcNow.AddSeconds(tokenExipry.TotalSeconds), - SigningCredentials = GetSigningCredentials(), + SigningCredentials = GetSigningCredentials(issuer), Audience = "altinn.no", - Issuer = "UnitTest" + Issuer = issuer }; SecurityToken token = tokenHandler.CreateToken(tokenDescriptor); @@ -35,9 +35,16 @@ public static string GenerateToken(ClaimsPrincipal principal, TimeSpan tokenExip return tokenstring; } - private static SigningCredentials GetSigningCredentials() + private static SigningCredentials GetSigningCredentials(string issuer) { string certPath = "jwtselfsignedcert.pfx"; + if (!issuer.Equals("UnitTest")) + { + certPath = $"{issuer}-org.pfx"; + + X509Certificate2 certIssuer = new(certPath); + return new X509SigningCredentials(certIssuer, SecurityAlgorithms.RsaSha256); + } X509Certificate2 cert = new(certPath, "qwer1234"); return new X509SigningCredentials(cert, SecurityAlgorithms.RsaSha256); diff --git a/test/Altinn.Notifications.Tests/Notifications/Mocks/Authentication/PublicSigningKeyProviderMock.cs b/test/Altinn.Notifications.Tests/Notifications/Mocks/Authentication/PublicSigningKeyProviderMock.cs new file mode 100644 index 00000000..833cf78b --- /dev/null +++ b/test/Altinn.Notifications.Tests/Notifications/Mocks/Authentication/PublicSigningKeyProviderMock.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography.X509Certificates; +using System.Threading.Tasks; + +using Altinn.Common.AccessToken.Services; + +using Microsoft.IdentityModel.Tokens; + +namespace Altinn.Notifications.Tests.Notifications.Mocks.Authentication +{ + public class PublicSigningKeyProviderMock : IPublicSigningKeyProvider + { + public SigningCredentials GetSigningCredentials() + { + throw new NotImplementedException(); + } + + public Task> GetSigningKeys(string issuer) + { + List signingKeys = new(); + + X509Certificate2 cert = new($"{issuer}-org.pem"); + SecurityKey key = new X509SecurityKey(cert); + + signingKeys.Add(key); + + return Task.FromResult(signingKeys.AsEnumerable()); + } + } +} diff --git a/test/Altinn.Notifications.Tests/Notifications/Utils/PrincipalUtil.cs b/test/Altinn.Notifications.Tests/Notifications/Utils/PrincipalUtil.cs index 85631d89..3aedcd95 100644 --- a/test/Altinn.Notifications.Tests/Notifications/Utils/PrincipalUtil.cs +++ b/test/Altinn.Notifications.Tests/Notifications/Utils/PrincipalUtil.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Security.Claims; +using Altinn.Common.AccessToken.Constants; using Altinn.Notifications.Tests.Notifications.Mocks.Authentication; using AltinnCore.Authentication.Constants; @@ -76,4 +77,19 @@ public static string GetUserToken(int userId, int authenticationLevel = 2, strin return token; } + + public static string GetAccessToken(string issuer, string app) + { + List claims = new() + { + new Claim(AccessTokenClaimTypes.App, app, ClaimValueTypes.String, issuer) + }; + + ClaimsIdentity identity = new("mock"); + identity.AddClaims(claims); + ClaimsPrincipal principal = new(identity); + string token = JwtTokenMock.GenerateToken(principal, new TimeSpan(0, 1, 5), issuer); + + return token; + } } From 58cd805287482bb8acf8c275cbd2389eed150fdf Mon Sep 17 00:00:00 2001 From: acn-sbuad Date: Thu, 12 Oct 2023 13:14:37 +0200 Subject: [PATCH 03/14] updated comments in test setup --- .../EmailNotificationOrdersControllerTests.cs | 2 +- .../EmailNotificationsOrderController/PostTests.cs | 2 +- .../Notifications/OrdersController/GetByIdTests.cs | 2 +- .../Notifications/OrdersController/GetBySendersRefTests.cs | 2 +- .../Notifications/OrdersController/GetWithStatusById.cs | 2 +- .../Notifications/OrdersController/OrdersControllerTests.cs | 2 +- .../Notifications/TriggerController/TriggerControllerTests.cs | 2 +- .../TriggerController/Trigger_PastDueOrdersTests.cs | 2 +- .../TriggerController/Trigger_SendEmailNotificationsTests.cs | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/EmailNotificationOrdersControllerTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/EmailNotificationOrdersControllerTests.cs index 74670432..cbbd6478 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/EmailNotificationOrdersControllerTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/EmailNotificationOrdersControllerTests.cs @@ -356,7 +356,7 @@ private HttpClient GetTestClient(IValidator? v services.AddSingleton(validator); services.AddSingleton(orderService); - // Set up mock authentication so that not well known endpoint is used + // Set up mock authentication and authorization services.AddSingleton, JwtCookiePostConfigureOptionsStub>(); services.AddSingleton(); }); diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/PostTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/PostTests.cs index 58970521..90ece8b0 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/PostTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/PostTests.cs @@ -130,7 +130,7 @@ private HttpClient GetTestClient() builder.ConfigureTestServices(services => { - // Set up mock authentication + // Set up mock authentication and authorization services.AddSingleton, JwtCookiePostConfigureOptionsStub>(); services.AddSingleton(); }); diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetByIdTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetByIdTests.cs index a4b75721..779573ba 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetByIdTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetByIdTests.cs @@ -121,7 +121,7 @@ private HttpClient GetTestClient() builder.ConfigureTestServices(services => { - // Set up mock authentication so that not well known endpoint is used + // Set up mock authentication and authorization services.AddSingleton, JwtCookiePostConfigureOptionsStub>(); services.AddSingleton(); }); diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetBySendersRefTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetBySendersRefTests.cs index 23506414..1a74858e 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetBySendersRefTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetBySendersRefTests.cs @@ -130,7 +130,7 @@ private HttpClient GetTestClient() builder.ConfigureTestServices(services => { - // Set up mock authentication so that not well known endpoint is used + // Set up mock authentication and authorization services.AddSingleton, JwtCookiePostConfigureOptionsStub>(); services.AddSingleton(); }); diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetWithStatusById.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetWithStatusById.cs index 424b36f4..d5f1fa5d 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetWithStatusById.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetWithStatusById.cs @@ -165,7 +165,7 @@ private HttpClient GetTestClient() builder.ConfigureTestServices(services => { - // Set up mock authentication so that not well known endpoint is used + // Set up mock authentication and authorization services.AddSingleton, JwtCookiePostConfigureOptionsStub>(); services.AddSingleton(); }); diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/OrdersControllerTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/OrdersControllerTests.cs index 3118c39b..fd328562 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/OrdersControllerTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/OrdersControllerTests.cs @@ -206,7 +206,7 @@ private HttpClient GetTestClient(IGetOrderService? orderService = null) { services.AddSingleton(orderService); - // Set up mock authentication so that not well known endpoint is used + // Set up mock authentication and authorization services.AddSingleton, JwtCookiePostConfigureOptionsStub>(); services.AddSingleton(); }); diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/TriggerController/TriggerControllerTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/TriggerController/TriggerControllerTests.cs index 2d5f06d5..cc3f11ba 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/TriggerController/TriggerControllerTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/TriggerController/TriggerControllerTests.cs @@ -89,7 +89,7 @@ private HttpClient GetTestClient(IOrderProcessingService? orderProcessingService services.AddSingleton(orderProcessingService); services.AddSingleton(emailNotificationService); - // Set up mock authentication so that not well known endpoint is used + // Set up mock authentication and authorization services.AddSingleton, JwtCookiePostConfigureOptionsStub>(); }); }).CreateClient(); diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/TriggerController/Trigger_PastDueOrdersTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/TriggerController/Trigger_PastDueOrdersTests.cs index 4bad8cef..12db96f6 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/TriggerController/Trigger_PastDueOrdersTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/TriggerController/Trigger_PastDueOrdersTests.cs @@ -85,7 +85,7 @@ private HttpClient GetTestClient() opts.PastDueOrdersTopicName = _topicName; }); - // Set up mock authentication so that not well known endpoint is used + // Set up mock authentication and authorization services.AddSingleton, JwtCookiePostConfigureOptionsStub>(); }); }).CreateClient(); diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/TriggerController/Trigger_SendEmailNotificationsTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/TriggerController/Trigger_SendEmailNotificationsTests.cs index 5081da24..d73f3282 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/TriggerController/Trigger_SendEmailNotificationsTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/TriggerController/Trigger_SendEmailNotificationsTests.cs @@ -86,7 +86,7 @@ private HttpClient GetTestClient() opts.PastDueOrdersTopicName = _topicName; }); - // Set up mock authentication so that not well known endpoint is used + // Set up mock authentication and authorization services.AddSingleton, JwtCookiePostConfigureOptionsStub>(); }); }).CreateClient(); From 362b24d1aea4c02ebf755047fab2387f3bb991f1 Mon Sep 17 00:00:00 2001 From: acn-sbuad Date: Thu, 12 Oct 2023 13:21:42 +0200 Subject: [PATCH 04/14] not applying middleware to health endpoint --- .../Middleware/OrgExtractorMiddleware.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Altinn.Notifications/Middleware/OrgExtractorMiddleware.cs b/src/Altinn.Notifications/Middleware/OrgExtractorMiddleware.cs index f60226a3..12bfee03 100644 --- a/src/Altinn.Notifications/Middleware/OrgExtractorMiddleware.cs +++ b/src/Altinn.Notifications/Middleware/OrgExtractorMiddleware.cs @@ -1,8 +1,8 @@ -using AltinnCore.Authentication.Constants; - -using System.IdentityModel.Tokens.Jwt; +using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; +using AltinnCore.Authentication.Constants; + namespace Altinn.Notifications.Middleware; /// @@ -74,7 +74,7 @@ private static string GetIssuerOfAccessToken(string accessToken) private static bool ShouldApplyMiddleware(string path) { - return !path.Contains("/token"); + return !(path.Contains("/token") || path.Contains("/health")); } } From 1fd0950bd797bf97ad493a71e22e94bd99d0c452 Mon Sep 17 00:00:00 2001 From: acn-sbuad Date: Thu, 12 Oct 2023 13:31:06 +0200 Subject: [PATCH 05/14] permanently allowing .pfx --- .gitignore | 1 - .../Altinn.Notifications.IntegrationTests.csproj | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 04d68e97..5adf7963 100644 --- a/.gitignore +++ b/.gitignore @@ -227,7 +227,6 @@ ClientBin/ *.dbmdl *.dbproj.schemaview *.jfm -*.pfx *.publishsettings orleans.codegen.cs diff --git a/test/Altinn.Notifications.IntegrationTests/Altinn.Notifications.IntegrationTests.csproj b/test/Altinn.Notifications.IntegrationTests/Altinn.Notifications.IntegrationTests.csproj index 410ca393..50d154e1 100644 --- a/test/Altinn.Notifications.IntegrationTests/Altinn.Notifications.IntegrationTests.csproj +++ b/test/Altinn.Notifications.IntegrationTests/Altinn.Notifications.IntegrationTests.csproj @@ -31,6 +31,7 @@ + From 209a81065f7e978e9b2d42f31ef435951c4d4c31 Mon Sep 17 00:00:00 2001 From: acn-sbuad Date: Thu, 12 Oct 2023 13:34:40 +0200 Subject: [PATCH 06/14] bugfix --- src/Altinn.Notifications/Middleware/OrgExtractorMiddleware.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Altinn.Notifications/Middleware/OrgExtractorMiddleware.cs b/src/Altinn.Notifications/Middleware/OrgExtractorMiddleware.cs index 12bfee03..94a9972a 100644 --- a/src/Altinn.Notifications/Middleware/OrgExtractorMiddleware.cs +++ b/src/Altinn.Notifications/Middleware/OrgExtractorMiddleware.cs @@ -74,7 +74,7 @@ private static string GetIssuerOfAccessToken(string accessToken) private static bool ShouldApplyMiddleware(string path) { - return !(path.Contains("/token") || path.Contains("/health")); + return !(path.Contains("/trigger") || path.Contains("/health")); } } From be3d9386c7cce02336fc70c464554de1a4741d0c Mon Sep 17 00:00:00 2001 From: acn-sbuad Date: Thu, 12 Oct 2023 13:38:32 +0200 Subject: [PATCH 07/14] added scope to k6 tests --- test/k6/src/tests/orders_email.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/k6/src/tests/orders_email.js b/test/k6/src/tests/orders_email.js index 93ebe629..393adac2 100644 --- a/test/k6/src/tests/orders_email.js +++ b/test/k6/src/tests/orders_email.js @@ -20,7 +20,7 @@ const orderRequestJson = JSON.parse( ); import { generateJUnitXML, reportPath } from "../report.js"; import { addErrorCount, stopIterationOnFail } from "../errorhandler.js"; -const scopes = "none"; +const scopes = "altinn:notifications.create"; const emailRecipient = __ENV.emailRecipient.toLowerCase(); export const options = { From 457d458918496e837bf72ee2f3f53896e27bcde2 Mon Sep 17 00:00:00 2001 From: acn-sbuad Date: Thu, 12 Oct 2023 15:59:02 +0200 Subject: [PATCH 08/14] updated testdata after merge --- .../Altinn.Notifications.IntegrationTests.csproj | 5 ----- .../EmailNotificationOrdersControllerTests.cs | 2 +- .../TestingExtensions/ServiceCollectionExtensionsTests.cs | 2 +- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/test/Altinn.Notifications.IntegrationTests/Altinn.Notifications.IntegrationTests.csproj b/test/Altinn.Notifications.IntegrationTests/Altinn.Notifications.IntegrationTests.csproj index 50d154e1..c4c8612d 100644 --- a/test/Altinn.Notifications.IntegrationTests/Altinn.Notifications.IntegrationTests.csproj +++ b/test/Altinn.Notifications.IntegrationTests/Altinn.Notifications.IntegrationTests.csproj @@ -29,11 +29,6 @@ - - - - - Always diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/EmailNotificationOrdersControllerTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/EmailNotificationOrdersControllerTests.cs index bbdcfce1..2c7f0d65 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/EmailNotificationOrdersControllerTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/EmailNotificationOrdersControllerTests.cs @@ -235,7 +235,7 @@ public async Task Post_ValidScope_ServiceReturnsOrder_Accepted() public async Task Post_ValidAccessToken_ServiceReturnsOrder_Accepted() { // Arrange - string expectedFromAddress = "sender@domain.com"; + string expectedFromAddress = "noreply@altinn.no"; Mock serviceMock = new(); serviceMock.Setup(s => s.RegisterEmailNotificationOrder(It.IsAny())) diff --git a/test/Altinn.Notifications.Tests/Notifications.Integrations/TestingExtensions/ServiceCollectionExtensionsTests.cs b/test/Altinn.Notifications.Tests/Notifications.Integrations/TestingExtensions/ServiceCollectionExtensionsTests.cs index 81c1288c..88d931b4 100644 --- a/test/Altinn.Notifications.Tests/Notifications.Integrations/TestingExtensions/ServiceCollectionExtensionsTests.cs +++ b/test/Altinn.Notifications.Tests/Notifications.Integrations/TestingExtensions/ServiceCollectionExtensionsTests.cs @@ -28,7 +28,7 @@ public void AddKafkaHealthChecks_KafkaSettingsMissing_ThrowsException() { Environment.SetEnvironmentVariable("KafkaSettings", null); - var config = new ConfigurationBuilder().Build(); + var config = new ConfigurationBuilder().AddEnvironmentVariables().Build(); IServiceCollection services = new ServiceCollection() .AddLogging(); From 82c47b8243456886749eb3a544ec67834bebec5b Mon Sep 17 00:00:00 2001 From: acn-sbuad Date: Thu, 12 Oct 2023 16:11:56 +0200 Subject: [PATCH 09/14] failing test fixed --- .../EmailNotificationOrdersControllerTests.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/EmailNotificationOrdersControllerTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/EmailNotificationOrdersControllerTests.cs index 2c7f0d65..1f05b583 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/EmailNotificationOrdersControllerTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/EmailNotificationOrdersControllerTests.cs @@ -235,8 +235,6 @@ public async Task Post_ValidScope_ServiceReturnsOrder_Accepted() public async Task Post_ValidAccessToken_ServiceReturnsOrder_Accepted() { // Arrange - string expectedFromAddress = "noreply@altinn.no"; - Mock serviceMock = new(); serviceMock.Setup(s => s.RegisterEmailNotificationOrder(It.IsAny())) .Callback(orderRequest => @@ -246,7 +244,7 @@ public async Task Post_ValidAccessToken_ServiceReturnsOrder_Accepted() .FirstOrDefault(); Assert.NotNull(emailTemplate); - Assert.Equal(expectedFromAddress, emailTemplate.FromAddress); + Assert.Empty(emailTemplate.FromAddress); }) .ReturnsAsync((_order, null)); From f2967972518f2e92b3a5e131a2a6ea06eab37ef2 Mon Sep 17 00:00:00 2001 From: acn-sbuad Date: Mon, 16 Oct 2023 11:37:50 +0200 Subject: [PATCH 10/14] extension method for httpcontext --- .../EmailNotificationOrdersController.cs | 2 +- .../Controllers/OrdersController.cs | 7 ++++--- .../Extensions/HttpContextExtensions.cs | 15 +++++++++++++++ 3 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 src/Altinn.Notifications/Extensions/HttpContextExtensions.cs diff --git a/src/Altinn.Notifications/Controllers/EmailNotificationOrdersController.cs b/src/Altinn.Notifications/Controllers/EmailNotificationOrdersController.cs index ffecadf7..47a1f842 100644 --- a/src/Altinn.Notifications/Controllers/EmailNotificationOrdersController.cs +++ b/src/Altinn.Notifications/Controllers/EmailNotificationOrdersController.cs @@ -61,7 +61,7 @@ public async Task> Post(EmailNotificationOrderRequestEx return ValidationProblem(ModelState); } - string? creator = HttpContext.Items["Org"] as string; + string? creator = HttpContext.GetOrg(); if (creator == null) { diff --git a/src/Altinn.Notifications/Controllers/OrdersController.cs b/src/Altinn.Notifications/Controllers/OrdersController.cs index 4a387e45..ce443a9e 100644 --- a/src/Altinn.Notifications/Controllers/OrdersController.cs +++ b/src/Altinn.Notifications/Controllers/OrdersController.cs @@ -1,4 +1,5 @@ using Altinn.Notifications.Core.Services.Interfaces; +using Altinn.Notifications.Extensions; using Altinn.Notifications.Mappers; using Altinn.Notifications.Models; @@ -42,7 +43,7 @@ public OrdersController(IGetOrderService getOrderService) [SwaggerResponse(404, "No order with the provided id was found")] public async Task> GetById([FromRoute] Guid id) { - string? expectedCreator = HttpContext.Items["Org"] as string; + string? expectedCreator = HttpContext.GetOrg(); if (expectedCreator == null) { @@ -69,7 +70,7 @@ public async Task> GetById([FromRoute] Guid i [SwaggerResponse(200, "The list of notification orders matching the provided senders ref was retrieved successfully", typeof(NotificationOrderListExt))] public async Task> GetBySendersRef([FromQuery, BindRequired] string sendersReference) { - string? expectedCreator = HttpContext.Items["Org"] as string; + string? expectedCreator = HttpContext.GetOrg(); if (expectedCreator == null) { return Forbid(); @@ -97,7 +98,7 @@ public async Task> GetBySendersRef([FromQ [SwaggerResponse(404, "No order with the provided id was found")] public async Task> GetWithStatusById([FromRoute] Guid id) { - string? expectedCreator = HttpContext.Items["Org"] as string; + string? expectedCreator = HttpContext.GetOrg(); if (expectedCreator == null) { return Forbid(); diff --git a/src/Altinn.Notifications/Extensions/HttpContextExtensions.cs b/src/Altinn.Notifications/Extensions/HttpContextExtensions.cs new file mode 100644 index 00000000..46ef5870 --- /dev/null +++ b/src/Altinn.Notifications/Extensions/HttpContextExtensions.cs @@ -0,0 +1,15 @@ +namespace Altinn.Notifications.Extensions; + +/// +/// Extensions for HTTP Context +/// +public static class HttpContextExtensions +{ + /// + /// Get the org identifier string or null if it is not an org. + /// + public static string? GetOrg(this HttpContext context) + { + return context.Items["Org"] as string; + } +} From 1ea34f7c0de1a25be06587de5db43ece8386acc8 Mon Sep 17 00:00:00 2001 From: Stephanie Buadu <47737608+acn-sbuad@users.noreply.github.com> Date: Mon, 16 Oct 2023 11:39:00 +0200 Subject: [PATCH 11/14] Update HttpContextExtensions.cs --- src/Altinn.Notifications/Extensions/HttpContextExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Altinn.Notifications/Extensions/HttpContextExtensions.cs b/src/Altinn.Notifications/Extensions/HttpContextExtensions.cs index 46ef5870..4db71ded 100644 --- a/src/Altinn.Notifications/Extensions/HttpContextExtensions.cs +++ b/src/Altinn.Notifications/Extensions/HttpContextExtensions.cs @@ -6,7 +6,7 @@ public static class HttpContextExtensions { /// - /// Get the org identifier string or null if it is not an org. + /// Get the org string from the context items or null if it is not defined /// public static string? GetOrg(this HttpContext context) { From 5dd549ee2606b4e9a2d2ade3c6a959cb3ce91a94 Mon Sep 17 00:00:00 2001 From: acn-sbuad Date: Tue, 17 Oct 2023 11:16:37 +0200 Subject: [PATCH 12/14] same policy for GET order endpoints --- .../EmailNotificationOrdersController.cs | 2 +- .../Controllers/OrdersController.cs | 5 +- ...tinn.Notifications.IntegrationTests.csproj | 4 + .../OrdersController/GetByIdTests.cs | 4 +- .../OrdersController/GetBySendersRefTests.cs | 6 +- .../OrdersController/GetWithStatusById.cs | 6 +- .../OrdersController/OrdersControllerTests.cs | 236 +++++++++++++++++- 7 files changed, 243 insertions(+), 20 deletions(-) diff --git a/src/Altinn.Notifications/Controllers/EmailNotificationOrdersController.cs b/src/Altinn.Notifications/Controllers/EmailNotificationOrdersController.cs index 47a1f842..36ba52b5 100644 --- a/src/Altinn.Notifications/Controllers/EmailNotificationOrdersController.cs +++ b/src/Altinn.Notifications/Controllers/EmailNotificationOrdersController.cs @@ -22,6 +22,7 @@ namespace Altinn.Notifications.Controllers; /// [Route("notifications/api/v1/orders/email")] [ApiController] +[Authorize(Policy = AuthorizationConstants.POLICY_CREATE_SCOPE_OR_PLATFORM_ACCESS)] [SwaggerResponse(401, "Caller is unauthorized")] [SwaggerResponse(403, "Caller is not authorized to access the requested resource")] @@ -45,7 +46,6 @@ public EmailNotificationOrdersController(IValidator /// The id of the registered notification order - [Authorize(Policy = AuthorizationConstants.POLICY_CREATE_SCOPE_OR_PLATFORM_ACCESS)] [HttpPost] [Consumes("application/json")] [Produces("application/json")] diff --git a/src/Altinn.Notifications/Controllers/OrdersController.cs b/src/Altinn.Notifications/Controllers/OrdersController.cs index ce443a9e..17b1bf0b 100644 --- a/src/Altinn.Notifications/Controllers/OrdersController.cs +++ b/src/Altinn.Notifications/Controllers/OrdersController.cs @@ -1,4 +1,5 @@ -using Altinn.Notifications.Core.Services.Interfaces; +using Altinn.Notifications.Configuration; +using Altinn.Notifications.Core.Services.Interfaces; using Altinn.Notifications.Extensions; using Altinn.Notifications.Mappers; using Altinn.Notifications.Models; @@ -16,7 +17,7 @@ namespace Altinn.Notifications.Controllers; /// [Route("notifications/api/v1/orders")] [ApiController] -[Authorize] +[Authorize(Policy = AuthorizationConstants.POLICY_CREATE_SCOPE_OR_PLATFORM_ACCESS)] [SwaggerResponse(401, "Caller is unauthorized")] [SwaggerResponse(403, "Caller is not authorized to access the requested resource")] public class OrdersController : ControllerBase diff --git a/test/Altinn.Notifications.IntegrationTests/Altinn.Notifications.IntegrationTests.csproj b/test/Altinn.Notifications.IntegrationTests/Altinn.Notifications.IntegrationTests.csproj index c4c8612d..d1c2374f 100644 --- a/test/Altinn.Notifications.IntegrationTests/Altinn.Notifications.IntegrationTests.csproj +++ b/test/Altinn.Notifications.IntegrationTests/Altinn.Notifications.IntegrationTests.csproj @@ -8,6 +8,10 @@ false + + + + diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetByIdTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetByIdTests.cs index 779573ba..cebf5f4e 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetByIdTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetByIdTests.cs @@ -42,7 +42,7 @@ public async Task GetById_NoMatchInDb_ReturnsNotFound() string uri = $"{_basePath}/{Guid.NewGuid()}"; HttpClient client = GetTestClient(); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd")); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:notifications.create")); HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, uri); @@ -86,7 +86,7 @@ public async Task GetById_SingleMatchInDb_ReturnsOk() string uri = $"{_basePath}/{persistedOrder.Id}"; HttpClient client = GetTestClient(); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd")); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:notifications.create")); HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, uri); diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetBySendersRefTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetBySendersRefTests.cs index 1a74858e..357b90ba 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetBySendersRefTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetBySendersRefTests.cs @@ -41,7 +41,7 @@ public async Task GetBySendersRef_NoMatchInDb_ReturnsOK_EmptyList() string sendersReference = $"{_sendersRefBase}-{Guid.NewGuid()}"; HttpClient client = GetTestClient(); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd")); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:notifications.create")); string uri = $"{_basePath}?sendersReference={sendersReference}"; HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, uri); @@ -65,7 +65,7 @@ public async Task GetBySendersRef_SingleMatchInDb_ReturnsOk_SingleElementInlList NotificationOrder persistedOrder = await PostgreUtil.PopulateDBWithOrder(sendersReference: sendersReference); HttpClient client = GetTestClient(); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd")); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:notifications.create")); string uri = $"{_basePath}?sendersReference={sendersReference}"; HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, uri); @@ -92,7 +92,7 @@ public async Task GetBySendersRef_MultipleMatchInDb_ReturnsOk_MultipleElementInl await PostgreUtil.PopulateDBWithOrder(sendersReference: sendersReference); HttpClient client = GetTestClient(); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd")); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:notifications.create")); string uri = $"{_basePath}?sendersReference={sendersReference}"; HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, uri); diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetWithStatusById.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetWithStatusById.cs index d5f1fa5d..21e476d0 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetWithStatusById.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetWithStatusById.cs @@ -41,7 +41,7 @@ public async Task GetWithStatusById_NoMatchInDb_ReturnsNotFound() string uri = $"{_basePath}/{Guid.NewGuid()}/status"; HttpClient client = GetTestClient(); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd")); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:notifications.create")); HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, uri); @@ -91,7 +91,7 @@ public async Task GetWithStatusById_SingleMatchInDbAndOneEmail_ReturnsOk() string uri = $"{_basePath}/{persistedOrder.Id}/status"; HttpClient client = GetTestClient(); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd")); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:notifications.create")); HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, uri); @@ -130,7 +130,7 @@ public async Task GetWithStatusById_SingleMatchInDb_ReturnsOk() string uri = $"{_basePath}/{persistedOrder.Id}/status"; HttpClient client = GetTestClient(); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd")); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:notifications.create")); HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, uri); diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/OrdersControllerTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/OrdersControllerTests.cs index fd328562..e12aa17e 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/OrdersControllerTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/OrdersControllerTests.cs @@ -16,6 +16,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Logging; +using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Moq; @@ -29,6 +30,7 @@ public class OrdersControllerTests : IClassFixture _factory; private readonly NotificationOrder _order; + private readonly NotificationOrderWithStatus _orderWithStatus; public OrdersControllerTests(IntegrationTestWebApplicationFactory factory) { @@ -43,6 +45,15 @@ public OrdersControllerTests(IntegrationTestWebApplicationFactory()); + + _orderWithStatus = new( + Guid.NewGuid(), + "senders-reference", + DateTime.UtcNow, + new Creator("ttd"), + DateTime.UtcNow, + NotificationChannel.Email, + new ProcessingStatus()); } [Fact] @@ -77,6 +88,69 @@ public async Task GetBySendersRef_CalledByUser_ReturnsForbidden() Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); } + [Fact] + public async Task GetBySendersRef_CalledWithInvalidScope_ReturnsForbidden() + { + // Arrange + HttpClient client = GetTestClient(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "dummy:scope")); + + string url = _basePath + "?sendersReference=" + "internal-ref"; + HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, url); + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + + // Assert + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + } + + [Fact] + public async Task GetBySendersRef_ValidBearerToken_CorrespondingServiceMethodCalled() + { + // Arrange + var orderService = new Mock(); + orderService + .Setup(o => o.GetOrdersBySendersReference(It.Is(s => s.Equals("internal-ref")), It.Is(s => s.Equals("ttd")))) + .ReturnsAsync((new List() { _order }, null)); + + HttpClient client = GetTestClient(orderService.Object); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:notifications.create")); + + string url = _basePath + "?sendersReference=" + "internal-ref"; + HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, url); + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + + // Assert + orderService.VerifyAll(); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } + + [Fact] + public async Task GetBySendersRef_ValidPlatformAccessToken_CorrespondingServiceMethodCalled() + { + // Arrange + var orderService = new Mock(); + orderService + .Setup(o => o.GetOrdersBySendersReference(It.Is(s => s.Equals("internal-ref")), It.Is(s => s.Equals("ttd")))) + .ReturnsAsync((new List() { _order }, null)); + + HttpClient client = GetTestClient(orderService.Object); + + string url = _basePath + "?sendersReference=" + "internal-ref"; + HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, url); + httpRequestMessage.Headers.Add("PlatformAccessToken", PrincipalUtil.GetAccessToken("ttd", "apps-test")); + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + + // Assert + orderService.VerifyAll(); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } + [Fact] public async Task GetById_MissingBearer_ReturnsUnauthorized() { @@ -110,18 +184,37 @@ public async Task GetById_CalledByUser_ReturnsForbidden() } [Fact] - public async Task GetBySendersRef_CorrespondingServiceMethodCalled() + public async Task GetById_CalledWithInvalidScope_ReturnsForbidden() + { + // Arrange + HttpClient client = GetTestClient(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "dummy:scope")); + + string url = _basePath + "/" + Guid.NewGuid(); + HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, url); + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + + // Assert + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + } + + [Fact] + public async Task GetById_ValidBearerToken_CorrespondingServiceMethodCalled() { // Arrange + Guid orderId = Guid.NewGuid(); + var orderService = new Mock(); orderService - .Setup(o => o.GetOrdersBySendersReference(It.Is(s => s.Equals("internal-ref")), It.Is(s => s.Equals("ttd")))) - .ReturnsAsync((new List() { _order }, null)); + .Setup(o => o.GetOrderById(It.Is(g => g.Equals(orderId)), It.Is(s => s.Equals("ttd")))) + .ReturnsAsync((_order, null)); HttpClient client = GetTestClient(orderService.Object); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd")); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:notifications.create")); - string url = _basePath + "?sendersReference=" + "internal-ref"; + string url = _basePath + "/" + orderId; HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, url); // Act @@ -133,7 +226,7 @@ public async Task GetBySendersRef_CorrespondingServiceMethodCalled() } [Fact] - public async Task GetById_CorrespondingServiceMethodCalled() + public async Task GetById_ValidPlatformAccessToken_CorrespondingServiceMethodCalled() { // Arrange Guid orderId = Guid.NewGuid(); @@ -144,10 +237,10 @@ public async Task GetById_CorrespondingServiceMethodCalled() .ReturnsAsync((_order, null)); HttpClient client = GetTestClient(orderService.Object); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd")); string url = _basePath + "/" + orderId; HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, url); + httpRequestMessage.Headers.Add("PlatformAccessToken", PrincipalUtil.GetAccessToken("ttd", "apps-test")); // Act HttpResponseMessage response = await client.SendAsync(httpRequestMessage); @@ -158,7 +251,7 @@ public async Task GetById_CorrespondingServiceMethodCalled() } [Fact] - public async Task GetById_ServicerReturnsError_StatusCodeMatchesError() + public async Task GetById_ServiceReturnsError_StatusCodeMatchesError() { // Arrange Guid orderId = Guid.NewGuid(); @@ -169,7 +262,7 @@ public async Task GetById_ServicerReturnsError_StatusCodeMatchesError() .ReturnsAsync((null, new ServiceError(404))); HttpClient client = GetTestClient(orderService.Object); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd")); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:notifications.create")); string url = _basePath + "/" + orderId; HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, url); @@ -182,6 +275,131 @@ public async Task GetById_ServicerReturnsError_StatusCodeMatchesError() Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } + [Fact] + public async Task GetWithStatusById_MissingBearer_ReturnsUnauthorized() + { + // Arrange + HttpClient client = GetTestClient(); + string url = _basePath + "/" + Guid.NewGuid() + "/status"; + HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, url); + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + + // Assert + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + } + + [Fact] + public async Task GetWithStatusById_CalledByUser_ReturnsForbidden() + { + // Arrange + HttpClient client = GetTestClient(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetUserToken(1337)); + + string url = _basePath + "/" + Guid.NewGuid() + "/status"; + HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, url); + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + + // Assert + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + } + + [Fact] + public async Task GetWithStatusById_CalledWithInvalidScope_ReturnsForbidden() + { + // Arrange + HttpClient client = GetTestClient(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "dummy:scope")); + + string url = _basePath + "/" + Guid.NewGuid() + "/status"; + HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, url); + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + + // Assert + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + } + + [Fact] + public async Task GetWithStatusById_ValidBearerToken_CorrespondingServiceMethodCalled() + { + // Arrange + Guid orderId = Guid.NewGuid(); + + var orderService = new Mock(); + orderService + .Setup(o => o.GetOrderWithStatuById(It.Is(g => g.Equals(orderId)), It.Is(s => s.Equals("ttd")))) + .ReturnsAsync((_orderWithStatus, null)); + + HttpClient client = GetTestClient(orderService.Object); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:notifications.create")); + + string url = _basePath + "/" + orderId + "/status"; + HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, url); + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + + // Assert + orderService.VerifyAll(); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } + + [Fact] + public async Task GetWithStatusById_ValidPlatformAccessToken_CorrespondingServiceMethodCalled() + { + // Arrange + Guid orderId = Guid.NewGuid(); + + var orderService = new Mock(); + orderService + .Setup(o => o.GetOrderWithStatuById(It.Is(g => g.Equals(orderId)), It.Is(s => s.Equals("ttd")))) + .ReturnsAsync((_orderWithStatus, null)); + + HttpClient client = GetTestClient(orderService.Object); + + string url = _basePath + "/" + orderId + "/status"; + + HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, url); + httpRequestMessage.Headers.Add("PlatformAccessToken", PrincipalUtil.GetAccessToken("ttd", "apps-test")); + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + + // Assert + orderService.VerifyAll(); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } + + [Fact] + public async Task GetWithStatusById_ServiceReturnsError_StatusCodeMatchesError() + { + // Arrange + Guid orderId = Guid.NewGuid(); + + var orderService = new Mock(); + orderService + .Setup(o => o.GetOrderWithStatuById(It.Is(g => g.Equals(orderId)), It.Is(s => s.Equals("ttd")))) + .ReturnsAsync((null, new ServiceError(404))); + + HttpClient client = GetTestClient(orderService.Object); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:notifications.create")); + + string url = _basePath + "/" + orderId + "/status"; + HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, url); + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + + // Assert + orderService.VerifyAll(); + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + private HttpClient GetTestClient(IGetOrderService? orderService = null) { if (orderService == null) From 56e53e3980d469c1192d76a63b0082071c2d3312 Mon Sep 17 00:00:00 2001 From: Stephanie Buadu <47737608+acn-sbuad@users.noreply.github.com> Date: Tue, 17 Oct 2023 15:13:28 +0200 Subject: [PATCH 13/14] Update Altinn.Notifications.IntegrationTests.csproj --- .../Altinn.Notifications.IntegrationTests.csproj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/Altinn.Notifications.IntegrationTests/Altinn.Notifications.IntegrationTests.csproj b/test/Altinn.Notifications.IntegrationTests/Altinn.Notifications.IntegrationTests.csproj index d1c2374f..c4c8612d 100644 --- a/test/Altinn.Notifications.IntegrationTests/Altinn.Notifications.IntegrationTests.csproj +++ b/test/Altinn.Notifications.IntegrationTests/Altinn.Notifications.IntegrationTests.csproj @@ -8,10 +8,6 @@ false - - - - From 30823564463e1d9bfb62283954a5dd225f32b062 Mon Sep 17 00:00:00 2001 From: acn-sbuad Date: Wed, 18 Oct 2023 14:42:32 +0200 Subject: [PATCH 14/14] fixed swagger comment --- src/Altinn.Notifications/Controllers/OrdersController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Altinn.Notifications/Controllers/OrdersController.cs b/src/Altinn.Notifications/Controllers/OrdersController.cs index 17b1bf0b..ab09e8ad 100644 --- a/src/Altinn.Notifications/Controllers/OrdersController.cs +++ b/src/Altinn.Notifications/Controllers/OrdersController.cs @@ -95,7 +95,7 @@ public async Task> GetBySendersRef([FromQ [HttpGet] [Route("{id}/status")] [Produces("application/json")] - [SwaggerResponse(200, "The notification order matching the provided id was retrieved successfully", typeof(NotificationOrderExt))] + [SwaggerResponse(200, "The notification order matching the provided id was retrieved successfully", typeof(NotificationOrderWithStatusExt))] [SwaggerResponse(404, "No order with the provided id was found")] public async Task> GetWithStatusById([FromRoute] Guid id) {