-
Notifications
You must be signed in to change notification settings - Fork 232
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
25 changed files
with
649 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
using Microsoft.AspNetCore.Mvc; | ||
|
||
namespace Crexi.Auctions.API.Controllers | ||
{ | ||
[ApiController] | ||
[Route("[controller]")] | ||
public class AuctionsController : ControllerBase | ||
{ | ||
private readonly ILogger<AuctionsController> _logger; | ||
|
||
public AuctionsController(ILogger<AuctionsController> logger) | ||
{ | ||
_logger = logger; | ||
} | ||
|
||
[HttpGet] | ||
public int Get() | ||
{ | ||
return 1; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<Project Sdk="Microsoft.NET.Sdk.Web"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net6.0</TargetFramework> | ||
<Nullable>enable</Nullable> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\RateLimiter\Crexi.API.Common.RateLimiter.csproj" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.0" /> | ||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.2.1" /> | ||
</ItemGroup> | ||
|
||
</Project> |
41 changes: 41 additions & 0 deletions
41
Crexi.Auctions.API/Middleware/RateLimiter/RateLimiterConfigurator.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
using Crexi.API.Common.RateLimiter.Interfaces; | ||
using Crexi.API.Common.RateLimiter.Rules; | ||
|
||
namespace Crexi.Auctions.API.Middleware.RateLimiter | ||
{ | ||
public static class RateLimiterConfigurator | ||
{ | ||
public static void ConfigureRateLimitingRules(IRateLimiter rateLimiter) | ||
{ | ||
var usRateLimitRule = new FixedWindowRateLimitRule( | ||
maxRequestsPerClient: 10, | ||
timeWindow: TimeSpan.FromSeconds(10) | ||
); | ||
|
||
var euRateLimitRule = new TimeSinceLastCallRule( | ||
requiredInterval: TimeSpan.FromSeconds(2) | ||
); | ||
|
||
var usConditionalRule = new ConditionalRateLimitRule( | ||
condition: client => client.Location == "US", | ||
rule: usRateLimitRule | ||
); | ||
|
||
var euConditionalRule = new ConditionalRateLimitRule( | ||
condition: client => client.Location == "EU", | ||
rule: euRateLimitRule | ||
); | ||
|
||
var compositeRule = new CompositeRateLimitRule(new[] | ||
{ | ||
usConditionalRule, | ||
euConditionalRule | ||
}); | ||
|
||
rateLimiter.ConfigureResource("/Auctions", compositeRule); | ||
|
||
var anotherRule = new FixedWindowRateLimitRule(50, TimeSpan.FromMinutes(10)); | ||
rateLimiter.ConfigureResource("/AuctionSettings", anotherRule); | ||
} | ||
} | ||
} |
42 changes: 42 additions & 0 deletions
42
Crexi.Auctions.API/Middleware/RateLimiter/RateLimiterMiddleware.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
using Crexi.API.Common.RateLimiter.Interfaces; | ||
|
||
public class RateLimiterMiddleware | ||
{ | ||
private readonly RequestDelegate _next; | ||
private readonly IRateLimiter _rateLimiter; | ||
private readonly ITokenToClientConverter _tokenToClientConverter; | ||
private readonly ITokenExtractor _tokenExtractor; | ||
|
||
public RateLimiterMiddleware(RequestDelegate next, IRateLimiter rateLimiter, ITokenToClientConverter tokenToClientConverter, ITokenExtractor tokenExtractor) | ||
{ | ||
_next = next; | ||
_rateLimiter = rateLimiter; | ||
_tokenToClientConverter = tokenToClientConverter; | ||
_tokenExtractor = tokenExtractor; | ||
} | ||
|
||
public async Task InvokeAsync(HttpContext context) | ||
{ | ||
var accessToken = _tokenExtractor.ExtractToken(context); | ||
|
||
var client = _tokenToClientConverter.ConvertTokenToClient(accessToken); | ||
|
||
if (client == null) | ||
{ | ||
context.Response.StatusCode = StatusCodes.Status401Unauthorized; | ||
await context.Response.WriteAsync("Invalid or malformed access token."); | ||
return; | ||
} | ||
|
||
var resource = context.Request.Path.Value; | ||
|
||
if (!_rateLimiter.IsRequestAllowed(client, resource)) | ||
{ | ||
context.Response.StatusCode = StatusCodes.Status429TooManyRequests; | ||
await context.Response.WriteAsync("Rate limit exceeded."); | ||
return; | ||
} | ||
|
||
await _next(context); | ||
} | ||
} |
5 changes: 5 additions & 0 deletions
5
Crexi.Auctions.API/Middleware/RateLimiter/Util/ITokenExtractor.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
| ||
public interface ITokenExtractor | ||
{ | ||
string ExtractToken(HttpContext context); | ||
} |
6 changes: 6 additions & 0 deletions
6
Crexi.Auctions.API/Middleware/RateLimiter/Util/ITokenToClientConverter.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
using Crexi.API.Common.RateLimiter.Models; | ||
|
||
public interface ITokenToClientConverter | ||
{ | ||
Client ConvertTokenToClient(string token); | ||
} |
13 changes: 13 additions & 0 deletions
13
Crexi.Auctions.API/Middleware/RateLimiter/Util/TokenExtractor.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
public class TokenExtractor : ITokenExtractor | ||
{ | ||
public string ExtractToken(HttpContext context) | ||
{ | ||
if (context.Request.Headers.TryGetValue("Authorization", out var authorizationHeader)) | ||
{ | ||
var token = authorizationHeader.ToString().Split(' ').Last(); | ||
return token; | ||
} | ||
|
||
return null; | ||
} | ||
} |
30 changes: 30 additions & 0 deletions
30
Crexi.Auctions.API/Middleware/RateLimiter/Util/TokenToClientConverter.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
using Crexi.API.Common.RateLimiter.Models; | ||
using System.IdentityModel.Tokens.Jwt; | ||
|
||
public class TokenToClientConverter : ITokenToClientConverter | ||
{ | ||
public Client ConvertTokenToClient(string token) | ||
{ | ||
try | ||
{ | ||
var handler = new JwtSecurityTokenHandler(); | ||
var jwtToken = handler.ReadJwtToken(token); | ||
|
||
// Extract client ID and location from token claims | ||
var clientId = jwtToken.Claims.FirstOrDefault(c => c.Type == "client_id")?.Value; | ||
var clientLocation = jwtToken.Claims.FirstOrDefault(c => c.Type == "location")?.Value; | ||
|
||
if (string.IsNullOrEmpty(clientId) || string.IsNullOrEmpty(clientLocation)) | ||
{ | ||
return null; | ||
} | ||
|
||
return new Client(clientId, clientLocation); | ||
} | ||
catch (Exception ex) | ||
{ | ||
// TODO logging | ||
return null; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
using Crexi.API.Common.RateLimiter; | ||
using Crexi.API.Common.RateLimiter.Interfaces; | ||
using Crexi.Auctions.API.Middleware.RateLimiter; | ||
using Microsoft.OpenApi.Models; | ||
|
||
var builder = WebApplication.CreateBuilder(args); | ||
|
||
// Register services | ||
builder.Services.AddControllers(); | ||
builder.Services.AddEndpointsApiExplorer(); | ||
builder.Services.AddSwaggerGen(c => | ||
{ | ||
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Crexi Auction API", Version = "v1" }); | ||
|
||
// Add the security definition for Bearer tokens | ||
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme | ||
{ | ||
Description = "JWT Authorization header using the Bearer scheme. Example: \"Bearer {token}\"", | ||
Name = "Authorization", | ||
In = ParameterLocation.Header, | ||
Type = SecuritySchemeType.ApiKey, | ||
Scheme = "Bearer" | ||
}); | ||
|
||
// Add the security requirement to include the Bearer token globally | ||
c.AddSecurityRequirement(new OpenApiSecurityRequirement() | ||
{ | ||
{ | ||
new OpenApiSecurityScheme | ||
{ | ||
Reference = new OpenApiReference | ||
{ | ||
Type = ReferenceType.SecurityScheme, | ||
Id = "Bearer" | ||
}, | ||
Scheme = "oauth2", | ||
Name = "Bearer", | ||
In = ParameterLocation.Header, | ||
}, | ||
new List<string>() | ||
} | ||
}); | ||
}); | ||
|
||
builder.Services.AddSingleton<IRateLimiter, RateLimiter>(); | ||
builder.Services.AddTransient<ITokenToClientConverter, TokenToClientConverter>(); | ||
builder.Services.AddTransient<ITokenExtractor, TokenExtractor>(); | ||
|
||
var app = builder.Build(); | ||
|
||
if (app.Environment.IsDevelopment()) | ||
{ | ||
app.UseSwagger(); | ||
app.UseSwaggerUI(); | ||
} | ||
|
||
app.UseHttpsRedirection(); | ||
|
||
app.UseAuthentication(); | ||
app.UseAuthorization(); | ||
|
||
RateLimiterConfigurator.ConfigureRateLimitingRules(app.Services.GetRequiredService<IRateLimiter>()); | ||
app.UseMiddleware<RateLimiterMiddleware>(); | ||
|
||
app.MapControllers(); | ||
|
||
app.Run(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
{ | ||
"$schema": "https://json.schemastore.org/launchsettings.json", | ||
"iisSettings": { | ||
"windowsAuthentication": false, | ||
"anonymousAuthentication": true, | ||
"iisExpress": { | ||
"applicationUrl": "http://localhost:42884", | ||
"sslPort": 44366 | ||
} | ||
}, | ||
"profiles": { | ||
"Crexi.Auctions.API": { | ||
"commandName": "Project", | ||
"dotnetRunMessages": true, | ||
"launchBrowser": true, | ||
"launchUrl": "swagger", | ||
"applicationUrl": "https://localhost:7145;http://localhost:5071", | ||
"environmentVariables": { | ||
"ASPNETCORE_ENVIRONMENT": "Development" | ||
} | ||
}, | ||
"IIS Express": { | ||
"commandName": "IISExpress", | ||
"launchBrowser": true, | ||
"launchUrl": "swagger", | ||
"environmentVariables": { | ||
"ASPNETCORE_ENVIRONMENT": "Development" | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"Logging": { | ||
"LogLevel": { | ||
"Default": "Information", | ||
"Microsoft.AspNetCore": "Warning" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"Logging": { | ||
"LogLevel": { | ||
"Default": "Information", | ||
"Microsoft.AspNetCore": "Warning" | ||
} | ||
}, | ||
"AllowedHosts": "*" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.