Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add /api/client/{clientId} Endpoint #344

Merged
merged 3 commits into from
Sep 7, 2024
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
17 changes: 17 additions & 0 deletions SharedLibraryCore/Dtos/ClientInfoResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;

namespace SharedLibraryCore.Dtos;

public class ClientInfoResult
{
public int ClientId { get; set; }
public string Name { get; set; }
public string Level { get; set; }
public long NetworkId { get; set; }
public string GameName { get; set; }
public string? Tag { get; set; }

Check warning on line 12 in SharedLibraryCore/Dtos/ClientInfoResult.cs

View workflow job for this annotation

GitHub Actions / build_pack

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 12 in SharedLibraryCore/Dtos/ClientInfoResult.cs

View workflow job for this annotation

GitHub Actions / build_pack

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 12 in SharedLibraryCore/Dtos/ClientInfoResult.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 12 in SharedLibraryCore/Dtos/ClientInfoResult.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 12 in SharedLibraryCore/Dtos/ClientInfoResult.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
public DateTime FirstConnection { get; set; }
public DateTime LastConnection { get; set; }
public int TotalConnectionTime { get; set; }
public int Connections { get; set; }
}
122 changes: 73 additions & 49 deletions WebfrontCore/Controllers/API/ClientController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
using SharedLibraryCore.Dtos;
using SharedLibraryCore.Interfaces;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Data.Models;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.Extensions.Logging;
Expand All @@ -24,20 +26,15 @@ namespace WebfrontCore.Controllers.API
/// </summary>
[ApiController]
[Route("api/[controller]")]
public class ClientController : BaseController
public class ClientController(
ILogger<ClientController> logger,
IResourceQueryHelper<FindClientRequest, FindClientResult> clientQueryHelper,
ClientService clientService,
IManager manager,
IMetaServiceV2 metaService)
: BaseController(manager)
{
private readonly IResourceQueryHelper<FindClientRequest, FindClientResult> _clientQueryHelper;
private readonly ILogger _logger;
private readonly ClientService _clientService;

public ClientController(ILogger<ClientController> logger,
IResourceQueryHelper<FindClientRequest, FindClientResult> clientQueryHelper,
ClientService clientService, IManager manager) : base(manager)
{
_logger = logger;
_clientQueryHelper = clientQueryHelper;
_clientService = clientService;
}
private readonly ILogger _logger = logger;

[HttpGet("find")]
[ProducesResponseType(StatusCodes.Status200OK)]
Expand All @@ -47,45 +44,75 @@ public async Task<IActionResult> FindAsync([FromQuery] FindClientRequest request
{
if (!ModelState.IsValid)
{
return BadRequest(new ErrorResponse()
return BadRequest(new ErrorResponse
{
Messages = ModelState.Values
.SelectMany(_value => _value.Errors.Select(_error => _error.ErrorMessage)).ToArray()
.SelectMany(value => value.Errors.Select(error => error.ErrorMessage)).ToArray()
});
}

try
{
var results = await _clientQueryHelper.QueryResource(request);
var results = await clientQueryHelper.QueryResource(request);

return Ok(new FindClientResponse
{
TotalFoundClients = results.TotalResultCount,
Clients = results.Results
});
}

catch (Exception e)
{
_logger.LogWarning(e, "Failed to retrieve clients with query - {@request}", request);
_logger.LogWarning(e, "Failed to retrieve clients with query - {@Request}", request);
return StatusCode(StatusCodes.Status500InternalServerError, new ErrorResponse { Messages = [e.Message] });
}
}

[HttpGet("{clientId:int}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> GetPlayerInfoAsync([FromRoute] int clientId)
{
try
{
var clientInfo = await clientService.Get(clientId);
if (clientInfo is null)
{
return BadRequest("Could not find client");
}

var metaResult = await metaService
.GetPersistentMetaByLookup(EFMeta.ClientTagV2, EFMeta.ClientTagNameV2, clientInfo.ClientId);

return StatusCode(StatusCodes.Status500InternalServerError, new ErrorResponse()
return Ok(new ClientInfoResult
{
Messages = new[] {e.Message}
ClientId = clientInfo.ClientId,
Name = clientInfo.CleanedName,
Level = clientInfo.Level.ToLocalizedLevelName(),
NetworkId = clientInfo.NetworkId,
GameName = clientInfo.GameName.ToString(),
Tag = metaResult?.Value,
FirstConnection = clientInfo.FirstConnection,
LastConnection = clientInfo.LastConnection,
TotalConnectionTime = clientInfo.TotalConnectionTime,
Connections = clientInfo.Connections,
});
}
catch (Exception e)
{
_logger.LogWarning(e, "Failed to retrieve information for Client - {ClientId}", clientId);
return StatusCode(StatusCodes.Status500InternalServerError, new ErrorResponse { Messages = [e.Message] });
}
}


[HttpPost("{clientId:int}/login")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> Login([FromRoute] int clientId,
[FromBody, Required] PasswordRequest request)
public async Task<IActionResult> Login([FromRoute] int clientId, [FromBody, Required] PasswordRequest request)
{
if (clientId == 0)
if (clientId is 0)
{
return Unauthorized();
}
Expand All @@ -97,7 +124,7 @@ public async Task<IActionResult> Login([FromRoute] int clientId,

try
{
var privilegedClient = await _clientService.GetClientForLogin(clientId);
var privilegedClient = await clientService.GetClientForLogin(clientId);
var loginSuccess = false;

if (!Authorized)
Expand All @@ -107,51 +134,49 @@ public async Task<IActionResult> Login([FromRoute] int clientId,
ClientId = clientId,
Token = request.Password
};

loginSuccess =
Manager.TokenAuthenticator.AuthorizeToken(tokenData) ||
(await Task.FromResult(Hashing.Hash(request.Password,
privilegedClient.PasswordSalt)))[0] == privilegedClient.Password;

loginSuccess = Manager.TokenAuthenticator.AuthorizeToken(tokenData) ||
(await Task.FromResult(Hashing.Hash(request.Password, privilegedClient.PasswordSalt)))[0] ==
privilegedClient.Password;
}

if (loginSuccess)
{
var claims = new[]
{
List<Claim> claims =
[
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);

Manager.AddEvent(new GameEvent
{
Origin = privilegedClient,
Type = GameEvent.EventType.Login,
Owner = Manager.GetServers().First(),
Data = HttpContext.Request.Headers.ContainsKey("X-Forwarded-For")
? HttpContext.Request.Headers["X-Forwarded-For"].ToString()
: HttpContext.Connection.RemoteIpAddress.ToString()
Data = HttpContext.Request.Headers.TryGetValue("X-Forwarded-For", out var gameStringValues)
? gameStringValues.ToString()
: HttpContext.Connection.RemoteIpAddress?.ToString()
});

Manager.QueueEvent(new LoginEvent
{
Source = this,
LoginSource = LoginEvent.LoginSourceType.Webfront,
EntityId = Client.ClientId.ToString(),
Identifier = HttpContext.Request.Headers.ContainsKey("X-Forwarded-For")
? HttpContext.Request.Headers["X-Forwarded-For"].ToString()
Identifier = HttpContext.Request.Headers.TryGetValue("X-Forwarded-For", out var loginStringValues)
? loginStringValues.ToString()
: HttpContext.Connection.RemoteIpAddress?.ToString()
});

return Ok();
}
}

catch (Exception)
{
return Unauthorized();
Expand All @@ -172,24 +197,23 @@ public async Task<IActionResult> Logout()
Origin = Client,
Type = GameEvent.EventType.Logout,
Owner = Manager.GetServers().First(),
Data = HttpContext.Request.Headers.ContainsKey("X-Forwarded-For")
? HttpContext.Request.Headers["X-Forwarded-For"].ToString()
: HttpContext.Connection.RemoteIpAddress.ToString()
Data = HttpContext.Request.Headers.TryGetValue("X-Forwarded-For", out var gameStringValues)
? gameStringValues.ToString()
: HttpContext.Connection.RemoteIpAddress?.ToString()
});

Manager.QueueEvent(new LogoutEvent
{
Source = this,
LoginSource = LoginEvent.LoginSourceType.Webfront,
EntityId = Client.ClientId.ToString(),
Identifier = HttpContext.Request.Headers.ContainsKey("X-Forwarded-For")
? HttpContext.Request.Headers["X-Forwarded-For"].ToString()
Identifier = HttpContext.Request.Headers.TryGetValue("X-Forwarded-For", out var logoutStringValues)
? logoutStringValues.ToString()
: HttpContext.Connection.RemoteIpAddress?.ToString()
});
}

await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);

await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return Ok();
}

Expand Down
Loading