Skip to content

Commit 0287f85

Browse files
committed
chore(dashboard): align OpenAPI security requirement with current Swashbuckle/OpenApi API" -m " - Update dashboard Swagger setup to use package-compatible OpenAPI types." -m " - Replace OpenApiReference with OpenApiSecuritySchemeReference and use AddSecurityRequirement(document => ...)." -m
" - Add .editorconfig rules for explicit types and mandatory braces.
1 parent 6dda911 commit 0287f85

463 files changed

Lines changed: 5234 additions & 2740 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.editorconfig

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,15 @@ trim_trailing_whitespace = true
1212
[*.cs]
1313
dotnet_language_version = latest
1414

15+
# Prefer explicit types over var for consistency.
16+
csharp_style_var_for_built_in_types = false:warning
17+
csharp_style_var_when_type_is_apparent = false:warning
18+
csharp_style_var_elsewhere = false:warning
19+
20+
# Always use braces for control flow and multiline statements.
21+
csharp_prefer_braces = true:warning
22+
csharp_new_line_before_open_brace = all
23+
1524
# Never qualify with `this.`
1625
dotnet_style_qualification_for_field = false:none
1726
dotnet_style_qualification_for_property = false:none
@@ -75,4 +84,4 @@ dotnet_diagnostic.CA2007.severity = none
7584
dotnet_diagnostic.CA2007.severity = none
7685

7786
[Turbo.Players/**/*.cs]
78-
dotnet_diagnostic.CA2007.severity = none
87+
dotnet_diagnostic.CA2007.severity = none

Directory.Packages.props

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,7 @@
1414
<PackageVersion Include="Microsoft.EntityFrameworkCore.Abstractions" Version="9.0.8" />
1515
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.8" />
1616
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0" />
17-
<PackageVersion
18-
Include="Microsoft.Extensions.DependencyInjection.Abstractions"
19-
Version="10.0.0"
20-
/>
17+
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0" />
2118
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="10.0.0" />
2219
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.0" />
2320
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.0" />
@@ -35,14 +32,14 @@
3532
<PackageVersion Include="SuperSocket.ProtoBase" Version="2.1.0" />
3633
<PackageVersion Include="SuperSocket.WebSocket.Server" Version="2.1.0" />
3734
<PackageVersion Include="Scrutor" Version="6.1.0" />
38-
<PackageVersion Include="Swashbuckle.AspNetCore" Version="7.2.0" />
35+
<PackageVersion Include="Swashbuckle.AspNetCore" Version="10.2.2" />
3936
<PackageVersion Include="BouncyCastle.Cryptography" Version="2.4.0" />
4037
<PackageVersion Include="BCrypt.Net-Next" Version="4.2.0" />
4138
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="10.0.0" />
4239
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
4340
<PackageVersion Include="xunit" Version="2.9.2" />
4441
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
45-
<PackageVersion Include="FluentAssertions" Version="6.12.1" />
42+
<PackageVersion Include="FluentAssertions" Version="8.10.0" />
4643
</ItemGroup>
4744
<!-- Test-only packages (see Turbo.*.Tests projects). -->
4845
<ItemGroup>
@@ -52,4 +49,4 @@
5249
<PackageVersion Include="FluentAssertions" Version="6.12.2" />
5350
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.8" />
5451
</ItemGroup>
55-
</Project>
52+
</Project>

Turbo.Authentication/AccountAuthenticator.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Threading.Tasks;
44
using Microsoft.EntityFrameworkCore;
55
using Turbo.Database.Context;
6+
using Turbo.Database.Entities.Players;
67
using Turbo.Primitives.Authentication;
78

