Added security logs. Removed unsued log models. Added dynamisc config load system for development

This commit is contained in:
Marcel Baumgartner
2023-07-19 20:07:57 +02:00
parent 6a30db07a7
commit 1cd0f0f96f
26 changed files with 1362 additions and 536 deletions

View File

@@ -1,9 +1,7 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Moonlight.App.Database.Entities; using Moonlight.App.Database.Entities;
using Moonlight.App.Database.Entities.LogsEntries;
using Moonlight.App.Database.Entities.Notification; using Moonlight.App.Database.Entities.Notification;
using Moonlight.App.Database.Interceptors; using Moonlight.App.Database.Interceptors;
using Moonlight.App.Models.Misc;
using Moonlight.App.Services; using Moonlight.App.Services;
namespace Moonlight.App.Database; namespace Moonlight.App.Database;
@@ -27,10 +25,6 @@ public class DataContext : DbContext
public DbSet<ServerVariable> ServerVariables { get; set; } public DbSet<ServerVariable> ServerVariables { get; set; }
public DbSet<User> Users { get; set; } public DbSet<User> Users { get; set; }
public DbSet<LoadingMessage> LoadingMessages { 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<SharedDomain> SharedDomains { get; set; }
public DbSet<Domain> Domains { get; set; } public DbSet<Domain> Domains { get; set; }
public DbSet<Revoke> Revokes { get; set; } public DbSet<Revoke> Revokes { get; set; }
@@ -47,6 +41,7 @@ public class DataContext : DbContext
public DbSet<SupportChatMessage> SupportChatMessages { get; set; } public DbSet<SupportChatMessage> SupportChatMessages { get; set; }
public DbSet<IpBan> IpBans { get; set; } public DbSet<IpBan> IpBans { get; set; }
public DbSet<PermissionGroup> PermissionGroups { get; set; } public DbSet<PermissionGroup> PermissionGroups { get; set; }
public DbSet<SecurityLog> SecurityLogs { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{ {

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View 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;
}

File diff suppressed because it is too large Load Diff

View File

@@ -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");
}
}
}

View File

