Add Server-Side Download Queue (#21)

* test add queueing to file service

* further adjustments to download queueing

* add check for whether the request is still in the queue to CheckQueue

* forcefully release slot if download didn't finish in 15s

* actually cancel the delay task

* add metrics and refactor some of the request queue service

* refactor pathing

* reuse httpclient

* add queue request dto to requestfile, enqueue users immediately if a slot is available

* change startup to include all controllers

* update server pathing

* update pathing, again

* several adjustments to auth, banning, jwt server tokens, renaming, authorization

* update api I guess

* adjust automated banning of charaident and reg

* generate jwt on servers for internal authentication

* remove mvcextensions

Co-authored-by: rootdarkarchon <root.darkarchon@outlook.com>
This commit is contained in:
rootdarkarchon
2023-01-11 12:22:22 +01:00
committed by GitHub
parent db2d0451ca
commit 42b15cb6b7
38 changed files with 1116 additions and 98 deletions

View File

@@ -1,3 +1,3 @@
namespace MareSynchronosServer.Authentication;
public record SecretKeyAuthReply(bool Success, string Uid, bool TempBan);
public record SecretKeyAuthReply(bool Success, string Uid, bool TempBan, bool Permaban);

View File

@@ -50,14 +50,14 @@ public class SecretKeyAuthenticatorService
_failedAuthorizations.Remove(ip, out _);
});
}
return new(Success: false, Uid: null, TempBan: true);
return new(Success: false, Uid: null, TempBan: true, Permaban: false);
}
using var scope = _serviceScopeFactory.CreateScope();
using var context = scope.ServiceProvider.GetService<MareDbContext>();
var authReply = await context.Auth.AsNoTracking().SingleOrDefaultAsync(u => u.HashedKey == hashedSecretKey).ConfigureAwait(false);
SecretKeyAuthReply reply = new(authReply != null, authReply?.UserUID, false);
SecretKeyAuthReply reply = new(authReply != null, authReply?.UserUID, false, authReply.IsBanned);
if (reply.Success)
{
@@ -97,6 +97,6 @@ public class SecretKeyAuthenticatorService
}
}
return new(Success: false, Uid: null, TempBan: false);
return new(Success: false, Uid: null, TempBan: false, Permaban: false);
}
}

View File

