Merge pull request #121 from Moonlight-Panel/AddIpBan
Implemented ip ban
This commit is contained in:
@@ -45,6 +45,7 @@ public class DataContext : DbContext
|
||||
public DbSet<MySqlDatabase> Databases { get; set; }
|
||||
public DbSet<WebSpace> WebSpaces { get; set; }
|
||||
public DbSet<SupportChatMessage> SupportChatMessages { get; set; }
|
||||
public DbSet<IpBan> IpBans { get; set; }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
|
||||
8
Moonlight/App/Database/Entities/IpBan.cs
Normal file
8
Moonlight/App/Database/Entities/IpBan.cs
Normal 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;
|
||||
}
|
||||
1052
Moonlight/App/Database/Migrations/20230519125351_AddIpBanModel.Designer.cs
generated
Normal file
1052
Moonlight/App/Database/Migrations/20230519125351_AddIpBanModel.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -204,6 +204,24 @@ namespace Moonlight.App.Database.Migrations
|
||||
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 =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
|
||||
30
Moonlight/App/Services/Sessions/IpBanService.cs
Normal file
30
Moonlight/App/Services/Sessions/IpBanService.cs
Normal 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)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -118,6 +118,7 @@ namespace Moonlight
|
||||
builder.Services.AddSingleton<BucketService>();
|
||||
builder.Services.AddScoped<RatingService>();
|
||||
builder.Services.AddScoped<ReCaptchaService>();
|
||||
builder.Services.AddScoped<IpBanService>();
|
||||
|
||||
builder.Services.AddScoped<GoogleOAuth2Service>();
|
||||
builder.Services.AddScoped<DiscordOAuth2Service>();
|
||||
|
||||
@@ -20,13 +20,8 @@
|
||||
</a>
|
||||
</li>
|
||||
<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">
|
||||
<TL>SecurityLog</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 class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 3 ? "active" : "")" href="/admin/system/security">
|
||||
<TL>Security</TL>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item mt-2">
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
@inject EventSystem Event
|
||||
@inject ToastService ToastService
|
||||
@inject SmartTranslateService SmartTranslateService
|
||||
@inject IpBanService IpBanService
|
||||
|
||||
<GlobalErrorBoundary>
|
||||
@{
|
||||
@@ -59,56 +60,72 @@
|
||||
<div id="kt_app_content_container" class="app-container container-fluid">
|
||||
<div class="mt-10">
|
||||
<SoftErrorBoundary>
|
||||
@if (UserProcessed)
|
||||
@if (!IsIpBanned)
|
||||
{
|
||||
@if (uri.LocalPath != "/login" &&
|
||||
uri.LocalPath != "/passwordreset" &&
|
||||
uri.LocalPath != "/register")
|
||||
if (UserProcessed)
|
||||
{
|
||||
if (User == null)
|
||||
if (uri.LocalPath != "/login" &&
|
||||
uri.LocalPath != "/passwordreset" &&
|
||||
uri.LocalPath != "/register")
|
||||
{
|
||||
<Login></Login>
|
||||
}
|
||||
else
|
||||
{
|
||||
if (User.Status == UserStatus.Banned)
|
||||
if (User == null)
|
||||
{
|
||||
<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>
|
||||
<Login></Login>
|
||||
}
|
||||
else
|
||||
{
|
||||
@Body
|
||||
|
||||
<RatingPopup />
|
||||
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/>
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (uri.LocalPath == "/login")
|
||||
{
|
||||
<Login></Login>
|
||||
}
|
||||
else if (uri.LocalPath == "/register")
|
||||
{
|
||||
<Register></Register>
|
||||
}
|
||||
else if (uri.LocalPath == "/passwordreset")
|
||||
{
|
||||
<PasswordReset></PasswordReset>
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (uri.LocalPath == "/login")
|
||||
{
|
||||
<Login></Login>
|
||||
}
|
||||
else if (uri.LocalPath == "/register")
|
||||
{
|
||||
<Register></Register>
|
||||
}
|
||||
else if (uri.LocalPath == "/passwordreset")
|
||||
{
|
||||
<PasswordReset></PasswordReset>
|
||||
}
|
||||
<div class="modal d-block">
|
||||
<div class="modal-dialog modal-dialog-centered mw-900px">
|
||||
<div class="modal-content">
|
||||
<div class="pt-2 modal-body py-lg-10 px-lg-10">
|
||||
<h2>@(SmartTranslateService.Translate("Authenticating"))...</h2>
|
||||
<p class="mt-3 fw-normal fs-6">@(SmartTranslateService.Translate("Verifying token, loading user data"))</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -117,8 +134,8 @@
|
||||
<div class="modal-dialog modal-dialog-centered mw-900px">
|
||||
<div class="modal-content">
|
||||
<div class="pt-2 modal-body py-lg-10 px-lg-10">
|
||||
<h2>@(SmartTranslateService.Translate("Authenticating"))...</h2>
|
||||
<p class="mt-3 fw-normal fs-6">@(SmartTranslateService.Translate("Verifying token, loading user data"))</p>
|
||||
<h2>@(SmartTranslateService.Translate("Your ip has been banned"))</h2>
|
||||
<p class="mt-3 fw-normal fs-6">@(SmartTranslateService.Translate("Your ip address has been banned by an admin"))</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -142,6 +159,8 @@
|
||||
private User? User;
|
||||
private bool UserProcessed = false;
|
||||
|
||||
private bool IsIpBanned = false;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
AddBodyAttribute("data-kt-app-page-loading", "on");
|
||||
@@ -170,6 +189,17 @@
|
||||
{
|
||||
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();
|
||||
UserProcessed = true;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
100
Moonlight/Shared/Views/Admin/Sys/Security.razor
Normal file
100
Moonlight/Shared/Views/Admin/Sys/Security.razor
Normal 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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user