implement more robust command api and login
improve web console command response reliability and consistency
This commit is contained in:
@ -3,9 +3,15 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using SharedLibraryCore.Dtos;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Services;
|
||||
using WebfrontCore.Controllers.API.Dtos;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
@ -15,29 +21,34 @@ namespace WebfrontCore.Controllers.API
|
||||
/// api controller for client operations
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/client")]
|
||||
public class ClientController : ControllerBase
|
||||
[Route("api/[controller]")]
|
||||
public class ClientController : BaseController
|
||||
{
|
||||
private readonly IResourceQueryHelper<FindClientRequest, FindClientResult> _clientQueryHelper;
|
||||
private readonly ILogger _logger;
|
||||
private readonly ClientService _clientService;
|
||||
|
||||
public ClientController(ILogger<ClientController> logger, IResourceQueryHelper<FindClientRequest, FindClientResult> clientQueryHelper)
|
||||
public ClientController(ILogger<ClientController> logger,
|
||||
IResourceQueryHelper<FindClientRequest, FindClientResult> clientQueryHelper,
|
||||
ClientService clientService, IManager manager) : base(manager)
|
||||
{
|
||||
_logger = logger;
|
||||
_clientQueryHelper = clientQueryHelper;
|
||||
_clientService = clientService;
|
||||
}
|
||||
|
||||
[HttpGet("find")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||
public async Task<IActionResult> FindAsync([FromQuery]FindClientRequest request)
|
||||
public async Task<IActionResult> FindAsync([FromQuery] FindClientRequest request)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(new ErrorResponse()
|
||||
{
|
||||
Messages = ModelState.Values.SelectMany(_value => _value.Errors.Select(_error => _error.ErrorMessage)).ToArray()
|
||||
Messages = ModelState.Values
|
||||
.SelectMany(_value => _value.Errors.Select(_error => _error.ErrorMessage)).ToArray()
|
||||
});
|
||||
}
|
||||
|
||||
@ -58,9 +69,84 @@ namespace WebfrontCore.Controllers.API
|
||||
|
||||
return StatusCode(StatusCodes.Status500InternalServerError, new ErrorResponse()
|
||||
{
|
||||
Messages = new[] { e.Message }
|
||||
Messages = new[] {e.Message}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[HttpPost("{clientId:int}/login")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||
public async Task<IActionResult> LoginAsync([FromRoute] int clientId,
|
||||
[FromBody, Required] PasswordRequest request)
|
||||
{
|
||||
if (clientId == 0)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
HttpContext.Request.Cookies.TryGetValue(".AspNetCore.Cookies", out var cookie);
|
||||
|
||||
if (Authorized)
|
||||
{
|
||||
return Ok();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var privilegedClient = await _clientService.GetClientForLogin(clientId);
|
||||
var loginSuccess = false;
|
||||
|
||||
if (!Authorized)
|
||||
{
|
||||
loginSuccess =
|
||||
Manager.TokenAuthenticator.AuthorizeToken(privilegedClient.NetworkId, request.Password) ||
|
||||
(await Task.FromResult(SharedLibraryCore.Helpers.Hashing.Hash(request.Password,
|
||||
privilegedClient.PasswordSalt)))[0] == privilegedClient.Password;
|
||||
}
|
||||
|
||||
if (loginSuccess)
|
||||
{
|
||||
var claims = new[]
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, privilegedClient.Name),
|
||||
new Claim(ClaimTypes.Role, privilegedClient.Level.ToString()),
|
||||
new Claim(ClaimTypes.Sid, privilegedClient.ClientId.ToString()),
|
||||
new Claim(ClaimTypes.PrimarySid, privilegedClient.NetworkId.ToString("X"))
|
||||
};
|
||||
|
||||
var claimsIdentity = new ClaimsIdentity(claims, "login");
|
||||
var claimsPrinciple = new ClaimsPrincipal(claimsIdentity);
|
||||
await SignInAsync(claimsPrinciple);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
|
||||
catch (Exception)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
[HttpPost("{clientId:int}/logout")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||
public async Task<IActionResult> LogoutAsync()
|
||||
{
|
||||
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
public class PasswordRequest
|
||||
{
|
||||
public string Password { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
7
WebfrontCore/Controllers/API/Models/CommandRequest.cs
Normal file
7
WebfrontCore/Controllers/API/Models/CommandRequest.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace WebfrontCore.Controllers.API.Models
|
||||
{
|
||||
public class CommandRequest
|
||||
{
|
||||
public string Command { get; set; }
|
||||
}
|
||||
}
|
93
WebfrontCore/Controllers/API/Server.cs
Normal file
93
WebfrontCore/Controllers/API/Server.cs
Normal file
@ -0,0 +1,93 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using WebfrontCore.Controllers.API.Models;
|
||||
|
||||
namespace WebfrontCore.Controllers.API
|
||||
{
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class Server : BaseController
|
||||
{
|
||||
|
||||
public Server(IManager manager) : base(manager)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public IActionResult Index()
|
||||
{
|
||||
return new JsonResult(Manager.GetServers().Select(server => new
|
||||
{
|
||||
Id = server.EndPoint,
|
||||
server.Hostname,
|
||||
server.IP,
|
||||
server.Port
|
||||
}));
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public IActionResult GetServerById(string id)
|
||||
{
|
||||
var foundServer = Manager.GetServers().FirstOrDefault(server => server.EndPoint == long.Parse(id));
|
||||
|
||||
if (foundServer == null)
|
||||
{
|
||||
return new NotFoundResult();
|
||||
}
|
||||
|
||||
return new JsonResult(new
|
||||
{
|
||||
Id = foundServer.EndPoint,
|
||||
foundServer.Hostname,
|
||||
foundServer.IP,
|
||||
foundServer.Port
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("{id}/execute")]
|
||||
public async Task<IActionResult> ExecuteCommandForServer(string id, [FromBody] CommandRequest commandRequest)
|
||||
{
|
||||
if (!Authorized)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
var foundServer = Manager.GetServers().FirstOrDefault(server => server.EndPoint == long.Parse(id));
|
||||
|
||||
if (foundServer == null)
|
||||
{
|
||||
return new BadRequestObjectResult($"No server with id '{id}' was found");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(commandRequest.Command))
|
||||
{
|
||||
return new BadRequestObjectResult("Command cannot be empty");
|
||||
}
|
||||
|
||||
var start = DateTime.Now;
|
||||
Client.CurrentServer = foundServer;
|
||||
|
||||
var commandEvent = new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.Command,
|
||||
Owner = foundServer,
|
||||
Origin = Client,
|
||||
Data = commandRequest.Command,
|
||||
Extra = commandRequest.Command
|
||||
};
|
||||
|
||||
Manager.AddEvent(commandEvent);
|
||||
var completedEvent = await commandEvent.WaitAsync(Utilities.DefaultCommandTimeout, foundServer.Manager.CancellationToken);
|
||||
|
||||
return new JsonResult(new
|
||||
{
|
||||
ExecutionTimeMs = Math.Round((DateTime.Now - start).TotalMilliseconds, 0),
|
||||
completedEvent.Output
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -52,8 +52,10 @@ namespace WebfrontCore.Controllers
|
||||
var remoteEvent = new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.Command,
|
||||
Data = command.StartsWith(_appconfig.CommandPrefix) || command.StartsWith(_appconfig.BroadcastCommandPrefix) ?
|
||||
command : $"{_appconfig.CommandPrefix}{command}",
|
||||
Data = command.StartsWith(_appconfig.CommandPrefix) ||
|
||||
command.StartsWith(_appconfig.BroadcastCommandPrefix)
|
||||
? command
|
||||
: $"{_appconfig.CommandPrefix}{command}",
|
||||
Origin = client,
|
||||
Owner = server,
|
||||
IsRemote = true
|
||||
@ -65,7 +67,8 @@ namespace WebfrontCore.Controllers
|
||||
try
|
||||
{
|
||||
// wait for the event to process
|
||||
var completedEvent = await remoteEvent.WaitAsync(Utilities.DefaultCommandTimeout, server.Manager.CancellationToken);
|
||||
var completedEvent =
|
||||
await remoteEvent.WaitAsync(Utilities.DefaultCommandTimeout, server.Manager.CancellationToken);
|
||||
|
||||
if (completedEvent.FailReason == GameEvent.EventFailReason.Timeout)
|
||||
{
|
||||
@ -81,13 +84,11 @@ namespace WebfrontCore.Controllers
|
||||
|
||||
else
|
||||
{
|
||||
response = response = server.CommandResult.Where(c => c.ClientId == client.ClientId).ToArray();
|
||||
}
|
||||
|
||||
// remove the added command response
|
||||
for (int i = 0; i < response?.Length; i++)
|
||||
{
|
||||
server.CommandResult.Remove(response[i]);
|
||||
response = completedEvent.Output.Select(output => new CommandResponseInfo()
|
||||
{
|
||||
Response = output,
|
||||
ClientId = client.ClientId
|
||||
}).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,4 +107,4 @@ namespace WebfrontCore.Controllers
|
||||
return View("_Response", response);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user