Permissions panel.
This commit is contained in:
29
Content.Server/Database/PlayerRecord.cs
Normal file
29
Content.Server/Database/PlayerRecord.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Content.Server.Database
|
||||
{
|
||||
public sealed class PlayerRecord
|
||||
{
|
||||
public NetUserId UserId { get; }
|
||||
public DateTimeOffset FirstSeenTime { get; }
|
||||
public string LastSeenUserName { get; }
|
||||
public DateTimeOffset LastSeenTime { get; }
|
||||
public IPAddress LastSeenAddress { get; }
|
||||
|
||||
public PlayerRecord(
|
||||
NetUserId userId,
|
||||
DateTimeOffset firstSeenTime,
|
||||
string lastSeenUserName,
|
||||
DateTimeOffset lastSeenTime,
|
||||
IPAddress lastSeenAddress)
|
||||
{
|
||||
UserId = userId;
|
||||
FirstSeenTime = firstSeenTime;
|
||||
LastSeenUserName = lastSeenUserName;
|
||||
LastSeenTime = lastSeenTime;
|
||||
LastSeenAddress = lastSeenAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Shared.Preferences;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@@ -211,6 +212,8 @@ namespace Content.Server.Database
|
||||
* PLAYER RECORDS
|
||||
*/
|
||||
public abstract Task UpdatePlayerRecord(NetUserId userId, string userName, IPAddress address);
|
||||
public abstract Task<PlayerRecord?> GetPlayerRecordByUserName(string userName, CancellationToken cancel);
|
||||
public abstract Task<PlayerRecord?> GetPlayerRecordByUserId(NetUserId userId, CancellationToken cancel);
|
||||
|
||||
/*
|
||||
* CONNECTION LOG
|
||||
@@ -220,7 +223,7 @@ namespace Content.Server.Database
|
||||
/*
|
||||
* ADMIN STUFF
|
||||
*/
|
||||
public async Task<Admin?> GetAdminDataForAsync(NetUserId userId)
|
||||
public async Task<Admin?> GetAdminDataForAsync(NetUserId userId, CancellationToken cancel)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
|
||||
@@ -228,7 +231,75 @@ namespace Content.Server.Database
|
||||
.Include(p => p.Flags)
|
||||
.Include(p => p.AdminRank)
|
||||
.ThenInclude(p => p!.Flags)
|
||||
.SingleOrDefaultAsync(p => p.UserId == userId.UserId);
|
||||
.SingleOrDefaultAsync(p => p.UserId == userId.UserId, cancel);
|
||||
}
|
||||
|
||||
public abstract Task<((Admin, string? lastUserName)[] admins, AdminRank[])>
|
||||
GetAllAdminAndRanksAsync(CancellationToken cancel);
|
||||
|
||||
public async Task<AdminRank?> GetAdminRankDataForAsync(int id, CancellationToken cancel = default)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
|
||||
return await db.DbContext.AdminRank
|
||||
.Include(r => r.Flags)
|
||||
.SingleOrDefaultAsync(r => r.Id == id, cancel);
|
||||
}
|
||||
|
||||
public async Task RemoveAdminAsync(NetUserId userId, CancellationToken cancel)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
|
||||
var admin = await db.DbContext.Admin.SingleAsync(a => a.UserId == userId.UserId, cancel);
|
||||
db.DbContext.Admin.Remove(admin);
|
||||
|
||||
await db.DbContext.SaveChangesAsync(cancel);
|
||||
}
|
||||
|
||||
public async Task AddAdminAsync(Admin admin, CancellationToken cancel)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
|
||||
db.DbContext.Admin.Add(admin);
|
||||
|
||||
await db.DbContext.SaveChangesAsync(cancel);
|
||||
}
|
||||
|
||||
public async Task UpdateAdminAsync(Admin admin, CancellationToken cancel)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
|
||||
db.DbContext.Admin.Update(admin);
|
||||
|
||||
await db.DbContext.SaveChangesAsync(cancel);
|
||||
}
|
||||
|
||||
public async Task RemoveAdminRankAsync(int rankId, CancellationToken cancel)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
|
||||
var admin = await db.DbContext.AdminRank.SingleAsync(a => a.Id == rankId, cancel);
|
||||
db.DbContext.AdminRank.Remove(admin);
|
||||
|
||||
await db.DbContext.SaveChangesAsync(cancel);
|
||||
}
|
||||
|
||||
public async Task AddAdminRankAsync(AdminRank rank, CancellationToken cancel)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
|
||||
db.DbContext.AdminRank.Add(rank);
|
||||
|
||||
await db.DbContext.SaveChangesAsync(cancel);
|
||||
}
|
||||
|
||||
public async Task UpdateAdminRankAsync(AdminRank rank, CancellationToken cancel)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
|
||||
db.DbContext.AdminRank.Update(rank);
|
||||
|
||||
await db.DbContext.SaveChangesAsync(cancel);
|
||||
}
|
||||
|
||||
protected abstract Task<DbGuard> GetDb();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Shared;
|
||||
using Content.Shared.Preferences;
|
||||
@@ -27,7 +28,9 @@ namespace Content.Server.Database
|
||||
// Preferences
|
||||
Task<PlayerPreferences> InitPrefsAsync(NetUserId userId, ICharacterProfile defaultProfile);
|
||||
Task SaveSelectedCharacterIndexAsync(NetUserId userId, int index);
|
||||
|
||||
Task SaveCharacterSlotAsync(NetUserId userId, ICharacterProfile? profile, int slot);
|
||||
|
||||
// Single method for two operations for transaction.
|
||||
Task DeleteSlotAndSetSelectedIndex(NetUserId userId, int deleteSlot, int newSlot);
|
||||
Task<PlayerPreferences?> GetPlayerPreferencesAsync(NetUserId userId);
|
||||
@@ -42,12 +45,26 @@ namespace Content.Server.Database
|
||||
|
||||
// Player records
|
||||
Task UpdatePlayerRecordAsync(NetUserId userId, string userName, IPAddress address);
|
||||
Task<PlayerRecord?> GetPlayerRecordByUserName(string userName, CancellationToken cancel = default);
|
||||
Task<PlayerRecord?> GetPlayerRecordByUserId(NetUserId userId, CancellationToken cancel = default);
|
||||
|
||||
// Connection log
|
||||
Task AddConnectionLogAsync(NetUserId userId, string userName, IPAddress address);
|
||||
|
||||
// Admins
|
||||
Task<Admin?> GetAdminDataForAsync(NetUserId userId);
|
||||
Task<Admin?> GetAdminDataForAsync(NetUserId userId, CancellationToken cancel = default);
|
||||
Task<AdminRank?> GetAdminRankAsync(int id, CancellationToken cancel = default);
|
||||
|
||||
Task<((Admin, string? lastUserName)[] admins, AdminRank[])> GetAllAdminAndRanksAsync(
|
||||
CancellationToken cancel = default);
|
||||
|
||||
Task RemoveAdminAsync(NetUserId userId, CancellationToken cancel = default);
|
||||
Task AddAdminAsync(Admin admin, CancellationToken cancel = default);
|
||||
Task UpdateAdminAsync(Admin admin, CancellationToken cancel = default);
|
||||
|
||||
Task RemoveAdminRankAsync(int rankId, CancellationToken cancel = default);
|
||||
Task AddAdminRankAsync(AdminRank rank, CancellationToken cancel = default);
|
||||
Task UpdateAdminRankAsync(AdminRank rank, CancellationToken cancel = default);
|
||||
}
|
||||
|
||||
public sealed class ServerDbManager : IServerDbManager
|
||||
@@ -135,14 +152,65 @@ namespace Content.Server.Database
|
||||
return _db.UpdatePlayerRecord(userId, userName, address);
|
||||
}
|
||||
|
||||
public Task<PlayerRecord?> GetPlayerRecordByUserName(string userName, CancellationToken cancel = default)
|
||||
{
|
||||
return _db.GetPlayerRecordByUserName(userName, cancel);
|
||||
}
|
||||
|
||||
public Task<PlayerRecord?> GetPlayerRecordByUserId(NetUserId userId, CancellationToken cancel = default)
|
||||
{
|
||||
return _db.GetPlayerRecordByUserId(userId, cancel);
|
||||
}
|
||||
|
||||
public Task AddConnectionLogAsync(NetUserId userId, string userName, IPAddress address)
|
||||
{
|
||||
return _db.AddConnectionLogAsync(userId, userName, address);
|
||||
}
|
||||
|
||||
public Task<Admin?> GetAdminDataForAsync(NetUserId userId)
|
||||
public Task<Admin?> GetAdminDataForAsync(NetUserId userId, CancellationToken cancel = default)
|
||||
{
|
||||
return _db.GetAdminDataForAsync(userId);
|
||||
return _db.GetAdminDataForAsync(userId, cancel);
|
||||
}
|
||||
|
||||
public Task<AdminRank?> GetAdminRankAsync(int id, CancellationToken cancel = default)
|
||||
{
|
||||
return _db.GetAdminRankDataForAsync(id, cancel);
|
||||
}
|
||||
|
||||
public Task<((Admin, string? lastUserName)[] admins, AdminRank[])> GetAllAdminAndRanksAsync(
|
||||
CancellationToken cancel = default)
|
||||
{
|
||||
return _db.GetAllAdminAndRanksAsync(cancel);
|
||||
}
|
||||
|
||||
public Task RemoveAdminAsync(NetUserId userId, CancellationToken cancel = default)
|
||||
{
|
||||
return _db.RemoveAdminAsync(userId, cancel);
|
||||
}
|
||||
|
||||
public Task AddAdminAsync(Admin admin, CancellationToken cancel = default)
|
||||
{
|
||||
return _db.AddAdminAsync(admin, cancel);
|
||||
}
|
||||
|
||||
public Task UpdateAdminAsync(Admin admin, CancellationToken cancel = default)
|
||||
{
|
||||
return _db.UpdateAdminAsync(admin, cancel);
|
||||
}
|
||||
|
||||
public Task RemoveAdminRankAsync(int rankId, CancellationToken cancel = default)
|
||||
{
|
||||
return _db.RemoveAdminRankAsync(rankId, cancel);
|
||||
}
|
||||
|
||||
public Task AddAdminRankAsync(AdminRank rank, CancellationToken cancel = default)
|
||||
{
|
||||
return _db.AddAdminRankAsync(rank, cancel);
|
||||
}
|
||||
|
||||
public Task UpdateAdminRankAsync(AdminRank rank, CancellationToken cancel = default)
|
||||
{
|
||||
return _db.UpdateAdminRankAsync(rank, cancel);
|
||||
}
|
||||
|
||||
private DbContextOptions<ServerDbContext> CreatePostgresOptions()
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Robust.Shared.Network;
|
||||
@@ -138,6 +140,45 @@ namespace Content.Server.Database
|
||||
await db.PgDbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public override async Task<PlayerRecord?> GetPlayerRecordByUserName(string userName, CancellationToken cancel)
|
||||
{
|
||||
await using var db = await GetDbImpl();
|
||||
|
||||
// Sort by descending last seen time.
|
||||
// So if, due to account renames, we have two people with the same username in the DB,
|
||||
// the most recent one is picked.
|
||||
var record = await db.PgDbContext.Player
|
||||
.OrderByDescending(p => p.LastSeenTime)
|
||||
.FirstOrDefaultAsync(p => p.LastSeenUserName == userName, cancel);
|
||||
|
||||
return MakePlayerRecord(record);
|
||||
}
|
||||
|
||||
public override async Task<PlayerRecord?> GetPlayerRecordByUserId(NetUserId userId, CancellationToken cancel)
|
||||
{
|
||||
await using var db = await GetDbImpl();
|
||||
|
||||
var record = await db.PgDbContext.Player
|
||||
.SingleOrDefaultAsync(p => p.UserId == userId.UserId, cancel);
|
||||
|
||||
return MakePlayerRecord(record);
|
||||
}
|
||||
|
||||
private static PlayerRecord? MakePlayerRecord(PostgresPlayer? record)
|
||||
{
|
||||
if (record == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new PlayerRecord(
|
||||
new NetUserId(record.UserId),
|
||||
new DateTimeOffset(record.FirstSeenTime, TimeSpan.Zero),
|
||||
record.LastSeenUserName,
|
||||
new DateTimeOffset(record.LastSeenTime, TimeSpan.Zero),
|
||||
record.LastSeenAddress);
|
||||
}
|
||||
|
||||
public override async Task AddConnectionLogAsync(NetUserId userId, string userName, IPAddress address)
|
||||
{
|
||||
await using var db = await GetDbImpl();
|
||||
@@ -153,6 +194,27 @@ namespace Content.Server.Database
|
||||
await db.PgDbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public override async Task<((Admin, string? lastUserName)[] admins, AdminRank[])>
|
||||
GetAllAdminAndRanksAsync(CancellationToken cancel)
|
||||
{
|
||||
await using var db = await GetDbImpl();
|
||||
|
||||
// Honestly this probably doesn't even matter but whatever.
|
||||
await using var tx =
|
||||
await db.DbContext.Database.BeginTransactionAsync(IsolationLevel.RepeatableRead, cancel);
|
||||
|
||||
// Join with the player table to find their last seen username, if they have one.
|
||||
var admins = await db.PgDbContext.Admin
|
||||
.Include(a => a.Flags)
|
||||
.GroupJoin(db.PgDbContext.Player, a => a.UserId, p => p.UserId, (a, grouping) => new {a, grouping})
|
||||
.SelectMany(t => t.grouping.DefaultIfEmpty(), (t, p) => new {t.a, p.LastSeenUserName})
|
||||
.ToArrayAsync(cancel);
|
||||
|
||||
var adminRanks = await db.DbContext.AdminRank.Include(a => a.Flags).ToArrayAsync(cancel);
|
||||
|
||||
return (admins.Select(p => (p.a, p.LastSeenUserName)).ToArray(), adminRanks)!;
|
||||
}
|
||||
|
||||
private async Task<DbGuardImpl> GetDbImpl()
|
||||
{
|
||||
await _dbReadyTask;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
@@ -105,6 +106,44 @@ namespace Content.Server.Database
|
||||
await db.SqliteDbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public override async Task<PlayerRecord?> GetPlayerRecordByUserName(string userName, CancellationToken cancel)
|
||||
{
|
||||
await using var db = await GetDbImpl();
|
||||
|
||||
// Sort by descending last seen time.
|
||||
// So if due to account renames we have two people with the same username in the DB,
|
||||
// the most recent one is picked.
|
||||
var record = await db.SqliteDbContext.Player
|
||||
.OrderByDescending(p => p.LastSeenTime)
|
||||
.FirstOrDefaultAsync(p => p.LastSeenUserName == userName, cancel);
|
||||
|
||||
return MakePlayerRecord(record);
|
||||
}
|
||||
|
||||
public override async Task<PlayerRecord?> GetPlayerRecordByUserId(NetUserId userId, CancellationToken cancel)
|
||||
{
|
||||
await using var db = await GetDbImpl();
|
||||
|
||||
var record = await db.SqliteDbContext.Player
|
||||
.SingleOrDefaultAsync(p => p.UserId == userId.UserId, cancel);
|
||||
|
||||
return MakePlayerRecord(record);
|
||||
}
|
||||
|
||||
private static PlayerRecord? MakePlayerRecord(SqlitePlayer? record)
|
||||
{
|
||||
if (record == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new PlayerRecord(
|
||||
new NetUserId(record.UserId),
|
||||
new DateTimeOffset(record.FirstSeenTime, TimeSpan.Zero),
|
||||
record.LastSeenUserName,
|
||||
new DateTimeOffset(record.LastSeenTime, TimeSpan.Zero),
|
||||
IPAddress.Parse(record.LastSeenAddress));
|
||||
}
|
||||
private static ServerBanDef? ConvertBan(SqliteServerBan? ban)
|
||||
{
|
||||
if (ban == null)
|
||||
@@ -156,6 +195,21 @@ namespace Content.Server.Database
|
||||
await db.SqliteDbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public override async Task<((Admin, string? lastUserName)[] admins, AdminRank[])> GetAllAdminAndRanksAsync(
|
||||
CancellationToken cancel)
|
||||
{
|
||||
await using var db = await GetDbImpl();
|
||||
|
||||
var admins = await db.SqliteDbContext.Admin
|
||||
.Include(a => a.Flags)
|
||||
.GroupJoin(db.SqliteDbContext.Player, a => a.UserId, p => p.UserId, (a, grouping) => new {a, grouping})
|
||||
.SelectMany(t => t.grouping.DefaultIfEmpty(), (t, p) => new {t.a, p.LastSeenUserName})
|
||||
.ToArrayAsync(cancel);
|
||||
|
||||
var adminRanks = await db.DbContext.AdminRank.Include(a => a.Flags).ToArrayAsync(cancel);
|
||||
|
||||
return (admins.Select(p => (p.a, p.LastSeenUserName)).ToArray(), adminRanks)!;
|
||||
}
|
||||
|
||||
private async Task<DbGuardImpl> GetDbImpl()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user