Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion SS14
Submodule SS14 updated 365 files
75 changes: 49 additions & 26 deletions SS14.Admin/Helpers/BanHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,41 +34,45 @@ public BanHelper(
_httpContext = httpContext;
}

public IQueryable<BanJoin<ServerBan, ServerUnban>> CreateServerBanJoin()
public IQueryable<BanJoin> CreateServerBanJoin()
{
return CreateBanJoin<ServerBan, ServerUnban>(_dbContext.Ban);
return CreateBanJoin().Where(b => b.Ban.Type == BanType.Server);
}

public IQueryable<BanJoin<ServerRoleBan, ServerRoleUnban>> CreateRoleBanJoin()
public IQueryable<BanJoin> CreateRoleBanJoin()
{
return CreateBanJoin<ServerRoleBan, ServerRoleUnban>(_dbContext.RoleBan);
return CreateBanJoin().Where(b => b.Ban.Type == BanType.Role);
}

private IQueryable<BanJoin<TBan, TUnban>> CreateBanJoin<TBan, TUnban>(DbSet<TBan> bans)
where TBan : class, IBanCommon<TUnban>
where TUnban : IUnbanCommon
private IQueryable<BanJoin> CreateBanJoin()
{
return bans
return _dbContext.Ban
.AsSplitQuery()
.Include(b => b.Unban)
.Include(b => b.Players)
.Include(b => b.Addresses)
.Include(b => b.Hwids)
.Include(b => b.Roles)
.Include(b => b.Rounds)
.LeftJoin(_dbContext.Player,
ban => ban.PlayerUserId, player => player.UserId,
(ban, player) => new { ban, player })
.LeftJoin(_dbContext.Player,
ban => ban.ban.BanningAdmin, admin => admin.UserId,
(ban, admin) => new { ban.ban, ban.player, admin })
ban => ban.BanningAdmin, admin => admin.UserId,
(ban, admin) => new { ban, admin })
.LeftJoin(_dbContext.Player,
ban => ban.ban.Unban!.UnbanningAdmin, unbanAdmin => unbanAdmin.UserId,
(ban, unbanAdmin) => new BanJoin<TBan, TUnban>
(ban, unbanAdmin) => new BanJoin
{
Players = ban.ban.Players!
.Select(p => _dbContext.Player.SingleOrDefault(pp => p.UserId == pp.UserId))
.Where(p => p != null)
.ToArray()!,
Ban = ban.ban,
Player = ban.player,
Admin = ban.admin,
UnbanAdmin = unbanAdmin
});
}

[Pure]
public static bool IsBanActive<TUnban>(IBanCommon<TUnban> b) where TUnban : IUnbanCommon
public static bool IsBanActive(Ban b)
{
return (b.ExpirationTime == null || b.ExpirationTime > DateTime.UtcNow) && b.Unban == null;
}
Expand All @@ -80,11 +84,11 @@ public static bool IsBanActive<TUnban>(IBanCommon<TUnban> b) where TUnban : IUnb
return hwid?.ToString();
}

public sealed class BanJoin<TBan, TUnban> where TBan: IBanCommon<TUnban> where TUnban : IUnbanCommon
public sealed class BanJoin
{
public TBan Ban { get; set; } = default!;
public Player? Player { get; set; }
public Ban Ban { get; set; } = default!;
public Player? Admin { get; set; }
public Player[] Players { get; set; } = [];
public Player? UnbanAdmin { get; set; }
}

Expand All @@ -111,14 +115,13 @@ public sealed class BanJoin<TBan, TUnban> where TBan: IBanCommon<TUnban> where T
}

