add admin related things

This commit is contained in:
Stanley Dimant
2022-06-30 01:53:28 +02:00
parent dd1a6e910e
commit 3e00bc4efd
14 changed files with 651 additions and 6 deletions

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MareSynchronos.API
{
public class BannedUserDto
{
public string CharacterHash { get; set; }
public string Reason { get; set; }
}
}

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MareSynchronos.API
{
public class ForbiddenFileDto
{
public string Hash { get; set; }
public string ForbiddenBy { get; set; }
}
}

View File

@@ -6,9 +6,10 @@ using System.Threading.Tasks;
namespace MareSynchronos.API namespace MareSynchronos.API
{ {
public class UserDto public class LoggedInUserDto
{ {
public bool IsAdmin { get; set; } public bool IsAdmin { get; set; }
public bool IsModerator { get; set; }
public string UID { get; set; } public string UID { get; set; }
} }
} }

View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MareSynchronos.API
{
public class OnlineUserDto
{
public string UID { get; set; }
public string CharacterNameHash { get; set; }
public bool IsModerator { get; set; }
public bool IsAdmin { get; set; }
}
}

View File

@@ -0,0 +1,171 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MareSynchronos.API;
using MareSynchronosServer.Authentication;
using MareSynchronosServer.Data;
using MareSynchronosServer.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace MareSynchronosServer.Hubs
{
public class AdminHub : BaseHub<AdminHub>
{
public AdminHub(MareDbContext context, ILogger<AdminHub> logger) : base(context, logger)
{
}
private List<string> OnlineAdmins => DbContext.Users.Where(u => !string.IsNullOrEmpty(u.CharacterIdentification) && (u.IsModerator || u.IsAdmin))
.Select(u => u.UID).ToList();
private bool IsModerator => DbContext.Users.Single(b => b.UID == AuthenticatedUserId).IsModerator || IsAdmin;
private bool IsAdmin => DbContext.Users.Single(b => b.UID == AuthenticatedUserId).IsAdmin;
[Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)]
public async Task<List<OnlineUserDto>> GetOnlineUsers()
{
if (!IsModerator) return null;
return await DbContext.Users.Where(b => !string.IsNullOrEmpty(b.CharacterIdentification)).Select(b => new OnlineUserDto
{
CharacterNameHash = b.CharacterIdentification,
UID = b.UID,
IsModerator = b.IsModerator,
IsAdmin = b.IsAdmin
}).ToListAsync();
}
[Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)]
public async Task<List<BannedUserDto>> GetBannedUsers()
{
if (!IsModerator) return null;
return await DbContext.BannedUsers.Select(b => new BannedUserDto()
{
CharacterHash = b.CharacterIdentification,
Reason = b.Reason
}).ToListAsync();
}
[Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)]
public async Task UpdateOrAddBannedUser(BannedUserDto dto)
{
if (!IsModerator || string.IsNullOrEmpty(dto.CharacterHash)) return;
var existingUser =
await DbContext.BannedUsers.SingleOrDefaultAsync(b => b.CharacterIdentification == dto.CharacterHash);
if (existingUser != null)
{
existingUser.Reason = dto.Reason;
DbContext.Update(existingUser);
}
else
{
await DbContext.BannedUsers.AddAsync(new Banned
{
CharacterIdentification = dto.CharacterHash,
Reason = dto.Reason
});
}
await DbContext.SaveChangesAsync();
await Clients.Users(OnlineAdmins).SendAsync("UpdateOrAddBannedUser", dto);
var bannedUser =
await DbContext.Users.SingleOrDefaultAsync(u => u.CharacterIdentification == dto.CharacterHash);
if (bannedUser != null)
{
await Clients.User(bannedUser.UID).SendAsync("ForcedReconnect");
}
}
[Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)]
public async Task DeleteBannedUser(BannedUserDto dto)
{
if (!IsModerator || string.IsNullOrEmpty(dto.CharacterHash)) return;
var existingUser =
await DbContext.BannedUsers.SingleOrDefaultAsync(b => b.CharacterIdentification == dto.CharacterHash);
if (existingUser == null)
{
return;
}
DbContext.Remove(existingUser);
await DbContext.SaveChangesAsync();
await Clients.Users(OnlineAdmins).SendAsync("DeleteBannedUser", dto);
}
[Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)]
public async Task<List<ForbiddenFileDto>> GetForbiddenFiles()
{
if (!IsModerator) return null;
return await DbContext.ForbiddenUploadEntries.Select(b => new ForbiddenFileDto()
{
Hash = b.Hash,
ForbiddenBy = b.ForbiddenBy
}).ToListAsync();
}
[Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)]
public async Task UpdateOrAddForbiddenFile(ForbiddenFileDto dto)
{
if (!IsAdmin || string.IsNullOrEmpty(dto.Hash)) return;
var existingForbiddenFile =
await DbContext.ForbiddenUploadEntries.SingleOrDefaultAsync(b => b.Hash == dto.Hash);
if (existingForbiddenFile != null)
{
existingForbiddenFile.ForbiddenBy = dto.ForbiddenBy;
DbContext.Update(existingForbiddenFile);
}
else
{
await DbContext.ForbiddenUploadEntries.AddAsync(new ForbiddenUploadEntry
{
Hash = dto.Hash,
ForbiddenBy = dto.ForbiddenBy
});
}
await DbContext.SaveChangesAsync();
await Clients.Users(OnlineAdmins).SendAsync("UpdateOrAddForbiddenFile", dto);
}
[Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)]
public async Task DeleteForbiddenFile(ForbiddenFileDto dto)
{
if (!IsAdmin || string.IsNullOrEmpty(dto.Hash)) return;
var existingFile =
await DbContext.ForbiddenUploadEntries.SingleOrDefaultAsync(b => b.Hash == dto.Hash);
if (existingFile == null)
{
return;
}
DbContext.Remove(existingFile);
await DbContext.SaveChangesAsync();
await Clients.Users(OnlineAdmins).SendAsync("DeleteForbiddenFile", dto);
}
[Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AuthScheme)]
public async Task ChangeModeratorStatus(string uid, bool isModerator)
{
if (!IsAdmin) return;
var user = await DbContext.Users.SingleOrDefaultAsync(u => u.UID == uid);
if (user == null) return;
user.IsModerator = isModerator;
DbContext.Update(user);
await DbContext.SaveChangesAsync();
await Clients.Users(user.UID).SendAsync("ForcedReconnect");
}
}
}