@@ -241,95 +241,6 @@ namespace Moonlight.App.Database.Migrations
b.ToTable("LoadingMessages"); 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 => modelBuilder.Entity("Moonlight.App.Database.Entities.MySqlDatabase", b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
@@ -509,6 +420,24 @@ namespace Moonlight.App.Database.Migrations
b.ToTable("Revokes"); 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 => modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")

View File

@@ -1,46 +1,70 @@
using System.Diagnostics; using System.Diagnostics;
using System.Reflection; using System.Reflection;
using Moonlight.App.Database;
using Moonlight.App.Services;
using Moonlight.App.Services.Files;
using Serilog; using Serilog;
namespace Moonlight.App.Helpers; namespace Moonlight.App.Helpers;
public static class Logger 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 #region String method calls
public static void Verbose(string message, string channel = "default") public static void Verbose(string message, string channel = "default")
{ {
Log.ForContext("SourceContext", GetNameOfCallingClass()) Log.ForContext("SourceContext", GetNameOfCallingClass())
.Verbose("{Message}", message); .Verbose("{Message}", message);
if(channel == "security")
LogSecurityInDb(message);
} }
public static void Info(string message, string channel = "default") public static void Info(string message, string channel = "default")
{ {
Log.ForContext("SourceContext", GetNameOfCallingClass()) Log.ForContext("SourceContext", GetNameOfCallingClass())
.Information("{Message}", message); .Information("{Message}", message);
if(channel == "security")
LogSecurityInDb(message);
} }
public static void Debug(string message, string channel = "default") public static void Debug(string message, string channel = "default")
{ {
Log.ForContext("SourceContext", GetNameOfCallingClass()) Log.ForContext("SourceContext", GetNameOfCallingClass())
.Debug("{Message}", message); .Debug("{Message}", message);
if(channel == "security")
LogSecurityInDb(message);
} }
public static void Error(string message, string channel = "default") public static void Error(string message, string channel = "default")
{ {
Log.ForContext("SourceContext", GetNameOfCallingClass()) Log.ForContext("SourceContext", GetNameOfCallingClass())
.Error("{Message}", message); .Error("{Message}", message);
if(channel == "security")
LogSecurityInDb(message);
} }
public static void Warn(string message, string channel = "default") public static void Warn(string message, string channel = "default")
{ {
Log.ForContext("SourceContext", GetNameOfCallingClass()) Log.ForContext("SourceContext", GetNameOfCallingClass())
.Warning("{Message}", message); .Warning("{Message}", message);
if(channel == "security")
LogSecurityInDb(message);
} }
public static void Fatal(string message, string channel = "default") public static void Fatal(string message, string channel = "default")
{ {
Log.ForContext("SourceContext", GetNameOfCallingClass()) Log.ForContext("SourceContext", GetNameOfCallingClass())
.Fatal("{Message}", message); .Fatal("{Message}", message);
if(channel == "security")
LogSecurityInDb(message);
} }
#endregion #endregion
@@ -49,36 +73,54 @@ public static class Logger
{ {
Log.ForContext("SourceContext", GetNameOfCallingClass()) Log.ForContext("SourceContext", GetNameOfCallingClass())
.Verbose(exception, ""); .Verbose(exception, "");
if(channel == "security")
LogSecurityInDb(exception);
} }
public static void Info(Exception exception, string channel = "default") public static void Info(Exception exception, string channel = "default")
{ {
Log.ForContext("SourceContext", GetNameOfCallingClass()) Log.ForContext("SourceContext", GetNameOfCallingClass())
.Information(exception, ""); .Information(exception, "");
if(channel == "security")
LogSecurityInDb(exception);
} }
public static void Debug(Exception exception, string channel = "default") public static void Debug(Exception exception, string channel = "default")
{ {
Log.ForContext("SourceContext", GetNameOfCallingClass()) Log.ForContext("SourceContext", GetNameOfCallingClass())
.Debug(exception, ""); .Debug(exception, "");
if(channel == "security")
LogSecurityInDb(exception);
} }
public static void Error(Exception exception, string channel = "default") public static void Error(Exception exception, string channel = "default")
{ {
Log.ForContext("SourceContext", GetNameOfCallingClass()) Log.ForContext("SourceContext", GetNameOfCallingClass())
.Error(exception, ""); .Error(exception, "");
if(channel == "security")
LogSecurityInDb(exception);
} }
public static void Warn(Exception exception, string channel = "default") public static void Warn(Exception exception, string channel = "default")
{ {
Log.ForContext("SourceContext", GetNameOfCallingClass()) Log.ForContext("SourceContext", GetNameOfCallingClass())
.Warning(exception, ""); .Warning(exception, "");
if(channel == "security")
LogSecurityInDb(exception);
} }
public static void Fatal(Exception exception, string channel = "default") public static void Fatal(Exception exception, string channel = "default")
{ {
Log.ForContext("SourceContext", GetNameOfCallingClass()) Log.ForContext("SourceContext", GetNameOfCallingClass())
.Fatal(exception, ""); .Fatal(exception, "");
if(channel == "security")
LogSecurityInDb(exception);
} }
#endregion #endregion
@@ -105,4 +147,25 @@ public static class Logger
return fullName; 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();
});
}
} }

View File

@@ -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,
}

View File

@@ -1,9 +0,0 @@
namespace Moonlight.App.Models.Misc;
public enum SecurityLogType
{
ManipulatedJwt,
PathTransversal,
SftpBruteForce,
LoginFail
}

View File

@@ -393,6 +393,13 @@ public static class Permissions
Name = "Admin security permission groups", Name = "Admin security permission groups",
Description = "View, add and delete 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) public static Permission? FromString(string name)
{ {

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -8,6 +8,7 @@ namespace Moonlight.App.Services;
public class ConfigService public class ConfigService
{ {
private readonly StorageService StorageService; private readonly StorageService StorageService;
private readonly string Path;
private ConfigV1 Configuration; private ConfigV1 Configuration;
public bool DebugMode { get; private set; } = false; public bool DebugMode { get; private set; } = false;
@@ -18,6 +19,11 @@ public class ConfigService
StorageService = storageService; StorageService = storageService;
StorageService.EnsureCreated(); 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(); Reload();
// Env vars // Env vars
@@ -40,18 +46,16 @@ public class ConfigService
public void Reload() 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>( Configuration = JsonConvert.DeserializeObject<ConfigV1>(
File.ReadAllText(path) File.ReadAllText(Path)
) ?? new ConfigV1(); ) ?? new ConfigV1();
File.WriteAllText(path, JsonConvert.SerializeObject(Configuration, Formatting.Indented)); File.WriteAllText(Path, JsonConvert.SerializeObject(Configuration, Formatting.Indented));
} }
public void Save(ConfigV1 configV1) public void Save(ConfigV1 configV1)
@@ -62,14 +66,12 @@ public class ConfigService
public void Save() 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(); Reload();
} }

