Add support for banning a user

This commit is contained in:
jvyden 2021-12-18 15:17:48 -05:00
parent ba91a9994f
commit 2a963567b5
No known key found for this signature in database
GPG key ID: 18BCF2BE0262B278
10 changed files with 173 additions and 21 deletions

View file

@ -8,7 +8,7 @@
<jdbc-url>jdbc:mysql://localhost:3306/lighthouse</jdbc-url> <jdbc-url>jdbc:mysql://localhost:3306/lighthouse</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir> <working-dir>$ProjectFileDir$</working-dir>
</data-source> </data-source>
<data-source source="LOCAL" name="Lighthouse Production" uuid="b323608d-d984-40d0-942e-2c2ea35006d8"> <data-source source="LOCAL" name="Lighthouse Production" read-only="true" uuid="b323608d-d984-40d0-942e-2c2ea35006d8">
<driver-ref>mariadb</driver-ref> <driver-ref>mariadb</driver-ref>
<synchronize>true</synchronize> <synchronize>true</synchronize>
<jdbc-driver>org.mariadb.jdbc.Driver</jdbc-driver> <jdbc-driver>org.mariadb.jdbc.Driver</jdbc-driver>

View file

@ -52,7 +52,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
if (token == null) return this.StatusCode(403, ""); if (token == null) return this.StatusCode(403, "");
User? user = await this.database.UserFromGameToken(token, true); User? user = await this.database.UserFromGameToken(token, true);
if (user == null) return this.StatusCode(403, ""); if (user == null || user.Banned) return this.StatusCode(403, "");
if (ServerSettings.Instance.UseExternalAuth) if (ServerSettings.Instance.UseExternalAuth)
{ {

View file

@ -19,7 +19,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers.Website.Admin
this.database = database; this.database = database;
} }
[Route("teamPick")] [HttpGet("teamPick")]
public async Task<IActionResult> TeamPick([FromRoute] int id) public async Task<IActionResult> TeamPick([FromRoute] int id)
{ {
User? user = this.database.UserFromWebRequest(this.Request); User? user = this.database.UserFromWebRequest(this.Request);
@ -35,7 +35,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers.Website.Admin
return this.Ok(); return this.Ok();
} }
[Route("removeTeamPick")] [HttpGet("removeTeamPick")]
public async Task<IActionResult> RemoveTeamPick([FromRoute] int id) public async Task<IActionResult> RemoveTeamPick([FromRoute] int id)
{ {
User? user = this.database.UserFromWebRequest(this.Request); User? user = this.database.UserFromWebRequest(this.Request);
@ -51,7 +51,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers.Website.Admin
return this.Ok(); return this.Ok();
} }
[Route("delete")] [HttpGet("delete")]
public async Task<IActionResult> DeleteLevel([FromRoute] int id) public async Task<IActionResult> DeleteLevel([FromRoute] int id)
{ {
User? user = this.database.UserFromWebRequest(this.Request); User? user = this.database.UserFromWebRequest(this.Request);

View file

@ -0,0 +1,61 @@
#nullable enable
using System.Linq;
using System.Threading.Tasks;
using LBPUnion.ProjectLighthouse.Types;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers.Website.Admin
{
[ApiController]
[Route("admin/user/{id:int}")]
public class AdminUserController : ControllerBase
{
private readonly Database database;
public AdminUserController(Database database)
{
this.database = database;
}
[HttpGet("ban")]
public async Task<IActionResult> BanUser([FromRoute] int id)
{
User? user = this.database.UserFromWebRequest(this.Request);
if (user == null || !user.IsAdmin) return this.StatusCode(403, "");
User? targetedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id);
;
if (targetedUser == null) return this.NotFound();
targetedUser.Banned = true;
targetedUser.BannedReason = $"Banned by admin {user.Username} (id: {user.UserId})";
// invalidate all currently active gametokens
this.database.GameTokens.RemoveRange(this.database.GameTokens.Where(t => t.UserId == targetedUser.UserId));
// invalidate all currently active webtokens
this.database.WebTokens.RemoveRange(this.database.WebTokens.Where(t => t.UserId == targetedUser.UserId));
await this.database.SaveChangesAsync();
return this.Redirect($"/user/{targetedUser.UserId}");
}
[HttpGet("unban")]
public async Task<IActionResult> UnbanUser([FromRoute] int id)
{
User? user = this.database.UserFromWebRequest(this.Request);
if (user == null || !user.IsAdmin) return this.StatusCode(403, "");
User? targetedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id);
;
if (targetedUser == null) return this.NotFound();
targetedUser.Banned = false;
targetedUser.BannedReason = null;
await this.database.SaveChangesAsync();
return this.Redirect($"/user/{targetedUser.UserId}");
}
}
}