View File

@@ -14,22 +14,23 @@ namespace MareSynchronosServer.Hubs
{ {
} }
public async Task<UserDto> Heartbeat() public async Task<LoggedInUserDto> Heartbeat()
{ {
var userId = Context.User!.Claims.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value; var userId = Context.User!.Claims.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value;
Logger.LogInformation("Heartbeat from " + (userId ?? "Unknown user")); Logger.LogInformation("Heartbeat from " + (userId ?? "Unknown user"));
if (userId != null) if (userId != null)
{ {
var isAdmin = (await DbContext.Users.SingleOrDefaultAsync(u => u.UID == userId))?.IsAdmin ?? false; var user = (await DbContext.Users.SingleAsync(u => u.UID == userId));
return new UserDto return new LoggedInUserDto
{ {
UID = userId, UID = userId,
IsAdmin = isAdmin IsModerator = user.IsModerator,
IsAdmin = user.IsAdmin
}; };
} }
return new UserDto(); return new LoggedInUserDto();
} }
} }
} }

View File

@@ -0,0 +1,182 @@
// <auto-generated />
using System;
using MareSynchronosServer.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace MareSynchronosServer.Migrations
{
[DbContext(typeof(MareDbContext))]
[Migration("20220629212721_AddBannedReason")]
partial class AddBannedReason
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "6.0.6")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1);
modelBuilder.Entity("MareSynchronosServer.Models.Banned", b =>
{
b.Property<string>("CharacterIdentification")
.HasColumnType("nvarchar(450)");
b.Property<string>("Reason")
.HasColumnType("nvarchar(max)");
b.HasKey("CharacterIdentification");
b.ToTable("BannedUsers", (string)null);
});
modelBuilder.Entity("MareSynchronosServer.Models.CharacterData", b =>
{
b.Property<string>("UserId")
.HasColumnType("nvarchar(450)");
b.Property<int>("JobId")
.HasColumnType("int");
b.Property<string>("CharacterCache")
.HasColumnType("nvarchar(max)");
b.Property<string>("Hash")
.HasColumnType("nvarchar(max)");
b.HasKey("UserId", "JobId");
b.ToTable("CharacterData", (string)null);
});
modelBuilder.Entity("MareSynchronosServer.Models.ClientPair", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);
b.Property<bool>("AllowReceivingMessages")
.HasColumnType("bit");
b.Property<bool>("IsPaused")
.HasColumnType("bit");
b.Property<string>("OtherUserUID")
.HasColumnType("nvarchar(450)");
b.Property<byte[]>("Timestamp")
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("rowversion");
b.Property<string>("UserUID")
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
b.HasIndex("OtherUserUID");
b.HasIndex("UserUID");
b.ToTable("ClientPairs", (string)null);
});
modelBuilder.Entity("MareSynchronosServer.Models.FileCache", b =>
{
b.Property<string>("Hash")
.HasColumnType("nvarchar(450)");
b.Property<DateTime>("LastAccessTime")
.HasColumnType("datetime2");
b.Property<byte[]>("Timestamp")
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("rowversion");
b.Property<bool>("Uploaded")
.HasColumnType("bit");
b.Property<string>("UploaderUID")
.HasColumnType("nvarchar(450)");
b.HasKey("Hash");
b.HasIndex("UploaderUID");
b.ToTable("FileCaches", (string)null);
});
modelBuilder.Entity("MareSynchronosServer.Models.ForbiddenUploadEntry", b =>
{
b.Property<string>("Hash")
.HasColumnType("nvarchar(450)");
b.Property<string>("ForbiddenBy")
.HasColumnType("nvarchar(max)");
b.HasKey("Hash");
b.ToTable("ForbiddenUploadEntries", (string)null);
});
modelBuilder.Entity("MareSynchronosServer.Models.User", b =>
{
b.Property<string>("UID")
.HasColumnType("nvarchar(450)");
b.Property<string>("CharacterIdentification")
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsAdmin")
.HasColumnType("bit");
b.Property<string>("SecretKey")
.HasColumnType("nvarchar(max)");
b.Property<byte[]>("Timestamp")
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("rowversion");
b.HasKey("UID");
b.ToTable("Users", (string)null);
});
modelBuilder.Entity("MareSynchronosServer.Models.ClientPair", b =>
{
b.HasOne("MareSynchronosServer.Models.User", "OtherUser")
.WithMany()
.HasForeignKey("OtherUserUID");
b.HasOne("MareSynchronosServer.Models.User", "User")
.WithMany()
.HasForeignKey("UserUID");
b.Navigation("OtherUser");
b.Navigation("User");
});
modelBuilder.Entity("MareSynchronosServer.Models.FileCache", b =>
{
b.HasOne("MareSynchronosServer.Models.User", "Uploader")
.WithMany()
.HasForeignKey("UploaderUID");
b.Navigation("Uploader");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,25 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace MareSynchronosServer.Migrations
{
public partial class AddBannedReason : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Reason",
table: "BannedUsers",
type: "nvarchar(max)",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Reason",
table: "BannedUsers");
}
}
}