View File

@@ -54,7 +54,10 @@ public class UserService
throw new DisplayException("This operation was disabled"); throw new DisplayException("This operation was disabled");
if (await TempMailService.IsTempMail(email)) 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"); throw new DisplayException("This email is blacklisted");
}
// Check if the email is already taken // Check if the email is already taken
var emailTaken = UserRepository.Get().FirstOrDefault(x => x.Email == email) != null; var emailTaken = UserRepository.Get().FirstOrDefault(x => x.Email == email) != null;

View File

@@ -80,6 +80,10 @@
<_ContentIncludedByDefault Remove="Shared\Views\Admin\AaPanels\Index.razor" /> <_ContentIncludedByDefault Remove="Shared\Views\Admin\AaPanels\Index.razor" />
<_ContentIncludedByDefault Remove="Shared\Views\Admin\Databases\Index.razor" /> <_ContentIncludedByDefault Remove="Shared\Views\Admin\Databases\Index.razor" />
<_ContentIncludedByDefault Remove="Shared\Components\StateLogic\OnlyAdmin.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>
<ItemGroup> <ItemGroup>

View File

@@ -15,7 +15,6 @@ using Moonlight.App.Helpers.Wings;
using Moonlight.App.LogMigrator; using Moonlight.App.LogMigrator;
using Moonlight.App.Repositories; using Moonlight.App.Repositories;
using Moonlight.App.Repositories.Domains; using Moonlight.App.Repositories.Domains;
using Moonlight.App.Repositories.LogEntries;
using Moonlight.App.Repositories.Servers; using Moonlight.App.Repositories.Servers;
using Moonlight.App.Services; using Moonlight.App.Services;
using Moonlight.App.Services.Addon; using Moonlight.App.Services.Addon;
@@ -167,9 +166,6 @@ namespace Moonlight
builder.Services.AddScoped<NewsEntryRepository>(); builder.Services.AddScoped<NewsEntryRepository>();
builder.Services.AddScoped<NodeAllocationRepository>(); builder.Services.AddScoped<NodeAllocationRepository>();
builder.Services.AddScoped<StatisticsRepository>(); builder.Services.AddScoped<StatisticsRepository>();
builder.Services.AddScoped<AuditLogEntryRepository>();
builder.Services.AddScoped<ErrorLogEntryRepository>();
builder.Services.AddScoped<SecurityLogEntryRepository>();
builder.Services.AddScoped(typeof(Repository<>)); builder.Services.AddScoped(typeof(Repository<>));
// Services // Services

View File

@@ -1,6 +1,8 @@
{ {
"profiles": { "profiles":
"Moonlight": { {
"Default":
{
"commandName": "Project", "commandName": "Project",
"launchBrowser": true, "launchBrowser": true,
"environmentVariables": { "environmentVariables": {
@@ -10,41 +12,29 @@
"applicationUrl": "http://moonlight.testy:5118;https://localhost:7118;http://localhost:5118", "applicationUrl": "http://moonlight.testy:5118;https://localhost:7118;http://localhost:5118",
"dotnetRunMessages": true "dotnetRunMessages": true
}, },
"Sql Debug": { "Live DB":
{
"commandName": "Project", "commandName": "Project",
"launchBrowser": true, "launchBrowser": true,
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development", "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", "applicationUrl": "http://moonlight.testy:5118;https://localhost:7118;http://localhost:5118",
"dotnetRunMessages": true "dotnetRunMessages": true
}, },
"Discord Bot": { "Dev DB 1":
{
"commandName": "Project", "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, "launchBrowser": true,
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development", "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
} }
} }
} }

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -21,6 +21,11 @@
<TL>Permission groups</TL> <TL>Permission groups</TL>
</a> </a>
</li> </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> </ul>
</div> </div>
</div> </div>

View 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;
}
}