Skip to content

Commit 356dbba

Browse files
first user
1 parent 8e907b5 commit 356dbba

5 files changed

Lines changed: 80 additions & 3 deletions

File tree

csharp/recipes_2026/challanges/Recipes/src/Recipes.Api/Controllers/AccountController.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,17 @@ public async Task<IActionResult> Register([FromBody] RegisterRequest req)
6262
{
6363
try
6464
{
65-
await m_userService.RegisterUserAsync(req.UserName, req.Password, isAdmin: false);
65+
66+
if (await m_userService.IsFirstUserAsync())
67+
{
68+
// Automatically create as admin
69+
await m_userService.RegisterFirstAdminAsync(req.UserName, req.Password);
70+
}
71+
else
72+
{
73+
74+
await m_userService.RegisterUserAsync(req.UserName, req.Password, isAdmin: false);
75+
}
6676
return NoContent();
6777
}
6878
catch (ArgumentException ex)
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using Microsoft.AspNetCore.Authorization;
2+
3+
namespace Recipes.Api.Extensions;
4+
5+
public static class AuthorizationExtensions
6+
{
7+
public static IServiceCollection AddAuthorizationPolicies(this IServiceCollection services)
8+
{
9+
services.AddAuthorization(options =>
10+
{
11+
// Admin Only Policy
12+
options.AddPolicy("AdminOnly", policy =>
13+
{
14+
policy.RequireAuthenticatedUser();
15+
policy.RequireRole("Admin");
16+
});
17+
18+
// User or Admin Policy
19+
options.AddPolicy("UserOrAdmin", policy =>
20+
{
21+
policy.RequireAuthenticatedUser();
22+
policy.RequireRole("User", "Admin");
23+
});
24+
25+
// Moderator Policy
26+
options.AddPolicy("ModeratorOnly", policy =>
27+
{
28+
policy.RequireAuthenticatedUser();
29+
policy.RequireRole("Moderator");
30+
});
31+
32+
// Staff Policy (Admin or Moderator)
33+
options.AddPolicy("StaffOnly", policy =>
34+
{
35+
policy.RequireAuthenticatedUser();
36+
policy.RequireRole("Admin", "Moderator");
37+
});
38+
39+
// Email Verified Policy
40+
options.AddPolicy("EmailVerified", policy =>
41+
{
42+
policy.RequireAuthenticatedUser();
43+
policy.RequireClaim("EmailVerified", "true");
44+
});
45+
46+
// Custom Permission Policy
47+
options.AddPolicy("CanManageUsers", policy =>
48+
{
49+
policy.RequireAuthenticatedUser();
50+
policy.RequireClaim("Permission", "ManageUsers");
51+
});
52+
53+
// Multiple Requirements Policy
54+
options.AddPolicy("AdminWithVerifiedEmail", policy =>
55+
{
56+
policy.RequireAuthenticatedUser();
57+
policy.RequireRole("Admin");
58+
policy.RequireClaim("EmailVerified", "true");
59+
});
60+
});
61+
62+
return services;
63+
}
64+
}

csharp/recipes_2026/challanges/Recipes/src/Recipes.Api/Security/JwtAuthExtensions.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using Microsoft.AspNetCore.Authentication.JwtBearer;
44
using Microsoft.Extensions.DependencyInjection;
55
using Microsoft.IdentityModel.Tokens;
6+
using Recipes.Api.Extensions;
67

78
namespace Recipes.Api.Security;
89

@@ -38,7 +39,9 @@ public static IServiceCollection AddJwtAuth(this IServiceCollection services, Jw
3839
};
3940
});
4041

41-
services.AddAuthorization();
42+
//services.AddAuthorization();
43+
services.AddAuthorizationPolicies();
44+
4245

4346
// optional: expose config for other services/controllers
4447
services.AddSingleton(jwt);

csharp/recipes_2026/challanges/Recipes/src/Recipes.Api/Services/RecipeAssistantService.cs

Whitespace-only changes.

csharp/recipes_2026/challanges/Recipes/src/Recipes.Api/Services/UserService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public async Task<AppUser> RegisterUserAsync(string userName, string rawPassword
5757
var user = new AppUser
5858
{
5959
UserName = userName,
60-
Roles = isAdmin ? "ADMIN,USER" : "USER",
60+
Roles = isAdmin ? "Admin,User" : "User",
6161
Enabled = true
6262
};
6363

0 commit comments

Comments
 (0)