View File

@@ -0,0 +1,185 @@
// <auto-generated />
using System;
using MareSynchronosServer.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace MareSynchronosServer.Migrations
{
[DbContext(typeof(MareDbContext))]
[Migration("20220629230923_ChangeAdminToModerator")]
partial class ChangeAdminToModerator
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "6.0.6")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1);
modelBuilder.Entity("MareSynchronosServer.Models.Banned", b =>
{
b.Property<string>("CharacterIdentification")
.HasColumnType("nvarchar(450)");
b.Property<string>("Reason")
.HasColumnType("nvarchar(max)");
b.HasKey("CharacterIdentification");
b.ToTable("BannedUsers", (string)null);
});
modelBuilder.Entity("MareSynchronosServer.Models.CharacterData", b =>
{
b.Property<string>("UserId")
.HasColumnType("nvarchar(450)");
b.Property<int>("JobId")
.HasColumnType("int");
b.Property<string>("CharacterCache")
.HasColumnType("nvarchar(max)");
b.Property<string>("Hash")
.HasColumnType("nvarchar(max)");
b.HasKey("UserId", "JobId");
b.ToTable("CharacterData", (string)null);
});
modelBuilder.Entity("MareSynchronosServer.Models.ClientPair", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);
b.Property<bool>("AllowReceivingMessages")
.HasColumnType("bit");
b.Property<bool>("IsPaused")
.HasColumnType("bit");
b.Property<string>("OtherUserUID")
.HasColumnType("nvarchar(450)");
b.Property<byte[]>("Timestamp")
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("rowversion");
b.Property<string>("UserUID")
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
b.HasIndex("OtherUserUID");
b.HasIndex("UserUID");
b.ToTable("ClientPairs", (string)null);
});
modelBuilder.Entity("MareSynchronosServer.Models.FileCache", b =>
{
b.Property<string>("Hash")
.HasColumnType("nvarchar(450)");
b.Property<DateTime>("LastAccessTime")
.HasColumnType("datetime2");
b.Property<byte[]>("Timestamp")
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("rowversion");
b.Property<bool>("Uploaded")
.HasColumnType("bit");
b.Property<string>("UploaderUID")
.HasColumnType("nvarchar(450)");
b.HasKey("Hash");
b.HasIndex("UploaderUID");
b.ToTable("FileCaches", (string)null);
});
modelBuilder.Entity("MareSynchronosServer.Models.ForbiddenUploadEntry", b =>
{
b.Property<string>("Hash")
.HasColumnType("nvarchar(450)");
b.Property<string>("ForbiddenBy")
.HasColumnType("nvarchar(max)");
b.HasKey("Hash");
b.ToTable("ForbiddenUploadEntries", (string)null);
});
modelBuilder.Entity("MareSynchronosServer.Models.User", b =>
{
b.Property<string>("UID")
.HasColumnType("nvarchar(450)");
b.Property<string>("CharacterIdentification")
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsAdmin")
.HasColumnType("bit");
b.Property<bool>("IsModerator")
.HasColumnType("bit");
b.Property<string>("SecretKey")
.HasColumnType("nvarchar(max)");
b.Property<byte[]>("Timestamp")
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("rowversion");
b.HasKey("UID");
b.ToTable("Users", (string)null);
});
modelBuilder.Entity("MareSynchronosServer.Models.ClientPair", b =>
{
b.HasOne("MareSynchronosServer.Models.User", "OtherUser")
.WithMany()
.HasForeignKey("OtherUserUID");
b.HasOne("MareSynchronosServer.Models.User", "User")
.WithMany()
.HasForeignKey("UserUID");
b.Navigation("OtherUser");
b.Navigation("User");
});
modelBuilder.Entity("MareSynchronosServer.Models.FileCache", b =>
{
b.HasOne("MareSynchronosServer.Models.User", "Uploader")
.WithMany()
.HasForeignKey("UploaderUID");
b.Navigation("Uploader");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,26 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace MareSynchronosServer.Migrations
{
public partial class ChangeAdminToModerator : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "IsModerator",
table: "Users",
type: "bit",
nullable: false,
defaultValue: false);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "IsModerator",
table: "Users");
}
}
}

