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 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)
{

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

View file

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

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",
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)
{

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

View file

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

View file

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

View file

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

View file

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

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>
</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>

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