Skip to content

Commit 8c86aa9

Browse files
committed
Merge branch 'develop' into feature/ip-enrichment-geo-vpn
# Conflicts: # API/Program.cs # Common/Extensions/ConfigurationExtensions.cs
2 parents 3647333 + f1188a0 commit 8c86aa9

189 files changed

Lines changed: 6815 additions & 1397 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.

.github/workflows/ci-build.yml

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ jobs:
4343

4444
steps:
4545
- name: Checkout
46-
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
46+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
4747

48-
- uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0
48+
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
4949
with:
5050
dotnet-version: '${{ env.DOTNET_VERSION }}'
5151

@@ -63,7 +63,7 @@ jobs:
6363
6464
- name: Upload test artifacts
6565
if: always()
66-
uses: actions/upload-artifact@v7
66+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
6767
with:
6868
name: test-results
6969
path: artifacts/test-results
@@ -78,14 +78,14 @@ jobs:
7878

7979
steps:
8080
- name: Checkout
81-
uses: actions/checkout@v6
81+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
8282

83-
- uses: actions/setup-dotnet@v5
83+
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
8484
with:
8585
dotnet-version: '${{ env.DOTNET_VERSION }}'
8686

8787
- name: Download test artifacts
88-
uses: actions/download-artifact@v8
88+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
8989
with:
9090
name: test-results
9191
path: artifacts/test-results
@@ -136,7 +136,7 @@ jobs:
136136
PY
137137
138138
- name: Upload merged coverage report
139-
uses: actions/upload-artifact@v7
139+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
140140
with:
141141
name: coverage-report
142142
path: artifacts/coverage
@@ -157,22 +157,22 @@ jobs:
157157

158158
steps:
159159
- name: Configure Pages
160-
uses: actions/configure-pages@v6
160+
uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0
161161

162162
- name: Download merged coverage report
163-
uses: actions/download-artifact@v8
163+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
164164
with:
165165
name: coverage-report
166166
path: _site/coverage
167167

168168
- name: Upload Pages artifact
169-
uses: actions/upload-pages-artifact@v5
169+
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
170170
with:
171171
path: _site
172172

173173
- name: Deploy Pages site
174174
id: deployment
175-
uses: actions/deploy-pages@v5
175+
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
176176

177177
build:
178178
name: Build (${{ matrix.image }})
@@ -190,7 +190,7 @@ jobs:
190190

191191
steps:
192192
- name: Checkout
193-
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
193+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
194194

195195
- name: Set up Docker Buildx
196196
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
@@ -226,7 +226,7 @@ jobs:
226226

227227
steps:
228228
- name: Checkout
229-
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
229+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
230230

231231
- name: Log in to Container Registry
232232
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
@@ -284,7 +284,7 @@ jobs:
284284
environment: development
285285

286286
steps:
287-
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
287+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
288288
with:
289289
sparse-checkout: |
290290
.github

.github/workflows/codeql.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,17 @@ jobs:
2727
contents: read
2828
steps:
2929
- name: Checkout repository
30-
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
30+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
3131

3232
# Initializes the CodeQL tools for scanning.
3333
- name: Initialize CodeQL
34-
uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
34+
uses: github/codeql-action/init@c35d1b164463ee62a100735382aaaa525c5d3496 # codeql-bundle-v2.25.6
3535
with:
3636
languages: csharp
3737
build-mode: manual
3838

3939
- name: Setup .NET SDK ${{ env.DOTNET_VERSION }}
40-
uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0
40+
uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
4141
with:
4242
dotnet-version: ${{ env.DOTNET_VERSION }}
4343

@@ -50,6 +50,6 @@ jobs:
5050
dotnet publish Cron/Cron.csproj -c Release
5151
5252
- name: Perform CodeQL Analysis
53-
uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
53+
uses: github/codeql-action/analyze@c35d1b164463ee62a100735382aaaa525c5d3496 # codeql-bundle-v2.25.6
5454
with:
5555
category: "/language:csharp"

.github/workflows/update-cloudflare-proxies.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ jobs:
1414
runs-on: ubuntu-latest
1515

1616
steps:
17-
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
17+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
1818
with:
1919
ref: ${{ github.ref }}
2020

21-
- uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0
21+
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
2222
with:
2323
global-json-file: global.json
2424

