about summary refs log tree commit diff
path: root/MatrixUtils.Web/Classes
diff options
context:
space:
mode:
Diffstat (limited to 'MatrixUtils.Web/Classes')
-rw-r--r--MatrixUtils.Web/Classes/Constants/RoomConstants.cs5
-rw-r--r--MatrixUtils.Web/Classes/LocalStorageProviderService.cs2
-rw-r--r--MatrixUtils.Web/Classes/RMUStorageWrapper.cs138
-rw-r--r--MatrixUtils.Web/Classes/RmuSessionStore.cs260
-rw-r--r--MatrixUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs5
-rw-r--r--MatrixUtils.Web/Classes/SessionStorageProviderService.cs2
6 files changed, 267 insertions, 145 deletions
diff --git a/MatrixUtils.Web/Classes/Constants/RoomConstants.cs b/MatrixUtils.Web/Classes/Constants/RoomConstants.cs

index 5df0d01..dc81d04 100644 --- a/MatrixUtils.Web/Classes/Constants/RoomConstants.cs +++ b/MatrixUtils.Web/Classes/Constants/RoomConstants.cs
@@ -1,6 +1,7 @@ namespace MatrixUtils.Web.Classes.Constants; public class RoomConstants { - public static readonly string[] DangerousRoomVersions = { "1", "8" }; - public const string RecommendedRoomVersion = "10"; + public static readonly string[] DangerousRoomVersions = ["1", "8"]; + public static readonly string[] UnsupportedRoomVersions = ["1", "2", "3", "4", "5", "6"]; + public const string RecommendedRoomVersion = "11"; } diff --git a/MatrixUtils.Web/Classes/LocalStorageProviderService.cs b/MatrixUtils.Web/Classes/LocalStorageProviderService.cs
index 3803a17..0e99cd4 100644 --- a/MatrixUtils.Web/Classes/LocalStorageProviderService.cs +++ b/MatrixUtils.Web/Classes/LocalStorageProviderService.cs
@@ -22,7 +22,7 @@ public class LocalStorageProviderService : IStorageProvider { async Task<bool> IStorageProvider.ObjectExistsAsync(string key) => await _localStorageService.ContainKeyAsync(key); - async Task<List<string>> IStorageProvider.GetAllKeysAsync() => (await _localStorageService.KeysAsync()).ToList(); + async Task<IEnumerable<string>> IStorageProvider.GetAllKeysAsync() => (await _localStorageService.KeysAsync()).ToList(); async Task IStorageProvider.DeleteObjectAsync(string key) => await _localStorageService.RemoveItemAsync(key); } diff --git a/MatrixUtils.Web/Classes/RMUStorageWrapper.cs b/MatrixUtils.Web/Classes/RMUStorageWrapper.cs deleted file mode 100644
index 45028ba..0000000 --- a/MatrixUtils.Web/Classes/RMUStorageWrapper.cs +++ /dev/null
@@ -1,138 +0,0 @@ -using LibMatrix; -using LibMatrix.Homeservers; -using LibMatrix.Services; -using Microsoft.AspNetCore.Components; - -namespace MatrixUtils.Web.Classes; - -public class RMUStorageWrapper(ILogger<RMUStorageWrapper> logger, TieredStorageService storageService, HomeserverProviderService homeserverProviderService, NavigationManager navigationManager) { - public async Task<List<UserAuth>?> GetAllTokens() { - logger.LogTrace("Getting all tokens."); - return await storageService.DataStorageProvider.LoadObjectAsync<List<UserAuth>>("rmu.tokens") ?? - new List<UserAuth>(); - } - - public async Task<UserAuth?> GetCurrentToken() { - logger.LogTrace("Getting current token."); - var currentToken = await storageService.DataStorageProvider.LoadObjectAsync<UserAuth>("rmu.token"); - var allTokens = await GetAllTokens(); - if (allTokens is null or { Count: 0 }) { - await SetCurrentToken(null); - return null; - } - - if (currentToken is null) { - await SetCurrentToken(currentToken = allTokens[0]); - } - - if (!allTokens.Any(x => x.AccessToken == currentToken.AccessToken)) { - await SetCurrentToken(currentToken = allTokens[0]); - } - - return currentToken; - } - - public async Task AddToken(UserAuth UserAuth) { - logger.LogTrace("Adding token."); - var tokens = await GetAllTokens() ?? new List<UserAuth>(); - - tokens.Add(UserAuth); - await storageService.DataStorageProvider.SaveObjectAsync("rmu.tokens", tokens); - } - - private async Task<AuthenticatedHomeserverGeneric?> GetCurrentSession() { - logger.LogTrace("Getting current session."); - var token = await GetCurrentToken(); - if (token == null) { - return null; - } - - return await homeserverProviderService.GetAuthenticatedWithToken(token.Homeserver, token.AccessToken, token.Proxy); - } - - public async Task<AuthenticatedHomeserverGeneric?> GetSession(UserAuth userAuth) { - logger.LogTrace("Getting session."); - return await homeserverProviderService.GetAuthenticatedWithToken(userAuth.Homeserver, userAuth.AccessToken, userAuth.Proxy); - } - - public async Task<AuthenticatedHomeserverGeneric?> GetCurrentSessionOrNavigate() { - logger.LogTrace("Getting current session or navigating."); - AuthenticatedHomeserverGeneric? session = null; - - try { - //catch if the token is invalid - session = await GetCurrentSession(); - } - catch (MatrixException e) { - if (e.ErrorCode == "M_UNKNOWN_TOKEN") { - var token = await GetCurrentToken(); - logger.LogWarning("Encountered invalid token for {user} on {homeserver}", token.UserId, token.Homeserver); - navigationManager.NavigateTo("/InvalidSession?ctx=" + token.AccessToken); - return null; - } - - throw; - } - - if (session is null) { - logger.LogInformation("No session found. Navigating to login."); - navigationManager.NavigateTo("/Login"); - } - - return session; - } - - public class Settings { - public DeveloperSettings DeveloperSettings { get; set; } = new(); - } - - public class DeveloperSettings { - public bool EnableLogViewers { get; set; } - public bool EnableConsoleLogging { get; set; } = true; - public bool EnablePortableDevtools { get; set; } - } - - public async Task RemoveToken(UserAuth auth) { - logger.LogTrace("Removing token."); - var tokens = await GetAllTokens(); - if (tokens == null) { - return; - } - - tokens.RemoveAll(x => x.AccessToken == auth.AccessToken); - await storageService.DataStorageProvider.SaveObjectAsync("rmu.tokens", tokens); - } - - public async Task SetCurrentToken(UserAuth? auth) { - logger.LogTrace("Setting current token."); - await storageService.DataStorageProvider.SaveObjectAsync("rmu.token", auth); - } - - public async Task MigrateFromMRU() { - logger.LogInformation("Migrating from MRU token namespace!"); - var dsp = storageService.DataStorageProvider!; - if(await dsp.ObjectExistsAsync("token")) { - var oldToken = await dsp.LoadObjectAsync<UserAuth>("token"); - if (oldToken != null) { - await dsp.SaveObjectAsync("rmu.token", oldToken); - await dsp.DeleteObjectAsync("tokens"); - } - } - - if(await dsp.ObjectExistsAsync("tokens")) { - var oldTokens = await dsp.LoadObjectAsync<List<UserAuth>>("tokens"); - if (oldTokens != null) { - await dsp.SaveObjectAsync("rmu.tokens", oldTokens); - await dsp.DeleteObjectAsync("tokens"); - } - } - - if(await dsp.ObjectExistsAsync("mru.tokens")) { - var oldTokens = await dsp.LoadObjectAsync<List<UserAuth>>("mru.tokens"); - if (oldTokens != null) { - await dsp.SaveObjectAsync("rmu.tokens", oldTokens); - await dsp.DeleteObjectAsync("mru.tokens"); - } - } - } -} diff --git a/MatrixUtils.Web/Classes/RmuSessionStore.cs b/MatrixUtils.Web/Classes/RmuSessionStore.cs new file mode 100644
index 0000000..746f68a --- /dev/null +++ b/MatrixUtils.Web/Classes/RmuSessionStore.cs
@@ -0,0 +1,260 @@ +using System.Text.Json; +using System.Text.Json.Nodes; +using LibMatrix; +using LibMatrix.Homeservers; +using LibMatrix.Services; +using Microsoft.AspNetCore.Components; + +namespace MatrixUtils.Web.Classes; + +public class RmuSessionStore( + ILogger<RmuSessionStore> logger, + TieredStorageService storageService, + HomeserverProviderService homeserverProviderService, + NavigationManager navigationManager) { + private SessionInfo? CurrentSession { get; set; } + private Dictionary<string, SessionInfo> SessionCache { get; set; } = []; + + private bool _isInitialized; + private static readonly SemaphoreSlim InitSemaphore = new(1, 1); + +#region Sessions + + public async Task<Dictionary<string, SessionInfo>> GetAllSessions() { + await LoadStorage(); + logger.LogTrace("Getting all tokens."); + return SessionCache; + } + + public async Task<SessionInfo?> GetSession(string sessionId) { + await LoadStorage(); + if (SessionCache.TryGetValue(sessionId, out var cachedSession)) + return cachedSession; + + logger.LogWarning("Session {sessionId} not found in all tokens.", sessionId); + return null; + } + + public async Task<SessionInfo?> GetCurrentSession(bool log = true) { + await LoadStorage(); + if (log) logger.LogTrace("Getting current token."); + if (CurrentSession is not null) return CurrentSession; + + var currentSessionId = await storageService.DataStorageProvider!.LoadObjectAsync<string>("rmu.session"); + return await GetSession(currentSessionId); + } + + public async Task<string> AddSession(UserAuth auth) { + await LoadStorage(); + logger.LogTrace("Adding token."); + + var sessionId = auth.GetHashCode().ToString(); + SessionCache[sessionId] = new() { + Auth = auth, + SessionId = sessionId + }; + + if (CurrentSession == null) await SetCurrentSession(sessionId); + else await SaveStorage(); + + return sessionId; + } + + public async Task RemoveSession(string sessionId) { + await LoadStorage(); + logger.LogTrace("Removing session {sessionId}.", sessionId); + var tokens = await GetAllSessions(); + if (tokens == null) { + return; + } + + if ((await GetCurrentSession())?.SessionId == sessionId) + await SetCurrentSession(tokens.First(x => x.Key != sessionId).Key); + + if (tokens.Remove(sessionId)) + await SaveStorage(); + } + + public async Task SetCurrentSession(string? sessionId) { + await LoadStorage(); + logger.LogTrace("Setting current session to {sessionId}.", sessionId); + CurrentSession = await GetSession(sessionId); + await SaveStorage(); + } + +#endregion + +#region Homeservers + + public async Task<AuthenticatedHomeserverGeneric?> GetHomeserver(string session, bool log = true) { + await LoadStorage(); + if (log) logger.LogTrace("Getting session."); + if (!SessionCache.TryGetValue(session, out var cachedSession)) return null; + if (cachedSession.Homeserver is not null) return cachedSession.Homeserver; + + try { + cachedSession.Homeserver = + await homeserverProviderService.GetAuthenticatedWithToken(cachedSession.Auth.Homeserver, cachedSession.Auth.AccessToken, cachedSession.Auth.Proxy); + } + catch (Exception e) { + logger.LogError("Failed to get info for {0} via {1}: {2}", cachedSession.Auth.UserId, cachedSession.Auth.Homeserver, e); + logger.LogError("Continuing with server-less session"); + cachedSession.Homeserver = await homeserverProviderService.GetAuthenticatedWithToken(cachedSession.Auth.Homeserver, cachedSession.Auth.AccessToken, + cachedSession.Auth.Proxy, useGeneric: true, enableServer: false); + } + + return cachedSession.Homeserver; + } + + public async Task<AuthenticatedHomeserverGeneric?> GetCurrentHomeserver(bool log = true, bool navigateOnFailure = false) { + await LoadStorage(); + if (log) logger.LogTrace("Getting current session."); + if (CurrentSession?.Homeserver is not null) return CurrentSession.Homeserver; + + var currentSession = CurrentSession ??= await GetCurrentSession(log: false); + if (currentSession == null) { + if (navigateOnFailure) { + logger.LogInformation("No session found. Navigating to login."); + navigationManager.NavigateTo("/Login"); + } + + return null; + } + + try { + return currentSession.Homeserver ??= await GetHomeserver(currentSession.SessionId); + } + catch (MatrixException e) { + if (e.ErrorCode == "M_UNKNOWN_TOKEN" && navigateOnFailure) { + logger.LogWarning("Encountered invalid token for {user} on {homeserver}", currentSession.Auth.UserId, currentSession.Auth.Homeserver); + if (navigateOnFailure) { + navigationManager.NavigateTo("/InvalidSession?ctx=" + currentSession.SessionId); + } + } + + throw; + } + } + +#endregion + +#region Storage + + private async Task LoadStorage(bool hasMigrated = false) { + if (!await storageService.DataStorageProvider!.ObjectExistsAsync("rmu.sessions") || !await storageService.DataStorageProvider.ObjectExistsAsync("rmu.session")) { + if (!hasMigrated) { + await RunMigrations(); + await LoadStorage(true); + } + else + logger.LogWarning("No sessions found in storage."); + + return; + } + + SessionCache = (await storageService.DataStorageProvider.LoadObjectAsync<Dictionary<string, UserAuth>>("rmu.sessions") ?? throw new Exception("Failed to load sessions")) + .ToDictionary(x => x.Key, x => new SessionInfo { + SessionId = x.Key, + Auth = x.Value + }); + + var currentSessionId = await storageService.DataStorageProvider.LoadObjectAsync<string>("rmu.session"); + if (currentSessionId == null) { + logger.LogWarning("No current session found in storage."); + return; + } + + if (!SessionCache.TryGetValue(currentSessionId, out var currentSession)) { + logger.LogWarning("Current session {currentSessionId} not found in storage.", currentSessionId); + return; + } + + CurrentSession = currentSession; + } + + private async Task SaveStorage() { + await storageService.DataStorageProvider!.SaveObjectAsync("rmu.sessions", + SessionCache.ToDictionary( + x => x.Key, + x => x.Value.Auth + ) + ); + await storageService.DataStorageProvider.SaveObjectAsync("rmu.session", CurrentSession?.SessionId); + } + +#endregion + +#region Migrations + + public async Task RunMigrations() { + await MigrateFromMru(); + await MigrateAccountsToKeyedStorage(); + } + + private async Task MigrateFromMru() { + logger.LogInformation("Migrating from MRU token namespace!"); + var dsp = storageService.DataStorageProvider!; + if (await dsp.ObjectExistsAsync("token")) { + var oldToken = await dsp.LoadObjectAsync<UserAuth>("token"); + if (oldToken != null) { + await dsp.SaveObjectAsync("rmu.token", oldToken); + await dsp.DeleteObjectAsync("token"); + } + } + + if (await dsp.ObjectExistsAsync("tokens")) { + var oldTokens = await dsp.LoadObjectAsync<List<UserAuth>>("tokens"); + if (oldTokens != null) { + await dsp.SaveObjectAsync("rmu.tokens", oldTokens); + await dsp.DeleteObjectAsync("tokens"); + } + } + + if (await dsp.ObjectExistsAsync("mru.tokens")) { + var oldTokens = await dsp.LoadObjectAsync<List<UserAuth>>("mru.tokens"); + if (oldTokens != null) { + await dsp.SaveObjectAsync("rmu.tokens", oldTokens); + await dsp.DeleteObjectAsync("mru.tokens"); + } + } + } + + private async Task MigrateAccountsToKeyedStorage() { + var dsp = storageService.DataStorageProvider!; + if (!await dsp.ObjectExistsAsync("rmu.tokens")) return; + logger.LogInformation("Migrating accounts to keyed storage!"); + var tokens = await dsp.LoadObjectAsync<UserAuth[]>("rmu.tokens") ?? throw new Exception("Failed to load tokens"); + Dictionary<string, UserAuth> keyedTokens = tokens.ToDictionary(x => x.GetHashCode().ToString(), x => x); + + if (await dsp.ObjectExistsAsync("rmu.token")) { + var token = await dsp.LoadObjectAsync<UserAuth>("rmu.token") ?? throw new Exception("Failed to load token"); + var sessionId = keyedTokens.FirstOrDefault(x => x.Value.Equals(token)).Key; + + if (sessionId is null) keyedTokens.Add(sessionId = token.GetHashCode().ToString(), token); + await dsp.SaveObjectAsync("rmu.session", sessionId); + + await dsp.DeleteObjectAsync("rmu.token"); + } + + await dsp.SaveObjectAsync("rmu.sessions", keyedTokens); + await dsp.DeleteObjectAsync("rmu.tokens"); + } + +#endregion + + public class Settings { + public DeveloperSettings DeveloperSettings { get; set; } = new(); + } + + public class DeveloperSettings { + public bool EnableLogViewers { get; set; } + public bool EnableConsoleLogging { get; set; } = true; + public bool EnablePortableDevtools { get; set; } + } + + public class SessionInfo { + public required string SessionId { get; set; } + public required UserAuth Auth { get; set; } + public AuthenticatedHomeserverGeneric? Homeserver { get; set; } + } +} \ No newline at end of file diff --git a/MatrixUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs b/MatrixUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs
index a627a9c..7078308 100644 --- a/MatrixUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs +++ b/MatrixUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs
@@ -1,6 +1,5 @@ using System.Text.Json.Nodes; using LibMatrix; -using LibMatrix.EventTypes.Spec.State; using LibMatrix.EventTypes.Spec.State.RoomInfo; using LibMatrix.Responses; @@ -34,7 +33,7 @@ public class DefaultRoomCreationTemplate : IRoomCreationTemplate { }, new() { Type = "m.room.server_acl", - TypedContent = new RoomServerACLEventContent() { + TypedContent = new RoomServerAclEventContent() { Allow = new List<string>() { "*" }, Deny = new List<string>(), AllowIpLiterals = false @@ -56,7 +55,7 @@ public class DefaultRoomCreationTemplate : IRoomCreationTemplate { Redact = 50, Kick = 50, Ban = 50, - NotificationsPl = new RoomPowerLevelEventContent.NotificationsPL { + NotificationsPl = new RoomPowerLevelEventContent.NotificationsPowerLevels { Room = 50 }, Events = new() { diff --git a/MatrixUtils.Web/Classes/SessionStorageProviderService.cs b/MatrixUtils.Web/Classes/SessionStorageProviderService.cs
index ae0bb79..da169de 100644 --- a/MatrixUtils.Web/Classes/SessionStorageProviderService.cs +++ b/MatrixUtils.Web/Classes/SessionStorageProviderService.cs
@@ -22,7 +22,7 @@ public class SessionStorageProviderService : IStorageProvider { async Task<bool> IStorageProvider.ObjectExistsAsync(string key) => await _sessionStorageService.ContainKeyAsync(key); - async Task<List<string>> IStorageProvider.GetAllKeysAsync() => (await _sessionStorageService.KeysAsync()).ToList(); + async Task<IEnumerable<string>> IStorageProvider.GetAllKeysAsync() => (await _sessionStorageService.KeysAsync()).ToList(); async Task IStorageProvider.DeleteObjectAsync(string key) => await _sessionStorageService.RemoveItemAsync(key); }