Skip to content

Commit 48ead3e

Browse files
committed
Configured rate limiting & security in Program.cs
1 parent d33a844 commit 48ead3e

File tree

4 files changed

+144
-4
lines changed

4 files changed

+144
-4
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using Microsoft.AspNetCore.Authorization;
2+
using Microsoft.AspNetCore.Mvc;
3+
4+
namespace Streetcode.WebApi.Controllers
5+
{
6+
[Route("admin")]
7+
[Authorize(Roles = "Admin")]
8+
public class AdminController : Controller
9+
{
10+
// Admin Panel Access
11+
public IActionResult AdminPanel()
12+
{
13+
return View(); // Admin panel logic
14+
}
15+
16+
// For Unauthorized Users
17+
[AllowAnonymous]
18+
public IActionResult UnauthorizedAccess()
19+
{
20+
return NotFound(); // Return 404 page for unauthorized users
21+
}
22+
}
23+
}

Streetcode/Streetcode.WebApi/Controllers/Authentication/AuthController.cs

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,15 @@
1414

1515
namespace Streetcode.WebApi.Controllers.Authentication
1616
{
17+
public static class Roles
18+
{
19+
public const string Admin = "Admin";
20+
public const string User = "User";
21+
}
22+
1723
[ApiController]
18-
[EnableRateLimiting("api")] // Apply rate limiting to all endpoints
24+
[EnableRateLimiting("api")] // General rate limiting for all endpoints
25+
[Authorize] // Require authentication by default
1926
[Route("api/[controller]")]
2027
public class AuthController : BaseApiController
2128
{
@@ -26,27 +33,46 @@ public AuthController(ILogger<AuthController> logger)
2633
_logger = logger;
2734
}
2835

36+
[AllowAnonymous] // Explicitly allow unauthenticated users
2937
[HttpPost("login")]
3038
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(LoginResponseDTO))]
3139
public async Task<IActionResult> Login([FromBody] LoginRequestDTO loginDTO)
3240
{
33-
_logger.LogInformation("Login attempt for user: {Email}", loginDTO.Email);
41+
_logger.LogInformation("Login attempt received.");
3442
return HandleResult(await Mediator.Send(new LoginQuery(loginDTO)));
3543
}
3644

45+
[AllowAnonymous]
3746
[HttpPost("register")]
47+
[EnableRateLimiting("registration")] // Stricter rate limiting for registration
48+
[ValidateAntiForgeryToken] // CSRF protection
3849
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(RegisterResponseDTO))]
3950
public async Task<IActionResult> Register([FromBody] RegisterRequestDTO registerDTO)
4051
{
41-
_logger.LogInformation("New user registration attempt: {Email}", registerDTO.Email);
52+
_logger.LogInformation("New user registration attempt received."); // No PII logging
53+
54+
if (!ModelState.IsValid)
55+
{
56+
return BadRequest(ModelState);
57+
}
58+
4259
return HandleResult(await Mediator.Send(new RegisterQuery(registerDTO)));
4360
}
4461

62+
[AllowAnonymous]
4563
[HttpPost("refresh-token")]
64+
[EnableRateLimiting("token-refresh")] // Stricter rate limiting for token refresh
65+
[ValidateAntiForgeryToken] // CSRF protection
4666
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(RefreshTokenResponceDTO))]
4767
public async Task<IActionResult> RefreshToken([FromBody] RefreshTokenRequestDTO token)
4868
{
4969
_logger.LogInformation("Refresh token attempt.");
70+
71+
if (!ModelState.IsValid)
72+
{
73+
return BadRequest(ModelState);
74+
}
75+
5076
return HandleResult(await Mediator.Send(new RefreshTokenQuery(token)));
5177
}
5278

@@ -75,6 +101,7 @@ public async Task<IActionResult> Logout()
75101
return Ok("Logout successful. Refresh token invalidated.");
76102
}
77103

104+
[AllowAnonymous]
78105
[HttpPost("google-login")]
79106
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(LoginResponseDTO))]
80107
public async Task<IActionResult> GoogleLogin([FromBody] string idToken)
@@ -90,5 +117,15 @@ public async Task<IActionResult> GoogleLogin([FromBody] string idToken)
90117
_logger.LogWarning("Google login failed.");
91118
return Unauthorized(new { message = result.Errors.FirstOrDefault()?.Message });
92119
}
120+
121+
// Admin-only endpoint to retrieve users
122+
[Authorize(Roles = Roles.Admin)]
123+
[HttpGet("users")]
124+
public async Task<IActionResult> GetUsers()
125+
{
126+
_logger.LogInformation("Admin accessing user list.");
127+
// Implementation of user retrieval logic here
128+
return Ok(new { message = "User list retrieved successfully." });
129+
}
93130
}
94131
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using Microsoft.AspNetCore.Authorization;
2+
using Microsoft.AspNetCore.Mvc;
3+
4+
namespace Streetcode.WebApi.Controllers
5+
{
6+
[Route("api/[controller]")]
7+
[ApiController]
8+
public class ProfileController : ControllerBase
9+
{
10+
[HttpGet]
11+
[Authorize]
12+
public IActionResult GetProfile()
13+
{
14+
var currentUser = User.Identity.Name; // Or use user ID to identify
15+
16+
if (User.IsInRole("Admin"))
17+
{
18+
return NotFound(); // Return 404 if admin attempts to access their profile
19+
}
20+
21+
// Logic for retrieving the profile goes here
22+
return Ok("Profile information");
23+
}
24+
25+
[HttpPut]
26+
[Authorize]
27+
public IActionResult UpdateProfile([FromBody] string newProfileInfo)
28+
{
29+
var currentUser = User.Identity.Name;
30+
31+
if (User.IsInRole("Admin"))
32+
{
33+
return NotFound(); // Return 404 if admin attempts to update their profile
34+
}
35+
36+
// Logic for updating the profile goes here
37+
return Ok("Profile updated successfully");
38+
}
39+
}
40+
}