/// <returns>Non-null string if an error occured that must be reported to the user.</returns>
public async Task<string?> FillBanCommon<TUnban>(
IBanCommon<TUnban> ban,
public async Task<string?> FillBanCommon(
Ban ban,
string? nameOrUid,
string? ip,
string? hwid,
int lengthMinutes,
string reason)
where TUnban : IUnbanCommon
{
if (string.IsNullOrWhiteSpace(nameOrUid) && string.IsNullOrWhiteSpace(ip) && string.IsNullOrWhiteSpace(hwid))
return "Must provide at least one of name/UID, IP address or HWID.";
Expand All @@ -128,9 +131,17 @@ public sealed class BanJoin<TBan, TUnban> where TBan: IBanCommon<TUnban> where T

if (!string.IsNullOrWhiteSpace(nameOrUid))
{
ban.PlayerUserId = await _playerLocator.Resolve(nameOrUid);
if (ban.PlayerUserId == null)
var userId = await _playerLocator.Resolve(nameOrUid);
if (userId == null)
return $"Unable to find user with name {nameOrUid}";

ban.Players =
[
new BanPlayer
{
UserId = userId.Value,
}
];
}

if (!string.IsNullOrWhiteSpace(ip))
Expand All @@ -144,7 +155,13 @@ public sealed class BanJoin<TBan, TUnban> where TBan: IBanCommon<TUnban> where T
// Ban /64 on IPv6.
parsedCidr ??= (byte)(parsedIp.AddressFamily == AddressFamily.InterNetwork ? 32 : 64);

ban.Address = new NpgsqlInet(parsedIp, parsedCidr.Value);
ban.Addresses =
[
new BanAddress
{
Address = new NpgsqlInet(parsedIp, parsedCidr.Value)
}
];
}

if (!string.IsNullOrWhiteSpace(hwid))
Expand All @@ -153,7 +170,13 @@ public sealed class BanJoin<TBan, TUnban> where TBan: IBanCommon<TUnban> where T
if (!ImmutableTypedHwid.TryParse(hwid, out var parsedHwid))
return "Invalid HWID";

ban.HWId = parsedHwid;
ban.Hwids =
[
new BanHwid
{
HWId = parsedHwid,
}
];
}

if (lengthMinutes != 0)
Expand Down
30 changes: 14 additions & 16 deletions SS14.Admin/Helpers/SearchHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,59 +35,57 @@ public static IQueryable<ConnectionLog> SearchConnectionLog(IQueryable<Connectio
return query.Where(expr);
}

