diff --git a/Application/ApplicationManager.cs b/Application/ApplicationManager.cs index d37d6e1c3..16ce05d64 100644 --- a/Application/ApplicationManager.cs +++ b/Application/ApplicationManager.cs @@ -21,6 +21,7 @@ using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; +using static SharedLibraryCore.GameEvent; namespace IW4MAdmin.Application { @@ -31,8 +32,6 @@ namespace IW4MAdmin.Application public ILogger Logger => GetLogger(0); public bool Running { get; private set; } public bool IsInitialized { get; private set; } - // define what the delagate function looks like - public delegate void OnServerEventEventHandler(object sender, GameEventArgs e); // expose the event handler so we can execute the events public OnServerEventEventHandler OnServerEvent { get; set; } public DateTime StartTime { get; private set; } @@ -53,7 +52,6 @@ namespace IW4MAdmin.Application public BaseConfigurationHandler ConfigHandler; GameEventHandler Handler; readonly IPageList PageList; - readonly SemaphoreSlim ProcessingEvent = new SemaphoreSlim(1, 1); readonly Dictionary Loggers = new Dictionary(); private readonly MetaService _metaService; private readonly TimeSpan _throttleTimeout = new TimeSpan(0, 1, 0); diff --git a/SharedLibraryCore/Events/GameEvent.cs b/SharedLibraryCore/Events/GameEvent.cs index 4dac83b9a..dba5cb3ee 100644 --- a/SharedLibraryCore/Events/GameEvent.cs +++ b/SharedLibraryCore/Events/GameEvent.cs @@ -1,4 +1,5 @@ using SharedLibraryCore.Database.Models; +using SharedLibraryCore.Events; using System; using System.Threading; using System.Threading.Tasks; @@ -7,6 +8,9 @@ namespace SharedLibraryCore { public class GameEvent { + // define what the delagate function looks like + public delegate void OnServerEventEventHandler(object sender, GameEventArgs e); + public enum EventFailReason { /// diff --git a/SharedLibraryCore/Interfaces/IManager.cs b/SharedLibraryCore/Interfaces/IManager.cs index bdce35aac..a6b39f513 100644 --- a/SharedLibraryCore/Interfaces/IManager.cs +++ b/SharedLibraryCore/Interfaces/IManager.cs @@ -5,6 +5,7 @@ using SharedLibraryCore.Configuration; using System.Reflection; using SharedLibraryCore.Database.Models; using System.Threading; +using static SharedLibraryCore.GameEvent; namespace SharedLibraryCore.Interfaces { @@ -43,5 +44,6 @@ namespace SharedLibraryCore.Interfaces string ExternalIPAddress { get; } CancellationToken CancellationToken { get; } bool IsRestartRequested { get; } + OnServerEventEventHandler OnServerEvent { get; set; } } } diff --git a/WebfrontCore/Middleware/ClaimsPermissionRemoval.cs b/WebfrontCore/Middleware/ClaimsPermissionRemoval.cs new file mode 100644 index 000000000..5a99d3556 --- /dev/null +++ b/WebfrontCore/Middleware/ClaimsPermissionRemoval.cs @@ -0,0 +1,91 @@ +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Http; +using SharedLibraryCore.Events; +using SharedLibraryCore.Interfaces; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; +using static SharedLibraryCore.Database.Models.EFClient; +using static SharedLibraryCore.GameEvent; + +namespace WebfrontCore.Middleware +{ + /// + /// Facilitates the removal of identity claims when client is demoted + /// + internal class ClaimsPermissionRemoval + { + private readonly IManager _manager; + private readonly List _privilegedClientIds; + private readonly RequestDelegate _nextRequest; + + public ClaimsPermissionRemoval(RequestDelegate nextRequest, IManager manager) + { + _manager = manager; + _manager.OnServerEvent += OnGameEvent; + _privilegedClientIds = new List(); + _nextRequest = nextRequest; + } + + /// + /// Callback for the game event + /// + /// + /// + private void OnGameEvent(object sender, GameEventArgs args) + { + if (args.Event.Type == EventType.ChangePermission && + args.Event.Extra is Permission perm) + { + // we want to remove the claims when the client is demoted + if (perm < Permission.Trusted) + { + lock (_privilegedClientIds) + { + _privilegedClientIds.RemoveAll(id => id == args.Event.Target.ClientId); + } + } + // and add if promoted + else if (perm > Permission.Trusted && + !_privilegedClientIds.Contains(args.Event.Target.ClientId)) + { + lock (_privilegedClientIds) + { + _privilegedClientIds.Add(args.Event.Target.ClientId); + } + } + } + } + + public async Task Invoke(HttpContext context) + { + // we want to load the initial list of privileged clients + if (_privilegedClientIds.Count == 0) + { + var ids = (await _manager.GetClientService().GetPrivilegedClients()) + .Select(_client => _client.ClientId); + + lock (_privilegedClientIds) + { + _privilegedClientIds.AddRange(ids); + } + } + + // sid stores the clientId + string claimsId = context.User.Claims.FirstOrDefault(_claim => _claim.Type == ClaimTypes.Sid)?.Value; + + if (!string.IsNullOrEmpty(claimsId)) + { + int clientId = int.Parse(claimsId); + // they've been removed + if (!_privilegedClientIds.Contains(clientId) && clientId != 1) + { + await context.SignOutAsync(); + } + } + + await _nextRequest.Invoke(context); + } + } +} diff --git a/WebfrontCore/Startup.cs b/WebfrontCore/Startup.cs index 8e64dd8b2..d9608d2ca 100644 --- a/WebfrontCore/Startup.cs +++ b/WebfrontCore/Startup.cs @@ -113,6 +113,9 @@ namespace WebfrontCore app.UseAuthentication(); app.UseCors("AllowAll"); + // prevents banned/demoted users from keeping their claims + app.UseMiddleware(Program.Manager); + app.UseMvc(routes => { routes.MapRoute(