View file

@ -0,0 +1,41 @@
using LBPUnion.ProjectLighthouse;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ProjectLighthouse.Migrations
{
[DbContext(typeof(Database))]
[Migration("20211217000749_AddBannedPropertiesToUser")]
public partial class AddBannedPropertiesToUser : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "Banned",
table: "Users",
type: "tinyint(1)",
nullable: false,
defaultValue: false);
migrationBuilder.AddColumn<string>(
name: "BannedReason",
table: "Users",
type: "longtext",
nullable: true)
.Annotation("MySql:CharSet", "utf8mb4");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Banned",
table: "Users");
migrationBuilder.DropColumn(
name: "BannedReason",
table: "Users");
}
}
}

View file

@ -541,6 +541,12 @@ namespace ProjectLighthouse.Migrations
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("int"); .HasColumnType("int");
b.Property<bool>("Banned")
.HasColumnType("tinyint(1)");
b.Property<string>("BannedReason")
.HasColumnType("longtext");
b.Property<string>("Biography") b.Property<string>("Biography")
.HasColumnType("longtext"); .HasColumnType("longtext");

View file

@ -10,12 +10,27 @@
Model.ShowTitleInPage = false; Model.ShowTitleInPage = false;
} }
@if (Model.User != null && Model.User.IsAdmin && Model.ProfileUser.Banned)
{
<div class="ui inverted red segment">
<p>
<b>User is banned!</b>
</p>
<p>Reason: @Model.ProfileUser.BannedReason</p>
<a class="ui inverted button" href="/admin/user/@Model.ProfileUser.UserId/unban">
<i class="ban icon"></i>
<span>Unban User</span>
</a>
</div>
}
<div class="ui grid"> <div class="ui grid">
<div class="eight wide column"> <div class="eight wide column">
<h1>@Model.Title</h1> <h1>@Model.Title</h1>
<p> <p>
<i>@Model.ProfileUser!.Status</i> <i>@Model.ProfileUser!.Status</i>
</p> </p>
<div class="statsUnderTitle"> <div class="statsUnderTitle">
<i class="pink heart icon" title="Hearts"></i> <span>@Model.ProfileUser.Hearts</span> <i class="pink heart icon" title="Hearts"></i> <span>@Model.ProfileUser.Hearts</span>
<i class="blue comment icon" title="Comments"></i> <span>@Model.ProfileUser.Comments</span> <i class="blue comment icon" title="Comments"></i> <span>@Model.ProfileUser.Comments</span>
@ -49,6 +64,13 @@
<span>Reset Password</span> <span>Reset Password</span>
</a> </a>
} }
@if (Model.User != null && Model.User.IsAdmin && !Model.ProfileUser.Banned)
{
<a class="ui red button" href="/admin/user/@Model.ProfileUser.UserId/ban">
<i class="ban icon"></i>
<span>Ban User</span>
</a>
}
</div> </div>
<div class="eight wide column"> <div class="eight wide column">
<div class="ui blue segment"> <div class="ui blue segment">

View file

