Skip to content

Commit

Permalink
Merge pull request #164 from SakuraIsayeki/develop
Browse files Browse the repository at this point in the history
v0.17.4.1 - Add user atomic lock
  • Loading branch information
SakuraIsayeki committed May 10, 2024
2 parents 26875c2 + ff664be commit 6f43217
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<PackageReference Include="JetBrains.Annotations" Version="[2023.3,)" />
<PackageReference Include="Microsoft.Extensions.Http" Version="[8.0.0,)" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="[8.0.0,)" />
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="[7.2.0,)" />
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="[7.5.0,)" />
<PackageReference Include="System.Net.Http.Json" Version="[8.0.0,)" />
</ItemGroup>

Expand Down
2 changes: 1 addition & 1 deletion WowsKarma.Api/Controllers/PostController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ public IActionResult GetLatestPosts(
/// <response code="422">Attached replay is invalid.</response>
/// <response code="403">Restrictions are in effect for one of the targeted accounts.</response>
/// <response code="404">One of the targeted accounts was not found.</response>
[HttpPost, Authorize(RequireNoPlatformBans)]
[HttpPost, Authorize(RequireNoPlatformBans), UserAtomicLock]
[ProducesResponseType(201), ProducesResponseType(400), ProducesResponseType(422), ProducesResponseType(typeof(string), 403), ProducesResponseType(typeof(string), 404)]
public async Task<IActionResult> CreatePost(
[FromForm] string postDto,
Expand Down
10 changes: 10 additions & 0 deletions WowsKarma.Api/Infrastructure/Attributes/UserAtomicLockAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace WowsKarma.Api.Infrastructure.Attributes;

/// <summary>
/// Provides an attribute to lock concurrency on a given action to a given user.
/// </summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class UserAtomicLockAttribute : Attribute
{
public UserAtomicLockAttribute() { }
}
64 changes: 64 additions & 0 deletions WowsKarma.Api/Middlewares/UserAtomicLockMiddleware.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using System.Collections.Concurrent;
using WowsKarma.Api.Infrastructure.Attributes;
using WowsKarma.Common;

namespace WowsKarma.Api.Middlewares;

/// <summary>
/// Provides a middleware to lock a given action to a given user.
/// </summary>
public class UserAtomicLockMiddleware : IMiddleware
{
private static readonly ConcurrentDictionary<Tuple<string, uint>, object> Locks = new();
private static object ConcurrencyLock = new();

/// <inheritdoc />
public async Task InvokeAsync(HttpContext ctx, RequestDelegate next)
{
// Get the current user and endpoint
uint? uid = ctx.User.ToAccountListing()?.Id;
PathString path = ctx.Request.Path;

if (uid is null)
{
// Pass through.
await next(ctx);
return;
}

if (ctx.GetEndpoint()?.Metadata.GetMetadata<UserAtomicLockAttribute>() is null)
{
// Pass through.
await next(ctx);
return;
}

bool lockExists;
// lock (ConcurrencyLock)
// {
lockExists = Locks.TryGetValue(new(path, uid.Value), out _) || !Locks.TryAdd(new(path, uid.Value), new());
// }

// Get or try to add the lock object.
if (lockExists)
{
// Lock is already taken.
ctx.Response.StatusCode = StatusCodes.Status429TooManyRequests;
return;
}

// Hold the lock for the request's duration.
try
{
await next(ctx);
}
finally
{
// lock (ConcurrencyLock)
// {
// Release the lock.
Locks.TryRemove(new(path, uid.Value), out _);
// }
}
}
}
7 changes: 5 additions & 2 deletions WowsKarma.Api/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
using System.IdentityModel.Tokens.Jwt;
using System.Net;
using System.Reflection;
using System.Text;
using System.Text;
using Hangfire;
using Hangfire.PostgreSql;
using Hangfire.Tags.PostgreSql;
Expand Down Expand Up @@ -280,6 +280,8 @@ public void ConfigureServices(IServiceCollection services)
services.AddResiliencePolicies();

services.AddSystemd();

services.AddSingleton<UserAtomicLockMiddleware>();
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
Expand Down Expand Up @@ -334,7 +336,8 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
app.UseAuthorization();

app.UseMiddleware<RequestLoggingMiddleware>();

app.UseMiddleware<UserAtomicLockMiddleware>();

app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
Expand Down
18 changes: 9 additions & 9 deletions WowsKarma.Api/WowsKarma.Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>

<Version>0.17.4</Version>
<Version>0.17.4.1</Version>
<Authors>Sakura Akeno Isayeki</Authors>
<Company>Nodsoft Systems</Company>
<Product>WOWS Karma (API)</Product>
Expand All @@ -28,32 +28,32 @@
<PackageReference Include="DSharpPlus" Version="4.4.6" />
<PackageReference Include="ExpressionDebugger" Version="2.2.1" />
<PackageReference Include="FlexLabs.EntityFrameworkCore.Upsert" Version="8.0.0" />
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.11" />
<PackageReference Include="Hangfire.PostgreSql" Version="1.20.6" />
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.12" />
<PackageReference Include="Hangfire.PostgreSql" Version="1.20.8" />
<PackageReference Include="Hangfire.Tags.PostgreSql" Version="1.9.6" />
<PackageReference Include="JetBrains.Annotations" Version="2023.3.0" />
<PackageReference Include="Mapster" Version="7.4.0" />
<PackageReference Include="Mapster.DependencyInjection" Version="1.0.1" />
<PackageReference Include="Mapster.EFCore" Version="5.1.1" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.22.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.2" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="8.0.1" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="8.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.4" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="8.0.4" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="8.0.4" />
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="8.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.2">
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="8.0.0" />
<PackageReference Include="NodaTime" Version="3.1.9" />
<PackageReference Include="NodaTime" Version="3.1.11" />
<PackageReference Include="Nodsoft.Wargaming.Api.Client" Version="0.3.5" />
<PackageReference Include="Nodsoft.WowsReplaysUnpack.ExtendedData" Version="2.0.4" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.2" />
<PackageReference Include="Polly.Extensions" Version="8.3.0" />
<PackageReference Include="Polly.Extensions" Version="8.3.1" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.1" />
<PackageReference Include="Serilog.Extensions.Logging" Version="8.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
Expand Down
8 changes: 4 additions & 4 deletions WowsKarma.Api/appsettings.Development.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,22 @@
"EU": {
"CookieName": "Auth_EU",
"CookieDomain": "localhost",
"WgAuthCallback": "http://localhost:5010/api/auth/wg-callback"
"WgAuthCallback": "https://localhost:5010/api/auth/wg-callback"
},
"NA": {
"CookieName": "Auth_NA",
"CookieDomain": "localhost",
"WgAuthCallback": "http://localhost:5010/api/auth/wg-callback"
"WgAuthCallback": "https://localhost:5010/api/auth/wg-callback"
},
"CIS": {
"CookieName": "Auth_CIS",
"CookieDomain": "localhost",
"WgAuthCallback": "http://localhost:5010/api/auth/wg-callback"
"WgAuthCallback": "https://localhost:5010/api/auth/wg-callback"
},
"SEA": {
"CookieName": "Auth_SEA",
"CookieDomain": "localhost",
"WgAuthCallback": "http://localhost:5010/api/auth/wg-callback"
"WgAuthCallback": "https://localhost:5010/api/auth/wg-callback"
}
},
"Discord": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
<ul class="navbar-nav flex-grow-0 me-xl-3">
<ng-container *ngIf="authService.userInfo$ | async as user else unauthorized">
<li class="navbar-text text-light">Welcome, {{user.username}}.&emsp;</li>
@if (authService.userInfo$ | async; as user) {
<li class="navbar-text text-white me-3">Welcome, {{user.username}}.</li>

<li class="nav-item">
<a class="nav-link text-white" [routerLink]="['/player', user.id +','+ user.username]">Profile</a>
<a class="nav-link" [routerLink]="['/player', user.id +','+ user.username]" routerLinkActive="active">Profile</a>
</li>

<li class="nav-item"><a class="nav-link text-white" routerLink="/settings">Settings</a></li>
<li class="nav-item"><a class="nav-link text-white" routerLink="/logout">Logout</a></li>
</ng-container>

<ng-template #unauthorized>
<li class="nav-item"><a class="nav-link" routerLink="/settings" routerLinkActive="active">Settings</a></li>
<li class="nav-item"><a class="nav-link" routerLink="/logout" routerLinkActive="active">Logout</a></li>
} @else {
<li class="nav-item">
<a class="nav-link text-white" routerLink="/login" [queryParams]="{redirectUri: (currentRelativePath$ | async)}">Login</a>
<a class="nav-link text-white" routerLink="/login" [queryParams]="{redirectUri: (currentRelativePath$ | async)}" routerLinkActive="active">Login</a>
</li>
</ng-template>
}
</ul>
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@

<div id="navbarNavContent" class="navbar-collapse collapse" [ngbCollapse]="isCollapsed">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item"><a class="nav-link" routerLink="/">Home</a></li>
<li class="nav-item"><a class="nav-link" routerLink="/player">Players</a></li>
<li class="nav-item"><a class="nav-link" routerLink="/clan">Clans</a></li>
<li class="nav-item"><a class="nav-link" routerLink="/posts">Posts</a></li>
<li class="nav-item"><a class="nav-link" routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">Home</a></li>
<li class="nav-item"><a class="nav-link" routerLink="/player" routerLinkActive="active">Players</a></li>
<li class="nav-item"><a class="nav-link" routerLink="/clan" routerLinkActive="active">Clans</a></li>
<li class="nav-item"><a class="nav-link" routerLink="/posts" routerLinkActive="active">Posts</a></li>
</ul>

<ul class="navbar-nav flex-grow 0">
Expand Down

0 comments on commit 6f43217

Please sign in to comment.