Compare commits

...

2 commits

Author SHA1 Message Date
5401d29d44 Add product table management endpoints
Implement API endpoints for creating and retrieving product tables.
This allows administrators to define product tables and retrieve them by name.
The creation endpoint is secured and only accessible to administrators.
2025-08-11 19:47:34 +02:00
9ec9139f69 Enhance AdminOnly authorization policy
Refactor the AdminOnly authorization policy to handle cases where a user profile is not found.
Instead of throwing a NotFoundException, it now throws a ForbiddenException, ensuring a more appropriate response for unauthorized access attempts.
Also introduces PolicyConstants for policy names.
2025-08-11 19:47:12 +02:00
9 changed files with 109 additions and 4 deletions

View file

@ -0,0 +1,7 @@
namespace DrinkRateAPI.ApiModels.ProductTable;
public class ProductTableGet
{
public string ProductTableName { get; set; }
public string ProductTableId { get; set; }
}

View file

@ -0,0 +1,6 @@
namespace DrinkRateAPI.ApiModels.ProductTable;
public class ProductTablePost
{
public string ProductTableName { get; set; }
}

View file

@ -1,4 +1,5 @@
using DrinkRateAPI.DbEntities;
using DrinkRateAPI.Exceptions;
using DrinkRateAPI.Services;
namespace DrinkRateAPI.AuthorizationPolicies;
@ -26,7 +27,16 @@ public class AdminOnlyHandler : AuthorizationHandler<AdminOnlyRequirement>
AuthorizationHandlerContext context,
AdminOnlyRequirement requirement)
{
var userProfile = await _applicationUserService.UserProfileByApplicationUserAsync(context.User);
DbUserProfile userProfile;
try
{
userProfile = await _applicationUserService.UserProfileByApplicationUserAsync(context.User);
}
catch (NotFoundException _)
{
throw new ForbiddenException();
}
if (_userProfileService.IsUserProfileAdmin(userProfile))
{

View file

@ -0,0 +1,7 @@
namespace DrinkRateAPI.AuthorizationPolicies;
public static class PolicyConstants
{
public const string AdminOnly = "AdminOnly";
}

View file

@ -0,0 +1,35 @@
using DrinkRateAPI.ApiModels.ProductTable;
using DrinkRateAPI.AuthorizationPolicies;
using DrinkRateAPI.DbEntities;
using DrinkRateAPI.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace DrinkRateAPI.Controllers;
[ApiController]
[Route("productTable")]
public class ProductTableController : ControllerBase
{
private ProductTableService _productTableService;
public ProductTableController(ProductTableService productTableService)
{
_productTableService = productTableService;
}
[HttpPost]
[Authorize(Policy = PolicyConstants.AdminOnly)]
[Produces("application/json")]
public async Task<ProductTableGet> PostProductTable([FromBody] ProductTablePost productTable)
{
return await _productTableService.PostProductTableAsync(productTable);
}
[HttpGet("{productTableName}")]
[Produces("application/json")]
public async Task<ProductTableGet> GetProductTable([FromRoute] string productTableName)
{
return await _productTableService.GetProductTable(productTableName);
}
}

View file

@ -1,5 +1,6 @@
using System.Security.Claims;
using DrinkRateAPI.ApiModels.UserProfile;
using DrinkRateAPI.AuthorizationPolicies;
using DrinkRateAPI.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@ -28,7 +29,7 @@ public class UserProfileController : ControllerBase
}
[HttpPut("{userId}/adminStatus")]
[Authorize(Policy = "AdminOnly")]
[Authorize(Policy = PolicyConstants.AdminOnly)]
[Produces("application/json")]
public async Task<IActionResult> PutUserAdminStatus(string userId, [FromBody] UserProfileAdminStatusPut body)
{

View file

@ -20,7 +20,6 @@
</ItemGroup>
<ItemGroup>
<Folder Include="ApiModels\" />
<Folder Include="Migrations\" />
</ItemGroup>

View file

@ -15,7 +15,7 @@ builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddAuthorizationBuilder()
.AddPolicy("AdminOnly", policy =>
.AddPolicy(PolicyConstants.AdminOnly, policy =>
policy.Requirements.Add(new AdminOnlyRequirement()));
builder.Services.AddIdentityApiEndpoints<DbApplicationUser>()
.AddEntityFrameworkStores<ApplicationDbContext>();
@ -58,6 +58,7 @@ builder.Services.AddSwaggerGen(c =>
builder.Services.AddDbContext<ApplicationDbContext>();
builder.Services.AddScoped<ApplicationUserService>();
builder.Services.AddScoped<UserProfileService>();
builder.Services.AddScoped<ProductTableService>();
var app = builder.Build();

View file

@ -0,0 +1,39 @@
using DrinkRateAPI.ApiModels.ProductTable;
using DrinkRateAPI.Contexts;
using DrinkRateAPI.DbEntities;
using DrinkRateAPI.Exceptions;
using Microsoft.EntityFrameworkCore;
namespace DrinkRateAPI.Services;
public class ProductTableService(ApplicationDbContext context)
{
private ApplicationDbContext _context = context;
public async Task<ProductTableGet> PostProductTableAsync(ProductTablePost productTablePost)
{
DbProductTable productTable = new()
{
ProductTableName = productTablePost.ProductTableName
};
_context.ProductTable.Add(productTable);
await _context.SaveChangesAsync();
var productTableGet = await GetProductTable(productTable.ProductTableName);
return productTableGet;
}
public async Task<ProductTableGet> GetProductTable(string productTableName)
{
var productTable =
await _context.ProductTable.FirstOrDefaultAsync(x => x.ProductTableName == productTableName) ??
throw new NotFoundException();
return new ProductTableGet
{
ProductTableName = productTable.ProductTableName,
ProductTableId = productTable.Id.ToString()
};
}
}