private static Expression<Func<BanHelper.BanJoin<TBan, TUnban>, bool>> MakeCommonBanSearchExpression<TBan, TUnban>(
private static Expression<Func<BanHelper.BanJoin, bool>> MakeCommonBanSearchExpression(
string search, ClaimsPrincipal user)
where TBan : IBanCommon<TUnban>
where TUnban : IUnbanCommon
{
var normalized = search.ToUpperInvariant();

Expression<Func<BanHelper.BanJoin<TBan, TUnban>, bool>> expr = u =>
u.Player!.LastSeenUserName.ToUpper().Contains(normalized) ||
Expression<Func<BanHelper.BanJoin, bool>> expr = u =>
u.Players.Any(p => p.LastSeenUserName.ToUpper().Contains(normalized)) ||
u.Admin!.LastSeenUserName.ToUpper().Contains(normalized);

if (Guid.TryParse(search, out var guid))
CombineSearch(ref expr, b => b.Ban.PlayerUserId == guid);
CombineSearch(ref expr, b => b.Ban.Players!.Any(p => p.UserId == guid));

if (user.IsInRole(Constants.PIIRole) && IPHelper.TryParseCidr(search, out var cidr))
CombineSearch(ref expr, b => EF.Functions.ContainsOrEqual(cidr, b.Ban.Address!.Value));
CombineSearch(ref expr, b => b.Ban.Addresses!.Any(a => EF.Functions.ContainsOrEqual(cidr, a.Address)));

if (user.IsInRole(Constants.PIIRole) && IPAddress.TryParse(search, out var ip))
CombineSearch(ref expr, u => EF.Functions.ContainsOrEqual(u.Ban.Address!.Value, ip));
CombineSearch(ref expr, u => u.Ban.Addresses!.Any(a => EF.Functions.ContainsOrEqual(a.Address, ip)));

if (user.IsInRole(Constants.PIIRole) && ImmutableTypedHwid.TryParse(search, out var hwid))
CombineSearch(ref expr, u => u.Ban.HWId!.Type == hwid.Type && u.Ban.HWId.Hwid == hwid.Hwid.ToArray());
CombineSearch(ref expr, u => u.Ban.Hwids!.Any(h => h.HWId.Type == hwid.Type && h.HWId.Hwid == hwid.Hwid.ToArray()));

return expr;
}

public static IQueryable<BanHelper.BanJoin<ServerBan, ServerUnban>> SearchServerBans(
IQueryable<BanHelper.BanJoin<ServerBan, ServerUnban>> query,
public static IQueryable<BanHelper.BanJoin> SearchServerBans(
IQueryable<BanHelper.BanJoin> query,
string? search, ClaimsPrincipal user)
{
if (string.IsNullOrEmpty(search))
return query;

search = search.Trim();

var expr = MakeCommonBanSearchExpression<ServerBan, ServerUnban>(search, user);
var expr = MakeCommonBanSearchExpression(search, user);

return query.Where(expr);
}

public static IQueryable<BanHelper.BanJoin<ServerRoleBan, ServerRoleUnban>> SearchRoleBans(
IQueryable<BanHelper.BanJoin<ServerRoleBan, ServerRoleUnban>> query,
public static IQueryable<BanHelper.BanJoin> SearchRoleBans(
IQueryable<BanHelper.BanJoin> query,
string? search, ClaimsPrincipal user)
{
if (string.IsNullOrEmpty(search))
return query;

search = search.Trim();

var expr = MakeCommonBanSearchExpression<ServerRoleBan, ServerRoleUnban>(search, user);
var expr = MakeCommonBanSearchExpression(search, user);

// Match role name exactly.
CombineSearch(ref expr, u => u.Ban.RoleId == search);
CombineSearch(ref expr, u => u.Ban.Roles!.Any(r => r.RoleId == search));

return query.Where(expr);
}
Expand Down
59 changes: 55 additions & 4 deletions SS14.Admin/Helpers/SortState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,59 @@ private static SortOrder Flip(SortOrder order)

public void AddColumn<TKey>(string name, Expression<Func<T, TKey>> expr, SortOrder? sortDefault = null)
{
_columns.Add(name, new ColumnReg<TKey>(expr, sortDefault));
AddColumn(name, expr, expr, sortDefault);
}

/// <summary>
/// Add a column that can be sorted.
/// </summary>
/// <param name="name">The ID of the column, used in the HTML.</param>
/// <param name="exprDesc">Expression to use as sort key when sorting descending.</param>
/// <param name="exprAsc">Expression to use as sort key when sorting ascending.</param>
/// <param name="sortDefault">If given, this column is the default sort column, with the given order.</param>
/// <typeparam name="TKey">The type of items to use as sort key.</typeparam>
public void AddColumn<TKey>(
string name,
Expression<Func<T, TKey>> exprDesc,
Expression<Func<T, TKey>> exprAsc,
SortOrder? sortDefault = null)
{
_columns.Add(name, new ColumnReg<TKey>(exprAsc, exprDesc, sortDefault));

if (sortDefault != null)
DefaultColumn = name;
}

/// <summary>
/// Add a column that can be sorted, containing multiple values.
/// </summary>
/// <remarks>
/// The values in the column are used as a sort key by their min/max,
/// depending on how the column is being sorted.
/// </remarks>
/// <param name="name">The ID of the column, used in the HTML.</param>
/// <param name="expr">Expression to use as sort key.</param>
/// <param name="sortDefault">If given, this column is the default sort column, with the given order.</param>
/// <typeparam name="TKey">The type of items to use as sort key.</typeparam>
public void AddColumnMultiple<TKey>(
string name,
Expression<Func<T, IEnumerable<TKey>>> expr,
SortOrder? sortDefault = null)
{
var param = Expression.Parameter(typeof(T));
var invokeExpr = Expression.Invoke(expr, param);

AddColumn(
name,
Expression.Lambda<Func<T, TKey>>(
Expression.Call(typeof(Enumerable), "Max", [typeof(TKey)], invokeExpr),
param),
Expression.Lambda<Func<T, TKey>>(
Expression.Call(typeof(Enumerable), "Min", [typeof(TKey)], invokeExpr),
param),
sortDefault);
}

public IOrderedQueryable<T> ApplyToQuery(IQueryable<T> query)
{
var col = _columns[CurColumn!];
Expand All @@ -104,14 +151,18 @@ private abstract record ColumnReg(SortOrder? SortDefault)
public abstract IOrderedQueryable<T> ApplyToQuery(IQueryable<T> query, SortOrder order);
}

private sealed record ColumnReg<TKey>(Expression<Func<T, TKey>> Expr, SortOrder? SortDefault) : ColumnReg(SortDefault)
private sealed record ColumnReg<TKey>(
Expression<Func<T, TKey>> ExprAscending,
Expression<Func<T, TKey>> ExprDescending,
SortOrder? SortDefault)
: ColumnReg(SortDefault)
{
public override IOrderedQueryable<T> ApplyToQuery(IQueryable<T> query, SortOrder order)
{
return order switch
{
SortOrder.Ascending => query.OrderBy(Expr),
SortOrder.Descending => query.OrderByDescending(Expr),
SortOrder.Ascending => query.OrderBy(ExprAscending),
SortOrder.Descending => query.OrderByDescending(ExprDescending),
_ => throw new InvalidOperationException()
};
}
Expand Down
5 changes: 4 additions & 1 deletion SS14.Admin/Pages/Bans/Create.cshtml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@ public async Task<IActionResult> OnPostCreateAsync()

ExemptFlags = BanExemptions.GetExemptionFromForm(Request.Form);

var ban = new ServerBan();
var ban = new Ban
{
Type = BanType.Server,
};

var ipAddr = Input.IP;
var hwid = Input.HWid;
Expand Down
6 changes: 5 additions & 1 deletion SS14.Admin/Pages/Bans/CreateMassBan.cshtml.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Globalization;
using Content.Server.Database;
using Content.Shared.Database;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
Expand Down Expand Up @@ -60,7 +61,10 @@ public async Task<IActionResult> OnPostAsync(IFormFile file)

foreach (var entry in entries)
{
var ban = new ServerBan();
var ban = new Ban
{
Type = BanType.Server,
};

var ipAddr = entry.Address;
var hwid = entry.Hwid;
Expand Down
8 changes: 4 additions & 4 deletions SS14.Admin/Pages/Bans/Hits.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@

<dl class="row">
<dt class="col-sm-2">Name:</dt>
<dd class="col-sm-10">@Model.Ban.Player?.LastSeenUserName</dd>
<dd class="col-sm-10">@(string.Join(", ", Model.Ban.Players.Select(p => p.LastSeenUserName)))</dd>
<dt class="col-sm-2">User ID:</dt>
<dd class="col-sm-10 font-monospace">@Model.Ban.Ban.PlayerUserId</dd>
<dd class="col-sm-10 font-monospace">@(string.Join(", ", Model.Ban.Ban.Id))</dd>
@if (User.IsInRole(Constants.PIIRole))
{
<dt class="col-sm-2">IP:</dt>
<dd class="col-sm-10 font-monospace">@Model.Ban.Ban.Address?.FormatCidr()</dd>
<dd class="col-sm-10 font-monospace">@(string.Join(", ", Model.Ban.Ban.Addresses!.Select(ba => ba.Address.FormatCidr())))</dd>
<dt class="col-sm-2">HWID:</dt>
<dd class="col-sm-10 font-monospace">@BanHelper.FormatHwid(Model.Ban.Ban.HWId)</dd>
<dd class="col-sm-10 font-monospace">@(string.Join(", ", Model.Ban.Ban.Hwids!.Select(ba => ba.HWId.ToImmutable().ToString())))</dd>
}
<dt class="col-sm-2">Time:</dt>
<dd class="col-sm-10">@Model.Ban.Ban.BanTime.ToString("yyyy-MM-dd HH:mm:ss")</dd>
Expand Down
2 changes: 1 addition & 1 deletion SS14.Admin/Pages/Bans/Hits.cshtml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class Hits : PageModel
private readonly PostgresServerDbContext _dbContext;
private readonly BanHelper _banHelper;

public BanHelper.BanJoin<ServerBan, ServerUnban> Ban { get; set; } = default!;
public BanHelper.BanJoin Ban { get; set; } = default!;

public ISortState SortState { get; private set; } = default!;
public PaginationState<ConnectionsIndexModel.Connection> Pagination { get; } = new(100);
Expand Down
Loading
Loading