diff --git a/Moonlight/App/Database/DataContext.cs b/Moonlight/App/Database/DataContext.cs index 750ba18c..25547eb4 100644 --- a/Moonlight/App/Database/DataContext.cs +++ b/Moonlight/App/Database/DataContext.cs @@ -46,6 +46,7 @@ public class DataContext : DbContext public DbSet WebSpaces { get; set; } public DbSet SupportChatMessages { get; set; } public DbSet IpBans { get; set; } + public DbSet PermissionGroups { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { diff --git a/Moonlight/App/Database/Entities/PermissionGroup.cs b/Moonlight/App/Database/Entities/PermissionGroup.cs new file mode 100644 index 00000000..f6b44bab --- /dev/null +++ b/Moonlight/App/Database/Entities/PermissionGroup.cs @@ -0,0 +1,8 @@ +namespace Moonlight.App.Database.Entities; + +public class PermissionGroup +{ + public int Id { get; set; } + public string Name { get; set; } = ""; + public byte[] Permissions { get; set; } = Array.Empty(); +} \ No newline at end of file diff --git a/Moonlight/App/Database/Entities/User.cs b/Moonlight/App/Database/Entities/User.cs index 5135b3f7..be6036f4 100644 --- a/Moonlight/App/Database/Entities/User.cs +++ b/Moonlight/App/Database/Entities/User.cs @@ -1,4 +1,5 @@ -using Moonlight.App.Models.Misc; +using System.ComponentModel.DataAnnotations; +using Moonlight.App.Models.Misc; namespace Moonlight.App.Database.Entities; @@ -39,6 +40,8 @@ public class User public bool TotpEnabled { get; set; } = false; public string TotpSecret { get; set; } = ""; public DateTime TokenValidTime { get; set; } = DateTime.UtcNow; + public byte[] Permissions { get; set; } = Array.Empty(); + public PermissionGroup? PermissionGroup { get; set; } // Discord public ulong DiscordId { get; set; } diff --git a/Moonlight/App/Database/Migrations/20230715095531_AddPermissions.Designer.cs b/Moonlight/App/Database/Migrations/20230715095531_AddPermissions.Designer.cs new file mode 100644 index 00000000..d1edbaa4 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230715095531_AddPermissions.Designer.cs @@ -0,0 +1,1109 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Moonlight.App.Database; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + [DbContext(typeof(DataContext))] + [Migration("20230715095531_AddPermissions")] + partial class AddPermissions + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Moonlight.App.Database.Entities.CloudPanel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ApiKey") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ApiUrl") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Host") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("CloudPanels"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.DdosAttack", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("bigint"); + + b.Property("Ip") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NodeId") + .HasColumnType("int"); + + b.Property("Ongoing") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("NodeId"); + + b.ToTable("DdosAttacks"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.DockerImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Default") + .HasColumnType("tinyint(1)"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.ToTable("DockerImages"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Domain", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("SharedDomainId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.HasIndex("SharedDomainId"); + + b.ToTable("Domains"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Image", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Allocations") + .HasColumnType("int"); + + b.Property("BackgroundImageUrl") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ConfigFiles") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InstallDockerImage") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InstallEntrypoint") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InstallScript") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Startup") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StartupDetection") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StopCommand") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TagsJson") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Uuid") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("Images"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ImageVariable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("DefaultValue") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Key") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.ToTable("ImageVariables"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.IpBan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Ip") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("IpBans"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.LoadingMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Message") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("LoadingMessages"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.AuditLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Ip") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("JsonData") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("System") + .HasColumnType("tinyint(1)"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("AuditLog"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.ErrorLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Class") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Ip") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("JsonData") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Stacktrace") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("System") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("ErrorLog"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.SecurityLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Ip") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("JsonData") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("System") + .HasColumnType("tinyint(1)"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("SecurityLog"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.MySqlDatabase", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("WebSpaceId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("WebSpaceId"); + + b.ToTable("Databases"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.NewsEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("Markdown") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Title") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("NewsEntries"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Node", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Fqdn") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("HttpPort") + .HasColumnType("int"); + + b.Property("MoonlightDaemonPort") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("SftpPort") + .HasColumnType("int"); + + b.Property("Ssl") + .HasColumnType("tinyint(1)"); + + b.Property("Token") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TokenId") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Nodes"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.NodeAllocation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("NodeId") + .HasColumnType("int"); + + b.Property("Port") + .HasColumnType("int"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("NodeId"); + + b.HasIndex("ServerId"); + + b.ToTable("NodeAllocations"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Action") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NotificationClientId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("NotificationClientId"); + + b.ToTable("NotificationActions"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationClient", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationClients"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Revoke", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Identifier") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Revokes"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ArchiveId") + .HasColumnType("int"); + + b.Property("Cpu") + .HasColumnType("int"); + + b.Property("Disk") + .HasColumnType("bigint"); + + b.Property("DockerImageIndex") + .HasColumnType("int"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Installing") + .HasColumnType("tinyint(1)"); + + b.Property("IsArchived") + .HasColumnType("tinyint(1)"); + + b.Property("IsCleanupException") + .HasColumnType("tinyint(1)"); + + b.Property("MainAllocationId") + .HasColumnType("int"); + + b.Property("Memory") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NodeId") + .HasColumnType("int"); + + b.Property("OverrideStartup") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("Suspended") + .HasColumnType("tinyint(1)"); + + b.Property("Uuid") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ArchiveId"); + + b.HasIndex("ImageId"); + + b.HasIndex("MainAllocationId"); + + b.HasIndex("NodeId"); + + b.HasIndex("OwnerId"); + + b.ToTable("Servers"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ServerBackup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Bytes") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("tinyint(1)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("Uuid") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ServerId"); + + b.ToTable("ServerBackups"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ServerVariable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Key") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ServerId"); + + b.ToTable("ServerVariables"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SharedDomain", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CloudflareId") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("SharedDomains"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.StatisticsData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Chart") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("Value") + .HasColumnType("double"); + + b.HasKey("Id"); + + b.ToTable("Statistics"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Subscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Currency") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Duration") + .HasColumnType("int"); + + b.Property("LimitsJson") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Price") + .HasColumnType("double"); + + b.Property("StripePriceId") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StripeProductId") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Subscriptions"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SupportChatMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Answer") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Attachment") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Content") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsQuestion") + .HasColumnType("tinyint(1)"); + + b.Property("QuestionType") + .HasColumnType("int"); + + b.Property("RecipientId") + .HasColumnType("int"); + + b.Property("SenderId") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("RecipientId"); + + b.HasIndex("SenderId"); + + b.ToTable("SupportChatMessages"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Address") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Admin") + .HasColumnType("tinyint(1)"); + + b.Property("City") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Country") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CurrentSubscriptionId") + .HasColumnType("int"); + + b.Property("DiscordId") + .HasColumnType("bigint unsigned"); + + b.Property("Email") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("HasRated") + .HasColumnType("tinyint(1)"); + + b.Property("LastIp") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastVisitedAt") + .HasColumnType("datetime(6)"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Permissions") + .IsRequired() + .HasColumnType("longblob"); + + b.Property("Rating") + .HasColumnType("int"); + + b.Property("RegisterIp") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerListLayoutJson") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("State") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("StreamerMode") + .HasColumnType("tinyint(1)"); + + b.Property("SubscriptionExpires") + .HasColumnType("datetime(6)"); + + b.Property("SubscriptionSince") + .HasColumnType("datetime(6)"); + + b.Property("SupportPending") + .HasColumnType("tinyint(1)"); + + b.Property("TokenValidTime") + .HasColumnType("datetime(6)"); + + b.Property("TotpEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("TotpSecret") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("CurrentSubscriptionId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.WebSpace", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CloudPanelId") + .HasColumnType("int"); + + b.Property("Domain") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("VHostTemplate") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CloudPanelId"); + + b.HasIndex("OwnerId"); + + b.ToTable("WebSpaces"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.DdosAttack", b => + { + b.HasOne("Moonlight.App.Database.Entities.Node", "Node") + .WithMany() + .HasForeignKey("NodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Node"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.DockerImage", b => + { + b.HasOne("Moonlight.App.Database.Entities.Image", null) + .WithMany("DockerImages") + .HasForeignKey("ImageId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Domain", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.SharedDomain", "SharedDomain") + .WithMany() + .HasForeignKey("SharedDomainId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + + b.Navigation("SharedDomain"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ImageVariable", b => + { + b.HasOne("Moonlight.App.Database.Entities.Image", null) + .WithMany("Variables") + .HasForeignKey("ImageId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.MySqlDatabase", b => + { + b.HasOne("Moonlight.App.Database.Entities.WebSpace", "WebSpace") + .WithMany("Databases") + .HasForeignKey("WebSpaceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("WebSpace"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.NodeAllocation", b => + { + b.HasOne("Moonlight.App.Database.Entities.Node", null) + .WithMany("Allocations") + .HasForeignKey("NodeId"); + + b.HasOne("Moonlight.App.Database.Entities.Server", null) + .WithMany("Allocations") + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationAction", b => + { + b.HasOne("Moonlight.App.Database.Entities.Notification.NotificationClient", "NotificationClient") + .WithMany() + .HasForeignKey("NotificationClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("NotificationClient"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationClient", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b => + { + b.HasOne("Moonlight.App.Database.Entities.ServerBackup", "Archive") + .WithMany() + .HasForeignKey("ArchiveId"); + + b.HasOne("Moonlight.App.Database.Entities.Image", "Image") + .WithMany() + .HasForeignKey("ImageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.NodeAllocation", "MainAllocation") + .WithMany() + .HasForeignKey("MainAllocationId"); + + b.HasOne("Moonlight.App.Database.Entities.Node", "Node") + .WithMany() + .HasForeignKey("NodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Archive"); + + b.Navigation("Image"); + + b.Navigation("MainAllocation"); + + b.Navigation("Node"); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ServerBackup", b => + { + b.HasOne("Moonlight.App.Database.Entities.Server", null) + .WithMany("Backups") + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ServerVariable", b => + { + b.HasOne("Moonlight.App.Database.Entities.Server", null) + .WithMany("Variables") + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SupportChatMessage", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "Recipient") + .WithMany() + .HasForeignKey("RecipientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.User", "Sender") + .WithMany() + .HasForeignKey("SenderId"); + + b.Navigation("Recipient"); + + b.Navigation("Sender"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.User", b => + { + b.HasOne("Moonlight.App.Database.Entities.Subscription", "CurrentSubscription") + .WithMany() + .HasForeignKey("CurrentSubscriptionId"); + + b.Navigation("CurrentSubscription"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.WebSpace", b => + { + b.HasOne("Moonlight.App.Database.Entities.CloudPanel", "CloudPanel") + .WithMany() + .HasForeignKey("CloudPanelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CloudPanel"); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Image", b => + { + b.Navigation("DockerImages"); + + b.Navigation("Variables"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Node", b => + { + b.Navigation("Allocations"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b => + { + b.Navigation("Allocations"); + + b.Navigation("Backups"); + + b.Navigation("Variables"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.WebSpace", b => + { + b.Navigation("Databases"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230715095531_AddPermissions.cs b/Moonlight/App/Database/Migrations/20230715095531_AddPermissions.cs new file mode 100644 index 00000000..05c6c80e --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230715095531_AddPermissions.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + /// + public partial class AddPermissions : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Permissions", + table: "Users", + type: "longblob", + nullable: false); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Permissions", + table: "Users"); + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230715214550_AddPermissionGroup.Designer.cs b/Moonlight/App/Database/Migrations/20230715214550_AddPermissionGroup.Designer.cs new file mode 100644 index 00000000..0e593f36 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230715214550_AddPermissionGroup.Designer.cs @@ -0,0 +1,1139 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Moonlight.App.Database; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + [DbContext(typeof(DataContext))] + [Migration("20230715214550_AddPermissionGroup")] + partial class AddPermissionGroup + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Moonlight.App.Database.Entities.CloudPanel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ApiKey") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ApiUrl") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Host") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("CloudPanels"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.DdosAttack", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("bigint"); + + b.Property("Ip") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NodeId") + .HasColumnType("int"); + + b.Property("Ongoing") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("NodeId"); + + b.ToTable("DdosAttacks"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.DockerImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Default") + .HasColumnType("tinyint(1)"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.ToTable("DockerImages"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Domain", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("SharedDomainId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.HasIndex("SharedDomainId"); + + b.ToTable("Domains"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Image", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Allocations") + .HasColumnType("int"); + + b.Property("BackgroundImageUrl") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ConfigFiles") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InstallDockerImage") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InstallEntrypoint") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InstallScript") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Startup") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StartupDetection") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StopCommand") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TagsJson") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Uuid") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("Images"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ImageVariable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("DefaultValue") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Key") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.ToTable("ImageVariables"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.IpBan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Ip") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("IpBans"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.LoadingMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Message") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("LoadingMessages"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.AuditLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Ip") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("JsonData") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("System") + .HasColumnType("tinyint(1)"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("AuditLog"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.ErrorLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Class") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Ip") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("JsonData") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Stacktrace") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("System") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("ErrorLog"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.SecurityLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Ip") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("JsonData") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("System") + .HasColumnType("tinyint(1)"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("SecurityLog"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.MySqlDatabase", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("WebSpaceId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("WebSpaceId"); + + b.ToTable("Databases"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.NewsEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("Markdown") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Title") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("NewsEntries"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Node", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Fqdn") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("HttpPort") + .HasColumnType("int"); + + b.Property("MoonlightDaemonPort") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("SftpPort") + .HasColumnType("int"); + + b.Property("Ssl") + .HasColumnType("tinyint(1)"); + + b.Property("Token") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TokenId") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Nodes"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.NodeAllocation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("NodeId") + .HasColumnType("int"); + + b.Property("Port") + .HasColumnType("int"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("NodeId"); + + b.HasIndex("ServerId"); + + b.ToTable("NodeAllocations"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Action") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NotificationClientId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("NotificationClientId"); + + b.ToTable("NotificationActions"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationClient", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationClients"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.PermissionGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Permissions") + .IsRequired() + .HasColumnType("longblob"); + + b.HasKey("Id"); + + b.ToTable("PermissionGroups"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Revoke", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Identifier") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Revokes"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ArchiveId") + .HasColumnType("int"); + + b.Property("Cpu") + .HasColumnType("int"); + + b.Property("Disk") + .HasColumnType("bigint"); + + b.Property("DockerImageIndex") + .HasColumnType("int"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Installing") + .HasColumnType("tinyint(1)"); + + b.Property("IsArchived") + .HasColumnType("tinyint(1)"); + + b.Property("IsCleanupException") + .HasColumnType("tinyint(1)"); + + b.Property("MainAllocationId") + .HasColumnType("int"); + + b.Property("Memory") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NodeId") + .HasColumnType("int"); + + b.Property("OverrideStartup") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("Suspended") + .HasColumnType("tinyint(1)"); + + b.Property("Uuid") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ArchiveId"); + + b.HasIndex("ImageId"); + + b.HasIndex("MainAllocationId"); + + b.HasIndex("NodeId"); + + b.HasIndex("OwnerId"); + + b.ToTable("Servers"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ServerBackup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Bytes") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("tinyint(1)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("Uuid") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ServerId"); + + b.ToTable("ServerBackups"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ServerVariable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Key") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ServerId"); + + b.ToTable("ServerVariables"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SharedDomain", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CloudflareId") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("SharedDomains"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.StatisticsData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Chart") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("Value") + .HasColumnType("double"); + + b.HasKey("Id"); + + b.ToTable("Statistics"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Subscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Currency") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Duration") + .HasColumnType("int"); + + b.Property("LimitsJson") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Price") + .HasColumnType("double"); + + b.Property("StripePriceId") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StripeProductId") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Subscriptions"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SupportChatMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Answer") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Attachment") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Content") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsQuestion") + .HasColumnType("tinyint(1)"); + + b.Property("QuestionType") + .HasColumnType("int"); + + b.Property("RecipientId") + .HasColumnType("int"); + + b.Property("SenderId") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("RecipientId"); + + b.HasIndex("SenderId"); + + b.ToTable("SupportChatMessages"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Address") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Admin") + .HasColumnType("tinyint(1)"); + + b.Property("City") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Country") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CurrentSubscriptionId") + .HasColumnType("int"); + + b.Property("DiscordId") + .HasColumnType("bigint unsigned"); + + b.Property("Email") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("HasRated") + .HasColumnType("tinyint(1)"); + + b.Property("LastIp") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastVisitedAt") + .HasColumnType("datetime(6)"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PermissionGroupId") + .HasColumnType("int"); + + b.Property("Permissions") + .IsRequired() + .HasColumnType("longblob"); + + b.Property("Rating") + .HasColumnType("int"); + + b.Property("RegisterIp") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerListLayoutJson") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("State") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("StreamerMode") + .HasColumnType("tinyint(1)"); + + b.Property("SubscriptionExpires") + .HasColumnType("datetime(6)"); + + b.Property("SubscriptionSince") + .HasColumnType("datetime(6)"); + + b.Property("SupportPending") + .HasColumnType("tinyint(1)"); + + b.Property("TokenValidTime") + .HasColumnType("datetime(6)"); + + b.Property("TotpEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("TotpSecret") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("CurrentSubscriptionId"); + + b.HasIndex("PermissionGroupId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.WebSpace", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CloudPanelId") + .HasColumnType("int"); + + b.Property("Domain") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("VHostTemplate") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CloudPanelId"); + + b.HasIndex("OwnerId"); + + b.ToTable("WebSpaces"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.DdosAttack", b => + { + b.HasOne("Moonlight.App.Database.Entities.Node", "Node") + .WithMany() + .HasForeignKey("NodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Node"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.DockerImage", b => + { + b.HasOne("Moonlight.App.Database.Entities.Image", null) + .WithMany("DockerImages") + .HasForeignKey("ImageId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Domain", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.SharedDomain", "SharedDomain") + .WithMany() + .HasForeignKey("SharedDomainId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + + b.Navigation("SharedDomain"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ImageVariable", b => + { + b.HasOne("Moonlight.App.Database.Entities.Image", null) + .WithMany("Variables") + .HasForeignKey("ImageId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.MySqlDatabase", b => + { + b.HasOne("Moonlight.App.Database.Entities.WebSpace", "WebSpace") + .WithMany("Databases") + .HasForeignKey("WebSpaceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("WebSpace"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.NodeAllocation", b => + { + b.HasOne("Moonlight.App.Database.Entities.Node", null) + .WithMany("Allocations") + .HasForeignKey("NodeId"); + + b.HasOne("Moonlight.App.Database.Entities.Server", null) + .WithMany("Allocations") + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationAction", b => + { + b.HasOne("Moonlight.App.Database.Entities.Notification.NotificationClient", "NotificationClient") + .WithMany() + .HasForeignKey("NotificationClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("NotificationClient"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationClient", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b => + { + b.HasOne("Moonlight.App.Database.Entities.ServerBackup", "Archive") + .WithMany() + .HasForeignKey("ArchiveId"); + + b.HasOne("Moonlight.App.Database.Entities.Image", "Image") + .WithMany() + .HasForeignKey("ImageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.NodeAllocation", "MainAllocation") + .WithMany() + .HasForeignKey("MainAllocationId"); + + b.HasOne("Moonlight.App.Database.Entities.Node", "Node") + .WithMany() + .HasForeignKey("NodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Archive"); + + b.Navigation("Image"); + + b.Navigation("MainAllocation"); + + b.Navigation("Node"); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ServerBackup", b => + { + b.HasOne("Moonlight.App.Database.Entities.Server", null) + .WithMany("Backups") + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ServerVariable", b => + { + b.HasOne("Moonlight.App.Database.Entities.Server", null) + .WithMany("Variables") + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SupportChatMessage", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "Recipient") + .WithMany() + .HasForeignKey("RecipientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.User", "Sender") + .WithMany() + .HasForeignKey("SenderId"); + + b.Navigation("Recipient"); + + b.Navigation("Sender"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.User", b => + { + b.HasOne("Moonlight.App.Database.Entities.Subscription", "CurrentSubscription") + .WithMany() + .HasForeignKey("CurrentSubscriptionId"); + + b.HasOne("Moonlight.App.Database.Entities.PermissionGroup", "PermissionGroup") + .WithMany() + .HasForeignKey("PermissionGroupId"); + + b.Navigation("CurrentSubscription"); + + b.Navigation("PermissionGroup"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.WebSpace", b => + { + b.HasOne("Moonlight.App.Database.Entities.CloudPanel", "CloudPanel") + .WithMany() + .HasForeignKey("CloudPanelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CloudPanel"); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Image", b => + { + b.Navigation("DockerImages"); + + b.Navigation("Variables"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Node", b => + { + b.Navigation("Allocations"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b => + { + b.Navigation("Allocations"); + + b.Navigation("Backups"); + + b.Navigation("Variables"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.WebSpace", b => + { + b.Navigation("Databases"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230715214550_AddPermissionGroup.cs b/Moonlight/App/Database/Migrations/20230715214550_AddPermissionGroup.cs new file mode 100644 index 00000000..c7201a48 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230715214550_AddPermissionGroup.cs @@ -0,0 +1,68 @@ +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + /// + public partial class AddPermissionGroup : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "PermissionGroupId", + table: "Users", + type: "int", + nullable: true); + + migrationBuilder.CreateTable( + name: "PermissionGroups", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Name = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Permissions = table.Column(type: "longblob", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_PermissionGroups", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_Users_PermissionGroupId", + table: "Users", + column: "PermissionGroupId"); + + migrationBuilder.AddForeignKey( + name: "FK_Users_PermissionGroups_PermissionGroupId", + table: "Users", + column: "PermissionGroupId", + principalTable: "PermissionGroups", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Users_PermissionGroups_PermissionGroupId", + table: "Users"); + + migrationBuilder.DropTable( + name: "PermissionGroups"); + + migrationBuilder.DropIndex( + name: "IX_Users_PermissionGroupId", + table: "Users"); + + migrationBuilder.DropColumn( + name: "PermissionGroupId", + table: "Users"); + } + } +} diff --git a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs index 701c21d8..b14e3239 100644 --- a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs +++ b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs @@ -475,6 +475,25 @@ namespace Moonlight.App.Database.Migrations b.ToTable("NotificationClients"); }); + modelBuilder.Entity("Moonlight.App.Database.Entities.PermissionGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Permissions") + .IsRequired() + .HasColumnType("longblob"); + + b.HasKey("Id"); + + b.ToTable("PermissionGroups"); + }); + modelBuilder.Entity("Moonlight.App.Database.Entities.Revoke", b => { b.Property("Id") @@ -798,6 +817,13 @@ namespace Moonlight.App.Database.Migrations .IsRequired() .HasColumnType("longtext"); + b.Property("PermissionGroupId") + .HasColumnType("int"); + + b.Property("Permissions") + .IsRequired() + .HasColumnType("longblob"); + b.Property("Rating") .HasColumnType("int"); @@ -845,6 +871,8 @@ namespace Moonlight.App.Database.Migrations b.HasIndex("CurrentSubscriptionId"); + b.HasIndex("PermissionGroupId"); + b.ToTable("Users"); }); @@ -1049,7 +1077,13 @@ namespace Moonlight.App.Database.Migrations .WithMany() .HasForeignKey("CurrentSubscriptionId"); + b.HasOne("Moonlight.App.Database.Entities.PermissionGroup", "PermissionGroup") + .WithMany() + .HasForeignKey("PermissionGroupId"); + b.Navigation("CurrentSubscription"); + + b.Navigation("PermissionGroup"); }); modelBuilder.Entity("Moonlight.App.Database.Entities.WebSpace", b => diff --git a/Moonlight/App/Helpers/BitHelper.cs b/Moonlight/App/Helpers/BitHelper.cs new file mode 100644 index 00000000..4f0dacd3 --- /dev/null +++ b/Moonlight/App/Helpers/BitHelper.cs @@ -0,0 +1,88 @@ +namespace Moonlight.App.Helpers; + +public class BitHelper +{ + public static bool ReadBit(byte[] byteArray, int bitIndex) + { + if (bitIndex < 0) + throw new ArgumentOutOfRangeException("bitIndex"); + + int byteIndex = bitIndex / 8; + if (byteIndex >= byteArray.Length) + throw new ArgumentOutOfRangeException("bitIndex"); + + int bitNumber = bitIndex % 8; + byte mask = (byte)(1 << bitNumber); + + return (byteArray[byteIndex] & mask) != 0; + } + + public static byte[] WriteBit(byte[] byteArray, int bitIndex, bool value) + { + if (bitIndex < 0) + throw new ArgumentOutOfRangeException("bitIndex"); + + int byteIndex = bitIndex / 8; + byte[] resultArray; + + if (byteIndex >= byteArray.Length) + { + // Create a new array with increased size and copy elements from old array + resultArray = new byte[byteIndex + 1]; + Array.Copy(byteArray, resultArray, byteArray.Length); + } + else + { + // Create a new array and copy elements from old array + resultArray = new byte[byteArray.Length]; + Array.Copy(byteArray, resultArray, byteArray.Length); + } + + int bitNumber = bitIndex % 8; + byte mask = (byte)(1 << bitNumber); + + if (value) + resultArray[byteIndex] |= mask; // Set the bit to 1 + else + resultArray[byteIndex] &= (byte)~mask; // Set the bit to 0 + + return resultArray; + } + + public static byte[] OverwriteByteArrays(byte[] targetArray, byte[] overwriteArray) + { + int targetLength = targetArray.Length; + int overwriteLength = overwriteArray.Length; + + int maxLength = Math.Max(targetLength, overwriteLength); + + byte[] resultArray = new byte[maxLength]; + + for (int i = 0; i < maxLength; i++) + { + byte targetByte = i < targetLength ? targetArray[i] : (byte)0; + byte overwriteByte = i < overwriteLength ? overwriteArray[i] : (byte)0; + + for (int j = 0; j < 8; j++) + { + bool overwriteBit = (overwriteByte & (1 << j)) != 0; + if (i < targetLength) + { + bool targetBit = (targetByte & (1 << j)) != 0; + if (overwriteBit) + { + targetByte = targetBit ? (byte)(targetByte | (1 << j)) : (byte)(targetByte & ~(1 << j)); + } + } + else if (overwriteBit) + { + targetByte |= (byte)(1 << j); + } + } + + resultArray[i] = targetByte; + } + + return resultArray; + } +} \ No newline at end of file diff --git a/Moonlight/App/Http/Controllers/Api/Moonlight/BillingController.cs b/Moonlight/App/Http/Controllers/Api/Moonlight/BillingController.cs index 9878b736..d31dd427 100644 --- a/Moonlight/App/Http/Controllers/Api/Moonlight/BillingController.cs +++ b/Moonlight/App/Http/Controllers/Api/Moonlight/BillingController.cs @@ -24,7 +24,7 @@ public class BillingController : Controller [HttpGet("cancel")] public async Task Cancel() { - var user = await IdentityService.Get(); + var user = IdentityService.User; if (user == null) return Redirect("/login"); @@ -35,7 +35,7 @@ public class BillingController : Controller [HttpGet("success")] public async Task Success() { - var user = await IdentityService.Get(); + var user = IdentityService.User; if (user == null) return Redirect("/login"); diff --git a/Moonlight/App/Http/Controllers/Api/Moonlight/Notifications/RegisterController.cs b/Moonlight/App/Http/Controllers/Api/Moonlight/Notifications/RegisterController.cs index 9cbade42..8fc13ffb 100644 --- a/Moonlight/App/Http/Controllers/Api/Moonlight/Notifications/RegisterController.cs +++ b/Moonlight/App/Http/Controllers/Api/Moonlight/Notifications/RegisterController.cs @@ -25,7 +25,7 @@ public class RegisterController : Controller [HttpGet] public async Task> Register() { - var user = await IdentityService.Get(); + var user = IdentityService.User; if (user == null) return NotFound(); diff --git a/Moonlight/App/Http/Controllers/Api/Moonlight/OAuth2Controller.cs b/Moonlight/App/Http/Controllers/Api/Moonlight/OAuth2Controller.cs index bae68b49..a21b804f 100644 --- a/Moonlight/App/Http/Controllers/Api/Moonlight/OAuth2Controller.cs +++ b/Moonlight/App/Http/Controllers/Api/Moonlight/OAuth2Controller.cs @@ -54,7 +54,7 @@ public class OAuth2Controller : Controller { try { - var currentUser = await IdentityService.Get(); + var currentUser = IdentityService.User; if (currentUser != null) { diff --git a/Moonlight/App/Models/Forms/UserEditDataModel.cs b/Moonlight/App/Models/Forms/UserEditDataModel.cs new file mode 100644 index 00000000..1af0ccb0 --- /dev/null +++ b/Moonlight/App/Models/Forms/UserEditDataModel.cs @@ -0,0 +1,34 @@ +using System.ComponentModel.DataAnnotations; +using Moonlight.App.Database.Entities; +using Moonlight.App.Models.Misc; + +namespace Moonlight.App.Models.Forms; + +public class UserEditDataModel +{ + [Required] + public string FirstName { get; set; } = ""; + + [Required] + public string LastName { get; set; } = ""; + + [Required] + public string Email { get; set; } = ""; + + [Required] + public string Address { get; set; } = ""; + + [Required] + public string City { get; set; } = ""; + + [Required] + public string State { get; set; } = ""; + + [Required] + public string Country { get; set; } = ""; + + public bool Admin { get; set; } + public bool TotpEnabled { get; set; } + public ulong DiscordId { get; set; } + public PermissionGroup? PermissionGroup { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Perms/Permission.cs b/Moonlight/App/Perms/Permission.cs new file mode 100644 index 00000000..affffd75 --- /dev/null +++ b/Moonlight/App/Perms/Permission.cs @@ -0,0 +1,10 @@ +namespace Moonlight.App.Perms; + +public class Permission +{ + public int Index { get; set; } = 0; + public string Name { get; set; } = ""; + public string Description { get; set; } = ""; + + public static implicit operator int(Permission permission) => permission.Index; +} \ No newline at end of file diff --git a/Moonlight/App/Perms/PermissionRequired.cs b/Moonlight/App/Perms/PermissionRequired.cs new file mode 100644 index 00000000..c6e321b8 --- /dev/null +++ b/Moonlight/App/Perms/PermissionRequired.cs @@ -0,0 +1,11 @@ +namespace Moonlight.App.Perms; + +public class PermissionRequired : Attribute +{ + public string Name { get; private set; } + + public PermissionRequired(string name) + { + Name = name; + } +} \ No newline at end of file diff --git a/Moonlight/App/Perms/PermissionStorage.cs b/Moonlight/App/Perms/PermissionStorage.cs new file mode 100644 index 00000000..f5ddaa91 --- /dev/null +++ b/Moonlight/App/Perms/PermissionStorage.cs @@ -0,0 +1,55 @@ +using System.Data; +using Moonlight.App.Helpers; + +namespace Moonlight.App.Perms; + +public class PermissionStorage +{ + public byte[] Data; + public bool IsReadyOnly; + + public PermissionStorage(byte[] data, bool isReadyOnly = false) + { + Data = data; + IsReadyOnly = isReadyOnly; + } + + public bool this[Permission permission] + { + get + { + try + { + return BitHelper.ReadBit(Data, permission.Index); + } + catch (ArgumentOutOfRangeException) + { + return false; + } + catch (Exception e) + { + Logger.Verbose("Error reading permissions. (Can be intentional)"); + Logger.Verbose(e); + return false; + } + } + set + { + if (IsReadyOnly) + throw new ReadOnlyException(); + + Data = BitHelper.WriteBit(Data, permission.Index, value); + } + } + + public bool HasAnyPermissions() + { + foreach (var permission in Permissions.GetAllPermissions()) + { + if (this[permission]) + return true; + } + + return false; + } +} \ No newline at end of file diff --git a/Moonlight/App/Perms/Permissions.cs b/Moonlight/App/Perms/Permissions.cs new file mode 100644 index 00000000..96701835 --- /dev/null +++ b/Moonlight/App/Perms/Permissions.cs @@ -0,0 +1,424 @@ +namespace Moonlight.App.Perms; + +public static class Permissions +{ + public static Permission AdminDashboard = new() + { + Index = 0, + Name = "Admin Dashboard", + Description = "Access the main admin dashboard page" + }; + + public static Permission AdminStatistics = new() + { + Index = 1, + Name = "Admin Statistics", + Description = "View statistical information about the moonlight instance" + }; + + public static Permission AdminDomains = new() + { + Index = 4, + Name = "Admin Domains", + Description = "Manage domains in the admin area" + }; + + public static Permission AdminNewDomain = new() + { + Index = 5, + Name = "Admin New Domain", + Description = "Create a new domain in the admin area" + }; + + public static Permission AdminSharedDomains = new() + { + Index = 6, + Name = "Admin Shared Domains", + Description = "Manage shared domains in the admin area" + }; + + public static Permission AdminNewSharedDomain = new() + { + Index = 7, + Name = "Admin New Shared Domain", + Description = "Create a new shared domain in the admin area" + }; + + public static Permission AdminNodeDdos = new() + { + Index = 8, + Name = "Admin Node DDoS", + Description = "Manage DDoS protection for nodes in the admin area" + }; + + public static Permission AdminNodeEdit = new() + { + Index = 9, + Name = "Admin Node Edit", + Description = "Edit node settings in the admin area" + }; + + public static Permission AdminNodes = new() + { + Index = 10, + Name = "Admin Node", + Description = "Access the node management page in the admin area" + }; + + public static Permission AdminNewNode = new() + { + Index = 11, + Name = "Admin New Node", + Description = "Create a new node in the admin area" + }; + + public static Permission AdminNodeSetup = new() + { + Index = 12, + Name = "Admin Node Setup", + Description = "Set up a node in the admin area" + }; + + public static Permission AdminNodeView = new() + { + Index = 13, + Name = "Admin Node View", + Description = "View node details in the admin area" + }; + + public static Permission AdminNotificationDebugging = new() + { + Index = 14, + Name = "Admin Notification Debugging", + Description = "Manage debugging notifications in the admin area" + }; + + public static Permission AdminServerCleanup = new() + { + Index = 15, + Name = "Admin Server Cleanup", + Description = "Perform server cleanup tasks in the admin area" + }; + + public static Permission AdminServerEdit = new() + { + Index = 16, + Name = "Admin Server Edit", + Description = "Edit server settings in the admin area" + }; + + public static Permission AdminServers = new() + { + Index = 17, + Name = "Admin Server", + Description = "Access the server management page in the admin area" + }; + + public static Permission AdminServerManager = new() + { + Index = 18, + Name = "Admin Server Manager", + Description = "Manage servers in the admin area" + }; + + public static Permission AdminNewServer = new() + { + Index = 19, + Name = "Admin New Server", + Description = "Create a new server in the admin area" + }; + + public static Permission AdminServerImageEdit = new() + { + Index = 20, + Name = "Admin Server Image Edit", + Description = "Edit server image settings in the admin area" + }; + + public static Permission AdminServerImages = new() + { + Index = 21, + Name = "Admin Server Images", + Description = "Access the server image management page in the admin area" + }; + + public static Permission AdminServerImageNew = new() + { + Index = 22, + Name = "Admin Server Image New", + Description = "Create a new server image in the admin area" + }; + + public static Permission AdminServerViewAllocations = new() + { + Index = 23, + Name = "Admin Server View Allocations", + Description = "View server allocations in the admin area" + }; + + public static Permission AdminServerViewArchive = new() + { + Index = 24, + Name = "Admin Server View Archive", + Description = "View server archive in the admin area" + }; + + public static Permission AdminServerViewDebug = new() + { + Index = 25, + Name = "Admin Server View Debug", + Description = "View server debugging information in the admin area" + }; + + public static Permission AdminServerViewImage = new() + { + Index = 26, + Name = "Admin Server View Image", + Description = "View server image details in the admin area" + }; + + public static Permission AdminServerViewIndex = new() + { + Index = 27, + Name = "Admin Server View", + Description = "Access the server view page in the admin area" + }; + + public static Permission AdminServerViewOverview = new() + { + Index = 28, + Name = "Admin Server View Overview", + Description = "View server overview in the admin area" + }; + + public static Permission AdminServerViewResources = new() + { + Index = 29, + Name = "Admin Server View Resources", + Description = "View server resources in the admin area" + }; + + public static Permission AdminSubscriptionEdit = new() + { + Index = 30, + Name = "Admin Subscription Edit", + Description = "Edit subscription settings in the admin area" + }; + + public static Permission AdminSubscriptions = new() + { + Index = 31, + Name = "Admin Subscriptions", + Description = "Access the subscription management page in the admin area" + }; + + public static Permission AdminNewSubscription = new() + { + Index = 32, + Name = "Admin New Subscription", + Description = "Create a new subscription in the admin area" + }; + + public static Permission AdminSupport = new() + { + Index = 33, + Name = "Admin Support", + Description = "Access the support page in the admin area" + }; + + public static Permission AdminSupportView = new() + { + Index = 34, + Name = "Admin Support View", + Description = "View support details in the admin area" + }; + + public static Permission AdminSysConfiguration = new() + { + Index = 35, + Name = "Admin system Configuration", + Description = "Access system configuration settings in the admin area" + }; + + public static Permission AdminSysDiscordBot = new() + { + Index = 36, + Name = "Admin system Discord Bot", + Description = "Manage Discord bot settings in the admin area" + }; + + public static Permission AdminSystem = new() + { + Index = 37, + Name = "Admin system", + Description = "Access the system management page in the admin area" + }; + + public static Permission AdminSysMail = new() + { + Index = 38, + Name = "Admin system Mail", + Description = "Manage mail settings in the admin area" + }; + + public static Permission AdminSecurityMalware = new() + { + Index = 39, + Name = "Admin security Malware", + Description = "Manage malware settings in the admin area" + }; + + public static Permission AdminSysResources = new() + { + Index = 40, + Name = "Admin system Resources", + Description = "View system resources in the admin area" + }; + + public static Permission AdminSecurity = new() + { + Index = 41, + Name = "Admin Security", + Description = "View security logs in the admin area" + }; + + public static Permission AdminSysSentry = new() + { + Index = 42, + Name = "Admin system Sentry", + Description = "Manage Sentry settings in the admin area" + }; + + public static Permission AdminSysNewsEdit = new() + { + Index = 43, + Name = "Admin system News Edit", + Description = "Edit system news in the admin area" + }; + + public static Permission AdminSysNews = new() + { + Index = 44, + Name = "Admin system News", + Description = "Access the system news management page in the admin area" + }; + + public static Permission AdminSysNewsNew = new() + { + Index = 45, + Name = "Admin system News New", + Description = "Create new system news in the admin area" + }; + + public static Permission AdminUserEdit = new() + { + Index = 46, + Name = "Admin User Edit", + Description = "Edit user settings in the admin area" + }; + + public static Permission AdminUsers = new() + { + Index = 47, + Name = "Admin Users", + Description = "Access the user management page in the admin area" + }; + + public static Permission AdminNewUser = new() + { + Index = 48, + Name = "Admin New User", + Description = "Create a new user in the admin area" + }; + + public static Permission AdminUserSessions = new() + { + Index = 49, + Name = "Admin User Sessions", + Description = "View user sessions in the admin area" + }; + + public static Permission AdminUserView = new() + { + Index = 50, + Name = "Admin User View", + Description = "View user details in the admin area" + }; + + public static Permission AdminWebspaces = new() + { + Index = 51, + Name = "Admin Webspaces", + Description = "Access the webspaces management page in the admin area" + }; + + public static Permission AdminNewWebspace = new() + { + Index = 52, + Name = "Admin New Webspace", + Description = "Create a new webspace in the admin area" + }; + + public static Permission AdminWebspacesServerEdit = new() + { + Index = 53, + Name = "Admin Webspaces Server Edit", + Description = "Edit webspace server settings in the admin area" + }; + + public static Permission AdminWebspacesServers = new() + { + Index = 54, + Name = "Admin Webspaces Servers", + Description = "Access the webspace server management page in the admin area" + }; + + public static Permission AdminWebspacesServerNew = new() + { + Index = 55, + Name = "Admin Webspaces Server New", + Description = "Create a new webspace server in the admin area" + }; + + public static Permission AdminSecurityIpBans = new() + { + Index = 56, + Name = "Admin security ip bans", + Description = "Manage ip bans in the admin area" + }; + + public static Permission AdminSecurityPermissionGroups = new() + { + Index = 57, + Name = "Admin security permission groups", + Description = "View, add and delete permission groups" + }; + + public static Permission? FromString(string name) + { + var type = typeof(Permissions); + + var field = type + .GetFields() + .FirstOrDefault(x => x.FieldType == typeof(Permission) && x.Name == name); + + if (field != null) + { + var value = field.GetValue(null); + return value as Permission; + } + + return null; + } + + public static Permission[] GetAllPermissions() + { + var type = typeof(Permissions); + + return type + .GetFields() + .Where(x => x.FieldType == typeof(Permission)) + .Select(x => (x.GetValue(null) as Permission)!) + .ToArray(); + } +} \ No newline at end of file diff --git a/Moonlight/App/Services/RatingService.cs b/Moonlight/App/Services/RatingService.cs index e0e6bef7..083ed4cc 100644 --- a/Moonlight/App/Services/RatingService.cs +++ b/Moonlight/App/Services/RatingService.cs @@ -39,7 +39,7 @@ public class RatingService if (!Enabled) return false; - var user = await IdentityService.Get(); + var user = IdentityService.User; if (user == null) return false; @@ -62,7 +62,7 @@ public class RatingService public async Task Rate(int rate) { - var user = await IdentityService.Get(); + var user = IdentityService.User; // Double check states: diff --git a/Moonlight/App/Services/Sessions/IdentityService.cs b/Moonlight/App/Services/Sessions/IdentityService.cs index b7f9e4de..f9a5d763 100644 --- a/Moonlight/App/Services/Sessions/IdentityService.cs +++ b/Moonlight/App/Services/Sessions/IdentityService.cs @@ -2,9 +2,10 @@ using JWT.Algorithms; using JWT.Builder; using JWT.Exceptions; +using Microsoft.EntityFrameworkCore; using Moonlight.App.Database.Entities; using Moonlight.App.Helpers; -using Moonlight.App.Models.Misc; +using Moonlight.App.Perms; using Moonlight.App.Repositories; using UAParser; @@ -12,16 +13,21 @@ namespace Moonlight.App.Services.Sessions; public class IdentityService { - private readonly UserRepository UserRepository; + private readonly Repository UserRepository; private readonly CookieService CookieService; private readonly IHttpContextAccessor HttpContextAccessor; private readonly string Secret; - - private User? UserCache; + + public User User { get; private set; } + public string Ip { get; private set; } = "N/A"; + public string Device { get; private set; } = "N/A"; + public PermissionStorage Permissions { get; private set; } + public PermissionStorage UserPermissions { get; private set; } + public PermissionStorage GroupPermissions { get; private set; } public IdentityService( CookieService cookieService, - UserRepository userRepository, + Repository userRepository, IHttpContextAccessor httpContextAccessor, ConfigService configService) { @@ -34,13 +40,17 @@ public class IdentityService .Moonlight.Security.Token; } - public async Task Get() + public async Task Load() + { + await LoadIp(); + await LoadDevice(); + await LoadUser(); + } + + private async Task LoadUser() { try { - if (UserCache != null) - return UserCache; - var token = "none"; // Load token via http context if available @@ -60,13 +70,13 @@ public class IdentityService if (token == "none") { - return null; + return; } if (string.IsNullOrEmpty(token)) - return null; + return; - var json = ""; + string json; try { @@ -77,18 +87,18 @@ public class IdentityService } catch (TokenExpiredException) { - return null; + return; } catch (SignatureVerificationException) { Logger.Warn($"Detected a manipulated JWT: {token}", "security"); - return null; + return; } catch (Exception e) { Logger.Error("Error reading jwt"); Logger.Error(e); - return null; + return; } // To make it easier to use the json data @@ -101,8 +111,9 @@ public class IdentityService if (user == null) { - Logger.Warn($"Cannot find user with the id '{userid}' in the database. Maybe the user has been deleted or a token has been successfully faked by a hacker"); - return null; + Logger.Warn( + $"Cannot find user with the id '{userid}' in the database. Maybe the user has been deleted or a token has been successfully faked by a hacker"); + return; } var iat = data.GetValue("iat", -1); @@ -110,46 +121,54 @@ public class IdentityService if (iat == -1) { Logger.Debug("Legacy token found (without the time the token has been issued at)"); - return null; + return; } var iatD = DateTimeOffset.FromUnixTimeSeconds(iat).ToUniversalTime().DateTime; - + if (iatD < user.TokenValidTime) - return null; + return; - UserCache = user; + User = user; - user.LastIp = GetIp(); - UserRepository.Update(user); - - return UserCache; + ConstructPermissions(); + + User.LastIp = Ip; + UserRepository.Update(User); } catch (Exception e) { Logger.Error("Unexpected error while processing token"); Logger.Error(e); - return null; + return; } } - public string GetIp() + private Task LoadIp() { if (HttpContextAccessor.HttpContext == null) - return "N/A"; - - if(HttpContextAccessor.HttpContext.Request.Headers.ContainsKey("X-Real-IP")) { - return HttpContextAccessor.HttpContext.Request.Headers["X-Real-IP"]!; + Ip = "N/A"; + return Task.CompletedTask; } - - return HttpContextAccessor.HttpContext.Connection.RemoteIpAddress!.ToString(); + + if (HttpContextAccessor.HttpContext.Request.Headers.ContainsKey("X-Real-IP")) + { + Ip = HttpContextAccessor.HttpContext.Request.Headers["X-Real-IP"]!; + return Task.CompletedTask; + } + + Ip = HttpContextAccessor.HttpContext.Connection.RemoteIpAddress!.ToString(); + return Task.CompletedTask; } - public string GetDevice() + private Task LoadDevice() { if (HttpContextAccessor.HttpContext == null) - return "N/A"; + { + Device = "N/A"; + return Task.CompletedTask; + } try { @@ -159,17 +178,86 @@ public class IdentityService { var version = userAgent.Remove(0, "Moonlight.App/".Length).Split(' ').FirstOrDefault(); - return "Moonlight App " + version; + Device = "Moonlight App " + version; + return Task.CompletedTask; } - + var uaParser = Parser.GetDefault(); var info = uaParser.Parse(userAgent); - return $"{info.OS} - {info.Device}"; + Device = $"{info.OS} - {info.Device}"; + return Task.CompletedTask; } catch (Exception e) { - return "UserAgent not present"; + Device = "UserAgent not present"; + return Task.CompletedTask; } } + + public Task SavePermissions() + { + if (User != null) + { + User.Permissions = UserPermissions.Data; + UserRepository.Update(User); + ConstructPermissions(); + } + + return Task.CompletedTask; + } + + private void ConstructPermissions() + { + if (User == null) + { + UserPermissions = new(Array.Empty()); + GroupPermissions = new(Array.Empty(), true); + Permissions = new(Array.Empty(), true); + + return; + } + + var user = UserRepository + .Get() + .Include(x => x.PermissionGroup) + .First(x => x.Id == User.Id); + + UserPermissions = new PermissionStorage(user.Permissions); + + if (user.PermissionGroup == null) + GroupPermissions = new PermissionStorage(Array.Empty(), true); + else + GroupPermissions = new PermissionStorage(user.PermissionGroup.Permissions, true); + + if (user.Admin) + { + Permissions = new PermissionStorage(Array.Empty()); + + foreach (var permission in Perms.Permissions.GetAllPermissions()) + { + Permissions[permission] = true; + } + + Permissions.IsReadyOnly = true; + return; + } + + Permissions = new(Array.Empty()); + + foreach (var permission in Perms.Permissions.GetAllPermissions()) + { + Permissions[permission] = GroupPermissions[permission]; + } + + foreach (var permission in Perms.Permissions.GetAllPermissions()) + { + if (UserPermissions[permission]) + { + Permissions[permission] = true; + } + } + + Permissions.IsReadyOnly = true; + } } \ No newline at end of file diff --git a/Moonlight/App/Services/Sessions/IpBanService.cs b/Moonlight/App/Services/Sessions/IpBanService.cs index 6fd6af15..1159b45a 100644 --- a/Moonlight/App/Services/Sessions/IpBanService.cs +++ b/Moonlight/App/Services/Sessions/IpBanService.cs @@ -19,7 +19,7 @@ public class IpBanService public Task IsBanned() { - var ip = IdentityService.GetIp(); + var ip = IdentityService.Ip; return Task.FromResult( IpBanRepository diff --git a/Moonlight/App/Services/Sessions/IpLocateService.cs b/Moonlight/App/Services/Sessions/IpLocateService.cs index 95d59a8c..4ce5a6bf 100644 --- a/Moonlight/App/Services/Sessions/IpLocateService.cs +++ b/Moonlight/App/Services/Sessions/IpLocateService.cs @@ -15,7 +15,7 @@ public class IpLocateService public async Task GetLocation() { - var ip = IdentityService.GetIp(); + var ip = IdentityService.Ip; var location = "N/A"; if (ip != "N/A") diff --git a/Moonlight/App/Services/Sessions/SessionClientService.cs b/Moonlight/App/Services/Sessions/SessionClientService.cs index e7040ba4..8ff084f6 100644 --- a/Moonlight/App/Services/Sessions/SessionClientService.cs +++ b/Moonlight/App/Services/Sessions/SessionClientService.cs @@ -40,9 +40,9 @@ public class SessionClientService public async Task Start() { - User = await IdentityService.Get(); - Ip = IdentityService.GetIp(); - Device = IdentityService.GetDevice(); + User = IdentityService.User; + Ip = IdentityService.Ip; + Device = IdentityService.Device; if (User != null) // Track users last visit { diff --git a/Moonlight/App/Services/SupportChat/SupportChatAdminService.cs b/Moonlight/App/Services/SupportChat/SupportChatAdminService.cs index d9acc9ef..20c67290 100644 --- a/Moonlight/App/Services/SupportChat/SupportChatAdminService.cs +++ b/Moonlight/App/Services/SupportChat/SupportChatAdminService.cs @@ -34,7 +34,7 @@ public class SupportChatAdminService : IDisposable public async Task Start(User recipient) { - User = await IdentityService.Get(); + User = IdentityService.User; Recipient = recipient; if (User != null) diff --git a/Moonlight/App/Services/SupportChat/SupportChatClientService.cs b/Moonlight/App/Services/SupportChat/SupportChatClientService.cs index 8c079cfc..c4f695ce 100644 --- a/Moonlight/App/Services/SupportChat/SupportChatClientService.cs +++ b/Moonlight/App/Services/SupportChat/SupportChatClientService.cs @@ -33,7 +33,7 @@ public class SupportChatClientService : IDisposable public async Task Start() { - User = await IdentityService.Get(); + User = IdentityService.User; if (User != null) { diff --git a/Moonlight/App/Services/TotpService.cs b/Moonlight/App/Services/TotpService.cs index ef46b9f0..2d624d92 100644 --- a/Moonlight/App/Services/TotpService.cs +++ b/Moonlight/App/Services/TotpService.cs @@ -25,32 +25,30 @@ public class TotpService return Task.FromResult(codeserver == code); } - public async Task GetEnabled() + public Task GetEnabled() { - var user = await IdentityService.Get(); - - return user!.TotpEnabled; + return Task.FromResult(IdentityService.User.TotpEnabled); } - public async Task GetSecret() + public Task GetSecret() { - var user = await IdentityService.Get(); - - return user!.TotpSecret; + return Task.FromResult(IdentityService.User.TotpSecret); } - public async Task GenerateSecret() + public Task GenerateSecret() { - var user = (await IdentityService.Get())!; + var user = IdentityService.User; user.TotpSecret = Base32Encoding.ToString(KeyGeneration.GenerateRandomKey(20));; UserRepository.Update(user); + + return Task.CompletedTask; } public async Task Enable(string code) { - var user = (await IdentityService.Get())!; + var user = IdentityService.User; if (!await Verify(user.TotpSecret, code)) { @@ -61,9 +59,9 @@ public class TotpService UserRepository.Update(user); } - public async Task Disable() + public Task Disable() { - var user = (await IdentityService.Get())!; + var user = IdentityService.User; user.TotpEnabled = false; user.TotpSecret = ""; @@ -71,5 +69,7 @@ public class TotpService UserRepository.Update(user); //TODO: AuditLog + + return Task.CompletedTask; } } \ No newline at end of file diff --git a/Moonlight/App/Services/UserService.cs b/Moonlight/App/Services/UserService.cs index cab1b644..77c26316 100644 --- a/Moonlight/App/Services/UserService.cs +++ b/Moonlight/App/Services/UserService.cs @@ -85,8 +85,8 @@ public class UserService TotpSecret = "", UpdatedAt = DateTimeService.GetCurrent(), TokenValidTime = DateTimeService.GetCurrent().AddDays(-5), - LastIp = IdentityService.GetIp(), - RegisterIp = IdentityService.GetIp() + LastIp = IdentityService.Ip, + RegisterIp = IdentityService.Ip }); await MailService.SendMail(user!, "register", values => {}); @@ -174,8 +174,8 @@ public class UserService await MailService.SendMail(user!, "passwordChange", values => { - values.Add("Ip", IdentityService.GetIp()); - values.Add("Device", IdentityService.GetDevice()); + values.Add("Ip", IdentityService.Ip); + values.Add("Device", IdentityService.Device); values.Add("Location", location); }); @@ -212,8 +212,8 @@ public class UserService { await MailService.SendMail(user!, "login", values => { - values.Add("Ip", IdentityService.GetIp()); - values.Add("Device", IdentityService.GetDevice()); + values.Add("Ip", IdentityService.Ip); + values.Add("Device", IdentityService.Device); values.Add("Location", location); }); } @@ -249,8 +249,8 @@ public class UserService await MailService.SendMail(user, "passwordReset", values => { - values.Add("Ip", IdentityService.GetIp()); - values.Add("Device", IdentityService.GetDevice()); + values.Add("Ip", IdentityService.Ip); + values.Add("Device", IdentityService.Device); values.Add("Location", location); values.Add("Password", newPassword); }); diff --git a/Moonlight/BlazorApp.razor b/Moonlight/BlazorApp.razor index 492dcb0c..5119c0fe 100644 --- a/Moonlight/BlazorApp.razor +++ b/Moonlight/BlazorApp.razor @@ -2,12 +2,19 @@ - + + + Not found -

Sorry, there's nothing at this address.

+
-
\ No newline at end of file + + +@code +{ + +} \ No newline at end of file diff --git a/Moonlight/Moonlight.csproj b/Moonlight/Moonlight.csproj index 9ef2f159..078f2ddc 100644 --- a/Moonlight/Moonlight.csproj +++ b/Moonlight/Moonlight.csproj @@ -77,6 +77,9 @@ <_ContentIncludedByDefault Remove="Shared\Views\Admin\Servers\Cleanup\Exceptions\Edit.razor" /> <_ContentIncludedByDefault Remove="Shared\Components\News\NewsEditor.razor" /> <_ContentIncludedByDefault Remove="Shared\News\Edit.razor" /> + <_ContentIncludedByDefault Remove="Shared\Views\Admin\AaPanels\Index.razor" /> + <_ContentIncludedByDefault Remove="Shared\Views\Admin\Databases\Index.razor" /> + <_ContentIncludedByDefault Remove="Shared\Components\StateLogic\OnlyAdmin.razor" /> diff --git a/Moonlight/Shared/Components/Alerts/NoPermissionAlert.razor b/Moonlight/Shared/Components/Alerts/NoPermissionAlert.razor new file mode 100644 index 00000000..1ae6e3c5 --- /dev/null +++ b/Moonlight/Shared/Components/Alerts/NoPermissionAlert.razor @@ -0,0 +1,13 @@ +
+
+
+ Warning +
+ + You have no permission to access this resource + +

+ You have no permission to access this resource. This attempt has been logged ;) +

+
+
\ No newline at end of file diff --git a/Moonlight/Shared/Components/Alerts/NotFoundAlert.razor b/Moonlight/Shared/Components/Alerts/NotFoundAlert.razor index f97c7b45..ae5070a9 100644 --- a/Moonlight/Shared/Components/Alerts/NotFoundAlert.razor +++ b/Moonlight/Shared/Components/Alerts/NotFoundAlert.razor @@ -14,7 +14,7 @@ The resource was deleted
  • - You have to permission to access this resource + You have no permission to access this resource
  • You may have entered invalid data diff --git a/Moonlight/Shared/Components/Alerts/SetupCompletedAlert.razor b/Moonlight/Shared/Components/Alerts/SetupCompletedAlert.razor deleted file mode 100644 index c1759f9b..00000000 --- a/Moonlight/Shared/Components/Alerts/SetupCompletedAlert.razor +++ /dev/null @@ -1,10 +0,0 @@ -@using Moonlight.App.Services - -
    -
    -

    Setup complete

    -
    - It looks like this moonlight instance is ready to go -
    -
    -
    \ No newline at end of file diff --git a/Moonlight/Shared/Components/Auth/PasswordChangeView.razor b/Moonlight/Shared/Components/Auth/PasswordChangeView.razor index f57d1aa7..38e0e866 100644 --- a/Moonlight/Shared/Components/Auth/PasswordChangeView.razor +++ b/Moonlight/Shared/Components/Auth/PasswordChangeView.razor @@ -49,9 +49,10 @@ private PasswordModel Password = new(); private User User; - private async Task Load(LazyLoader loader) + private Task Load(LazyLoader loader) { - User = await IdentityService.Get(); + User = IdentityService.User; + return Task.CompletedTask; } private async Task DoChange() diff --git a/Moonlight/Shared/Components/Auth/UserDataSetView.razor b/Moonlight/Shared/Components/Auth/UserDataSetView.razor index d1191ec1..f8c64e8a 100644 --- a/Moonlight/Shared/Components/Auth/UserDataSetView.razor +++ b/Moonlight/Shared/Components/Auth/UserDataSetView.razor @@ -50,9 +50,10 @@ private User User; private NameModel Name = new (); - private async Task Load(LazyLoader loader) + private Task Load(LazyLoader loader) { - User = await IdentityService.Get(); + User = IdentityService.User; + return Task.CompletedTask; } private async Task SetName() diff --git a/Moonlight/Shared/Components/ErrorBoundaries/PageErrorBoundary.razor b/Moonlight/Shared/Components/ErrorBoundaries/PageErrorBoundary.razor index 4cc364fa..587e43f1 100644 --- a/Moonlight/Shared/Components/ErrorBoundaries/PageErrorBoundary.razor +++ b/Moonlight/Shared/Components/ErrorBoundaries/PageErrorBoundary.razor @@ -57,7 +57,7 @@ else { receivedExceptions.Add(exception); - var user = await IdentityService.Get(); + var user = IdentityService.User; var id = user == null ? -1 : user.Id; Logger.Error($"[{id}] An unhanded exception occured:"); diff --git a/Moonlight/Shared/Components/Navigations/AdminNodesNavigation.razor b/Moonlight/Shared/Components/Navigations/AdminNodesNavigation.razor deleted file mode 100644 index 16db4eb5..00000000 --- a/Moonlight/Shared/Components/Navigations/AdminNodesNavigation.razor +++ /dev/null @@ -1,22 +0,0 @@ -
    -
    - -
    -
    - -@code -{ - [Parameter] - public int Index { get; set; } = 0; -} \ No newline at end of file diff --git a/Moonlight/Shared/Components/Navigations/AdminSecurityNavigation.razor b/Moonlight/Shared/Components/Navigations/AdminSecurityNavigation.razor new file mode 100644 index 00000000..85589958 --- /dev/null +++ b/Moonlight/Shared/Components/Navigations/AdminSecurityNavigation.razor @@ -0,0 +1,32 @@ + + +@code +{ + [Parameter] + public int Index { get; set; } = 0; +} \ No newline at end of file diff --git a/Moonlight/Shared/Components/Navigations/AdminServersNavigation.razor b/Moonlight/Shared/Components/Navigations/AdminServersNavigation.razor new file mode 100644 index 00000000..d307f551 --- /dev/null +++ b/Moonlight/Shared/Components/Navigations/AdminServersNavigation.razor @@ -0,0 +1,37 @@ + + +@code +{ + [Parameter] + public int Index { get; set; } = 0; +} \ No newline at end of file diff --git a/Moonlight/Shared/Components/Navigations/AdminSystemNavigation.razor b/Moonlight/Shared/Components/Navigations/AdminSystemNavigation.razor index 1a92236d..9733aed1 100644 --- a/Moonlight/Shared/Components/Navigations/AdminSystemNavigation.razor +++ b/Moonlight/Shared/Components/Navigations/AdminSystemNavigation.razor @@ -9,21 +9,6 @@ Overview
  • - - -