View File

@@ -27,6 +27,9 @@ namespace MareSynchronosServer.Migrations
b.Property<string>("CharacterIdentification") b.Property<string>("CharacterIdentification")
.HasColumnType("nvarchar(450)"); .HasColumnType("nvarchar(450)");
b.Property<string>("Reason")
.HasColumnType("nvarchar(max)");
b.HasKey("CharacterIdentification"); b.HasKey("CharacterIdentification");
b.ToTable("BannedUsers", (string)null); b.ToTable("BannedUsers", (string)null);
@@ -135,6 +138,9 @@ namespace MareSynchronosServer.Migrations
b.Property<bool>("IsAdmin") b.Property<bool>("IsAdmin")
.HasColumnType("bit"); .HasColumnType("bit");
b.Property<bool>("IsModerator")
.HasColumnType("bit");
b.Property<string>("SecretKey") b.Property<string>("SecretKey")
.HasColumnType("nvarchar(max)"); .HasColumnType("nvarchar(max)");

View File

@@ -6,5 +6,6 @@ namespace MareSynchronosServer.Models
{ {
[Key] [Key]
public string CharacterIdentification { get; set; } public string CharacterIdentification { get; set; }
public string Reason { get; set; }
} }
} }

View File

@@ -12,6 +12,8 @@ namespace MareSynchronosServer.Models
[Timestamp] [Timestamp]
public byte[] Timestamp { get; set; } public byte[] Timestamp { get; set; }
public bool IsModerator { get; set; } = false;
public bool IsAdmin { get; set; } = false; public bool IsAdmin { get; set; } = false;
} }
} }

View File

@@ -94,6 +94,7 @@ namespace MareSynchronosServer
{ {
options.Transports = HttpTransportType.WebSockets; options.Transports = HttpTransportType.WebSockets;
}); });
endpoints.MapHub<AdminHub>("/admin", options => options.Transports = HttpTransportType.WebSockets);
endpoints.MapHub<FilesHub>("/files", options => endpoints.MapHub<FilesHub>("/files", options =>
{ {
options.ApplicationMaxBufferSize = long.MaxValue; options.ApplicationMaxBufferSize = long.MaxValue;