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/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..36ba52b5 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,7 @@ namespace Altinn.Notifications.Controllers;
///
[Route("notifications/api/v1/orders/email")]
[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")]
@@ -60,7 +61,7 @@ public async Task> Post(EmailNotificationOrderRequestEx
return ValidationProblem(ModelState);
}
- string? creator = User.GetOrg();
+ string? creator = HttpContext.GetOrg();
if (creator == null)
{
diff --git a/src/Altinn.Notifications/Controllers/OrdersController.cs b/src/Altinn.Notifications/Controllers/OrdersController.cs
index b8ccaf3d..ab09e8ad 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
@@ -43,7 +44,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.GetOrg();
+
if (expectedCreator == null)
{
return Forbid();
@@ -69,7 +71,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.GetOrg();
if (expectedCreator == null)
{
return Forbid();
@@ -93,11 +95,11 @@ 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)
{
- string? expectedCreator = User.GetOrg();
+ string? expectedCreator = HttpContext.GetOrg();
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/Extensions/HttpContextExtensions.cs b/src/Altinn.Notifications/Extensions/HttpContextExtensions.cs
new file mode 100644
index 00000000..4db71ded
--- /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 string from the context items or null if it is not defined
+ ///
+ public static string? GetOrg(this HttpContext context)
+ {
+ return context.Items["Org"] as string;
+ }
+}
diff --git a/src/Altinn.Notifications/Middleware/OrgExtractorMiddleware.cs b/src/Altinn.Notifications/Middleware/OrgExtractorMiddleware.cs
new file mode 100644
index 00000000..94a9972a
--- /dev/null
+++ b/src/Altinn.Notifications/Middleware/OrgExtractorMiddleware.cs
@@ -0,0 +1,94 @@
+using System.IdentityModel.Tokens.Jwt;
+using System.Security.Claims;
+
+using AltinnCore.Authentication.Constants;
+
+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("/trigger") || path.Contains("/health"));
+ }
+}
+
+///
+/// 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/Altinn.Notifications.IntegrationTests.csproj b/test/Altinn.Notifications.IntegrationTests/Altinn.Notifications.IntegrationTests.csproj
index 410ca393..c4c8612d 100644
--- a/test/Altinn.Notifications.IntegrationTests/Altinn.Notifications.IntegrationTests.csproj
+++ b/test/Altinn.Notifications.IntegrationTests/Altinn.Notifications.IntegrationTests.csproj
@@ -29,10 +29,6 @@
-
-
-
-
Always
diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/EmailNotificationOrdersControllerTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/EmailNotificationOrdersControllerTests.cs
index 7dda8d21..1f05b583 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;
@@ -86,11 +87,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)
{
@@ -116,7 +132,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)
{
@@ -161,7 +177,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)
{
@@ -177,7 +193,7 @@ public async Task Post_ServiceReturnsError_ServerError()
}
[Fact]
- public async Task Post_ServiceReturnsOrder_Accepted()
+ public async Task Post_ValidScope_ServiceReturnsOrder_Accepted()
{
// Arrange
Mock serviceMock = new();
@@ -194,7 +210,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)
{
@@ -204,10 +220,49 @@ 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
+ Mock serviceMock = new();
+ serviceMock.Setup(s => s.RegisterEmailNotificationOrder(It.IsAny()))
+ .Callback(orderRequest =>
+ {
+ var emailTemplate = orderRequest.Templates
+ .OfType()
+ .FirstOrDefault();
+
+ Assert.NotNull(emailTemplate);
+ Assert.Empty(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());
@@ -234,7 +289,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()
{
@@ -296,8 +351,9 @@ 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();
});
}).CreateClient();
diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/PostTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/PostTests.cs
index 685ab806..63586f1f 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;
@@ -66,7 +67,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)
{
@@ -76,10 +77,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());
}
@@ -89,7 +90,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)
{
@@ -99,10 +100,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());
}
@@ -128,8 +129,9 @@ 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();
});
}).CreateClient();
diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetByIdTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetByIdTests.cs
index b6c8dea8..cebf5f4e 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;
@@ -41,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);
@@ -85,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);
@@ -120,8 +121,9 @@ 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();
});
}).CreateClient();
diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetBySendersRefTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/GetBySendersRefTests.cs
index 4e296b13..357b90ba 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;
@@ -40,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);
@@ -64,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);
@@ -91,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);
@@ -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 and authorization
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..21e476d0 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;
@@ -40,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);
@@ -90,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);
@@ -129,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);
@@ -164,8 +165,9 @@ 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();
});
}).CreateClient();
diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/OrdersControllerTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/OrdersControllerTests.cs
index f2f00233..e12aa17e 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;
@@ -15,6 +16,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Logging;
+using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Moq;
@@ -28,6 +30,7 @@ public class OrdersControllerTests : IClassFixture _factory;
private readonly NotificationOrder _order;
+ private readonly NotificationOrderWithStatus _orderWithStatus;
public OrdersControllerTests(IntegrationTestWebApplicationFactory factory)
{
@@ -42,6 +45,15 @@ public OrdersControllerTests(IntegrationTestWebApplicationFactory());
+
+ _orderWithStatus = new(
+ Guid.NewGuid(),
+ "senders-reference",
+ DateTime.UtcNow,
+ new Creator("ttd"),
+ DateTime.UtcNow,
+ NotificationChannel.Email,
+ new ProcessingStatus());
}
[Fact]
@@ -76,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()
{
@@ -109,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
@@ -132,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();
@@ -143,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);
@@ -157,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();
@@ -168,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);
@@ -181,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)
@@ -205,8 +424,9 @@ 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();
});
}).CreateClient();
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();
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.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();
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;
+ }
}
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 00000000..6da835fb
Binary files /dev/null and b/test/Altinn.Notifications.Tests/ttd-org.pfx differ
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 = {