mirror of
https://github.com/LBPUnion/ProjectLighthouse.git
synced 2025-07-19 11:41:30 +00:00
Merge branch 'main' into lbp3-collections
This commit is contained in:
commit
757a96d435
39 changed files with 840 additions and 59 deletions
20
.github/workflows/ci.yml
vendored
20
.github/workflows/ci.yml
vendored
|
@ -13,9 +13,9 @@ jobs:
|
|||
fail-fast: false
|
||||
matrix:
|
||||
os:
|
||||
- { prettyName: Windows, fullName: windows-latest, database: true }
|
||||
- { prettyName: macOS, fullName: macos-latest, database: true }
|
||||
- { prettyName: Linux, fullName: ubuntu-latest, database: true }
|
||||
- { prettyName: Windows, fullName: windows-latest, database: true, webTest: false }
|
||||
- { prettyName: macOS, fullName: macos-latest, database: true, webTest: false }
|
||||
- { prettyName: Linux, fullName: ubuntu-latest, database: true, webTest: true }
|
||||
timeout-minutes: 10
|
||||
env:
|
||||
DB_DATABASE: lighthouse
|
||||
|
@ -44,10 +44,18 @@ jobs:
|
|||
- name: Compile
|
||||
run: dotnet build -c Debug
|
||||
|
||||
- name: Test
|
||||
- name: Run tests on ProjectLighthouse.Tests
|
||||
continue-on-error: true
|
||||
run: dotnet test --logger "trx;LogFileName=${{github.workspace}}/TestResults-${{matrix.os.prettyName}}.trx"
|
||||
run: dotnet test --logger "trx;LogFileName=${{github.workspace}}/TestResults-${{matrix.os.prettyName}}-Tests.trx" ProjectLighthouse.Tests
|
||||
|
||||
- name: Run tests on ProjectLighthouse.Tests.GameApiTests
|
||||
continue-on-error: true
|
||||
run: dotnet test --logger "trx;LogFileName=${{github.workspace}}/TestResults-${{matrix.os.prettyName}}-GameApiTests.trx" ProjectLighthouse.Tests.GameApiTests
|
||||
|
||||
- name: Run tests on ProjectLighthouse.Tests.WebsiteTests
|
||||
if: ${{ matrix.os.webTest }}
|
||||
continue-on-error: true
|
||||
run: dotnet test --logger "trx;LogFileName=${{github.workspace}}/TestResults-${{matrix.os.prettyName}}-WebsiteTests.trx" ProjectLighthouse.Tests.WebsiteTests
|
||||
|
||||
# Attempt to upload results even if test fails.
|
||||
# https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#always
|
||||
|
@ -56,7 +64,7 @@ jobs:
|
|||
if: ${{ always() }}
|
||||
with:
|
||||
name: lighthouse-test-results-${{matrix.os.prettyName}}
|
||||
path: ${{github.workspace}}/TestResults-${{matrix.os.prettyName}}.trx
|
||||
path: ${{github.workspace}}/TestResults-${{matrix.os.prettyName}}-*.trx
|
||||
|
||||
- name: Process Test Results
|
||||
id: process-trx
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<jdbc-url>jdbc:mysql://localhost:3306/lighthouse</jdbc-url>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
<data-source source="LOCAL" name="Lighthouse Production" uuid="b323608d-d984-40d0-942e-2c2ea35006d8">
|
||||
<data-source source="LOCAL" name="Lighthouse Production" read-only="true" uuid="b323608d-d984-40d0-942e-2c2ea35006d8">
|
||||
<driver-ref>mariadb</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.mariadb.jdbc.Driver</jdbc-driver>
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using LBPUnion.ProjectLighthouse.Tests;
|
||||
using LBPUnion.ProjectLighthouse.Types;
|
||||
using LBPUnion.ProjectLighthouse.Types.Settings;
|
||||
using Xunit;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Tests
|
||||
namespace ProjectLighthouse.Tests.GameApiTests
|
||||
{
|
||||
public class AuthenticationTests : LighthouseTest
|
||||
public class AuthenticationTests : LighthouseServerTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task ShouldReturnErrorOnNoPostData()
|
|
@ -1,12 +1,14 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using LBPUnion.ProjectLighthouse;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Tests;
|
||||
using LBPUnion.ProjectLighthouse.Types;
|
||||
using Xunit;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Tests
|
||||
namespace ProjectLighthouse.Tests.GameApiTests
|
||||
{
|
||||
public class DatabaseTests : LighthouseTest
|
||||
public class DatabaseTests : LighthouseServerTest
|
||||
{
|
||||
[DatabaseFact]
|
||||
public async Task CanCreateUserTwice()
|
||||
|
@ -21,8 +23,6 @@ namespace LBPUnion.ProjectLighthouse.Tests
|
|||
Assert.NotNull(userB);
|
||||
|
||||
await database.RemoveUser(userA); // Only remove userA since userA and userB are the same user
|
||||
|
||||
await database.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,12 +4,13 @@ using System.Text;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Tests;
|
||||
using LBPUnion.ProjectLighthouse.Types;
|
||||
using Xunit;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Tests
|
||||
namespace ProjectLighthouse.Tests.GameApiTests
|
||||
{
|
||||
public class MatchTests : LighthouseTest
|
||||
public class MatchTests : LighthouseServerTest
|
||||
{
|
||||
private static readonly SemaphoreSlim semaphore = new(1, 1);
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2021.3.0"/>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.0"/>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0"/>
|
||||
<PackageReference Include="xunit" Version="2.4.1"/>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ProjectLighthouse.Tests\ProjectLighthouse.Tests.csproj"/>
|
||||
<ProjectReference Include="..\ProjectLighthouse\ProjectLighthouse.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="..\ProjectLighthouse.Tests\ExampleFiles\**">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<ProjectReference Include="..\ProjectLighthouse\ProjectLighthouse.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,15 +1,17 @@
|
|||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using LBPUnion.ProjectLighthouse;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Tests;
|
||||
using LBPUnion.ProjectLighthouse.Types;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Profiles;
|
||||
using Xunit;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Tests
|
||||
namespace ProjectLighthouse.Tests.GameApiTests
|
||||
{
|
||||
public class SlotTests : LighthouseTest
|
||||
public class SlotTests : LighthouseServerTest
|
||||
{
|
||||
[DatabaseFact]
|
||||
public async Task ShouldOnlyShowUsersLevels()
|
|
@ -3,11 +3,12 @@ using System.IO;
|
|||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using LBPUnion.ProjectLighthouse.Tests;
|
||||
using Xunit;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Tests
|
||||
namespace ProjectLighthouse.Tests.GameApiTests
|
||||
{
|
||||
public class UploadTests : LighthouseTest
|
||||
public class UploadTests : LighthouseServerTest
|
||||
{
|
||||
public UploadTests()
|
||||
{
|
64
ProjectLighthouse.Tests.WebsiteTests/AdminTests.cs
Normal file
64
ProjectLighthouse.Tests.WebsiteTests/AdminTests.cs
Normal file
|
@ -0,0 +1,64 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using LBPUnion.ProjectLighthouse;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Tests;
|
||||
using LBPUnion.ProjectLighthouse.Types;
|
||||
using OpenQA.Selenium;
|
||||
using Xunit;
|
||||
|
||||
namespace ProjectLighthouse.Tests.WebsiteTests
|
||||
{
|
||||
public class AdminTests : LighthouseWebTest
|
||||
{
|
||||
public const string AdminPanelButtonXPath = "/html/body/div/header/div/div/div/a[2]";
|
||||
|
||||
[DatabaseFact]
|
||||
public async Task ShouldShowAdminPanelButtonWhenAdmin()
|
||||
{
|
||||
await using Database database = new();
|
||||
Random random = new();
|
||||
User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure"));
|
||||
|
||||
WebToken webToken = new()
|
||||
{
|
||||
UserId = user.UserId,
|
||||
UserToken = HashHelper.GenerateAuthToken(),
|
||||
};
|
||||
|
||||
database.WebTokens.Add(webToken);
|
||||
user.IsAdmin = true;
|
||||
await database.SaveChangesAsync();
|
||||
|
||||
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/");
|
||||
this.Driver.Manage().Cookies.AddCookie(new Cookie("LighthouseToken", webToken.UserToken));
|
||||
this.Driver.Navigate().Refresh();
|
||||
|
||||
Assert.Contains("Admin Panel", this.Driver.FindElement(By.XPath(AdminPanelButtonXPath)).Text);
|
||||
}
|
||||
|
||||
[DatabaseFact]
|
||||
public async Task ShouldNotShowAdminPanelButtonWhenNotAdmin()
|
||||
{
|
||||
await using Database database = new();
|
||||
Random random = new();
|
||||
User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure"));
|
||||
|
||||
WebToken webToken = new()
|
||||
{
|
||||
UserId = user.UserId,
|
||||
UserToken = HashHelper.GenerateAuthToken(),
|
||||
};
|
||||
|
||||
database.WebTokens.Add(webToken);
|
||||
user.IsAdmin = false;
|
||||
await database.SaveChangesAsync();
|
||||
|
||||
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/");
|
||||
this.Driver.Manage().Cookies.AddCookie(new Cookie("LighthouseToken", webToken.UserToken));
|
||||
this.Driver.Navigate().Refresh();
|
||||
|
||||
Assert.DoesNotContain("Admin Panel", this.Driver.FindElement(By.XPath(AdminPanelButtonXPath)).Text);
|
||||
}
|
||||
}
|
||||
}
|
106
ProjectLighthouse.Tests.WebsiteTests/AuthenticationTests.cs
Normal file
106
ProjectLighthouse.Tests.WebsiteTests/AuthenticationTests.cs
Normal file
|
@ -0,0 +1,106 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using LBPUnion.ProjectLighthouse;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Tests;
|
||||
using LBPUnion.ProjectLighthouse.Types;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using OpenQA.Selenium;
|
||||
using Xunit;
|
||||
|
||||
namespace ProjectLighthouse.Tests.WebsiteTests
|
||||
{
|
||||
public class AuthenticationTests : LighthouseWebTest
|
||||
{
|
||||
[DatabaseFact]
|
||||
public async Task ShouldLoginWithPassword()
|
||||
{
|
||||
await using Database database = new();
|
||||
Random random = new();
|
||||
|
||||
string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray());
|
||||
User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash(HashHelper.Sha256Hash(password)));
|
||||
|
||||
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/login");
|
||||
|
||||
this.Driver.FindElement(By.Id("text")).SendKeys(user.Username);
|
||||
this.Driver.FindElement(By.Id("password")).SendKeys(password);
|
||||
|
||||
this.Driver.FindElement(By.Id("submit")).Click();
|
||||
|
||||
WebToken? webToken = await database.WebTokens.FirstOrDefaultAsync(t => t.UserId == user.UserId);
|
||||
Assert.NotNull(webToken);
|
||||
|
||||
await database.RemoveUser(user);
|
||||
}
|
||||
|
||||
[DatabaseFact]
|
||||
public async Task ShouldNotLoginWithNoPassword()
|
||||
{
|
||||
await using Database database = new();
|
||||
Random random = new();
|
||||
User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("just like the hindenberg,"));
|
||||
|
||||
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/login");
|
||||
|
||||
this.Driver.FindElement(By.Id("text")).SendKeys(user.Username);
|
||||
|
||||
this.Driver.FindElement(By.Id("submit")).Click();
|
||||
|
||||
WebToken? webToken = await database.WebTokens.FirstOrDefaultAsync(t => t.UserId == user.UserId);
|
||||
Assert.Null(webToken);
|
||||
|
||||
await database.RemoveUser(user);
|
||||
}
|
||||
|
||||
[DatabaseFact]
|
||||
public async Task ShouldNotLoginWithWrongPassword()
|
||||
{
|
||||
await using Database database = new();
|
||||
Random random = new();
|
||||
User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure"));
|
||||
|
||||
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/login");
|
||||
|
||||
this.Driver.FindElement(By.Id("text")).SendKeys(user.Username);
|
||||
this.Driver.FindElement(By.Id("password")).SendKeys("nah man");
|
||||
|
||||
this.Driver.FindElement(By.Id("submit")).Click();
|
||||
|
||||
WebToken? webToken = await database.WebTokens.FirstOrDefaultAsync(t => t.UserId == user.UserId);
|
||||
Assert.Null(webToken);
|
||||
|
||||
await database.RemoveUser(user);
|
||||
}
|
||||
|
||||
[DatabaseFact]
|
||||
public async Task ShouldLoginWithInjectedCookie()
|
||||
{
|
||||
const string loggedInAsUsernameTextXPath = "/html/body/div/div/div/p[1]/b";
|
||||
|
||||
await using Database database = new();
|
||||
Random random = new();
|
||||
User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure"));
|
||||
|
||||
WebToken webToken = new()
|
||||
{
|
||||
UserId = user.UserId,
|
||||
UserToken = HashHelper.GenerateAuthToken(),
|
||||
};
|
||||
|
||||
database.WebTokens.Add(webToken);
|
||||
await database.SaveChangesAsync();
|
||||
|
||||
INavigation navigation = this.Driver.Navigate();
|
||||
|
||||
navigation.GoToUrl(this.BaseAddress + "/");
|
||||
this.Driver.Manage().Cookies.AddCookie(new Cookie("LighthouseToken", webToken.UserToken));
|
||||
Assert.Throws<NoSuchElementException>(() => this.Driver.FindElement(By.XPath(loggedInAsUsernameTextXPath)));
|
||||
navigation.Refresh();
|
||||
Assert.True(this.Driver.FindElement(By.XPath(loggedInAsUsernameTextXPath)).Text == user.Username);
|
||||
|
||||
await database.RemoveUser(user);
|
||||
}
|
||||
}
|
||||
}
|
50
ProjectLighthouse.Tests.WebsiteTests/LighthouseWebTest.cs
Normal file
50
ProjectLighthouse.Tests.WebsiteTests/LighthouseWebTest.cs
Normal file
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using LBPUnion.ProjectLighthouse;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Hosting.Server.Features;
|
||||
using OpenQA.Selenium;
|
||||
using OpenQA.Selenium.Chrome;
|
||||
using Xunit;
|
||||
|
||||
namespace ProjectLighthouse.Tests.WebsiteTests
|
||||
{
|
||||
[Collection(nameof(LighthouseWebTest))]
|
||||
public class LighthouseWebTest : IDisposable
|
||||
{
|
||||
public readonly IWebHost WebHost = new WebHostBuilder().UseKestrel().UseStartup<TestStartup>().UseWebRoot("StaticFiles").Build();
|
||||
public readonly string BaseAddress;
|
||||
|
||||
public readonly IWebDriver Driver;
|
||||
|
||||
public LighthouseWebTest()
|
||||
{
|
||||
this.WebHost.Start();
|
||||
|
||||
IServerAddressesFeature? serverAddressesFeature = WebHost.ServerFeatures.Get<IServerAddressesFeature>();
|
||||
if (serverAddressesFeature == null) throw new ArgumentNullException();
|
||||
|
||||
this.BaseAddress = serverAddressesFeature.Addresses.First();
|
||||
|
||||
ChromeOptions chromeOptions = new();
|
||||
if (Convert.ToBoolean(Environment.GetEnvironmentVariable("CI") ?? "false"))
|
||||
{
|
||||
chromeOptions.AddArgument("headless");
|
||||
chromeOptions.AddArgument("no-sandbox");
|
||||
chromeOptions.AddArgument("disable-dev-shm-usage");
|
||||
Console.WriteLine("We are in a CI environment, so chrome headless mode has been enabled.");
|
||||
}
|
||||
|
||||
this.Driver = new ChromeDriver(chromeOptions);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.Driver.Close();
|
||||
this.Driver.Dispose();
|
||||
this.WebHost.Dispose();
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2021.3.0"/>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.0"/>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0"/>
|
||||
<PackageReference Include="Selenium.WebDriver" Version="4.1.0"/>
|
||||
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="96.0.4664.4500"/>
|
||||
<PackageReference Include="xunit" Version="2.4.1"/>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ProjectLighthouse.Tests\ProjectLighthouse.Tests.csproj"/>
|
||||
<ProjectReference Include="..\ProjectLighthouse\ProjectLighthouse.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
84
ProjectLighthouse.Tests.WebsiteTests/RegisterTests.cs
Normal file
84
ProjectLighthouse.Tests.WebsiteTests/RegisterTests.cs
Normal file
|
@ -0,0 +1,84 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using LBPUnion.ProjectLighthouse;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Tests;
|
||||
using LBPUnion.ProjectLighthouse.Types;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using OpenQA.Selenium;
|
||||
using Xunit;
|
||||
|
||||
namespace ProjectLighthouse.Tests.WebsiteTests
|
||||
{
|
||||
public class RegisterTests : LighthouseWebTest
|
||||
{
|
||||
[DatabaseFact]
|
||||
public async Task ShouldRegister()
|
||||
{
|
||||
await using Database database = new();
|
||||
|
||||
string username = "unitTestUser" + new Random().Next();
|
||||
string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray());
|
||||
|
||||
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/register");
|
||||
|
||||
this.Driver.FindElement(By.Id("text")).SendKeys(username);
|
||||
|
||||
this.Driver.FindElement(By.Id("password")).SendKeys(password);
|
||||
this.Driver.FindElement(By.Id("confirmPassword")).SendKeys(password);
|
||||
|
||||
this.Driver.FindElement(By.Id("submit")).Click();
|
||||
|
||||
User? user = await database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
||||
Assert.NotNull(user);
|
||||
|
||||
await database.RemoveUser(user);
|
||||
}
|
||||
|
||||
[DatabaseFact]
|
||||
public async Task ShouldNotRegisterWithMismatchingPasswords()
|
||||
{
|
||||
await using Database database = new();
|
||||
|
||||
string username = "unitTestUser" + new Random().Next();
|
||||
string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray());
|
||||
|
||||
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/register");
|
||||
|
||||
this.Driver.FindElement(By.Id("text")).SendKeys(username);
|
||||
|
||||
this.Driver.FindElement(By.Id("password")).SendKeys(password);
|
||||
this.Driver.FindElement(By.Id("confirmPassword")).SendKeys(password + "a");
|
||||
|
||||
this.Driver.FindElement(By.Id("submit")).Click();
|
||||
|
||||
User? user = await database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
||||
Assert.Null(user);
|
||||
}
|
||||
|
||||
[DatabaseFact]
|
||||
public async Task ShouldNotRegisterWithTakenUsername()
|
||||
{
|
||||
await using Database database = new();
|
||||
|
||||
string username = "unitTestUser" + new Random().Next();
|
||||
string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray());
|
||||
|
||||
await database.CreateUser(username, HashHelper.BCryptHash(password));
|
||||
User? user = await database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
||||
Assert.NotNull(user);
|
||||
|
||||
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/register");
|
||||
|
||||
this.Driver.FindElement(By.Id("text")).SendKeys(username);
|
||||
|
||||
this.Driver.FindElement(By.Id("password")).SendKeys(password);
|
||||
this.Driver.FindElement(By.Id("confirmPassword")).SendKeys(password);
|
||||
|
||||
this.Driver.FindElement(By.Id("submit")).Click();
|
||||
|
||||
Assert.Contains("The username you've chosen is already taken.", this.Driver.PageSource);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,15 +14,14 @@ using Microsoft.EntityFrameworkCore;
|
|||
namespace LBPUnion.ProjectLighthouse.Tests
|
||||
{
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
public class LighthouseTest
|
||||
public class LighthouseServerTest
|
||||
{
|
||||
public readonly HttpClient Client;
|
||||
public readonly TestServer Server;
|
||||
|
||||
public LighthouseTest()
|
||||
public LighthouseServerTest()
|
||||
{
|
||||
this.Server = new TestServer(new WebHostBuilder().UseStartup<TestStartup>());
|
||||
|
||||
this.Client = this.Server.CreateClient();
|
||||
}
|
||||
public async Task<HttpResponseMessage> AuthenticateResponse(int number = -1, bool createUser = true)
|
|
@ -32,9 +32,9 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ProjectLighthouse\ProjectLighthouse.csproj"/>
|
||||
<Content Include="ExampleFiles\**">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<ProjectReference Include="..\ProjectLighthouse\ProjectLighthouse.csproj"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
@ -4,7 +4,7 @@ using Xunit;
|
|||
|
||||
namespace LBPUnion.ProjectLighthouse.Tests
|
||||
{
|
||||
public class SerializerTests : LighthouseTest
|
||||
public class SerializerTests
|
||||
{
|
||||
[Fact]
|
||||
public void BlankElementWorks()
|
|
@ -4,6 +4,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectLighthouse", "Projec
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectLighthouse.Tests", "ProjectLighthouse.Tests\ProjectLighthouse.Tests.csproj", "{AFC74569-B289-4ACC-B21C-313A3A62C017}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{D360C08E-EA47-43AC-A566-FDF413442980}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectLighthouse.Tests.GameApiTests", "ProjectLighthouse.Tests.GameApiTests\ProjectLighthouse.Tests.GameApiTests.csproj", "{200EED99-FE3E-45C6-A51E-76ED9819CA2B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectLighthouse.Tests.WebsiteTests", "ProjectLighthouse.Tests.WebsiteTests\ProjectLighthouse.Tests.WebsiteTests.csproj", "{CF65EB5B-5364-4D2A-8639-F147A67F08E7}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -17,6 +23,16 @@ Global
|
|||
{AFC74569-B289-4ACC-B21C-313A3A62C017}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AFC74569-B289-4ACC-B21C-313A3A62C017}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AFC74569-B289-4ACC-B21C-313A3A62C017}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AFC74569-B289-4ACC-B21C-313A3A62C017}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{200EED99-FE3E-45C6-A51E-76ED9819CA2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{200EED99-FE3E-45C6-A51E-76ED9819CA2B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{200EED99-FE3E-45C6-A51E-76ED9819CA2B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CF65EB5B-5364-4D2A-8639-F147A67F08E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CF65EB5B-5364-4D2A-8639-F147A67F08E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CF65EB5B-5364-4D2A-8639-F147A67F08E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{AFC74569-B289-4ACC-B21C-313A3A62C017} = {D360C08E-EA47-43AC-A566-FDF413442980}
|
||||
{200EED99-FE3E-45C6-A51E-76ED9819CA2B} = {D360C08E-EA47-43AC-A566-FDF413442980}
|
||||
{CF65EB5B-5364-4D2A-8639-F147A67F08E7} = {D360C08E-EA47-43AC-A566-FDF413442980}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
@ -90,6 +90,7 @@
|
|||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Affero/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=airfryer/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=asdf/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=BCAS/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=BCES/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=BCET/@EntryIndexedValue">True</s:Boolean>
|
||||
|
|
|
@ -73,7 +73,8 @@ namespace LBPUnion.ProjectLighthouse.Controllers
|
|||
}
|
||||
|
||||
User? user = await this.database.UserFromGameToken(token, true);
|
||||
if (user == null)
|
||||
|
||||
if (user == null || user.Banned)
|
||||
{
|
||||
Logger.Log("unable to find a user from a token, rejecting login", LoggerLevelLogin.Instance);
|
||||
return this.StatusCode(403, "");
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
|
|||
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||
if (user == null) return this.StatusCode(403, "");
|
||||
|
||||
return this.Ok(ServerSettings.Instance.EulaText + "\n" + $"{EulaHelper.License}\n");
|
||||
return this.Ok($"{EulaHelper.License}\n{ServerSettings.Instance.EulaText}");
|
||||
}
|
||||
|
||||
[HttpGet("announce")]
|
||||
|
@ -47,19 +47,24 @@ namespace LBPUnion.ProjectLighthouse.Controllers
|
|||
GameToken gameToken = userAndToken.Value.Item2;
|
||||
#endif
|
||||
|
||||
string announceText = ServerSettings.Instance.AnnounceText;
|
||||
|
||||
announceText = announceText.Replace("%user", user.Username);
|
||||
announceText = announceText.Replace("%id", user.UserId.ToString());
|
||||
|
||||
return this.Ok
|
||||
(
|
||||
$"You are now logged in as {user.Username}.\n\n" +
|
||||
announceText +
|
||||
#if DEBUG
|
||||
"---DEBUG INFO---\n" +
|
||||
"\n\n---DEBUG INFO---\n" +
|
||||
$"user.UserId: {user.UserId}\n" +
|
||||
$"token.Approved: {gameToken.Approved}\n" +
|
||||
$"token.Used: {gameToken.Used}\n" +
|
||||
$"token.UserLocation: {gameToken.UserLocation}\n" +
|
||||
$"token.GameVersion: {gameToken.GameVersion}\n" +
|
||||
"---DEBUG INFO---\n\n" +
|
||||
"---DEBUG INFO---" +
|
||||
#endif
|
||||
ServerSettings.Instance.EulaText
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -91,7 +91,21 @@ namespace LBPUnion.ProjectLighthouse.Controllers
|
|||
|
||||
[HttpGet("slots/lbp2cool")]
|
||||
[HttpGet("slots/cool")]
|
||||
public async Task<IActionResult> CoolSlots([FromQuery] int page) => await this.LuckyDipSlots(30 * page, 30, 69);
|
||||
public async Task<IActionResult> CoolSlots
|
||||
(
|
||||
[FromQuery] int pageStart,
|
||||
[FromQuery] int pageSize,
|
||||
[FromQuery] string gameFilterType,
|
||||
[FromQuery] int players,
|
||||
[FromQuery] Boolean move,
|
||||
[FromQuery] int? page = null
|
||||
)
|
||||
{
|
||||
int _pageStart = pageStart;
|
||||
if (page != null) _pageStart = (int)page * 30;
|
||||
// bit of a better placeholder until we can track average user interaction with /stream endpoint
|
||||
return await ThumbsSlots(_pageStart, Math.Min(pageSize, 30), gameFilterType, players, move, "thisWeek");
|
||||
}
|
||||
|
||||
[HttpGet("slots")]
|
||||
public async Task<IActionResult> NewestSlots([FromQuery] int pageStart, [FromQuery] int pageSize)
|
||||
|
@ -198,5 +212,137 @@ namespace LBPUnion.ProjectLighthouse.Controllers
|
|||
)
|
||||
);
|
||||
}
|
||||
|
||||
[HttpGet("slots/thumbs")]
|
||||
public async Task<IActionResult> ThumbsSlots
|
||||
(
|
||||
[FromQuery] int pageStart,
|
||||
[FromQuery] int pageSize,
|
||||
[FromQuery] string gameFilterType,
|
||||
[FromQuery] int players,
|
||||
[FromQuery] Boolean move,
|
||||
[FromQuery] string? dateFilterType = null
|
||||
)
|
||||
{
|
||||
Random rand = new();
|
||||
|
||||
IEnumerable<Slot> slots = FilterByRequest(gameFilterType, dateFilterType)
|
||||
.AsEnumerable()
|
||||
.OrderByDescending(s => s.Thumbsup)
|
||||
.ThenBy(_ => rand.Next())
|
||||
.Skip(pageStart - 1)
|
||||
.Take(Math.Min(pageSize, 30));
|
||||
|
||||
string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize());
|
||||
|
||||
return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "hint_start", pageStart + Math.Min(pageSize, 30)));
|
||||
}
|
||||
|
||||
[HttpGet("slots/mostUniquePlays")]
|
||||
public async Task<IActionResult> MostUniquePlaysSlots
|
||||
(
|
||||
[FromQuery] int pageStart,
|
||||
[FromQuery] int pageSize,
|
||||
[FromQuery] string gameFilterType,
|
||||
[FromQuery] int players,
|
||||
[FromQuery] Boolean move,
|
||||
[FromQuery] string? dateFilterType = null
|
||||
)
|
||||
{
|
||||
Random rand = new();
|
||||
|
||||
IEnumerable<Slot> slots = FilterByRequest(gameFilterType, dateFilterType)
|
||||
.AsEnumerable()
|
||||
.OrderByDescending
|
||||
(
|
||||
s =>
|
||||
{
|
||||
// probably not the best way to do this?
|
||||
return GetGameFilter(gameFilterType) switch
|
||||
{
|
||||
GameVersion.LittleBigPlanet1 => s.PlaysLBP1Unique,
|
||||
GameVersion.LittleBigPlanet2 => s.PlaysLBP2Unique,
|
||||
GameVersion.LittleBigPlanet3 => s.PlaysLBP3Unique,
|
||||
GameVersion.LittleBigPlanetVita => s.PlaysLBPVitaUnique,
|
||||
_ => s.PlaysUnique,
|
||||
};
|
||||
}
|
||||
)
|
||||
.ThenBy(_ => rand.Next())
|
||||
.Skip(pageStart - 1)
|
||||
.Take(Math.Min(pageSize, 30));
|
||||
|
||||
string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize());
|
||||
|
||||
return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "hint_start", pageStart + Math.Min(pageSize, 30)));
|
||||
}
|
||||
|
||||
[HttpGet("slots/mostHearted")]
|
||||
public async Task<IActionResult> MostHeartedSlots
|
||||
(
|
||||
[FromQuery] int pageStart,
|
||||
[FromQuery] int pageSize,
|
||||
[FromQuery] string gameFilterType,
|
||||
[FromQuery] int players,
|
||||
[FromQuery] Boolean move,
|
||||
[FromQuery] string? dateFilterType = null
|
||||
)
|
||||
{
|
||||
Random rand = new();
|
||||
|
||||
IEnumerable<Slot> slots = FilterByRequest(gameFilterType, dateFilterType)
|
||||
.AsEnumerable()
|
||||
.OrderByDescending(s => s.Hearts)
|
||||
.ThenBy(_ => rand.Next())
|
||||
.Skip(pageStart - 1)
|
||||
.Take(Math.Min(pageSize, 30));
|
||||
|
||||
string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize());
|
||||
|
||||
return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "hint_start", pageStart + Math.Min(pageSize, 30)));
|
||||
}
|
||||
|
||||
public GameVersion GetGameFilter(string gameFilterType)
|
||||
{
|
||||
return gameFilterType switch
|
||||
{
|
||||
"lbp1" => GameVersion.LittleBigPlanet1,
|
||||
"lbp2" => GameVersion.LittleBigPlanet2,
|
||||
"lbp3" => GameVersion.LittleBigPlanet3,
|
||||
"both" => GameVersion.LittleBigPlanet2, // LBP2 default option
|
||||
_ => GameVersion.Unknown,
|
||||
};
|
||||
}
|
||||
|
||||
public IQueryable<Slot> FilterByRequest(string gameFilterType, string? dateFilterType)
|
||||
{
|
||||
string _dateFilterType = dateFilterType ?? "";
|
||||
|
||||
long oldestTime = _dateFilterType switch
|
||||
{
|
||||
"thisWeek" => DateTimeOffset.Now.AddDays(-7).ToUnixTimeMilliseconds(),
|
||||
"thisMonth" => DateTimeOffset.Now.AddDays(-31).ToUnixTimeMilliseconds(),
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
GameVersion gameVersion = GetGameFilter(gameFilterType);
|
||||
|
||||
IQueryable<Slot> whereSlots;
|
||||
|
||||
// ReSharper disable once ConvertIfStatementToConditionalTernaryExpression
|
||||
if (gameFilterType == "both")
|
||||
{
|
||||
// Get game versions less than the current version
|
||||
// Needs support for LBP3 ("both" = LBP1+2)
|
||||
whereSlots = this.database.Slots.Where(s => s.GameVersion <= gameVersion && s.FirstUploaded >= oldestTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get game versions exactly equal to gamefiltertype
|
||||
whereSlots = this.database.Slots.Where(s => s.GameVersion == gameVersion && s.FirstUploaded >= oldestTime);
|
||||
}
|
||||
|
||||
return whereSlots.Include(s => s.Creator).Include(s => s.Location);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,7 +19,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers.Website.Admin
|
|||
this.database = database;
|
||||
}
|
||||
|
||||
[Route("teamPick")]
|
||||
[HttpGet("teamPick")]
|
||||
public async Task<IActionResult> TeamPick([FromRoute] int id)
|
||||
{
|
||||
User? user = this.database.UserFromWebRequest(this.Request);
|
||||
|
@ -35,7 +35,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers.Website.Admin
|
|||
return this.Ok();
|
||||
}
|
||||
|
||||
[Route("removeTeamPick")]
|
||||
[HttpGet("removeTeamPick")]
|
||||
public async Task<IActionResult> RemoveTeamPick([FromRoute] int id)
|
||||
{
|
||||
User? user = this.database.UserFromWebRequest(this.Request);
|
||||
|
@ -51,7 +51,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers.Website.Admin
|
|||
return this.Ok();
|
||||
}
|
||||
|
||||
[Route("delete")]
|
||||
[HttpGet("delete")]
|
||||
public async Task<IActionResult> DeleteLevel([FromRoute] int id)
|
||||
{
|
||||
User? user = this.database.UserFromWebRequest(this.Request);
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
#nullable enable
|
||||
using System.Threading.Tasks;
|
||||
using LBPUnion.ProjectLighthouse.Types;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Controllers.Website.Admin
|
||||
{
|
||||
[ApiController]
|
||||
[Route("admin/user/{id:int}")]
|
||||
public class AdminUserController : ControllerBase
|
||||
{
|
||||
private readonly Database database;
|
||||
|
||||
public AdminUserController(Database database)
|
||||
{
|
||||
this.database = database;
|
||||
}
|
||||
|
||||
[HttpGet("unban")]
|
||||
public async Task<IActionResult> UnbanUser([FromRoute] int id)
|
||||
{
|
||||
User? user = this.database.UserFromWebRequest(this.Request);
|
||||
if (user == null || !user.IsAdmin) return this.NotFound();
|
||||
|
||||
User? targetedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id);
|
||||
;
|
||||
if (targetedUser == null) return this.NotFound();
|
||||
|
||||
targetedUser.Banned = false;
|
||||
targetedUser.BannedReason = null;
|
||||
|
||||
await this.database.SaveChangesAsync();
|
||||
return this.Redirect($"/user/{targetedUser.UserId}");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,11 +11,11 @@ namespace LBPUnion.ProjectLighthouse.Maintenance.Commands
|
|||
public class DeleteUserCommand : ICommand
|
||||
{
|
||||
private readonly Database database = new();
|
||||
public string Name() => "Delete/Ban User";
|
||||
public string Name() => "Delete User";
|
||||
public string[] Aliases()
|
||||
=> new[]
|
||||
{
|
||||
"deleteUser", "wipeUser", "banUser",
|
||||
"deleteUser", "wipeUser",
|
||||
};
|
||||
public string Arguments() => "<username/userId>";
|
||||
public int RequiredArgs() => 1;
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
using LBPUnion.ProjectLighthouse;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace ProjectLighthouse.Migrations
|
||||
{
|
||||
[DbContext(typeof(Database))]
|
||||
[Migration("20211217000749_AddBannedPropertiesToUser")]
|
||||
public partial class AddBannedPropertiesToUser : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "Banned",
|
||||
table: "Users",
|
||||
type: "tinyint(1)",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "BannedReason",
|
||||
table: "Users",
|
||||
type: "longtext",
|
||||
nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Banned",
|
||||
table: "Users");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "BannedReason",
|
||||
table: "Users");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -546,6 +546,12 @@ namespace ProjectLighthouse.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<bool>("Banned")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("BannedReason")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Biography")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
|
|
20
ProjectLighthouse/Pages/Admin/AdminBanUserPage.cshtml
Normal file
20
ProjectLighthouse/Pages/Admin/AdminBanUserPage.cshtml
Normal file
|
@ -0,0 +1,20 @@
|
|||
@page "/admin/user/{id:int}/ban"
|
||||
@model LBPUnion.ProjectLighthouse.Pages.Admin.AdminBanUserPage
|
||||
|
||||
@{
|
||||
Layout = "Layouts/BaseLayout";
|
||||
Model.Title = "Ban " + Model.TargetedUser!.Username + "?";
|
||||
}
|
||||
|
||||
<p>Are you sure you want to ban this user?</p>
|
||||
|
||||
<form method="post">
|
||||
@Html.AntiForgeryToken()
|
||||
|
||||
<div class="ui left labeled input">
|
||||
<label for="text" class="ui blue label">Reason: </label>
|
||||
<input type="text" name="reason" id="text">
|
||||
</div><br><br>
|
||||
|
||||
<input type="submit" value="Yes, ban @Model.TargetedUser.Username!" id="submit" class="ui red button"><br>
|
||||
</form>
|
49
ProjectLighthouse/Pages/Admin/AdminBanUserPage.cshtml.cs
Normal file
49
ProjectLighthouse/Pages/Admin/AdminBanUserPage.cshtml.cs
Normal file
|
@ -0,0 +1,49 @@
|
|||
#nullable enable
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using LBPUnion.ProjectLighthouse.Pages.Layouts;
|
||||
using LBPUnion.ProjectLighthouse.Types;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Pages.Admin;
|
||||
|
||||
public class AdminBanUserPage : BaseLayout
|
||||
{
|
||||
public AdminBanUserPage(Database database) : base(database)
|
||||
{}
|
||||
|
||||
public User? TargetedUser;
|
||||
|
||||
public async Task<IActionResult> OnGet([FromRoute] int id)
|
||||
{
|
||||
User? user = this.Database.UserFromWebRequest(this.Request);
|
||||
if (user == null || !user.IsAdmin) return this.NotFound();
|
||||
|
||||
this.TargetedUser = await this.Database.Users.FirstOrDefaultAsync(u => u.UserId == id);
|
||||
if (this.TargetedUser == null) return this.NotFound();
|
||||
|
||||
return this.Page();
|
||||
}
|
||||
|
||||
public async Task<IActionResult> OnPost([FromRoute] int id, string reason)
|
||||
{
|
||||
User? user = this.Database.UserFromWebRequest(this.Request);
|
||||
if (user == null || !user.IsAdmin) return this.NotFound();
|
||||
|
||||
this.TargetedUser = await this.Database.Users.FirstOrDefaultAsync(u => u.UserId == id);
|
||||
if (this.TargetedUser == null) return this.NotFound();
|
||||
|
||||
this.TargetedUser.Banned = true;
|
||||
this.TargetedUser.BannedReason = reason;
|
||||
|
||||
// invalidate all currently active gametokens
|
||||
this.Database.GameTokens.RemoveRange(this.Database.GameTokens.Where(t => t.UserId == this.TargetedUser.UserId));
|
||||
|
||||
// invalidate all currently active webtokens
|
||||
this.Database.WebTokens.RemoveRange(this.Database.WebTokens.Where(t => t.UserId == this.TargetedUser.UserId));
|
||||
|
||||
await this.Database.SaveChangesAsync();
|
||||
return this.Redirect($"/user/{this.TargetedUser.UserId}");
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
@page "/admin"
|
||||
@using LBPUnion.ProjectLighthouse.Helpers
|
||||
@using LBPUnion.ProjectLighthouse.Maintenance
|
||||
@model LBPUnion.ProjectLighthouse.Pages.AdminPanelPage
|
||||
@model LBPUnion.ProjectLighthouse.Pages.Admin.AdminPanelPage
|
||||
|
||||
@{
|
||||
Layout = "Layouts/BaseLayout";
|
|
@ -7,7 +7,7 @@ using LBPUnion.ProjectLighthouse.Pages.Layouts;
|
|||
using LBPUnion.ProjectLighthouse.Types;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Pages
|
||||
namespace LBPUnion.ProjectLighthouse.Pages.Admin
|
||||
{
|
||||
public class AdminPanelPage : BaseLayout
|
||||
{
|
|
@ -24,7 +24,7 @@
|
|||
<div class="header">
|
||||
Uh oh!
|
||||
</div>
|
||||
<p>@Model.Error</p>
|
||||
<p style="white-space: pre-line">@Model.Error</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
#nullable enable
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Kettu;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Pages.Layouts;
|
||||
using LBPUnion.ProjectLighthouse.Types;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
@ -36,16 +38,25 @@ namespace LBPUnion.ProjectLighthouse.Pages
|
|||
User? user = await this.Database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
||||
if (user == null)
|
||||
{
|
||||
Logger.Log($"User {username} failed to login on web due to invalid username", LoggerLevelLogin.Instance);
|
||||
this.Error = "The username or password you entered is invalid.";
|
||||
return this.Page();
|
||||
}
|
||||
|
||||
if (!BCrypt.Net.BCrypt.Verify(password, user.Password))
|
||||
{
|
||||
Logger.Log($"User {user.Username} (id: {user.UserId}) failed to login on web due to invalid password", LoggerLevelLogin.Instance);
|
||||
this.Error = "The username or password you entered is invalid.";
|
||||
return this.Page();
|
||||
}
|
||||
|
||||
if (user.Banned)
|
||||
{
|
||||
Logger.Log($"User {user.Username} (id: {user.UserId}) failed to login on web due to being banned", LoggerLevelLogin.Instance);
|
||||
this.Error = "You have been banned. Please contact an administrator for more information.\nReason: " + user.BannedReason;
|
||||
return this.Page();
|
||||
}
|
||||
|
||||
WebToken webToken = new()
|
||||
{
|
||||
UserId = user.UserId,
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
Model.Title = "Password Reset Required";
|
||||
}
|
||||
|
||||
<p>An admin has deemed it necessary that you reset your password. Please do so.</p>
|
||||
<p>An administrator has deemed it necessary that you reset your password. Please do so.</p>
|
||||
|
||||
<a href="/passwordReset">
|
||||
<div class="ui blue button">Reset Password</div>
|
||||
|
|
|
@ -14,12 +14,34 @@
|
|||
Model.Description = Model.ProfileUser!.Biography;
|
||||
}
|
||||
|
||||
@if (Model.ProfileUser.Banned)
|
||||
{
|
||||
<div class="ui inverted red segment">
|
||||
<p>
|
||||
<b>User is currently banned!</b>
|
||||
</p>
|
||||
@if (Model.User != null && Model.User.IsAdmin)
|
||||
{
|
||||
<p>Reason: @Model.ProfileUser.BannedReason</p>
|
||||
<a class="ui inverted button" href="/admin/user/@Model.ProfileUser.UserId/unban">
|
||||
<i class="ban icon"></i>
|
||||
<span>Unban User</span>
|
||||
</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p>For shame...</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="ui grid">
|
||||
<div class="eight wide column">
|
||||
<h1>@Model.Title</h1>
|
||||
<p>
|
||||
<i>@Model.ProfileUser!.Status</i>
|
||||
</p>
|
||||
|
||||
<div class="statsUnderTitle">
|
||||
<i class="pink heart icon" title="Hearts"></i> <span>@Model.ProfileUser.Hearts</span>
|
||||
<i class="blue comment icon" title="Comments"></i> <span>@Model.ProfileUser.Comments</span>
|
||||
|
@ -53,6 +75,13 @@
|
|||
<span>Reset Password</span>
|
||||
</a>
|
||||
}
|
||||
@if (Model.User != null && Model.User.IsAdmin && !Model.ProfileUser.Banned)
|
||||
{
|
||||
<a class="ui red button" href="/admin/user/@Model.ProfileUser.UserId/ban">
|
||||
<i class="ban icon"></i>
|
||||
<span>Ban User</span>
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
<div class="eight wide column">
|
||||
<div class="ui blue segment">
|
||||
|
|
|
@ -87,11 +87,34 @@ namespace LBPUnion.ProjectLighthouse
|
|||
Stopwatch requestStopwatch = new();
|
||||
requestStopwatch.Start();
|
||||
|
||||
context.Request.EnableBuffering(); // Allows us to reset the position of Request.Body for later logging
|
||||
|
||||
// Log all headers.
|
||||
// foreach (KeyValuePair<string, StringValues> header in context.Request.Headers) Logger.Log($"{header.Key}: {header.Value}");
|
||||
|
||||
context.Request.EnableBuffering(); // Allows us to reset the position of Request.Body for later logging
|
||||
await next(context); // Handle the request so we can get the status code from it
|
||||
|
||||
requestStopwatch.Stop();
|
||||
|
||||
Logger.Log
|
||||
(
|
||||
$"{context.Response.StatusCode}, {requestStopwatch.ElapsedMilliseconds}ms: {context.Request.Method} {context.Request.Path}{context.Request.QueryString}",
|
||||
LoggerLevelHttp.Instance
|
||||
);
|
||||
|
||||
if (context.Request.Method == "POST")
|
||||
{
|
||||
context.Request.Body.Position = 0;
|
||||
Logger.Log(await new StreamReader(context.Request.Body).ReadToEndAsync(), LoggerLevelHttp.Instance);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Digest check
|
||||
app.Use
|
||||
(
|
||||
async (context, next) =>
|
||||
{
|
||||
// Client digest check.
|
||||
if (!context.Request.Cookies.TryGetValue("MM_AUTH", out string authCookie)) authCookie = string.Empty;
|
||||
string digestPath = context.Request.Path;
|
||||
|
@ -119,7 +142,7 @@ namespace LBPUnion.ProjectLighthouse
|
|||
Stream oldResponseStream = context.Response.Body;
|
||||
context.Response.Body = responseBuffer;
|
||||
|
||||
await next(); // Handle the request so we can get the status code from it
|
||||
await next(context); // Handle the request so we can get the server digest hash
|
||||
|
||||
// Compute the server digest hash.
|
||||
if (computeDigests)
|
||||
|
@ -138,7 +161,13 @@ namespace LBPUnion.ProjectLighthouse
|
|||
responseBuffer.Position = 0;
|
||||
await responseBuffer.CopyToAsync(oldResponseStream);
|
||||
context.Response.Body = oldResponseStream;
|
||||
}
|
||||
);
|
||||
|
||||
app.Use
|
||||
(
|
||||
async (context, next) =>
|
||||
{
|
||||
#nullable enable
|
||||
// Log LastContact for LBP1. This is done on LBP2/3/V on a Match request.
|
||||
if (context.Request.Path.ToString().StartsWith("/LITTLEBIGPLANETPS3_XML"))
|
||||
|
@ -153,19 +182,7 @@ namespace LBPUnion.ProjectLighthouse
|
|||
}
|
||||
#nullable disable
|
||||
|
||||
requestStopwatch.Stop();
|
||||
|
||||
Logger.Log
|
||||
(
|
||||
$"{context.Response.StatusCode}, {requestStopwatch.ElapsedMilliseconds}ms: {context.Request.Method} {context.Request.Path}{context.Request.QueryString}",
|
||||
LoggerLevelHttp.Instance
|
||||
);
|
||||
|
||||
if (context.Request.Method == "POST")
|
||||
{
|
||||
context.Request.Body.Position = 0;
|
||||
Logger.Log(await new StreamReader(context.Request.Body).ReadToEndAsync(), LoggerLevelHttp.Instance);
|
||||
}
|
||||
await next(context);
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace LBPUnion.ProjectLighthouse.Types.Settings
|
|||
public class ServerSettings
|
||||
{
|
||||
|
||||
public const int CurrentConfigVersion = 13; // MUST BE INCREMENTED FOR EVERY CONFIG CHANGE!
|
||||
public const int CurrentConfigVersion = 14; // MUST BE INCREMENTED FOR EVERY CONFIG CHANGE!
|
||||
static ServerSettings()
|
||||
{
|
||||
if (ServerStatics.IsUnitTesting) return; // Unit testing, we don't want to read configurations here since the tests will provide their own
|
||||
|
@ -74,6 +74,12 @@ namespace LBPUnion.ProjectLighthouse.Types.Settings
|
|||
|
||||
public string EulaText { get; set; } = "";
|
||||
|
||||
#if !DEBUG
|
||||
public string AnnounceText { get; set; } = "You are now logged in as %user.";
|
||||
#else
|
||||
public string AnnounceText { get; set; } = "You are now logged in as %user (id: %id).";
|
||||
#endif
|
||||
|
||||
public string DbConnectionString { get; set; } = "server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse";
|
||||
|
||||
public string ExternalUrl { get; set; } = "http://localhost:10060";
|
||||
|
|
|
@ -125,6 +125,10 @@ namespace LBPUnion.ProjectLighthouse.Types
|
|||
}
|
||||
#nullable disable
|
||||
|
||||
public bool Banned { get; set; }
|
||||
|
||||
public string BannedReason { get; set; }
|
||||
|
||||
public string Serialize(GameVersion gameVersion = GameVersion.LittleBigPlanet1)
|
||||
{
|
||||
string user = LbpSerializer.TaggedStringElement("npHandle", this.Username, "icon", this.IconHash) +
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# Project Lighthouse
|
||||
|
||||
Project Lighthouse is an umbrella project for all work to investigate and develop private servers for LittleBigPlanet.
|
||||
This project is the main server component that LittleBigPlanet games connect to.
|
||||
Project Lighthouse is a clean-room, open-source custom server for LittleBigPlanet. This is a project conducted by the [LBP Union Ministry of Technology Research and Development team.](https://www.lbpunion.com/technology) For concerns and inquiries about the project, please [contact us here.](https://www.lbpunion.com/contact) For general questions and discussion about Project Lighthouse, please see the [megathread](https://www.lbpunion.com/forum/union-hall/project-lighthouse-littlebigplanet-private-servers-megathread) on our forum.
|
||||
|
||||
## WARNING!
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue