Added security logs. Removed unsued log models. Added dynamisc config load system for development
This commit is contained in:
parent
6a30db07a7
commit
1cd0f0f96f
26 changed files with 1362 additions and 536 deletions
|
@ -1,9 +1,7 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Moonlight.App.Database.Entities;
|
||||
using Moonlight.App.Database.Entities.LogsEntries;
|
||||
using Moonlight.App.Database.Entities.Notification;
|
||||
using Moonlight.App.Database.Interceptors;
|
||||
using Moonlight.App.Models.Misc;
|
||||
using Moonlight.App.Services;
|
||||
|
||||
namespace Moonlight.App.Database;
|
||||
|
@ -27,10 +25,6 @@ public class DataContext : DbContext
|
|||
public DbSet<ServerVariable> ServerVariables { get; set; }
|
||||
public DbSet<User> Users { get; set; }
|
||||
public DbSet<LoadingMessage> LoadingMessages { get; set; }
|
||||
public DbSet<AuditLogEntry> AuditLog { get; set; }
|
||||
public DbSet<ErrorLogEntry> ErrorLog { get; set; }
|
||||
public DbSet<SecurityLogEntry> SecurityLog { get; set; }
|
||||
|
||||
public DbSet<SharedDomain> SharedDomains { get; set; }
|
||||
public DbSet<Domain> Domains { get; set; }
|
||||
public DbSet<Revoke> Revokes { get; set; }
|
||||
|
@ -47,6 +41,7 @@ public class DataContext : DbContext
|
|||
public DbSet<SupportChatMessage> SupportChatMessages { get; set; }
|
||||
public DbSet<IpBan> IpBans { get; set; }
|
||||
public DbSet<PermissionGroup> PermissionGroups { get; set; }
|
||||
public DbSet<SecurityLog> SecurityLogs { get; set; }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
using Moonlight.App.Models.Misc;
|
||||
|
||||
namespace Moonlight.App.Database.Entities.LogsEntries;
|
||||
|
||||
public class AuditLogEntry
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public AuditLogType Type { get; set; }
|
||||
public string JsonData { get; set; } = "";
|
||||
public bool System { get; set; }
|
||||
public string Ip { get; set; } = "";
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
namespace Moonlight.App.Database.Entities.LogsEntries;
|
||||
|
||||
public class ErrorLogEntry
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Stacktrace { get; set; } = "";
|
||||
public bool System { get; set; }
|
||||
public string JsonData { get; set; } = "";
|
||||
public string Ip { get; set; } = "";
|
||||
public string Class { get; set; } = "";
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
using Moonlight.App.Models.Misc;
|
||||
|
||||
namespace Moonlight.App.Database.Entities.LogsEntries;
|
||||
|
||||
public class SecurityLogEntry
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public bool System { get; set; }
|
||||
public string Ip { get; set; } = "";
|
||||
public SecurityLogType Type { get; set; }
|
||||
public string JsonData { get; set; } = "";
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
8
Moonlight/App/Database/Entities/SecurityLog.cs
Normal file
8
Moonlight/App/Database/Entities/SecurityLog.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Moonlight.App.Database.Entities;
|
||||
|
||||
public class SecurityLog
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Text { get; set; } = "";
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
1068
Moonlight/App/Database/Migrations/20230718123232_RemovedOldLogsAndAddedErrorLog.Designer.cs
generated
Normal file
1068
Moonlight/App/Database/Migrations/20230718123232_RemovedOldLogsAndAddedErrorLog.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,111 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Moonlight.App.Database.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class RemovedOldLogsAndAddedErrorLog : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "AuditLog");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ErrorLog");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "SecurityLog");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "SecurityLogs",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
Text = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_SecurityLogs", x => x.Id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "SecurityLogs");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AuditLog",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
Ip = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
JsonData = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
System = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
||||
Type = table.Column<int>(type: "int", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AuditLog", x => x.Id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ErrorLog",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
Class = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
Ip = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
JsonData = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
Stacktrace = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
System = table.Column<bool>(type: "tinyint(1)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ErrorLog", x => x.Id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "SecurityLog",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
Ip = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
JsonData = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
System = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
||||
Type = table.Column<int>(type: "int", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_SecurityLog", x => x.Id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -241,95 +241,6 @@ namespace Moonlight.App.Database.Migrations
|
|||
b.ToTable("LoadingMessages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.AuditLogEntry", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Ip")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("JsonData")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<bool>("System")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("AuditLog");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.ErrorLogEntry", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Class")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Ip")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("JsonData")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Stacktrace")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<bool>("System")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("ErrorLog");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.SecurityLogEntry", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Ip")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("JsonData")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<bool>("System")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SecurityLog");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.MySqlDatabase", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
|
@ -509,6 +420,24 @@ namespace Moonlight.App.Database.Migrations
|
|||
b.ToTable("Revokes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.SecurityLog", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Text")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SecurityLogs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
|
|
|
@ -1,46 +1,70 @@
|
|||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using Moonlight.App.Database;
|
||||
using Moonlight.App.Services;
|
||||
using Moonlight.App.Services.Files;
|
||||
using Serilog;
|
||||
|
||||
namespace Moonlight.App.Helpers;
|
||||
|
||||
public static class Logger
|
||||
{
|
||||
// The private static instance of the config service, because we have no di here
|
||||
private static ConfigService ConfigService = new(new StorageService());
|
||||
|
||||
#region String method calls
|
||||
public static void Verbose(string message, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Verbose("{Message}", message);
|
||||
|
||||
if(channel == "security")
|
||||
LogSecurityInDb(message);
|
||||
}
|
||||
|
||||
public static void Info(string message, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Information("{Message}", message);
|
||||
|
||||
if(channel == "security")
|
||||
LogSecurityInDb(message);
|
||||
}
|
||||
|
||||
public static void Debug(string message, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Debug("{Message}", message);
|
||||
|
||||
if(channel == "security")
|
||||
LogSecurityInDb(message);
|
||||
}
|
||||
|
||||
public static void Error(string message, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Error("{Message}", message);
|
||||
|
||||
if(channel == "security")
|
||||
LogSecurityInDb(message);
|
||||
}
|
||||
|
||||
public static void Warn(string message, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Warning("{Message}", message);
|
||||
|
||||
if(channel == "security")
|
||||
LogSecurityInDb(message);
|
||||
}
|
||||
|
||||
public static void Fatal(string message, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Fatal("{Message}", message);
|
||||
|
||||
if(channel == "security")
|
||||
LogSecurityInDb(message);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
@ -49,36 +73,54 @@ public static class Logger
|
|||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Verbose(exception, "");
|
||||
|
||||
if(channel == "security")
|
||||
LogSecurityInDb(exception);
|
||||
}
|
||||
|
||||
public static void Info(Exception exception, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Information(exception, "");
|
||||
|
||||
if(channel == "security")
|
||||
LogSecurityInDb(exception);
|
||||
}
|
||||
|
||||
public static void Debug(Exception exception, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Debug(exception, "");
|
||||
|
||||
if(channel == "security")
|
||||
LogSecurityInDb(exception);
|
||||
}
|
||||
|
||||
public static void Error(Exception exception, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Error(exception, "");
|
||||
|
||||
if(channel == "security")
|
||||
LogSecurityInDb(exception);
|
||||
}
|
||||
|
||||
public static void Warn(Exception exception, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Warning(exception, "");
|
||||
|
||||
if(channel == "security")
|
||||
LogSecurityInDb(exception);
|
||||
}
|
||||
|
||||
public static void Fatal(Exception exception, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Fatal(exception, "");
|
||||
|
||||
if(channel == "security")
|
||||
LogSecurityInDb(exception);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
@ -105,4 +147,25 @@ public static class Logger
|
|||
|
||||
return fullName;
|
||||
}
|
||||
|
||||
|
||||
private static void LogSecurityInDb(Exception exception)
|
||||
{
|
||||
LogSecurityInDb(exception.ToStringDemystified());
|
||||
}
|
||||
private static void LogSecurityInDb(string text)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
var dataContext = new DataContext(ConfigService);
|
||||
|
||||
dataContext.SecurityLogs.Add(new()
|
||||
{
|
||||
Text = text
|
||||
});
|
||||
|
||||
dataContext.SaveChanges();
|
||||
dataContext.Dispose();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
namespace Moonlight.App.Models.Misc;
|
||||
|
||||
public enum AuditLogType
|
||||
{
|
||||
Login,
|
||||
Register,
|
||||
ChangePassword,
|
||||
ChangePowerState,
|
||||
CreateBackup,
|
||||
RestoreBackup,
|
||||
DeleteBackup,
|
||||
DownloadBackup,
|
||||
CreateServer,
|
||||
ReinstallServer,
|
||||
CancelSubscription,
|
||||
ApplySubscriptionCode,
|
||||
EnableTotp,
|
||||
DisableTotp,
|
||||
AddDomainRecord,
|
||||
UpdateDomainRecord,
|
||||
DeleteDomainRecord,
|
||||
PasswordReset,
|
||||
CleanupEnabled,
|
||||
CleanupDisabled,
|
||||
CleanupTriggered,
|
||||
PasswordChange,
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
namespace Moonlight.App.Models.Misc;
|
||||
|
||||
public enum SecurityLogType
|
||||
{
|
||||
ManipulatedJwt,
|
||||
PathTransversal,
|
||||
SftpBruteForce,
|
||||
LoginFail
|
||||
}
|
|
@ -393,6 +393,13 @@ public static class Permissions
|
|||
Name = "Admin security permission groups",
|
||||
Description = "View, add and delete permission groups"
|
||||
};
|
||||
|
||||
public static Permission AdminSecurityLogs = new()
|
||||
{
|
||||
Index = 58,
|
||||
Name = "Admin security logs",
|
||||
Description = "View the security logs"
|
||||
};
|
||||
|
||||
public static Permission? FromString(string name)
|
||||
{
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Moonlight.App.Database;
|
||||
using Moonlight.App.Database.Entities.LogsEntries;
|
||||
|
||||
namespace Moonlight.App.Repositories.LogEntries;
|
||||
|
||||
public class AuditLogEntryRepository : IDisposable
|
||||
{
|
||||
private readonly DataContext DataContext;
|
||||
|
||||
public AuditLogEntryRepository(DataContext dataContext)
|
||||
{
|
||||
DataContext = dataContext;
|
||||
}
|
||||
|
||||
public AuditLogEntry Add(AuditLogEntry entry)
|
||||
{
|
||||
var x = DataContext.AuditLog.Add(entry);
|
||||
DataContext.SaveChanges();
|
||||
return x.Entity;
|
||||
}
|
||||
|
||||
public DbSet<AuditLogEntry> Get()
|
||||
{
|
||||
return DataContext.AuditLog;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DataContext.Dispose();
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Moonlight.App.Database;
|
||||
using Moonlight.App.Database.Entities.LogsEntries;
|
||||
|
||||
namespace Moonlight.App.Repositories.LogEntries;
|
||||
|
||||
public class ErrorLogEntryRepository : IDisposable
|
||||
{
|
||||
private readonly DataContext DataContext;
|
||||
|
||||
public ErrorLogEntryRepository(DataContext dataContext)
|
||||
{
|
||||
DataContext = dataContext;
|
||||
}
|
||||
|
||||
public ErrorLogEntry Add(ErrorLogEntry errorLogEntry)
|
||||
{
|
||||
var x = DataContext.ErrorLog.Add(errorLogEntry);
|
||||
DataContext.SaveChanges();
|
||||
return x.Entity;
|
||||
}
|
||||
|
||||
public DbSet<ErrorLogEntry> Get()
|
||||
{
|
||||
return DataContext.ErrorLog;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DataContext.Dispose();
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Moonlight.App.Database;
|
||||
using Moonlight.App.Database.Entities.LogsEntries;
|
||||
|
||||
namespace Moonlight.App.Repositories.LogEntries;
|
||||
|
||||
public class SecurityLogEntryRepository : IDisposable
|
||||
{
|
||||
private readonly DataContext DataContext;
|
||||
|
||||
public SecurityLogEntryRepository(DataContext dataContext)
|
||||
{
|
||||
DataContext = dataContext;
|
||||
}
|
||||
|
||||
public SecurityLogEntry Add(SecurityLogEntry securityLogEntry)
|
||||
{
|
||||
var x = DataContext.SecurityLog.Add(securityLogEntry);
|
||||
DataContext.SaveChanges();
|
||||
return x.Entity;
|
||||
}
|
||||
|
||||
public DbSet<SecurityLogEntry> Get()
|
||||
{
|
||||
return DataContext.SecurityLog;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DataContext.Dispose();
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ namespace Moonlight.App.Services;
|
|||
public class ConfigService
|
||||
{
|
||||
private readonly StorageService StorageService;
|
||||
private readonly string Path;
|
||||
private ConfigV1 Configuration;
|
||||
|
||||
public bool DebugMode { get; private set; } = false;
|
||||
|
@ -18,6 +19,11 @@ public class ConfigService
|
|||
StorageService = storageService;
|
||||
StorageService.EnsureCreated();
|
||||
|
||||
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("ML_CONFIG_PATH")))
|
||||
Path = Environment.GetEnvironmentVariable("ML_CONFIG_PATH")!;
|
||||
else
|
||||
Path = PathBuilder.File("storage", "configs", "config.json");
|
||||
|
||||
Reload();
|
||||
|
||||
// Env vars
|
||||
|
@ -40,18 +46,16 @@ public class ConfigService
|
|||
|
||||
public void Reload()
|
||||
{
|
||||
var path = PathBuilder.File("storage", "configs", "config.json");
|
||||
|
||||
if (!File.Exists(path))
|
||||
if (!File.Exists(Path))
|
||||
{
|
||||
File.WriteAllText(path, "{}");
|
||||
File.WriteAllText(Path, "{}");
|
||||
}
|
||||
|
||||
Configuration = JsonConvert.DeserializeObject<ConfigV1>(
|
||||
File.ReadAllText(path)
|
||||
File.ReadAllText(Path)
|
||||
) ?? new ConfigV1();
|
||||
|
||||
File.WriteAllText(path, JsonConvert.SerializeObject(Configuration, Formatting.Indented));
|
||||
File.WriteAllText(Path, JsonConvert.SerializeObject(Configuration, Formatting.Indented));
|
||||
}
|
||||
|
||||
public void Save(ConfigV1 configV1)
|
||||
|
@ -62,14 +66,12 @@ public class ConfigService
|
|||
|
||||
public void Save()
|
||||
{
|
||||
var path = PathBuilder.File("storage", "configs", "config.json");
|
||||
|
||||
if (!File.Exists(path))
|
||||
if (!File.Exists(Path))
|
||||
{
|
||||
File.WriteAllText(path, "{}");
|
||||
File.WriteAllText(Path, "{}");
|
||||
}
|
||||
|
||||
File.WriteAllText(path, JsonConvert.SerializeObject(Configuration, Formatting.Indented));
|
||||
File.WriteAllText(Path, JsonConvert.SerializeObject(Configuration, Formatting.Indented));
|
||||
|
||||
Reload();
|
||||
}
|
||||
|
|
|
@ -54,7 +54,10 @@ public class UserService
|
|||
throw new DisplayException("This operation was disabled");
|
||||
|
||||
if (await TempMailService.IsTempMail(email))
|
||||
{
|
||||
Logger.Warn($"A user tried to use a blacklisted domain to register. Email: '{email}'", "security");
|
||||
throw new DisplayException("This email is blacklisted");
|
||||
}
|
||||
|
||||
// Check if the email is already taken
|
||||
var emailTaken = UserRepository.Get().FirstOrDefault(x => x.Email == email) != null;
|
||||
|
|
|
@ -80,6 +80,10 @@
|
|||
<_ContentIncludedByDefault Remove="Shared\Views\Admin\AaPanels\Index.razor" />
|
||||
<_ContentIncludedByDefault Remove="Shared\Views\Admin\Databases\Index.razor" />
|
||||
<_ContentIncludedByDefault Remove="Shared\Components\StateLogic\OnlyAdmin.razor" />
|
||||
<_ContentIncludedByDefault Remove="Shared\Components\AuditLogEntrys\AuditLogEntryChangePassword.razor" />
|
||||
<_ContentIncludedByDefault Remove="Shared\Components\AuditLogEntrys\AuditLogEntryChangePowerState.razor" />
|
||||
<_ContentIncludedByDefault Remove="Shared\Components\AuditLogEntrys\AuditLogEntryLogin.razor" />
|
||||
<_ContentIncludedByDefault Remove="Shared\Components\AuditLogEntrys\AuditLogEntryRegister.razor" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -15,7 +15,6 @@ using Moonlight.App.Helpers.Wings;
|
|||
using Moonlight.App.LogMigrator;
|
||||
using Moonlight.App.Repositories;
|
||||
using Moonlight.App.Repositories.Domains;
|
||||
using Moonlight.App.Repositories.LogEntries;
|
||||
using Moonlight.App.Repositories.Servers;
|
||||
using Moonlight.App.Services;
|
||||
using Moonlight.App.Services.Addon;
|
||||
|
@ -167,9 +166,6 @@ namespace Moonlight
|
|||
builder.Services.AddScoped<NewsEntryRepository>();
|
||||
builder.Services.AddScoped<NodeAllocationRepository>();
|
||||
builder.Services.AddScoped<StatisticsRepository>();
|
||||
builder.Services.AddScoped<AuditLogEntryRepository>();
|
||||
builder.Services.AddScoped<ErrorLogEntryRepository>();
|
||||
builder.Services.AddScoped<SecurityLogEntryRepository>();
|
||||
builder.Services.AddScoped(typeof(Repository<>));
|
||||
|
||||
// Services
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
{
|
||||
"profiles": {
|
||||
"Moonlight": {
|
||||
"profiles":
|
||||
{
|
||||
"Default":
|
||||
{
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
|
@ -10,41 +12,29 @@
|
|||
"applicationUrl": "http://moonlight.testy:5118;https://localhost:7118;http://localhost:5118",
|
||||
"dotnetRunMessages": true
|
||||
},
|
||||
"Sql Debug": {
|
||||
"Live DB":
|
||||
{
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||
"ML_SQL_DEBUG": "true",
|
||||
"ML_DEBUG": "true"
|
||||
"ML_DEBUG": "true",
|
||||
"ML_CONFIG_PATH": "storage\\configs\\live_config.json"
|
||||
},
|
||||
"applicationUrl": "http://moonlight.testy:5118;https://localhost:7118;http://localhost:5118",
|
||||
"dotnetRunMessages": true
|
||||
},
|
||||
"Discord Bot": {
|
||||
"Dev DB 1":
|
||||
{
|
||||
"commandName": "Project",
|
||||
"launchBrowser": false,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||
"ML_SQL_DEBUG": "true",
|
||||
"ML_DEBUG": "true"
|
||||
},
|
||||
"applicationUrl": "http://moonlight.testy:5118;https://localhost:7118;http://localhost:5118",
|
||||
"dotnetRunMessages": true
|
||||
},
|
||||
"Watch": {
|
||||
"commandName": "Executable",
|
||||
"executablePath": "dotnet",
|
||||
"workingDirectory": "$(ProjectDir)",
|
||||
"hotReloadEnabled": true,
|
||||
"hotReloadProfile": "aspnetcore",
|
||||
"commandLineArgs": "watch run",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||
"ML_DEBUG": "true"
|
||||
"ML_DEBUG": "true",
|
||||
"ML_CONFIG_PATH": "storage\\configs\\dev_1_config.json"
|
||||
},
|
||||
"applicationUrl": "http://moonlight.testy:5118;https://localhost:7118;http://localhost:5118"
|
||||
"applicationUrl": "http://moonlight.testy:5118;https://localhost:7118;http://localhost:5118",
|
||||
"dotnetRunMessages": true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
@using Moonlight.App.Database.Entities.LogsEntries
|
||||
@using Moonlight.App.Helpers
|
||||
@using Moonlight.App.Repositories
|
||||
@using Newtonsoft.Json
|
||||
@using Moonlight.App.Database.Entities
|
||||
@using Moonlight.App.Models.Log
|
||||
|
||||
@inject UserRepository UserRepository
|
||||
|
||||
<div class="timeline-item">
|
||||
<div class="timeline-line w-40px"></div>
|
||||
<div class="timeline-icon symbol symbol-circle symbol-40px">
|
||||
<div class="symbol-label bg-light">
|
||||
<i class="bx bx-md bx-log-in"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="timeline-content mb-10 mt-n2">
|
||||
<div class="overflow-auto pe-3">
|
||||
<div class="fs-5 fw-semibold mb-2">
|
||||
@if (User == null)
|
||||
{
|
||||
<TL>Password change for</TL> @(Data[0].Value)
|
||||
}
|
||||
else
|
||||
{
|
||||
<TL>Password change for</TL> <a href="/admin/users/view/@(User.Id)">@(User.Email)</a>
|
||||
}
|
||||
</div>
|
||||
<div class="d-flex align-items-center mt-1 fs-6">
|
||||
<div class="text-muted me-2 fs-7">@(Formatter.FormatDate(Entry.CreatedAt)), @(Entry.System ? "System" : Entry.Ip)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter]
|
||||
public AuditLogEntry Entry { get; set; }
|
||||
|
||||
private User? User;
|
||||
private LogData[] Data;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Data = JsonConvert.DeserializeObject<LogData[]>(Entry.JsonData)!;
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
User = UserRepository.Get().FirstOrDefault(x => x.Email == Data[0].Value);
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
@using Moonlight.App.Database.Entities.LogsEntries
|
||||
@using Moonlight.App.Helpers
|
||||
@using Newtonsoft.Json
|
||||
@using Moonlight.App.Database.Entities
|
||||
@using Moonlight.App.Models.Log
|
||||
@using Moonlight.App.Repositories.Servers
|
||||
|
||||
@inject ServerRepository ServerRepository
|
||||
|
||||
<div class="timeline-item">
|
||||
<div class="timeline-line w-40px"></div>
|
||||
<div class="timeline-icon symbol symbol-circle symbol-40px">
|
||||
<div class="symbol-label bg-light">
|
||||
<i class="bx bx-md bx-log-in"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="timeline-content mb-10 mt-n2">
|
||||
<div class="overflow-auto pe-3">
|
||||
<div class="fs-5 fw-semibold mb-2">
|
||||
@if (Server == null)
|
||||
{
|
||||
<TL>Change power state for</TL> @(Data[0].Value) <TL>to</TL> @(Data[1].Value)
|
||||
}
|
||||
else
|
||||
{
|
||||
<TL>Change power state for</TL> <a href="/admin/servers/edit/@(Server.Id)">@(Server.Name)</a> <TL>to</TL> @(Data[1].Value)
|
||||
}
|
||||
</div>
|
||||
<div class="d-flex align-items-center mt-1 fs-6">
|
||||
<div class="text-muted me-2 fs-7">@(Formatter.FormatDate(Entry.CreatedAt)), @(Entry.System ? "System" : Entry.Ip)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter]
|
||||
public AuditLogEntry Entry { get; set; }
|
||||
|
||||
private Server? Server;
|
||||
private LogData[] Data;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Data = JsonConvert.DeserializeObject<LogData[]>(Entry.JsonData)!;
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
Server = ServerRepository.Get().FirstOrDefault(x => x.Uuid == Guid.Parse(Data[0].Value));
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
@using Moonlight.App.Database.Entities.LogsEntries
|
||||
@using Moonlight.App.Helpers
|
||||
@using Moonlight.App.Repositories
|
||||
@using Newtonsoft.Json
|
||||
@using Moonlight.App.Database.Entities
|
||||
@using Moonlight.App.Models.Log
|
||||
|
||||
@inject UserRepository UserRepository
|
||||
|
||||
<div class="timeline-item">
|
||||
<div class="timeline-line w-40px"></div>
|
||||
<div class="timeline-icon symbol symbol-circle symbol-40px">
|
||||
<div class="symbol-label bg-light">
|
||||
<i class="bx bx-md bx-log-in"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="timeline-content mb-10 mt-n2">
|
||||
<div class="overflow-auto pe-3">
|
||||
<div class="fs-5 fw-semibold mb-2">
|
||||
@if (User == null)
|
||||
{
|
||||
<TL>New login for</TL> @(Data[0].Value)
|
||||
}
|
||||
else
|
||||
{
|
||||
<TL>New login for</TL> <a href="/admin/users/view/@(User.Id)">@(User.Email)</a>
|
||||
}
|
||||
</div>
|
||||
<div class="d-flex align-items-center mt-1 fs-6">
|
||||
<div class="text-muted me-2 fs-7">@(Formatter.FormatDate(Entry.CreatedAt)), @(Entry.System ? "System" : Entry.Ip)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter]
|
||||
public AuditLogEntry Entry { get; set; }
|
||||
|
||||
private User? User;
|
||||
private LogData[] Data;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Data = JsonConvert.DeserializeObject<LogData[]>(Entry.JsonData)!;
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
User = UserRepository.Get().FirstOrDefault(x => x.Email == Data[0].Value);
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
@using Moonlight.App.Database.Entities.LogsEntries
|
||||
@using Moonlight.App.Helpers
|
||||
@using Moonlight.App.Repositories
|
||||
@using Newtonsoft.Json
|
||||
@using Moonlight.App.Database.Entities
|
||||
@using Moonlight.App.Models.Log
|
||||
|
||||
@inject UserRepository UserRepository
|
||||
|
||||
<div class="timeline-item">
|
||||
<div class="timeline-line w-40px"></div>
|
||||
<div class="timeline-icon symbol symbol-circle symbol-40px">
|
||||
<div class="symbol-label bg-light">
|
||||
<i class="bx bx-md bx-log-in"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="timeline-content mb-10 mt-n2">
|
||||
<div class="overflow-auto pe-3">
|
||||
<div class="fs-5 fw-semibold mb-2">
|
||||
@if (User == null)
|
||||
{
|
||||
<TL>Register for</TL> @(Data[0].Value)
|
||||
}
|
||||
else
|
||||
{
|
||||
<TL>Register for</TL> <a href="/admin/users/view/@(User.Id)">@(User.Email)</a>
|
||||
}
|
||||
</div>
|
||||
<div class="d-flex align-items-center mt-1 fs-6">
|
||||
<div class="text-muted me-2 fs-7">@(Formatter.FormatDate(Entry.CreatedAt)), @(Entry.System ? "System" : Entry.Ip)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter]
|
||||
public AuditLogEntry Entry { get; set; }
|
||||
|
||||
private User? User;
|
||||
private LogData[] Data;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Data = JsonConvert.DeserializeObject<LogData[]>(Entry.JsonData)!;
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
User = UserRepository.Get().FirstOrDefault(x => x.Email == Data[0].Value);
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,6 +21,11 @@
|
|||
<TL>Permission groups</TL>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item mt-2">
|
||||
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 4 ? "active" : "")" href="/admin/security/logs">
|
||||
<TL>Logs</TL>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
47
Moonlight/Shared/Views/Admin/Security/Logs.razor
Normal file
47
Moonlight/Shared/Views/Admin/Security/Logs.razor
Normal file
|
@ -0,0 +1,47 @@
|
|||
@page "/admin/security/logs"
|
||||
|
||||
@using Moonlight.App.Repositories
|
||||
@using Moonlight.App.Database.Entities
|
||||
@using BlazorTable
|
||||
@using Moonlight.Shared.Components.Navigations
|
||||
@using Moonlight.App.Services
|
||||
|
||||
@inject Repository<SecurityLog> SecurityLogRepository
|
||||
@inject SmartTranslateService SmartTranslateService
|
||||
|
||||
@attribute [PermissionRequired(nameof(Permissions.AdminSecurityLogs))]
|
||||
|
||||
<AdminSecurityNavigation Index="4"/>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<span class="card-title">
|
||||
<TL>Security logs</TL>
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<LazyLoader Load="Load">
|
||||
<div class="table-responsive">
|
||||
<Table TableItem="SecurityLog" Items="SecurityLogs" PageSize="25" TableClass="table table-row-bordered table-row-gray-100 align-middle gs-0 gy-3" TableHeadClass="fw-bold text-muted">
|
||||
<Column TableItem="SecurityLog" Width="80%" Title="@(SmartTranslateService.Translate("Text"))" Field="@(x => x.Text)" Filterable="true" Sortable="false"/>
|
||||
<Column TableItem="SecurityLog" Width="20%" Title="@(SmartTranslateService.Translate("Timestamp"))" Field="@(x => x.CreatedAt)" Filterable="true" Sortable="true"/>
|
||||
<Pager ShowPageNumber="true" ShowTotalCount="true"/>
|
||||
</Table>
|
||||
</div>
|
||||
</LazyLoader>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
private SecurityLog[] SecurityLogs;
|
||||
|
||||
private Task Load(LazyLoader arg)
|
||||
{
|
||||
SecurityLogs = SecurityLogRepository
|
||||
.Get()
|
||||
.ToArray();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue