From e051007ecdb3097bc9942fd9db369101a9568a0d Mon Sep 17 00:00:00 2001 From: TheArcaneBrony Date: Sat, 23 Dec 2023 08:54:55 +0100 Subject: Basic test --- .../AuthenticatedHomeserverProviderService.cs | 47 +++++++++++++ ModAS.Server/Services/AuthenticationService.cs | 79 ++++++++++++++++++++++ ModAS.Server/Services/ModASConfiguration.cs | 10 +++ ModAS.Server/Services/UserContextService.cs | 32 +++++++++ 4 files changed, 168 insertions(+) create mode 100644 ModAS.Server/Services/AuthenticatedHomeserverProviderService.cs create mode 100644 ModAS.Server/Services/AuthenticationService.cs create mode 100644 ModAS.Server/Services/ModASConfiguration.cs create mode 100644 ModAS.Server/Services/UserContextService.cs (limited to 'ModAS.Server/Services') diff --git a/ModAS.Server/Services/AuthenticatedHomeserverProviderService.cs b/ModAS.Server/Services/AuthenticatedHomeserverProviderService.cs new file mode 100644 index 0000000..fa8e00a --- /dev/null +++ b/ModAS.Server/Services/AuthenticatedHomeserverProviderService.cs @@ -0,0 +1,47 @@ +using ArcaneLibs.Extensions; +using LibMatrix; +using LibMatrix.Homeservers; +using LibMatrix.Services; +using MxApiExtensions.Services; + +namespace ModAS.Server.Services; + +public class AuthenticatedHomeserverProviderService( + AuthenticationService authenticationService, + HomeserverProviderService homeserverProviderService, + IHttpContextAccessor request, + ModASConfiguration config, + AppServiceRegistration asRegistration + ) { + public HttpContext? _context = request.HttpContext; + public Dictionary KnownUsers { get; set; } = new(); + + public async Task GetImpersonatedHomeserver(string mxid) { + if (KnownUsers.TryGetValue(mxid, out var homeserver)) return homeserver; + var hs = await homeserverProviderService.GetAuthenticatedWithToken(config.ServerName, asRegistration.AsToken, config.HomeserverUrl); + await hs.SetImpersonate(mxid); + KnownUsers.TryAdd(mxid, hs); + return hs; + } + + public async Task GetHomeserver() { + var token = authenticationService.GetToken(); + if (token == null) { + throw new MatrixException { + ErrorCode = "M_MISSING_TOKEN", + Error = "Missing access token" + }; + } + + var mxid = await authenticationService.GetMxidFromToken(token); + if (mxid == "@anonymous:*") { + throw new MatrixException { + ErrorCode = "M_MISSING_TOKEN", + Error = "Missing access token" + }; + } + + var hsCanonical = string.Join(":", mxid.Split(':').Skip(1)); + return await homeserverProviderService.GetAuthenticatedWithToken(hsCanonical, token); + } +} \ No newline at end of file diff --git a/ModAS.Server/Services/AuthenticationService.cs b/ModAS.Server/Services/AuthenticationService.cs new file mode 100644 index 0000000..27e12ad --- /dev/null +++ b/ModAS.Server/Services/AuthenticationService.cs @@ -0,0 +1,79 @@ +using LibMatrix; +using LibMatrix.Services; +using MxApiExtensions.Services; + +namespace ModAS.Server.Services; + +public class AuthenticationService(ILogger logger, ModASConfiguration config, IHttpContextAccessor request, HomeserverProviderService homeserverProviderService) { + private readonly HttpRequest _request = request.HttpContext!.Request; + + private static Dictionary _tokenMap = new(); + + internal string? GetToken(bool fail = true) { + string? token; + if (_request.Headers.TryGetValue("Authorization", out var tokens)) { + token = tokens.FirstOrDefault()?[7..]; + } + else { + token = _request.Query["access_token"]; + } + + if (string.IsNullOrWhiteSpace(token) && fail) { + throw new MatrixException() { + ErrorCode = "M_MISSING_TOKEN", + Error = "Missing access token" + }; + } + + return token; + } + + public async Task GetMxidFromToken(string? token = null, bool fail = true) { + token ??= GetToken(fail); + if (string.IsNullOrWhiteSpace(token)) { + if (fail) { + throw new MatrixException() { + ErrorCode = "M_MISSING_TOKEN", + Error = "Missing access token" + }; + } + + return "@anonymous:*"; + } + + if (_tokenMap is not { Count: > 0 } && File.Exists("token_map")) { + _tokenMap = (await File.ReadAllLinesAsync("token_map")) + .Select(l => l.Split('\t')) + .ToDictionary(l => l[0], l => l[1]); + } + + + if (_tokenMap.TryGetValue(token, out var mxid)) return mxid; + + var lookupTasks = new Dictionary>(); + + + logger.LogInformation("Looking up mxid for token {}", token); + var hs = await homeserverProviderService.GetAuthenticatedWithToken(config.ServerName, token, config.HomeserverUrl); + try { + var res = hs.WhoAmI.UserId; + logger.LogInformation("Got mxid {} for token {}", res, token); + await SaveMxidForToken(token, mxid); + + return res; + } + catch (MatrixException e) { + if (e.ErrorCode == "M_UNKNOWN_TOKEN") { + return null; + } + + throw; + } + } + + + public async Task SaveMxidForToken(string token, string mxid) { + _tokenMap.Add(token, mxid); + await File.AppendAllLinesAsync("token_map", new[] { $"{token}\t{mxid}" }); + } +} diff --git a/ModAS.Server/Services/ModASConfiguration.cs b/ModAS.Server/Services/ModASConfiguration.cs new file mode 100644 index 0000000..dc7c552 --- /dev/null +++ b/ModAS.Server/Services/ModASConfiguration.cs @@ -0,0 +1,10 @@ +namespace MxApiExtensions.Services; + + +public class ModASConfiguration { + public ModASConfiguration(IConfiguration configuration) { + configuration.GetRequiredSection("ModAS").Bind(this); + } + public string ServerName { get; set; } + public string HomeserverUrl { get; set; } +} \ No newline at end of file diff --git a/ModAS.Server/Services/UserContextService.cs b/ModAS.Server/Services/UserContextService.cs new file mode 100644 index 0000000..23969cc --- /dev/null +++ b/ModAS.Server/Services/UserContextService.cs @@ -0,0 +1,32 @@ +// using System.Collections.Concurrent; +// using System.Text.Json.Serialization; +// using ArcaneLibs.Extensions; +// using LibMatrix.Homeservers; +// using MxApiExtensions.Services; +// +// namespace ModAS.Server.Services; +// +// public class UserContextService(ModASConfiguration config, AuthenticatedHomeserverProviderService hsProvider) { +// internal static ConcurrentDictionary UserContextStore { get; set; } = new(); +// public readonly int SessionCount = UserContextStore.Count; +// +// public class UserContext { +// [JsonIgnore] +// public AuthenticatedHomeserverGeneric Homeserver { get; set; } +// } +// +// private readonly SemaphoreSlim _getUserContextSemaphore = new SemaphoreSlim(1, 1); +// public async Task GetCurrentUserContext() { +// var hs = await hsProvider.GetHomeserver(); +// // await _getUserContextSemaphore.WaitAsync(); +// var ucs = await UserContextStore.GetOrCreateAsync($"{hs.WhoAmI.UserId}/{hs.WhoAmI.DeviceId}/{hs.ServerName}:{hs.AccessToken}", async x => { +// var userContext = new UserContext() { +// Homeserver = hs +// }; +// +// return userContext; +// }, _getUserContextSemaphore); +// // _getUserContextSemaphore.Release(); +// return ucs; +// } +// } -- cgit 1.5.1