@@ -2,6 +2,7 @@
using MareSynchronosServer.Authentication;
using MareSynchronosShared;
using MareSynchronosShared.Data;
using MareSynchronosShared.Models;
using MareSynchronosShared.Services;
using MareSynchronosShared.Utils;
using Microsoft.AspNetCore.Authorization;
@@ -38,7 +39,7 @@ public class JwtController : Controller
}
[AllowAnonymous]
[HttpPost(MareAuth.AuthCreateIdent)]
[HttpPost(MareAuth.Auth_CreateIdent)]
public async Task<IActionResult> CreateToken(string auth, string charaIdent)
{
if (string.IsNullOrEmpty(auth)) return BadRequest("No Authkey");
@@ -52,10 +53,47 @@ public class JwtController : Controller
var authResult = await _secretKeyAuthenticatorService.AuthorizeAsync(ip, auth);
if (!authResult.Success && !authResult.TempBan) return Unauthorized("The provided secret key is invalid. Verify your accounts existence and/or recover the secret key.");
if (!authResult.Success && authResult.TempBan) return Unauthorized("You are temporarily banned. Try connecting again later.");
if (!authResult.Success && authResult.TempBan) return Unauthorized("You are temporarily banned. Try connecting again in 5 minutes.");
if (authResult.Permaban)
{
if (!_mareDbContext.BannedUsers.Any(c => c.CharacterIdentification == charaIdent))
{
_mareDbContext.BannedUsers.Add(new Banned()
{
CharacterIdentification = charaIdent,
Reason = "Autobanned CharacterIdent (" + authResult.Uid + ")"
});
await _mareDbContext.SaveChangesAsync();
}
var lodestone = await _mareDbContext.LodeStoneAuth.Include(a => a.User).FirstOrDefaultAsync(c => c.User.UID == authResult.Uid);
if (lodestone != null)
{
if (!_mareDbContext.BannedRegistrations.Any(c => c.DiscordIdOrLodestoneAuth == lodestone.HashedLodestoneId))
{
_mareDbContext.BannedRegistrations.Add(new BannedRegistrations()
{
DiscordIdOrLodestoneAuth = lodestone.HashedLodestoneId
});
}
if (!_mareDbContext.BannedRegistrations.Any(c => c.DiscordIdOrLodestoneAuth == lodestone.DiscordId.ToString()))
{
_mareDbContext.BannedRegistrations.Add(new BannedRegistrations()
{
DiscordIdOrLodestoneAuth = lodestone.DiscordId.ToString()
});
}
await _mareDbContext.SaveChangesAsync();
}
return Unauthorized("You are permanently banned.");
}
var existingIdent = await _redis.GetAsync<string>("UID:" + authResult.Uid);
if (!string.IsNullOrEmpty(existingIdent)) return Unauthorized("Already logged in to this account.");
if (!string.IsNullOrEmpty(existingIdent)) return Unauthorized("Already logged in to this account. Reconnect in 60 seconds. If you keep seeing this issue, restart your game.");
var token = CreateToken(new List<Claim>()
{
@@ -66,26 +104,6 @@ public class JwtController : Controller
return Content(token.RawData);
}
[AllowAnonymous]
[HttpPost(MareAuth.AuthCreate)]
public async Task<IActionResult> CreateToken(string auth)
{
if (string.IsNullOrEmpty(auth)) return BadRequest("No Authkey");
var ip = _accessor.GetIpAddress();
var authResult = await _secretKeyAuthenticatorService.AuthorizeAsync(ip, auth);
if (!authResult.Success) return Unauthorized("Invalid Authkey");
var token = CreateToken(new List<Claim>()
{
new Claim(MareClaimTypes.Uid, authResult.Uid)
});
return Content(token.RawData);
}
private JwtSecurityToken CreateToken(IEnumerable<Claim> authClaims)
{
var authSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(_configuration.GetValue<string>(nameof(MareConfigurationAuthBase.Jwt))));

View File

@@ -36,7 +36,7 @@ public partial class MareHub
request.Hash.AddRange(ownFiles.Select(f => f.Hash));
Metadata headers = new Metadata()
{
{ "Authorization", Context.GetHttpContext().Request.Headers["Authorization"].ToString() }
{ "Authorization", "Bearer " + _generator.Token }
};
_ = await _fileServiceClient.DeleteFilesAsync(request, headers).ConfigureAwait(false);
}
@@ -69,7 +69,7 @@ public partial class MareHub
IsForbidden = forbiddenFile != null,
Hash = file.Hash,
Size = file.Size,
Url = new Uri(baseUrl, file.Hash.ToUpperInvariant()).ToString()
Url = baseUrl.ToString()
});
}
@@ -210,7 +210,7 @@ public partial class MareHub
Metadata headers = new Metadata()
{
{ "Authorization", Context.GetHttpContext().Request.Headers["Authorization"].ToString() }
{ "Authorization", "Bearer " + _generator.Token }
};
var streamingCall = _fileServiceClient.UploadFile(headers);
using var tempFileStream = new FileStream(tempFileName, FileMode.Open, FileAccess.Read);

View File

@@ -29,11 +29,12 @@ public partial class MareHub : Hub<IMareHub>, IMareHub
private readonly int _maxGroupUserCount;
private readonly IConfigurationService<ServerConfiguration> _configurationService;
private readonly IRedisDatabase _redis;
private readonly ServerTokenGenerator _generator;
public MareHub(MareMetrics mareMetrics, FileService.FileServiceClient fileServiceClient,
MareDbContext mareDbContext, ILogger<MareHub> logger, SystemInfoService systemInfoService,
IConfigurationService<ServerConfiguration> configuration, IHttpContextAccessor contextAccessor,
IRedisDatabase redisDb)
IRedisDatabase redisDb, ServerTokenGenerator generator)
{
_mareMetrics = mareMetrics;
_fileServiceClient = fileServiceClient;
@@ -46,6 +47,7 @@ public partial class MareHub : Hub<IMareHub>, IMareHub
_maxGroupUserCount = configuration.GetValueOrDefault(nameof(ServerConfiguration.MaxGroupUserCount), 100);
_contextAccessor = contextAccessor;
_redis = redisDb;
_generator = generator;
_logger = new MareHubLogger(this, logger);
_dbContext = mareDbContext;
}

View File

@@ -78,6 +78,7 @@ public class Startup
services.Configure<MareConfigurationBase>(mareConfig);
services.Configure<MareConfigurationAuthBase>(mareConfig);
services.AddSingleton<ServerTokenGenerator>();
services.AddSingleton<SystemInfoService>();
services.AddSingleton<IUserIdProvider, IdBasedUserIdProvider>();
services.AddHostedService(provider => provider.GetService<SystemInfoService>());