diff --git a/.idea/.idea.ProjectLighthouse/.idea/jsLibraryMappings.xml b/.idea/.idea.ProjectLighthouse/.idea/jsLibraryMappings.xml index e0f60e14..6f9a9516 100644 --- a/.idea/.idea.ProjectLighthouse/.idea/jsLibraryMappings.xml +++ b/.idea/.idea.ProjectLighthouse/.idea/jsLibraryMappings.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/CaptchaHelper.cs b/ProjectLighthouse/Helpers/CaptchaHelper.cs new file mode 100644 index 00000000..3008b084 --- /dev/null +++ b/ProjectLighthouse/Helpers/CaptchaHelper.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Types.Settings; +using Newtonsoft.Json.Linq; + +namespace LBPUnion.ProjectLighthouse.Helpers; + +public static class CaptchaHelper +{ + private static readonly HttpClient client = new() + { + BaseAddress = new Uri("https://hcaptcha.com"), + }; + + public static async Task Verify(string token) + { + if (!ServerSettings.Instance.HCaptchaEnabled) return true; + + List> payload = new() + { + new("secret", ServerSettings.Instance.HCaptchaSecret), + new("response", token), + }; + + HttpResponseMessage response = await client.PostAsync("/siteverify", new FormUrlEncodedContent(payload)); + + response.EnsureSuccessStatusCode(); + + string responseBody = await response.Content.ReadAsStringAsync(); + + // We only really care about the success result, nothing else that hcaptcha sends us, so lets only parse that. + bool success = bool.Parse(JObject.Parse(responseBody)["success"]?.ToString() ?? "false"); + return success; + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Pages/LoginForm.cshtml b/ProjectLighthouse/Pages/LoginForm.cshtml index 2e5494a4..f07daa9f 100644 --- a/ProjectLighthouse/Pages/LoginForm.cshtml +++ b/ProjectLighthouse/Pages/LoginForm.cshtml @@ -50,6 +50,11 @@ + @if (ServerSettings.Instance.HCaptchaEnabled) + { + @await Html.PartialAsync("Partials/CaptchaPartial") + } + @if (ServerSettings.Instance.RegistrationEnabled) { diff --git a/ProjectLighthouse/Pages/LoginForm.cshtml.cs b/ProjectLighthouse/Pages/LoginForm.cshtml.cs index 5e19c5a4..d10d3f7d 100644 --- a/ProjectLighthouse/Pages/LoginForm.cshtml.cs +++ b/ProjectLighthouse/Pages/LoginForm.cshtml.cs @@ -6,8 +6,10 @@ using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Pages.Layouts; using LBPUnion.ProjectLighthouse.Types; +using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Primitives; namespace LBPUnion.ProjectLighthouse.Pages; @@ -18,8 +20,6 @@ public class LoginForm : BaseLayout public string Error { get; private set; } - public bool WasLoginRequest { get; private set; } - [UsedImplicitly] public async Task OnPost(string username, string password) { @@ -35,6 +35,19 @@ public class LoginForm : BaseLayout return this.Page(); } + if (ServerSettings.Instance.HCaptchaEnabled) + { + // && (!this.Request.Form.TryGetValue("h-captcha-response", out StringValues values) || !await CaptchaHelper.Verify(values[0]))) + bool gotCaptcha = this.Request.Form.TryGetValue("h-captcha-response", out StringValues values); + string? token = gotCaptcha ? values[0] : null; + + if (token == null || !await CaptchaHelper.Verify(token)) + { + this.Error = "You must solve the captcha correctly."; + return this.Page(); + } + } + User? user = await this.Database.Users.FirstOrDefaultAsync(u => u.Username == username); if (user == null) { @@ -68,6 +81,8 @@ public class LoginForm : BaseLayout this.Response.Cookies.Append("LighthouseToken", webToken.UserToken); + Logger.Log($"User {user.Username} (id: {user.UserId}) successfully logged in on web", LoggerLevelLogin.Instance); + if (user.PasswordResetRequired) return this.Redirect("~/passwordResetRequired"); return this.RedirectToPage(nameof(LandingPage)); diff --git a/ProjectLighthouse/Pages/Partials/CaptchaPartial.cshtml b/ProjectLighthouse/Pages/Partials/CaptchaPartial.cshtml new file mode 100644 index 00000000..e1891722 --- /dev/null +++ b/ProjectLighthouse/Pages/Partials/CaptchaPartial.cshtml @@ -0,0 +1,6 @@ +@using LBPUnion.ProjectLighthouse.Types.Settings +@if (ServerSettings.Instance.HCaptchaEnabled) +{ +
+ +} \ No newline at end of file diff --git a/ProjectLighthouse/Types/Settings/ServerSettings.cs b/ProjectLighthouse/Types/Settings/ServerSettings.cs index 53ce334e..fd99f22e 100644 --- a/ProjectLighthouse/Types/Settings/ServerSettings.cs +++ b/ProjectLighthouse/Types/Settings/ServerSettings.cs @@ -5,7 +5,6 @@ using System.Text.Json; using System.Text.Json.Serialization; using JetBrains.Annotations; using Kettu; -using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Logging; namespace LBPUnion.ProjectLighthouse.Types.Settings; @@ -13,7 +12,7 @@ namespace LBPUnion.ProjectLighthouse.Types.Settings; [Serializable] public class ServerSettings { - public const int CurrentConfigVersion = 18; // MUST BE INCREMENTED FOR EVERY CONFIG CHANGE! + public const int CurrentConfigVersion = 19; // MUST BE INCREMENTED FOR EVERY CONFIG CHANGE! private static FileSystemWatcher fileWatcher; static ServerSettings() { @@ -150,6 +149,12 @@ public class ServerSettings public string MissingIconHash { get; set; } = ""; + public bool HCaptchaEnabled { get; set; } + + public string HCaptchaSiteKey { get; set; } = ""; + + public string HCaptchaSecret { get; set; } = ""; + #region Meta [NotNull]