API.IntegrationTests/Helpers/TestHelper.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Net;
33
using System.Text;
44
using System.Text.Json;
5+
using Microsoft.AspNetCore.Mvc.Testing;
56
using Microsoft.Extensions.DependencyInjection;
67
using OpenShock.Common.Constants;
78
using OpenShock.Common.OpenShockDb;
@@ -50,7 +51,7 @@ public static async Task<AuthenticatedUser> CreateAndLoginUser(
5051
/// </summary>
5152
public static HttpClient CreateAuthenticatedClient(WebApplicationFactory factory, string sessionToken)
5253
{
53-
var client = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions
54+
var client = factory.CreateClient(new WebApplicationFactoryClientOptions
5455
{
5556
AllowAutoRedirect = false,
5657
HandleCookies = false
@@ -64,7 +65,7 @@ public static HttpClient CreateAuthenticatedClient(WebApplicationFactory factory
6465
/// </summary>
6566
public static HttpClient CreateApiTokenClient(WebApplicationFactory factory, string apiToken)
6667
{
67-
var client = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions
68+
var client = factory.CreateClient(new WebApplicationFactoryClientOptions
6869
{
6970
AllowAutoRedirect = false,
7071
HandleCookies = false
@@ -78,7 +79,7 @@ public static HttpClient CreateApiTokenClient(WebApplicationFactory factory, str
7879
/// </summary>
7980
public static HttpClient CreateHubTokenClient(WebApplicationFactory factory, string hubToken)
8081
{
81-
var client = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions
82+
var client = factory.CreateClient(new WebApplicationFactoryClientOptions
8283
{
8384
AllowAutoRedirect = false,
8485
HandleCookies = false
@@ -146,7 +147,7 @@ public static async Task<Guid> CreateUserInDb(
146147
WebApplicationFactory factory,
147148
Guid userId,
148149
string name = "TestToken",
149-
List<Common.Models.PermissionType>? permissions = null)
150+
List<PermissionType>? permissions = null)
150151
{
151152
await using var scope = factory.Services.CreateAsyncScope();
152153
var db = scope.ServiceProvider.GetRequiredService<OpenShockContext>();
@@ -160,7 +161,7 @@ public static async Task<Guid> CreateUserInDb(
160161
Name = name,
161162
TokenHash = HashingUtils.HashToken(rawToken),
162163
CreatedByIp = IPAddress.Loopback,
163-
Permissions = permissions ?? [Common.Models.PermissionType.Shockers_Use]
164+
Permissions = permissions ?? [PermissionType.Shockers_Use]
164165
});
165166
await db.SaveChangesAsync();
166167
return (tokenId, rawToken);
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using Microsoft.AspNetCore.Hosting;
2+
using Microsoft.AspNetCore.TestHost;
3+
using Microsoft.Extensions.DependencyInjection;
4+
using OpenShock.Common.Options;
5+
6+
namespace OpenShock.API.IntegrationTests;
7+
8+
/// <summary>
9+
/// Variant of <see cref="WebApplicationFactory"/> that runs with user registration disabled.
10+
/// </summary>
11+
public sealed class RegistrationDisabledWebApplicationFactory : WebApplicationFactory
12+
{
13+
protected override void ConfigureWebHost(IWebHostBuilder builder)
14+
{
15+
base.ConfigureWebHost(builder);
16+
17+
builder.ConfigureTestServices(services =>
18+
{
19+
var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(AccountOptions));
20+
if (descriptor is not null)
21+
services.Remove(descriptor);
22+
23+
services.AddSingleton(new AccountOptions { RegistrationEnabled = false });
24+
});
25+
}
26+
}

API.IntegrationTests/Tests/AccountLoginTests.cs

Lines changed: 5 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -10,60 +10,20 @@ public sealed class AccountLoginTests
1010
[ClassDataSource<WebApplicationFactory>(Shared = SharedType.PerTestSession)]
1111
public required WebApplicationFactory WebApplicationFactory { get; init; }
1212

13-
// --- V1 Login ---
13+
// --- V1 Login (retired) ---
1414

1515
[Test]
16-
public async Task V1Login_Success_ReturnsCookie()
16+
public async Task V1Login_Retired_Returns410Gone()
1717
{
18-
await TestHelper.CreateUserInDb(WebApplicationFactory, "loginv1", "loginv1@test.org", "SecurePassword123#");
19-
20-
using var client = WebApplicationFactory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions
21-
{
22-
AllowAutoRedirect = false,
23-
HandleCookies = false
24-
});
25-
26-
var response = await client.PostAsync("/1/account/login", TestHelper.JsonContent(new
27-
{
28-
email = "loginv1@test.org",
29-
password = "SecurePassword123#"
30-
}));
31-
32-
await Assert.That(response.StatusCode).IsEqualTo(HttpStatusCode.OK);
33-
34-
var setCookie = response.Headers.GetValues("Set-Cookie").ToArray();
35-
var hasSessionCookie = setCookie.Any(c => c.Contains(AuthConstants.UserSessionCookieName));
36-
await Assert.That(hasSessionCookie).IsTrue();
37-
}
38-
39-
[Test]
40-
public async Task V1Login_InvalidPassword_Returns401()
41-
{
42-
await TestHelper.CreateUserInDb(WebApplicationFactory, "loginv1bad", "loginv1bad@test.org", "SecurePassword123#");
43-
4418
using var client = WebApplicationFactory.CreateClient();
4519

4620
var response = await client.PostAsync("/1/account/login", TestHelper.JsonContent(new
4721
{
48-
email = "loginv1bad@test.org",
49-
password = "WrongPassword999!"
50-
}));
51-
52-
await Assert.That(response.StatusCode).IsEqualTo(HttpStatusCode.Unauthorized);
53-
}
54-
55-
[Test]
56-
public async Task V1Login_NonexistentUser_Returns401()
57-
{
58-
using var client = WebApplicationFactory.CreateClient();
59-
60-
var response = await client.PostAsync("/1/account/login", TestHelper.JsonContent(new
61-
{
62-
email = "doesnotexist@test.org",
63-
password = "SomePassword123#"
22+
email = "whatever@test.org",
23+
password = "SecurePassword123#"
6424
}));
6525

66-
await Assert.That(response.StatusCode).IsEqualTo(HttpStatusCode.Unauthorized);
26+
await Assert.That(response.StatusCode).IsEqualTo(HttpStatusCode.Gone);
6727
}
6828

6929
// --- V2 Login ---

API.IntegrationTests/Tests/AccountSignupTests.cs

Lines changed: 21 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -11,56 +11,21 @@ public sealed class AccountSignupTests
1111
[ClassDataSource<WebApplicationFactory>(Shared = SharedType.PerTestSession)]
1212
public required WebApplicationFactory WebApplicationFactory { get; init; }
1313

14-
// --- V1 Signup ---
14+
// --- V1 Signup (retired) ---
1515

1616
[Test]
17-
public async Task V1Signup_Success_CreatesUser()
17+
public async Task V1Signup_Retired_Returns410Gone()
1818
{
1919
using var client = WebApplicationFactory.CreateClient();
2020

2121
var response = await client.PostAsync("/1/account/signup", TestHelper.JsonContent(new
2222
{
23-
username = "v1user",
23+
username = "v1retired",
2424
password = "SecurePassword123#",
25-
email = "v1user@test.org"
25+
email = "v1retired@test.org"
2626
}));
2727

28-
await Assert.That(response.StatusCode).IsEqualTo(HttpStatusCode.OK);
29-
30-
await using var scope = WebApplicationFactory.Services.CreateAsyncScope();
31-
var db = scope.ServiceProvider.GetRequiredService<OpenShockContext>();
32-
var user = await db.Users.FirstOrDefaultAsync(u => u.Email == "v1user@test.org");
33-
await Assert.That(user).IsNotNull();
34-
}
35-
36-
[Test, DependsOn(nameof(V1Signup_Success_CreatesUser))]
37-
public async Task V1Signup_DuplicateEmail_Returns409()
38-
{
39-
using var client = WebApplicationFactory.CreateClient();
40-
41-
var response = await client.PostAsync("/1/account/signup", TestHelper.JsonContent(new
42-
{
43-
username = "v1userDifferent",
44-
password = "SecurePassword123#",
45-
email = "v1user@test.org" // same email
46-
}));
47-
48-
await Assert.That(response.StatusCode).IsEqualTo(HttpStatusCode.Conflict);
49-
}
50-
51-
[Test, DependsOn(nameof(V1Signup_Success_CreatesUser))]
52-
public async Task V1Signup_DuplicateUsername_Returns409()
53-
{
54-
using var client = WebApplicationFactory.CreateClient();
55-
56-
var response = await client.PostAsync("/1/account/signup", TestHelper.JsonContent(new
57-
{
58-
username = "v1user", // same username
59-
password = "SecurePassword123#",
60-
email = "v1different@test.org"
61-
}));
62-
63-
await Assert.That(response.StatusCode).IsEqualTo(HttpStatusCode.Conflict);
28+
await Assert.That(response.StatusCode).IsEqualTo(HttpStatusCode.Gone);
6429
}
6530

6631
// --- V2 Signup ---
@@ -118,6 +83,22 @@ public async Task V2Signup_DuplicateEmail_Returns409()
11883
await Assert.That(response.StatusCode).IsEqualTo(HttpStatusCode.Conflict);
11984
}
12085

86+
[Test, DependsOn(nameof(V2Signup_Success_CreatesUser))]
87+
public async Task V2Signup_DuplicateUsername_Returns409()
88+
{
89+
using var client = WebApplicationFactory.CreateClient();
90+
91+
var response = await client.PostAsync("/2/account/signup", TestHelper.JsonContent(new
92+
{
93+
username = "v2user", // same username
94+
password = "SecurePassword123#",
95+
email = "v2different@test.org",
96+
turnstileResponse = "valid-token"
97+
}));
98+
99+
await Assert.That(response.StatusCode).IsEqualTo(HttpStatusCode.Conflict);
100+
}
101+
121102
// --- Validation ---
122103

123104
[Test]

0 commit comments

Comments
 (0)