Streetcode/Streetcode.WebApi/Program.cs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@
1414

1515
builder.Host.ConfigureApplication(builder);
1616

17+
// Localization
1718
builder.Services.AddLocalization(option => option.ResourcesPath = "Resources");
19+
20+
// Add application services
1821
builder.Services.AddApplicationServices(builder.Configuration);
1922
builder.Services.AddSwaggerServices();
2023
builder.Services.AddCustomServices();
@@ -25,13 +28,35 @@
2528
builder.Services.ConfigureRequestResponseMiddlewareOptions(builder);
2629
builder.Services.ConfigureRateLimitMiddleware(builder);
2730
builder.Services.ConfigureResponseCompressingMiddleware(builder);
31+
32+
// Configure forwarded headers
2833
builder.Services.Configure<ForwardedHeadersOptions>(options =>
2934
{
3035
options.ForwardedHeaders =
3136
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
3237
});
38+
39+
// ✅ **Enhanced Rate Limiting Configuration**
3340
builder.Services.AddRateLimiter(options =>
3441
{
42+
options.AddFixedWindowLimiter("api", opt =>
43+
{
44+
opt.Window = TimeSpan.FromMinutes(1);
45+
opt.PermitLimit = 100; // General API: 100 requests per minute
46+
});
47+
48+
options.AddFixedWindowLimiter("registration", opt =>
49+
{
50+
opt.Window = TimeSpan.FromMinutes(10);
51+
opt.PermitLimit = 5; // Registration: 5 attempts per 10 minutes
52+
});
53+
54+
options.AddFixedWindowLimiter("token-refresh", opt =>
55+
{
56+
opt.Window = TimeSpan.FromMinutes(5);
57+
opt.PermitLimit = 10; // Token Refresh: 10 attempts per 5 minutes
58+
});
59+
3560
options.AddPolicy("EmailRateLimit", context => RateLimitPartition.GetFixedWindowLimiter(
3661
partitionKey: context.User.Identity?.Name ?? context.Request.Headers.Host.ToString(),
3762
factory: _ => new FixedWindowRateLimiterOptions
@@ -42,19 +67,22 @@
4267
Window = TimeSpan.FromMinutes(5)
4368
}));
4469

70+
// Global Rate Limiting Handler
4571
options.OnRejected = async (context, token) =>
4672
{
4773
context.HttpContext.Response.StatusCode = 429;
4874
await context.HttpContext.Response.WriteAsync("Too many requests.", cancellationToken: token);
4975
};
5076
});
5177

78+
// Add HttpClient
5279
builder.Services.AddHttpClient();
5380

5481
var app = builder.Build();
5582

5683
app.UseForwardedHeaders();
5784

85+
// ✅ **Enhanced Localization**
5886
var supportedCulture = new[]
5987
{
6088
new CultureInfo("en-US"),
@@ -67,6 +95,8 @@
6795
SupportedUICultures = supportedCulture,
6896
ApplyCurrentCultureToResponseHeaders = true
6997
});
98+
99+
// ✅ **Swagger only for non-production**
70100
if (app.Environment.EnvironmentName != "Production")
71101
{
72102
app.UseSwagger();
@@ -77,10 +107,14 @@
77107
app.UseHsts();
78108
}
79109

110+
// Apply migrations
80111
await app.ApplyMigrations();
81112

113+
// Hangfire jobs
82114
app.AddCleanAudiosJob();
83115
app.AddCleanImagesJob();
116+
117+
// ✅ **Security & Middleware**
84118
app.UseCors();
85119
app.UseHttpsRedirection();
86120
app.UseRequestResponseMiddleware();
@@ -92,20 +126,26 @@
92126
Authorization = new[] { new HangfireDashboardAuthorizationFilter() }
93127
});
94128

129+
// ✅ **Enable Rate Limiting & IP Rate Limiting**
95130
app.UseIpRateLimiting();
96131
app.UseRateLimiter();
97132

133+
// ✅ **Background Jobs**
98134
BackgroundJob.Schedule<WebParsingUtils>(
99135
wp => wp.ParseZipFileFromWebAsync(), TimeSpan.FromMinutes(1));
100136
RecurringJob.AddOrUpdate<WebParsingUtils>(
101137
"ParseZipFileFromWebAsync",
102138
wp => wp.ParseZipFileFromWebAsync(),
103139
Cron.Monthly);
104140

141+
// ✅ **Ensure All Controllers Are Mapped**
105142
app.MapControllers();
143+
144+
// ✅ **Custom Response Compression Middleware**
106145
app.UseMiddleware<CustomResponseCompressionMiddleware>();
107146

108147
app.Run();
148+
109149
public partial class Program
110150
{
111-
}
151+
}

0 commit comments

Comments
 (0)