summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Controllers/LoginController.cs26
-rw-r--r--HBContext.cs23
-rw-r--r--Media.cs2
-rw-r--r--Migrations/20230914040737_Users.Designer.cs358
-rw-r--r--Migrations/20230914040737_Users.cs61
-rw-r--r--Migrations/HBContextModelSnapshot.cs35
-rw-r--r--Pages/Component/Titlebar.razor16
-rw-r--r--Program.cs1
-rw-r--r--Services/UserService.cs13
-rw-r--r--User.cs9
10 files changed, 518 insertions, 26 deletions
diff --git a/Controllers/LoginController.cs b/Controllers/LoginController.cs
index fff3e6e..aa680a0 100644
--- a/Controllers/LoginController.cs
+++ b/Controllers/LoginController.cs
@@ -1,5 +1,7 @@
-using Microsoft.AspNetCore.Authentication;
+using HyperBooru.Services;
+using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
+using Microsoft.AspNetCore.Cryptography.KeyDerivation;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;
@@ -15,10 +17,20 @@ public class LoginController : Controller {
[HttpPost("Login")]
public async Task<IActionResult> Login(
[FromForm] string username,
- [FromForm] string password) {
+ [FromForm] string password,
+ HBContext db) {
+
+ var user = db.Users.FirstOrDefault(u => u.Username == username);
+ if(user is null)
+ return StatusCode(403);
+
+ var hash = UserService.HashPassword(password);
+ if(hash != user.PasswordHash)
+ return StatusCode(403);
var claims = new Claim[] {
- new Claim(ClaimTypes.NameIdentifier, username)
+ new Claim(ClaimTypes.Name, user.Username),
+ new Claim("ObjectId", user.ObjectId.ToString())
};
var claimsIdentity = new ClaimsIdentity(
@@ -27,12 +39,8 @@ public class LoginController : Controller {
var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
- if(username == "admin" && password == "test") {
- await httpContextAccessor.HttpContext!.SignInAsync(claimsPrincipal);
- return Ok();
- } else {
- return StatusCode(403);
- }
+ await httpContextAccessor.HttpContext!.SignInAsync(claimsPrincipal);
+ return Ok();
}
[HttpPost("Logout")]
diff --git a/HBContext.cs b/HBContext.cs
index 415b745..c15c20b 100644
--- a/HBContext.cs
+++ b/HBContext.cs
@@ -3,14 +3,18 @@ using HyperBooru.Services;
namespace HyperBooru;
-public class HBContext : DbContext {
- public const int NsfwTagId = -1;
- public const int IngestTagId = -2;
+enum HBObjectId {
+ NsfwTag = -1,
+ IngestTag = -2,
+ AdminUser = -3
+}
+public class HBContext : DbContext {
public static readonly Guid NsfwTag = new("EBDAD4F8-455A-4351-8017-1D4854D6FA38");
public static readonly Guid IngestTag = new("EA212801-5BCC-4C0E-814F-FB9D30DB58BC");
public DbSet<HBObject> Objects { get; set; }
+ public DbSet<User> Users { get; set; }
public DbSet<TagDefinition> TagDefinitions { get; set; }
public DbSet<Tag> Tags { get; set; }
public DbSet<Media> Media { get; set; }
@@ -42,19 +46,28 @@ public class HBContext : DbContext {
// These should NEVER change
modelBuilder.Entity<TagDefinition>().HasData(new TagDefinition[] {
new() {
- ObjectId = NsfwTagId,
+ ObjectId = (int) HBObjectId.NsfwTag,
Guid = NsfwTag,
Source = TagSource.Internal,
Name = "nsfw"
},
new() {
- ObjectId = IngestTagId,
+ ObjectId = (int) HBObjectId.IngestTag,
Guid = IngestTag,
Source = TagSource.Internal,
Name = "ingest"
}
});
+ // Seed initial admin user
+ modelBuilder.Entity<User>().HasData(new User[] {
+ new() {
+ ObjectId = (int) HBObjectId.AdminUser,
+ Username = "admin",
+ PasswordHash = UserService.HashPassword("admin")
+ }
+ });
+
// Some complex relationships cannot be inferred and require
// additional configuration, as seen below.
modelBuilder.Entity<TagDefinition>()
diff --git a/Media.cs b/Media.cs
index 8c15ad0..9664250 100644
--- a/Media.cs
+++ b/Media.cs
@@ -15,7 +15,7 @@ public class Media : HBObject {
public bool IsIngest => Tags
.Select(t => t.TagDefinitionId)
- .Contains(HBContext.IngestTagId);
+ .Contains((int) HBObjectId.IngestTag);
public string? DisplayName {
get {
diff --git a/Migrations/20230914040737_Users.Designer.cs b/Migrations/20230914040737_Users.Designer.cs
new file mode 100644
index 0000000..7e3a9d9
--- /dev/null
+++ b/Migrations/20230914040737_Users.Designer.cs
@@ -0,0 +1,358 @@
+// <auto-generated />
+using System;
+using HyperBooru;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+
+#nullable disable
+
+namespace HyperBooru.Migrations
+{
+ [DbContext(typeof(HBContext))]
+ [Migration("20230914040737_Users")]
+ partial class Users
+ {
+ /// <inheritdoc />
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "7.0.10")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("HyperBooru.HBObject", b =>
+ {
+ b.Property<int>("ObjectId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("ObjectId"));
+
+ b.Property<Guid>("Guid")
+ .HasColumnType("uuid");
+
+ b.HasKey("ObjectId");
+
+ b.HasIndex("Guid");
+
+ b.ToTable("Objects", (string)null);
+
+ b.UseTptMappingStrategy();
+ });
+
+ modelBuilder.Entity("HyperBooru.OcrData", b =>
+ {
+ b.Property<int>("OcrDataId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("OcrDataId"));
+
+ b.Property<int>("MediaId")
+ .HasColumnType("integer");
+
+ b.Property<string>("SearchableText")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property<string>("Text")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property<DateTime>("Timestamp")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("OcrDataId");
+
+ b.HasIndex("MediaId")
+ .IsUnique();
+
+ b.ToTable("OcrData");
+ });
+
+ modelBuilder.Entity("TagDefinitionTagDefinition", b =>
+ {
+ b.Property<int>("ImplicitTagsObjectId")
+ .HasColumnType("integer");
+
+ b.Property<int>("TagDefinitionObjectId")
+ .HasColumnType("integer");
+
+ b.HasKey("ImplicitTagsObjectId", "TagDefinitionObjectId");
+
+ b.HasIndex("TagDefinitionObjectId");
+
+ b.ToTable("TagDefinitionTagDefinition");
+ });
+
+ modelBuilder.Entity("HyperBooru.Media", b =>
+ {
+ b.HasBaseType("HyperBooru.HBObject");
+
+ b.Property<int>("CurrentUploadedFileId")
+ .HasColumnType("integer");
+
+ b.Property<string>("LongDescription")
+ .HasColumnType("text");
+
+ b.Property<string>("ShortDescription")
+ .HasColumnType("text");
+
+ b.HasIndex("CurrentUploadedFileId")
+ .IsUnique();
+
+ b.ToTable("Media", (string)null);
+ });
+
+ modelBuilder.Entity("HyperBooru.Tag", b =>
+ {
+ b.HasBaseType("HyperBooru.HBObject");
+
+ b.Property<DateTime>("CreateTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property<int>("TagDefinitionId")
+ .HasColumnType("integer");
+
+ b.Property<int>("TargetObjectId")
+ .HasColumnType("integer");
+
+ b.HasIndex("TagDefinitionId");
+
+ b.HasIndex("TargetObjectId");
+
+ b.ToTable("Tags", (string)null);
+ });
+
+ modelBuilder.Entity("HyperBooru.TagDefinition", b =>
+ {
+ b.HasBaseType("HyperBooru.HBObject");
+
+ b.Property<string>("Alias")
+ .HasColumnType("text");
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property<string>("Namespace")
+ .HasColumnType("text");
+
+ b.Property<int>("Source")
+ .HasColumnType("integer");
+
+ b.ToTable("TagDefinitions", (string)null);
+
+ b.HasData(
+ new
+ {
+ ObjectId = -1,
+ Guid = new Guid("ebdad4f8-455a-4351-8017-1d4854d6fa38"),
+ Name = "nsfw",
+ Source = 0
+ },
+ new
+ {
+ ObjectId = -2,
+ Guid = new Guid("ea212801-5bcc-4c0e-814f-fb9d30db58bc"),
+ Name = "ingest",
+ Source = 0
+ });
+ });
+
+ modelBuilder.Entity("HyperBooru.UploadedFile", b =>
+ {
+ b.HasBaseType("HyperBooru.HBObject");
+
+ b.Property<string>("Checksum")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property<bool>("ChecksumVerified")
+ .HasColumnType("boolean");
+
+ b.Property<DateTime?>("CreateTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property<string>("Filename")
+ .HasColumnType("text");
+
+ b.Property<int?>("Height")
+ .HasColumnType("integer");
+
+ b.Property<DateTime?>("LastAccessTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property<DateTime?>("LastWriteTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property<long>("Length")
+ .HasColumnType("bigint");
+
+ b.Property<int>("MediaObjectId")
+ .HasColumnType("integer");
+
+ b.Property<string>("MimeType")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property<DateTime>("UploadTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property<int?>("Width")
+ .HasColumnType("integer");
+
+ b.HasIndex("MediaObjectId");
+
+ b.ToTable("UploadedFiles", (string)null);
+ });
+
+ modelBuilder.Entity("HyperBooru.User", b =>
+ {
+ b.HasBaseType("HyperBooru.HBObject");
+
+ b.Property<string>("PasswordHash")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property<string>("Username")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasIndex("Username");
+
+ b.ToTable("Users");
+
+ b.HasData(
+ new
+ {
+ ObjectId = -3,
+ Guid = new Guid("4fa948f4-7c45-4f81-bb6b-e417491e6c96"),
+ PasswordHash = "P4geAuE2yX/PDRHuJSq74FF5vO782rWz5c0LAQPR8m45DEYAONhu1wYnAn60PSNyjocqEBdnCeKCJfK3sKyuWw==",
+ Username = "admin"
+ });
+ });
+
+ modelBuilder.Entity("HyperBooru.OcrData", b =>
+ {
+ b.HasOne("HyperBooru.Media", "Media")
+ .WithOne("OcrData")
+ .HasForeignKey("HyperBooru.OcrData", "MediaId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Media");
+ });
+
+ modelBuilder.Entity("TagDefinitionTagDefinition", b =>
+ {
+ b.HasOne("HyperBooru.TagDefinition", null)
+ .WithMany()
+ .HasForeignKey("ImplicitTagsObjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("HyperBooru.TagDefinition", null)
+ .WithMany()
+ .HasForeignKey("TagDefinitionObjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("HyperBooru.Media", b =>
+ {
+ b.HasOne("HyperBooru.UploadedFile", "CurrentUploadedFile")
+ .WithOne()
+ .HasForeignKey("HyperBooru.Media", "CurrentUploadedFileId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("HyperBooru.HBObject", null)
+ .WithOne()
+ .HasForeignKey("HyperBooru.Media", "ObjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("CurrentUploadedFile");
+ });
+
+ modelBuilder.Entity("HyperBooru.Tag", b =>
+ {
+ b.HasOne("HyperBooru.HBObject", null)
+ .WithOne()
+ .HasForeignKey("HyperBooru.Tag", "ObjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("HyperBooru.TagDefinition", "TagDefinition")
+ .WithMany()
+ .HasForeignKey("TagDefinitionId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("HyperBooru.HBObject", "Target")
+ .WithMany("Tags")
+ .HasForeignKey("TargetObjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("TagDefinition");
+
+ b.Navigation("Target");
+ });
+
+ modelBuilder.Entity("HyperBooru.TagDefinition", b =>
+ {
+ b.HasOne("HyperBooru.HBObject", null)
+ .WithOne()
+ .HasForeignKey("HyperBooru.TagDefinition", "ObjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("HyperBooru.UploadedFile", b =>
+ {
+ b.HasOne("HyperBooru.Media", "Media")
+ .WithMany("UploadedFiles")
+ .HasForeignKey("MediaObjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("HyperBooru.HBObject", null)
+ .WithOne()
+ .HasForeignKey("HyperBooru.UploadedFile", "ObjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Media");
+ });
+
+ modelBuilder.Entity("HyperBooru.User", b =>
+ {
+ b.HasOne("HyperBooru.HBObject", null)
+ .WithOne()
+ .HasForeignKey("HyperBooru.User", "ObjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("HyperBooru.HBObject", b =>
+ {
+ b.Navigation("Tags");
+ });
+
+ modelBuilder.Entity("HyperBooru.Media", b =>
+ {
+ b.Navigation("OcrData");
+
+ b.Navigation("UploadedFiles");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Migrations/20230914040737_Users.cs b/Migrations/20230914040737_Users.cs
new file mode 100644
index 0000000..3c77a28
--- /dev/null
+++ b/Migrations/20230914040737_Users.cs
@@ -0,0 +1,61 @@
+using System;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace HyperBooru.Migrations
+{
+ /// <inheritdoc />
+ public partial class Users : Migration
+ {
+ /// <inheritdoc />
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.CreateTable(
+ name: "Users",
+ columns: table => new
+ {
+ ObjectId = table.Column<int>(type: "integer", nullable: false),
+ Username = table.Column<string>(type: "text", nullable: false),
+ PasswordHash = table.Column<string>(type: "text", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Users", x => x.ObjectId);
+ table.ForeignKey(
+ name: "FK_Users_Objects_ObjectId",
+ column: x => x.ObjectId,
+ principalTable: "Objects",
+ principalColumn: "ObjectId",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ migrationBuilder.InsertData(
+ table: "Objects",
+ columns: new[] { "ObjectId", "Guid" },
+ values: new object[] { -3, new Guid("4fa948f4-7c45-4f81-bb6b-e417491e6c96") });
+
+ migrationBuilder.InsertData(
+ table: "Users",
+ columns: new[] { "ObjectId", "PasswordHash", "Username" },
+ values: new object[] { -3, "P4geAuE2yX/PDRHuJSq74FF5vO782rWz5c0LAQPR8m45DEYAONhu1wYnAn60PSNyjocqEBdnCeKCJfK3sKyuWw==", "admin" });
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Users_Username",
+ table: "Users",
+ column: "Username");
+ }
+
+ /// <inheritdoc />
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable(
+ name: "Users");
+
+ migrationBuilder.DeleteData(
+ table: "Objects",
+ keyColumn: "ObjectId",
+ keyValue: -3);
+ }
+ }
+}
diff --git a/Migrations/HBContextModelSnapshot.cs b/Migrations/HBContextModelSnapshot.cs
index a24b920..5dc4d8d 100644
--- a/Migrations/HBContextModelSnapshot.cs
+++ b/Migrations/HBContextModelSnapshot.cs
@@ -209,6 +209,32 @@ namespace HyperBooru.Migrations
b.ToTable("UploadedFiles", (string)null);
});
+ modelBuilder.Entity("HyperBooru.User", b =>
+ {
+ b.HasBaseType("HyperBooru.HBObject");
+
+ b.Property<string>("PasswordHash")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property<string>("Username")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasIndex("Username");
+
+ b.ToTable("Users");
+
+ b.HasData(
+ new
+ {
+ ObjectId = -3,
+ Guid = new Guid("4fa948f4-7c45-4f81-bb6b-e417491e6c96"),
+ PasswordHash = "P4geAuE2yX/PDRHuJSq74FF5vO782rWz5c0LAQPR8m45DEYAONhu1wYnAn60PSNyjocqEBdnCeKCJfK3sKyuWw==",
+ Username = "admin"
+ });
+ });
+
modelBuilder.Entity("HyperBooru.OcrData", b =>
{
b.HasOne("HyperBooru.Media", "Media")
@@ -303,6 +329,15 @@ namespace HyperBooru.Migrations
b.Navigation("Media");
});
+ modelBuilder.Entity("HyperBooru.User", b =>
+ {
+ b.HasOne("HyperBooru.HBObject", null)
+ .WithOne()
+ .HasForeignKey("HyperBooru.User", "ObjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
modelBuilder.Entity("HyperBooru.HBObject", b =>
{
b.Navigation("Tags");
diff --git a/Pages/Component/Titlebar.razor b/Pages/Component/Titlebar.razor
index bcd5f61..1772519 100644
--- a/Pages/Component/Titlebar.razor
+++ b/Pages/Component/Titlebar.razor
@@ -1,7 +1,4 @@
-@inject IUserService userService
-@inject NavigationManager navigationManager
-@inject IJSRuntime jsRuntime
-@inject AuthenticationStateProvider authStateProvider
+@inject IJSRuntime jsRuntime
<script suppress-error="BL9992">
async function login() {
@@ -18,11 +15,7 @@
});
if(resp.ok) {
- if(document.referrer) {
- window.location.href = document.referrer;
- } else {
- window.location.href = '/';
- }
+ window.location.href = '/';
} else if(resp.status == 403) {
var form = document.querySelector('form.login');
form.classList.remove('bad-login');
@@ -79,5 +72,10 @@
</AuthorizeView>
@code {
+ [CascadingParameter]
+ public Task<AuthenticationState> AuthState { get; set; }
+
private AboutDialog aboutDialog;
+
+ private string username => AuthState.GetAwaiter().GetResult().User.Identity?.Name ?? "fugg";
}
diff --git a/Program.cs b/Program.cs
index cda77d3..e78f0d4 100644
--- a/Program.cs
+++ b/Program.cs
@@ -8,7 +8,6 @@ namespace HyperBooru;
public class Program {
public static void Main(string[] args) {
var builder = WebApplication.CreateBuilder(args);
- builder.Services.AddAuthentication().AddCookie();
builder.Services.AddHttpContextAccessor();
builder.Services.AddAuthentication(
CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();
diff --git a/Services/UserService.cs b/Services/UserService.cs
index d2abea3..c333c4f 100644
--- a/Services/UserService.cs
+++ b/Services/UserService.cs
@@ -1,4 +1,6 @@
-namespace HyperBooru.Services;
+using Microsoft.AspNetCore.Cryptography.KeyDerivation;
+
+namespace HyperBooru.Services;
public interface IUserService {
public bool ShowNsfw { get; set; }
@@ -18,4 +20,13 @@ public class UserService : IUserService {
public event EventHandler<bool> ShowNsfwChanged;
private bool showNsfw = false;
+
+ public static string HashPassword(string password) =>
+ Convert.ToBase64String(
+ KeyDerivation.Pbkdf2(
+ password,
+ Array.Empty<byte>(),
+ KeyDerivationPrf.HMACSHA512,
+ 100_000,
+ 512 / 8));
}
diff --git a/User.cs b/User.cs
new file mode 100644
index 0000000..61ef03f
--- /dev/null
+++ b/User.cs
@@ -0,0 +1,9 @@
+using Microsoft.EntityFrameworkCore;
+
+namespace HyperBooru;
+
+[Index(nameof(Username))]
+public class User : HBObject {
+ public string Username { get; set; }
+ public string PasswordHash { get; set; }
+}