@ -24,8 +24,9 @@ namespace LBPUnion.ProjectLighthouse.Pages
public async Task<IActionResult> OnGet([FromRoute] int userId) public async Task<IActionResult> OnGet([FromRoute] int userId)
{ {
bool canViewBannedUsers = this.User != null && this.User.IsAdmin;
this.ProfileUser = await this.Database.Users.FirstOrDefaultAsync(u => u.UserId == userId); this.ProfileUser = await this.Database.Users.FirstOrDefaultAsync(u => u.UserId == userId);
if (this.ProfileUser == null) return this.NotFound(); if (this.ProfileUser == null || !canViewBannedUsers && this.ProfileUser.Banned) return this.NotFound();
this.Photos = await this.Database.Photos.OrderByDescending(p => p.Timestamp).Where(p => p.CreatorId == userId).Take(5).ToListAsync(); this.Photos = await this.Database.Photos.OrderByDescending(p => p.Timestamp).Where(p => p.CreatorId == userId).Take(5).ToListAsync();
this.Comments = await this.Database.Comments.Include this.Comments = await this.Database.Comments.Include

View file

@ -87,11 +87,34 @@ namespace LBPUnion.ProjectLighthouse
Stopwatch requestStopwatch = new(); Stopwatch requestStopwatch = new();
requestStopwatch.Start(); requestStopwatch.Start();
context.Request.EnableBuffering(); // Allows us to reset the position of Request.Body for later logging
// Log all headers. // Log all headers.
// foreach (KeyValuePair<string, StringValues> header in context.Request.Headers) Logger.Log($"{header.Key}: {header.Value}"); // foreach (KeyValuePair<string, StringValues> header in context.Request.Headers) Logger.Log($"{header.Key}: {header.Value}");
context.Request.EnableBuffering(); // Allows us to reset the position of Request.Body for later logging await next(context); // Handle the request so we can get the status code from it
requestStopwatch.Stop();
Logger.Log
(
$"{context.Response.StatusCode}, {requestStopwatch.ElapsedMilliseconds}ms: {context.Request.Method} {context.Request.Path}{context.Request.QueryString}",
LoggerLevelHttp.Instance
);
if (context.Request.Method == "POST")
{
context.Request.Body.Position = 0;
Logger.Log(await new StreamReader(context.Request.Body).ReadToEndAsync(), LoggerLevelHttp.Instance);
}
}
);
// Digest check
app.Use
(
async (context, next) =>
{
// Client digest check. // Client digest check.
if (!context.Request.Cookies.TryGetValue("MM_AUTH", out string authCookie)) authCookie = string.Empty; if (!context.Request.Cookies.TryGetValue("MM_AUTH", out string authCookie)) authCookie = string.Empty;
string digestPath = context.Request.Path; string digestPath = context.Request.Path;
@ -119,7 +142,7 @@ namespace LBPUnion.ProjectLighthouse
Stream oldResponseStream = context.Response.Body; Stream oldResponseStream = context.Response.Body;
context.Response.Body = responseBuffer; context.Response.Body = responseBuffer;
await next(); // Handle the request so we can get the status code from it await next(context); // Handle the request so we can get the server digest hash
// Compute the server digest hash. // Compute the server digest hash.
if (computeDigests) if (computeDigests)
@ -138,7 +161,13 @@ namespace LBPUnion.ProjectLighthouse
responseBuffer.Position = 0; responseBuffer.Position = 0;
await responseBuffer.CopyToAsync(oldResponseStream); await responseBuffer.CopyToAsync(oldResponseStream);
context.Response.Body = oldResponseStream; context.Response.Body = oldResponseStream;
}
);
app.Use
(
async (context, next) =>
{
#nullable enable #nullable enable
// Log LastContact for LBP1. This is done on LBP2/3/V on a Match request. // Log LastContact for LBP1. This is done on LBP2/3/V on a Match request.
if (context.Request.Path.ToString().StartsWith("/LITTLEBIGPLANETPS3_XML")) if (context.Request.Path.ToString().StartsWith("/LITTLEBIGPLANETPS3_XML"))
@ -153,19 +182,7 @@ namespace LBPUnion.ProjectLighthouse
} }
#nullable disable #nullable disable
requestStopwatch.Stop(); await next(context);
Logger.Log
(
$"{context.Response.StatusCode}, {requestStopwatch.ElapsedMilliseconds}ms: {context.Request.Method} {context.Request.Path}{context.Request.QueryString}",
LoggerLevelHttp.Instance
);
if (context.Request.Method == "POST")
{
context.Request.Body.Position = 0;
Logger.Log(await new StreamReader(context.Request.Body).ReadToEndAsync(), LoggerLevelHttp.Instance);
}
} }
); );

View file

@ -125,6 +125,10 @@ namespace LBPUnion.ProjectLighthouse.Types
} }
#nullable disable #nullable disable
public bool Banned { get; set; }
public string BannedReason { get; set; }
public string Serialize(GameVersion gameVersion = GameVersion.LittleBigPlanet1) public string Serialize(GameVersion gameVersion = GameVersion.LittleBigPlanet1)
{ {
string user = LbpSerializer.TaggedStringElement("npHandle", this.Username, "icon", this.IconHash) + string user = LbpSerializer.TaggedStringElement("npHandle", this.Username, "icon", this.IconHash) +