summaryrefslogtreecommitdiff
path: root/HBContext.cs
blob: dee100d7f9e57ce092811f6c372c0926d950e957 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
using Microsoft.EntityFrameworkCore;
using HyperBooru.Services;
using HyperBooru.PrincipalProviders;

namespace HyperBooru;

enum HBObjectId {
    NsfwTag   = -1,
    IngestTag = -2,
}

enum LocalPrincipalId {
    AdminUser = -1
}

public static class HBObjectGuid {
    public static readonly Guid NsfwTag   = new("EBDAD4F8-455A-4351-8017-1D4854D6FA38");
    public static readonly Guid IngestTag = new("EA212801-5BCC-4C0E-814F-FB9D30DB58BC");
}

public class HBContext : DbContext {
    public DbSet<HBObject>      Objects        { get; set; }
    public DbSet<TagDefinition> TagDefinitions { get; set; }
    public DbSet<Tag>           Tags           { get; set; }
    public DbSet<Media>         Media          { get; set; }
    public DbSet<UploadedFile>  UploadedFiles  { get; set; }
    public DbSet<OcrData>       OcrData        { get; set; }

    // Security-related tables
    public DbSet<LocalPrincipal> Principals { get; set; }
    public DbSet<LocalUser>      Users      { get; set; }
    public DbSet<LocalGroup>     Groups     { get; set; }
    public DbSet<Acl>            Acls       { get; set; }
    public DbSet<AclRule>        AclRules   { get; set; }

    private IConfigService config;

    public HBContext(DbContextOptions<HBContext> options, IConfigService config) : base(options) =>
        this.config = config;

    protected override void OnConfiguring(DbContextOptionsBuilder options) {
        options.UseNpgsql(config.DbConnectionString);

        #if DEBUG
        options.EnableSensitiveDataLogging();
        #endif
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder) {
        // Don't use shared tables for inherited types
        modelBuilder.Entity<HBObject>().ToTable("Objects");
        modelBuilder.Entity<TagDefinition>().ToTable("TagDefinitions");
        modelBuilder.Entity<Tag>().ToTable("Tags");
        modelBuilder.Entity<Media>().ToTable("Media");
        modelBuilder.Entity<UploadedFile>().ToTable("UploadedFiles");
        modelBuilder.Entity<LocalPrincipal>().ToTable("SecurityPrincipals");

        modelBuilder.Entity<HBObject>()
            .Property(o => o.Owner)
            .HasDefaultValue(WellKnownSid.WorldSid);

        // Seed internal tag definitions
        // These should NEVER change
        modelBuilder.Entity<TagDefinition>().HasData(new TagDefinition[] {
            new() {
                ObjectId = (int) HBObjectId.NsfwTag,
                Guid     = HBObjectGuid.NsfwTag,
                Source   = TagSource.Internal,
                Name     = "nsfw"
            },
            new() {
                ObjectId = (int) HBObjectId.IngestTag,
                Guid     = HBObjectGuid.IngestTag,
                Source   = TagSource.Internal,
                Name     = "ingest"
            }
        });

        // Seed initial admin user
        modelBuilder.Entity<LocalUser>().HasData(new LocalUser[] {
            new() {
                LocalPrincipalId = (int) LocalPrincipalId.AdminUser,
                Name             = "admin",
                Sid              = new SecurityIdentifier("S-1-5-18"),
                PasswordHash     = LocalPrincipalProvider.HashPassword("admin")
            }
        });

        // Some complex relationships cannot be inferred and require
        // additional configuration, as seen below.
        modelBuilder.Entity<TagDefinition>()
            .HasMany(e => e.ImplicitTags)
            .WithMany()
            .UsingEntity(e => e.ToTable("ImplicitTags"));

        modelBuilder.Entity<Media>()
            .HasOne(m => m.CurrentUploadedFile)
            .WithOne()
            .HasForeignKey<Media>("CurrentUploadedFileId");

        modelBuilder.Entity<LocalPrincipal>()
            .HasMany(p => p.MemberOf)
            .WithMany()
            .UsingEntity(e => e.ToTable("SecurityPrincipalMemberships"));
    }

    protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) {
        configurationBuilder
            .Properties<SecurityIdentifier>()
            .HaveConversion<SecurityIdentifierConverter>();
    }
}