89
namespace Turbo.Authentication;
@@ -26,22 +27,24 @@ internal sealed class AccountAuthenticator(IDbContextFactory<TurboDbContext> dbC
2627
)
2728
{
2829
if (string.IsNullOrWhiteSpace(email) || string.IsNullOrEmpty(password))
30+
{
2931
return null;
32+
}
3033

31-
var normalizedEmail = email.Trim().ToLowerInvariant();
34+
string normalizedEmail = email.Trim().ToLowerInvariant();
3235

33-
await using var dbCtx = await dbContextFactory
36+
await using TurboDbContext dbCtx = await dbContextFactory
3437
.CreateDbContextAsync(ct)
3538
.ConfigureAwait(false);
3639

37-
var account = await dbCtx
40+
PlayerAccountEntity? account = await dbCtx
3841
.PlayerAccounts.AsNoTracking()
3942
.FirstOrDefaultAsync(a => a.Email == normalizedEmail, ct)
4043
.ConfigureAwait(false);
4144

42-
var hash = account?.PasswordHash ?? DummyHash;
45+
string hash = account?.PasswordHash ?? DummyHash;
4346

44-
var valid = await Task.Run(() => BCrypt.Net.BCrypt.Verify(password, hash), ct)
47+
bool valid = await Task.Run(() => BCrypt.Net.BCrypt.Verify(password, hash), ct)
4548
.ConfigureAwait(false);
4649

4750
return valid && account is not null ? account.Id : null;

Turbo.Authentication/AuthenticationService.cs

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using Microsoft.Extensions.Options;
88
using Turbo.Authentication.Configuration;
99
using Turbo.Database.Context;
10+
using Turbo.Database.Entities.Security;
1011
using Turbo.Primitives.Authentication;
1112
using Turbo.Primitives.Events;
1213

@@ -30,16 +31,20 @@ public async Task<int> GetPlayerIdFromTicketAsync(
3031
)
3132
{
3233
if (ticket is null || ticket.Length == 0)
34+
{
3335
return 0;
36+
}
3437

35-
var dbCtx = await _dbCtxFactory.CreateDbContextAsync(ct).ConfigureAwait(false);
38+
TurboDbContext dbCtx = await _dbCtxFactory.CreateDbContextAsync(ct).ConfigureAwait(false);
3639

3740
try
3841
{
3942
if (dbCtx.SecurityTickets is null)
43+
{
4044
return 0;
45+
}
4146

42-
var entity = await dbCtx
47+
SecurityTicketEntity? entity = await dbCtx
4348
.SecurityTickets.AsNoTracking()
4449
.FirstOrDefaultAsync(entity => entity.Ticket == ticket, ct)
4550
.ConfigureAwait(false);
@@ -53,9 +58,14 @@ await _events
5358
return 0;
5459
}
5560

56-
var now = DateTime.UtcNow;
57-
var expiry = entity.ExpiresAt
58-
?? (_ticketTtlSeconds > 0 ? entity.CreatedAt.AddSeconds(_ticketTtlSeconds) : (DateTime?)null);
61+
DateTime now = DateTime.UtcNow;
62+
DateTime? expiry =
63+
entity.ExpiresAt
64+
?? (
65+
_ticketTtlSeconds > 0
66+
? entity.CreatedAt.AddSeconds(_ticketTtlSeconds)
67+
: (DateTime?)null
68+
);
5969

6070
if (expiry.HasValue && now > expiry.Value)
6171
{
@@ -75,8 +85,8 @@ await _events
7585

7686
if (!entity.IsLocked)
7787
{
78-
dbCtx.SecurityTickets.Remove(entity);
79-
await dbCtx.SaveChangesAsync(ct).ConfigureAwait(false);
88+
dbCtx.SecurityTickets.Remove(entity);
89+
await dbCtx.SaveChangesAsync(ct).ConfigureAwait(false);
8090
}
8191

8292
await _events
@@ -94,16 +104,20 @@ await _events
94104
private string? HashIp(string? remoteIp)
95105
{
96106
if (string.IsNullOrWhiteSpace(remoteIp))
107+
{
97108
return null;
109+
}
98110

99-
var key = _ipHashSecret;
111+
string key = _ipHashSecret;
100112
if (string.IsNullOrWhiteSpace(key))
113+
{
101114
return null;
115+
}
102116

103-
var keyBytes = Encoding.UTF8.GetBytes(key);
104-
var ipBytes = Encoding.UTF8.GetBytes(remoteIp.Trim());
117+
byte[] keyBytes = Encoding.UTF8.GetBytes(key);
118+
byte[] ipBytes = Encoding.UTF8.GetBytes(remoteIp.Trim());
105119

106-
var hash = HMACSHA256.HashData(keyBytes, ipBytes);
120+
byte[] hash = HMACSHA256.HashData(keyBytes, ipBytes);
107121

108122
return Convert.ToHexString(hash).ToLowerInvariant();
109123
}

Turbo.Authentication/Permissions/PermissionSeederService.cs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,27 +29,29 @@ public async Task StartAsync(CancellationToken cancellationToken)
2929
{
3030
try
3131
{
32-
var db = await dbContextFactory
32+
TurboDbContext db = await dbContextFactory
3333
.CreateDbContextAsync(cancellationToken)
3434
.ConfigureAwait(false);
3535

3636
try
3737
{
38-
foreach (var seed in DefaultRoles.All)
38+
foreach (DefaultRoles.RoleSeed seed in DefaultRoles.All)
3939
{
40-
var exists = await db
40+
bool exists = await db
4141
.Roles.AsNoTracking()
4242
.AnyAsync(r => r.Key == seed.Key, cancellationToken)
4343
.ConfigureAwait(false);
4444

4545
if (exists)
46+
{
4647
continue;
48+
}
4749

48-
var role = new RoleEntity { Key = seed.Key, Name = seed.Name };
50+
RoleEntity role = new RoleEntity { Key = seed.Key, Name = seed.Name };
4951
db.Roles.Add(role);
5052
await db.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
5153

52-
foreach (var capability in seed.Capabilities)
54+
foreach (string capability in seed.Capabilities)
5355
{
5456
db.RolePermissions.Add(
5557
new RolePermissionEntity
@@ -61,7 +63,9 @@ public async Task StartAsync(CancellationToken cancellationToken)
6163
}
6264

6365
if (seed.Capabilities.Count > 0)
66+
{
6467
await db.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
68+
}
6569

6670
logger.LogInformation(
6771
"Seeded default role '{RoleKey}' with {CapabilityCount} capabilities",
@@ -93,12 +97,14 @@ public async Task StartAsync(CancellationToken cancellationToken)
9397
/// </summary>
9498
private async Task EnsureBootstrapOwnerAsync(TurboDbContext db, CancellationToken ct)
9599
{
96-
var email = _config.BootstrapOwnerEmail?.Trim().ToLowerInvariant();
100+
string? email = _config.BootstrapOwnerEmail?.Trim().ToLowerInvariant();
97101

98102
if (string.IsNullOrWhiteSpace(email))
103+
{
99104
return;
105+
}
100106

101-
var accountId = await db
107+
int? accountId = await db
102108
.PlayerAccounts.AsNoTracking()
103109
.Where(a => a.Email == email)
104110
.Select(a => (int?)a.Id)
@@ -114,17 +120,19 @@ private async Task EnsureBootstrapOwnerAsync(TurboDbContext db, CancellationToke
114120
return;
115121
}
116122

117-
var ownerRoleId = await db
123+
int? ownerRoleId = await db
118124
.Roles.AsNoTracking()
119125
.Where(r => r.Key == DefaultRoles.OwnerKey)
120126
.Select(r => (int?)r.Id)
121127
.FirstOrDefaultAsync(ct)
122128
.ConfigureAwait(false);
123129

124130
if (ownerRoleId is null)
131+
{
125132
return;
133+
}
126134

127-
var alreadyAssigned = await db
135+
bool alreadyAssigned = await db
128136
.PlayerAccountRoles.AsNoTracking()
129137
.AnyAsync(
130138
x => x.PlayerAccountEntityId == accountId && x.RoleEntityId == ownerRoleId,
@@ -133,7 +141,9 @@ private async Task EnsureBootstrapOwnerAsync(TurboDbContext db, CancellationToke
133141
.ConfigureAwait(false);
134142

135143
if (alreadyAssigned)
144+
{
136145
return;
146+
}
137147

138148
db.PlayerAccountRoles.Add(
139149
new PlayerAccountRoleEntity

Turbo.Authentication/Permissions/PermissionService.cs

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Concurrent;
3+
using System.Collections.Generic;
34
using System.Linq;
45
using System.Threading;
56
using System.Threading.Tasks;
@@ -35,15 +36,19 @@ public async Task<PermissionSet> ResolveForAccountAsync(
3536
)
3637
{
3738
if (accountId <= 0)
39+
{
3840
return PermissionSet.Empty;
41+
}
3942

4043
if (
41-
_byAccount.TryGetValue(accountId, out var entry)
44+
_byAccount.TryGetValue(accountId, out CacheEntry entry)
4245
&& entry.ExpiresAtUtc > DateTime.UtcNow
4346
)
47+
{
4448
return entry.Set;
49+
}
4550

46-
var set = await LoadAccountAsync(accountId, ct).ConfigureAwait(false);
51+
PermissionSet set = await LoadAccountAsync(accountId, ct).ConfigureAwait(false);
4752
_byAccount[accountId] = new CacheEntry(set, DateTime.UtcNow + CacheTtl);
4853

4954
return set;
@@ -55,9 +60,11 @@ public async Task<PermissionSet> ResolveForPlayerAsync(
5560
)
5661
{
5762
if (playerId <= 0)
63+
{
5864
return PermissionSet.Empty;
65+
}
5966

60-
var accountId = await ResolveAccountIdAsync(playerId, ct).ConfigureAwait(false);
67+
int accountId = await ResolveAccountIdAsync(playerId, ct).ConfigureAwait(false);
6168

6269
return accountId <= 0
6370
? PermissionSet.Empty
@@ -68,28 +75,34 @@ public async Task<PermissionSet> ResolveForPlayerAsync(
6875

6976
public void InvalidatePlayer(int playerId)
7077
{
71-
if (_playerToAccount.TryGetValue(playerId, out var accountId))
78+
if (_playerToAccount.TryGetValue(playerId, out int accountId))
79+
{
7280
InvalidateAccount(accountId);
81+
}
7382
}
7483

7584
private async Task<int> ResolveAccountIdAsync(int playerId, CancellationToken ct)
7685
{
77-
if (_playerToAccount.TryGetValue(playerId, out var cached))
86+
if (_playerToAccount.TryGetValue(playerId, out int cached))
87+
{
7888
return cached;
89+
}
7990

80-
var db = await _dbContextFactory.CreateDbContextAsync(ct).ConfigureAwait(false);
91+
TurboDbContext db = await _dbContextFactory.CreateDbContextAsync(ct).ConfigureAwait(false);
8192

8293
try
8394
{
84-
var accountId = await db
95+
int? accountId = await db
8596
.Players.AsNoTracking()
8697
.Where(p => p.Id == playerId)
8798
.Select(p => p.PlayerAccountEntityId)
8899
.FirstOrDefaultAsync(ct)
89100
.ConfigureAwait(false);
90101

91102
if (accountId is > 0)
103+
{
92104
_playerToAccount[playerId] = accountId.Value;
105+
}
93106

94107
return accountId ?? 0;
95108
}
@@ -101,28 +114,30 @@ private async Task<int> ResolveAccountIdAsync(int playerId, CancellationToken ct
101114

102115
private async Task<PermissionSet> LoadAccountAsync(int accountId, CancellationToken ct)
103116
{
104-
var db = await _dbContextFactory.CreateDbContextAsync(ct).ConfigureAwait(false);
117+
TurboDbContext db = await _dbContextFactory.CreateDbContextAsync(ct).ConfigureAwait(false);
105118

106119
try
107120
{
108-
var roleIds = await db
121+
List<int> roleIds = await db
109122
.PlayerAccountRoles.AsNoTracking()
110123
.Where(ar => ar.PlayerAccountEntityId == accountId)
111124
.Select(ar => ar.RoleEntityId)
112125
.ToListAsync(ct)
113126
.ConfigureAwait(false);
114127

115128
if (roleIds.Count == 0)
129+
{
116130
return PermissionSet.Empty;
131+
}
117132

118-
var roles = await db
133+
List<string> roles = await db
119134
.Roles.AsNoTracking()
120135
.Where(r => roleIds.Contains(r.Id))
121136
.Select(r => r.Key)
122137
.ToListAsync(ct)
123138
.ConfigureAwait(false);
124139

125-
var capabilities = await db
140+
List<string> capabilities = await db
126141
.RolePermissions.AsNoTracking()
127142
.Where(rp => roleIds.Contains(rp.RoleEntityId))
128143
.Select(rp => rp.CapabilityKey)

0 commit comments

Comments
 (0)