Merge pull request #121 from Moonlight-Panel/AddIpBan

Implemented ip ban
This commit is contained in:
Marcel Baumgartner
2023-05-19 16:03:50 +02:00
committed by GitHub
10 changed files with 1321 additions and 47 deletions

View File

@@ -45,6 +45,7 @@ public class DataContext : DbContext
public DbSet<MySqlDatabase> Databases { get; set; } public DbSet<MySqlDatabase> Databases { get; set; }
public DbSet<WebSpace> WebSpaces { get; set; } public DbSet<WebSpace> WebSpaces { get; set; }
public DbSet<SupportChatMessage> SupportChatMessages { get; set; } public DbSet<SupportChatMessage> SupportChatMessages { get; set; }
public DbSet<IpBan> IpBans { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{ {

View File

@@ -0,0 +1,8 @@
namespace Moonlight.App.Database.Entities;
public class IpBan
{
public int Id { get; set; }
public string Ip { get; set; } = "";
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,39 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Moonlight.App.Database.Migrations
{
/// <inheritdoc />
public partial class AddIpBanModel : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "IpBans",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
Ip = 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_IpBans", x => x.Id);
})
.Annotation("MySql:CharSet", "utf8mb4");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "IpBans");
}
}
}

View File

@@ -204,6 +204,24 @@ namespace Moonlight.App.Database.Migrations
b.ToTable("ImageVariables"); b.ToTable("ImageVariables");
}); });
modelBuilder.Entity("Moonlight.App.Database.Entities.IpBan", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Ip")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("IpBans");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.LoadingMessage", b => modelBuilder.Entity("Moonlight.App.Database.Entities.LoadingMessage", b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")

View File

@@ -0,0 +1,30 @@
using Moonlight.App.Database.Entities;
using Moonlight.App.Events;
using Moonlight.App.Repositories;
namespace Moonlight.App.Services.Sessions;
public class IpBanService
{
private readonly IdentityService IdentityService;
private readonly Repository<IpBan> IpBanRepository;
public IpBanService(
IdentityService identityService,
Repository<IpBan> ipBanRepository)
{
IdentityService = identityService;
IpBanRepository = ipBanRepository;
}
public Task<bool> IsBanned()
{
var ip = IdentityService.GetIp();
return Task.FromResult(
IpBanRepository
.Get()
.Any(x => x.Ip == ip)
);
}
}

View File

@@ -118,6 +118,7 @@ namespace Moonlight
builder.Services.AddSingleton<BucketService>(); builder.Services.AddSingleton<BucketService>();
builder.Services.AddScoped<RatingService>(); builder.Services.AddScoped<RatingService>();
builder.Services.AddScoped<ReCaptchaService>(); builder.Services.AddScoped<ReCaptchaService>();
builder.Services.AddScoped<IpBanService>();
builder.Services.AddScoped<GoogleOAuth2Service>(); builder.Services.AddScoped<GoogleOAuth2Service>();
builder.Services.AddScoped<DiscordOAuth2Service>(); builder.Services.AddScoped<DiscordOAuth2Service>();

View File

@@ -20,13 +20,8 @@
</a> </a>
</li> </li>
<li class="nav-item mt-2"> <li class="nav-item mt-2">
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 3 ? "active" : "")" href="/admin/system/auditlog"> <a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 3 ? "active" : "")" href="/admin/system/security">
<TL>SecurityLog</TL> <TL>Security</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/system/auditlog">
<TL>ErrorLog</TL>
</a> </a>
</li> </li>
<li class="nav-item mt-2"> <li class="nav-item mt-2">

View File

@@ -19,6 +19,7 @@
@inject EventSystem Event @inject EventSystem Event
@inject ToastService ToastService @inject ToastService ToastService
@inject SmartTranslateService SmartTranslateService @inject SmartTranslateService SmartTranslateService
@inject IpBanService IpBanService
<GlobalErrorBoundary> <GlobalErrorBoundary>
@{ @{
@@ -59,56 +60,72 @@
<div id="kt_app_content_container" class="app-container container-fluid"> <div id="kt_app_content_container" class="app-container container-fluid">
<div class="mt-10"> <div class="mt-10">
<SoftErrorBoundary> <SoftErrorBoundary>
@if (UserProcessed) @if (!IsIpBanned)
{ {
@if (uri.LocalPath != "/login" && if (UserProcessed)
uri.LocalPath != "/passwordreset" &&
uri.LocalPath != "/register")
{ {
if (User == null) if (uri.LocalPath != "/login" &&
uri.LocalPath != "/passwordreset" &&
uri.LocalPath != "/register")
{ {
<Login></Login> if (User == null)
}
else
{
if (User.Status == UserStatus.Banned)
{ {
<BannedAlert></BannedAlert> <Login></Login>
}
else if (User.Status == UserStatus.Disabled)
{
<DisabledAlert></DisabledAlert>
}
else if (User.Status == UserStatus.PasswordPending)
{
<PasswordChangeView></PasswordChangeView>
}
else if (User.Status == UserStatus.DataPending)
{
<UserDataSetView></UserDataSetView>
} }
else else
{ {
@Body if (User.Status == UserStatus.Banned)
{
<BannedAlert></BannedAlert>
}
else if (User.Status == UserStatus.Disabled)
{
<DisabledAlert></DisabledAlert>
}
else if (User.Status == UserStatus.PasswordPending)
{
<PasswordChangeView></PasswordChangeView>
}
else if (User.Status == UserStatus.DataPending)
{
<UserDataSetView></UserDataSetView>
}
else
{
@Body
<RatingPopup /> <RatingPopup/>
}
}
}
else
{
if (uri.LocalPath == "/login")
{
<Login></Login>
}
else if (uri.LocalPath == "/register")
{
<Register></Register>
}
else if (uri.LocalPath == "/passwordreset")
{
<PasswordReset></PasswordReset>
} }
} }
} }
else else
{ {
if (uri.LocalPath == "/login") <div class="modal d-block">
{ <div class="modal-dialog modal-dialog-centered mw-900px">
<Login></Login> <div class="modal-content">
} <div class="pt-2 modal-body py-lg-10 px-lg-10">
else if (uri.LocalPath == "/register") <h2>@(SmartTranslateService.Translate("Authenticating"))...</h2>
{ <p class="mt-3 fw-normal fs-6">@(SmartTranslateService.Translate("Verifying token, loading user data"))</p>
<Register></Register> </div>
} </div>
else if (uri.LocalPath == "/passwordreset") </div>
{ </div>
<PasswordReset></PasswordReset>
}
} }
} }
else else
@@ -117,8 +134,8 @@
<div class="modal-dialog modal-dialog-centered mw-900px"> <div class="modal-dialog modal-dialog-centered mw-900px">
<div class="modal-content"> <div class="modal-content">
<div class="pt-2 modal-body py-lg-10 px-lg-10"> <div class="pt-2 modal-body py-lg-10 px-lg-10">
<h2>@(SmartTranslateService.Translate("Authenticating"))...</h2> <h2>@(SmartTranslateService.Translate("Your ip has been banned"))</h2>
<p class="mt-3 fw-normal fs-6">@(SmartTranslateService.Translate("Verifying token, loading user data"))</p> <p class="mt-3 fw-normal fs-6">@(SmartTranslateService.Translate("Your ip address has been banned by an admin"))</p>
</div> </div>
</div> </div>
</div> </div>
@@ -142,6 +159,8 @@
private User? User; private User? User;
private bool UserProcessed = false; private bool UserProcessed = false;
private bool IsIpBanned = false;
protected override void OnInitialized() protected override void OnInitialized()
{ {
AddBodyAttribute("data-kt-app-page-loading", "on"); AddBodyAttribute("data-kt-app-page-loading", "on");
@@ -170,6 +189,17 @@
{ {
try try
{ {
IsIpBanned = await IpBanService.IsBanned();
if(IsIpBanned)
await InvokeAsync(StateHasChanged);
await Event.On<Object>("ipBan.update", this, async o =>
{
IsIpBanned = await IpBanService.IsBanned();
await InvokeAsync(StateHasChanged);
});
User = await IdentityService.Get(); User = await IdentityService.Get();
UserProcessed = true; UserProcessed = true;
await InvokeAsync(StateHasChanged); await InvokeAsync(StateHasChanged);

View File

@@ -0,0 +1,100 @@
@page "/admin/system/security"
@using Moonlight.Shared.Components.Navigations
@using BlazorTable
@using Moonlight.App.Database.Entities
@using Moonlight.App.Events
@using Moonlight.App.Repositories
@using Moonlight.App.Services
@using Moonlight.App.Services.Interop
@inject Repository<IpBan> IpBanRepository
@inject SmartTranslateService SmartTranslateService
@inject EventSystem Event
@inject ToastService ToastService
<OnlyAdmin>
<AdminSystemNavigation Index="3"/>
<div class="card mb-5">
<div class="card-header">
<div class="card-title">
<TL>Ip Bans</TL>
</div>
<div class="card-toolbar">
<table class="w-100">
<tr>
<td class="w-100">
<input @bind="Ip" type="text" class="form-control" placeholder="@(SmartTranslateService.Translate("Enter a ip"))"/>
</td>
<td>
<WButton OnClick="AddIpBan"
CssClasses="btn btn-primary ms-2"
Text="@(SmartTranslateService.Translate("Add"))"
WorkingText="@(SmartTranslateService.Translate("Adding"))">
</WButton>
</td>
</tr>
</table>
</div>
</div>
<div class="card-body">
<LazyLoader @ref="LazyLoader" Load="Load">
<Table TableItem="IpBan" Items="IpBans" PageSize="25" TableClass="table table-row-bordered table-row-gray-100 align-middle gs-0 gy-3" TableHeadClass="fw-bold text-muted">
<Column TableItem="IpBan" Title="@(SmartTranslateService.Translate("Ip"))" Field="@(x => x.Ip)" Filterable="true" Sortable="false"/>
<Column TableItem="IpBan" Title="" Field="@(x => x.Id)" Filterable="false" Sortable="false">
<Template>
<div class="text-end">
<DeleteButton Confirm="true" OnClick="() => DeleteIpBan(context)"></DeleteButton>
</div>
</Template>
</Column>
<Pager ShowPageNumber="true" ShowTotalCount="true"/>
</Table>
</LazyLoader>
</div>
</div>
</OnlyAdmin>
@code
{
private IpBan[] IpBans;
private string Ip;
private LazyLoader LazyLoader;
private Task Load(LazyLoader arg)
{
IpBans = IpBanRepository.Get().ToArray();
return Task.CompletedTask;
}
// Ip Bans
private async Task AddIpBan()
{
var ipBan = IpBanRepository.Add(new()
{
Ip = Ip
});
await LazyLoader.Reload();
Ip = "";
await InvokeAsync(StateHasChanged);
await Event.Emit("ipBan.update");
await ToastService.Success(
SmartTranslateService.Translate($"Successfully banned {ipBan.Ip}"));
}
private async Task DeleteIpBan(IpBan ban)
{
IpBanRepository.Delete(ban);
await Event.Emit("ipBan.update");
await LazyLoader.Reload();
}
}