From 1beca653b772cf10586c417b2c25df03a67df8a2 Mon Sep 17 00:00:00 2001
From: TheArcaneBrony
Date: Mon, 17 Jul 2023 00:21:24 +0200
Subject: Handle external logouts
---
.../Extensions/HttpClientExtensions.cs | 3 +-
MatrixRoomUtils.Core/MatrixException.cs | 4 +-
MatrixRoomUtils.Web/Classes/MRUStorageWrapper.cs | 40 +++++++--
MatrixRoomUtils.Web/Pages/About.razor | 4 +-
MatrixRoomUtils.Web/Pages/Index.razor | 18 +++-
MatrixRoomUtils.Web/Pages/InvalidSession.razor | 97 ++++++++++++++++++++++
MatrixRoomUtils.Web/Pages/Rooms/PolicyList.razor | 10 ++-
MatrixRoomUtils.Web/Shared/InlineUserItem.razor | 9 +-
MatrixRoomUtils.Web/Shared/ModalWindow.razor | 22 +++--
MatrixRoomUtils.Web/Shared/ModalWindow.razor.css | 4 +-
MatrixRoomUtils.Web/Shared/RoomList.razor | 15 ++--
.../Shared/SimpleComponents/LinkButton.razor | 14 ++--
MatrixRoomUtils.Web/_Imports.razor | 3 +-
MatrixRoomUtils.Web/wwwroot/css/app.css | 2 +-
MatrixRoomUtils.Web/wwwroot/index.html | 13 ++-
15 files changed, 212 insertions(+), 46 deletions(-)
create mode 100644 MatrixRoomUtils.Web/Pages/InvalidSession.razor
diff --git a/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs b/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs
index 060867d..695e8e3 100644
--- a/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs
+++ b/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs
@@ -40,6 +40,7 @@ public class MatrixHttpClient : HttpClient {
var content = await a.Content.ReadAsStringAsync(cancellationToken);
if (content.StartsWith('{')) {
var ex = JsonSerializer.Deserialize(content);
+ ex.RawContent = content;
Console.WriteLine($"Failed to send request: {ex}");
if (ex?.RetryAfterMs is not null) {
await Task.Delay(ex.RetryAfterMs.Value, cancellationToken);
@@ -61,4 +62,4 @@ public class MatrixHttpClient : HttpClient {
await using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken);
return await JsonSerializer.DeserializeAsync(responseStream, cancellationToken: cancellationToken);
}
-}
\ No newline at end of file
+}
diff --git a/MatrixRoomUtils.Core/MatrixException.cs b/MatrixRoomUtils.Core/MatrixException.cs
index 4795d6d..a469a62 100644
--- a/MatrixRoomUtils.Core/MatrixException.cs
+++ b/MatrixRoomUtils.Core/MatrixException.cs
@@ -16,6 +16,8 @@ public class MatrixException : Exception {
[JsonPropertyName("retry_after_ms")]
public int? RetryAfterMs { get; set; }
+ public string RawContent { get; set; }
+
public override string Message =>
$"{ErrorCode}: {ErrorCode switch {
// common
@@ -54,4 +56,4 @@ public class MatrixException : Exception {
"M_CANNOT_LEAVE_SERVER_NOTICE_ROOM" => $"Cannot leave server notice room: {Error}",
_ => $"Unknown error: {new { ErrorCode, Error, SoftLogout, RetryAfterMs }.ToJson(ignoreNull: true)}"
}}";
-}
\ No newline at end of file
+}
diff --git a/MatrixRoomUtils.Web/Classes/MRUStorageWrapper.cs b/MatrixRoomUtils.Web/Classes/MRUStorageWrapper.cs
index 41f604d..d4b256b 100644
--- a/MatrixRoomUtils.Web/Classes/MRUStorageWrapper.cs
+++ b/MatrixRoomUtils.Web/Classes/MRUStorageWrapper.cs
@@ -21,8 +21,10 @@ public class MRUStorageWrapper {
}
public async Task?> GetAllTokens() {
- return await _storageService.DataStorageProvider.LoadObjectAsync>("mru.tokens") ?? new List();
+ return await _storageService.DataStorageProvider.LoadObjectAsync>("mru.tokens") ??
+ new List();
}
+
public async Task GetCurrentToken() {
var currentToken = await _storageService.DataStorageProvider.LoadObjectAsync("token");
var allTokens = await GetAllTokens();
@@ -30,12 +32,15 @@ public class MRUStorageWrapper {
await SetCurrentToken(null);
return null;
}
+
if (currentToken is null) {
await SetCurrentToken(currentToken = allTokens[0]);
}
- if(!allTokens.Any(x=>x.AccessToken == currentToken.AccessToken)) {
+
+ if (!allTokens.Any(x => x.AccessToken == currentToken.AccessToken)) {
await SetCurrentToken(currentToken = allTokens[0]);
}
+
return currentToken;
}
@@ -49,22 +54,39 @@ public class MRUStorageWrapper {
await _storageService.DataStorageProvider.SaveObjectAsync("mru.tokens", tokens);
}
- public async Task GetCurrentSession() {
+ private async Task GetCurrentSession() {
var token = await GetCurrentToken();
if (token == null) {
return null;
}
-
- return await _homeserverProviderService.GetAuthenticatedWithToken(token.Homeserver, token.AccessToken);
+
+ return await _homeserverProviderService.GetAuthenticatedWithToken(token.Homeserver, token.AccessToken);
}
public async Task GetCurrentSessionOrNavigate() {
- var session = await GetCurrentSession();
- if (session == null) {
+ AuthenticatedHomeServer? 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();
+ _navigationManager.NavigateTo("/InvalidSession?ctx=" + token.AccessToken);
+ return null;
+ }
+
+ throw;
+ }
+
+ if (session is null) {
_navigationManager.NavigateTo("/Login");
}
+
return session;
}
+
public class Settings {
public DeveloperSettings DeveloperSettings { get; set; } = new();
}
@@ -81,11 +103,11 @@ public class MRUStorageWrapper {
return;
}
- tokens.RemoveAll(x=>x.AccessToken == auth.AccessToken);
+ tokens.RemoveAll(x => x.AccessToken == auth.AccessToken);
await _storageService.DataStorageProvider.SaveObjectAsync("mru.tokens", tokens);
}
public async Task SetCurrentToken(LoginResponse? auth) {
_storageService.DataStorageProvider.SaveObjectAsync("token", auth);
}
-}
\ No newline at end of file
+}
diff --git a/MatrixRoomUtils.Web/Pages/About.razor b/MatrixRoomUtils.Web/Pages/About.razor
index b8d9c4a..971bd9b 100644
--- a/MatrixRoomUtils.Web/Pages/About.razor
+++ b/MatrixRoomUtils.Web/Pages/About.razor
@@ -1,4 +1,4 @@
-@page "/About"
+@page "/About"
@using System.Net
@using System.Net.Sockets
@inject NavigationManager NavigationManager
@@ -56,7 +56,7 @@
var message = "Hello, World!\nThis is a terminal emulator!\n\nYou can type stuff here, and it will be sent to the server!\n\nThis is a test of the emergency broadcast system.\n\nThis is only a t";
_terminal.Options.RendererType = RendererType.Dom;
_terminal.Options.ScreenReaderMode = true;
- TcpClient.
+// TcpClient.
for (var i = 0; i < message.Length; i++) {
await _terminal.Write(message[i].ToString());
diff --git a/MatrixRoomUtils.Web/Pages/Index.razor b/MatrixRoomUtils.Web/Pages/Index.razor
index 16a6cee..01e2be4 100644
--- a/MatrixRoomUtils.Web/Pages/Index.razor
+++ b/MatrixRoomUtils.Web/Pages/Index.razor
@@ -21,10 +21,10 @@ Small collection of tools to do not-so-everyday things.
SwitchSession(_auth))" style="text-decoration-line: unset;"/>
@_user.DisplayName on @_auth.Homeserver RemoveUser(_auth))">Remove
-
+
Member of @_user.RoomCount rooms
-
+
}
@@ -39,7 +39,17 @@ Small collection of tools to do not-so-everyday things.
var tokens = await MRUStorage.GetAllTokens();
var profileTasks = tokens.Select(async token => {
UserInfo userInfo = new();
- var hs = await HomeserverProvider.GetAuthenticatedWithToken(token.Homeserver, token.AccessToken);
+ AuthenticatedHomeServer hs;
+ try {
+ hs = await HomeserverProvider.GetAuthenticatedWithToken(token.Homeserver, token.AccessToken);
+ }
+ catch (MatrixException e) {
+ if (e.ErrorCode == "M_UNKNOWN_TOKEN") {
+ NavigationManager.NavigateTo("/InvalidSession?ctx="+token.AccessToken);
+ return;
+ }
+ throw;
+ }
var roomCountTask = hs.GetJoinedRooms();
var profile = await hs.GetProfile(hs.WhoAmI.UserId);
userInfo.DisplayName = profile.DisplayName ?? hs.WhoAmI.UserId;
@@ -75,4 +85,4 @@ Small collection of tools to do not-so-everyday things.
await MRUStorage.SetCurrentToken(auth);
await OnInitializedAsync();
}
-}
\ No newline at end of file
+}
diff --git a/MatrixRoomUtils.Web/Pages/InvalidSession.razor b/MatrixRoomUtils.Web/Pages/InvalidSession.razor
new file mode 100644
index 0000000..3bcd797
--- /dev/null
+++ b/MatrixRoomUtils.Web/Pages/InvalidSession.razor
@@ -0,0 +1,97 @@
+@page "/InvalidSession"
+@using MatrixRoomUtils.Core.Helpers
+@using MatrixRoomUtils.Core.Responses
+@using MatrixRoomUtils.Web.Shared.SimpleComponents
+
+Invalid session
+
+
Rory&::MatrixUtils - Invalid session encountered
+
A session was encountered that is no longer valid. This can happen if you have logged out of the account on another device, or if the access token has expired.
+
+@if (_login is not null) {
+
It appears that the affected user is @_login.UserId (@_login.DeviceId) on @_login.Homeserver!
+ Refresh token
+ Remove
+
+ @if (_showRefreshDialog) {
+
+
+ Log in
+ @if (_loginException is not null) {
+
@_loginException.RawContent
+ }
+
+ }
+}
+
+@code
+{
+ [Parameter]
+ [SupplyParameterFromQuery(Name = "ctx")]
+ public string Context { get; set; }
+
+ private LoginResponse _login { get; set; }
+
+ private bool _showRefreshDialog { get; set; } = false;
+
+ private string _password { get; set; } = "";
+
+ private MatrixException _loginException { get; set; }
+
+ protected override async Task OnInitializedAsync() {
+ var tokens = await MRUStorage.GetAllTokens();
+ if (tokens is null || tokens.Count == 0) {
+ NavigationManager.NavigateTo("/Login");
+ return;
+ }
+
+ _login = tokens.FirstOrDefault(x => x.AccessToken == Context);
+
+ if (_login is null) {
+ Console.WriteLine($"Could not find {_login} in stored tokens!");
+ }
+
+ await base.OnInitializedAsync();
+ }
+
+ private async Task RemoveUser() {
+ await MRUStorage.RemoveToken(_login);
+ if ((await MRUStorage.GetCurrentToken()).AccessToken == _login.AccessToken)
+ MRUStorage.SetCurrentToken((await MRUStorage.GetAllTokens()).FirstOrDefault());
+ await OnInitializedAsync();
+ }
+
+ private async Task OpenRefreshDialog() {
+ _showRefreshDialog = true;
+ StateHasChanged();
+ }
+
+ private async Task SwitchSession(LoginResponse auth) {
+ Console.WriteLine($"Switching to {auth.Homeserver} {auth.AccessToken} {auth.UserId}");
+ await MRUStorage.SetCurrentToken(auth);
+ await OnInitializedAsync();
+ }
+
+ private async Task TryLogin() {
+ try {
+ var result = await HomeserverProvider.Login(_login.Homeserver, _login.UserId, _password);
+ if (result is null) {
+ Console.WriteLine($"Failed to login to {_login.Homeserver} as {_login.UserId}!");
+ return;
+ }
+ Console.WriteLine($"Obtained access token for {result.UserId}!");
+
+ await RemoveUser();
+ await MRUStorage.AddToken(result);
+ if (result.UserId == (await MRUStorage.GetCurrentToken())?.UserId)
+ await MRUStorage.SetCurrentToken(result);
+ NavigationManager.NavigateTo("/");
+ }
+ catch (MatrixException e) {
+ Console.WriteLine($"Failed to login to {_login.Homeserver} as {_login.UserId}!");
+ Console.WriteLine(e);
+ _loginException = e;
+ StateHasChanged();
+ }
+ }
+}
diff --git a/MatrixRoomUtils.Web/Pages/Rooms/PolicyList.razor b/MatrixRoomUtils.Web/Pages/Rooms/PolicyList.razor
index 4cb16b8..cd4788b 100644
--- a/MatrixRoomUtils.Web/Pages/Rooms/PolicyList.razor
+++ b/MatrixRoomUtils.Web/Pages/Rooms/PolicyList.razor
@@ -207,7 +207,9 @@ else {
}
private async Task LoadStatesAsync() {
- var hs = await MRUStorage.GetCurrentSession();
+ var hs = await MRUStorage.GetCurrentSessionOrNavigate();
+ if (hs is null) return;
+
var room = await hs.GetRoom(RoomId);
var states = room.GetFullStateAsync();
@@ -215,8 +217,8 @@ else {
if (!state.Type.StartsWith("m.policy.rule")) continue;
PolicyEvents.Add(state);
}
-
-
+
+
// var stateEventsQuery = await room.GetStateAsync("");
// var stateEvents = stateEventsQuery.Value.Deserialize>();
// PolicyEvents = stateEvents.Where(x => x.Type.StartsWith("m.policy.rule"))
@@ -247,4 +249,4 @@ else {
StateHasChanged();
}
-}
\ No newline at end of file
+}
diff --git a/MatrixRoomUtils.Web/Shared/InlineUserItem.razor b/MatrixRoomUtils.Web/Shared/InlineUserItem.razor
index ffccc25..f9cef91 100644
--- a/MatrixRoomUtils.Web/Shared/InlineUserItem.razor
+++ b/MatrixRoomUtils.Web/Shared/InlineUserItem.razor
@@ -35,14 +35,15 @@
protected override async Task OnInitializedAsync() {
await base.OnInitializedAsync();
- var hs = await MRUStorage.GetCurrentSession();
-
+ var hs = await MRUStorage.GetCurrentSessionOrNavigate();
+ if(hs is null) return;
+
await _semaphoreSlim.WaitAsync();
if (User == null && UserId == null)
throw new ArgumentNullException(nameof(UserId));
User ??= await hs.GetProfile(UserId);
-
+
ProfileAvatar ??= MediaResolver.ResolveMediaUri(hs.FullHomeServerDomain, User.AvatarUrl);
ProfileName ??= User.DisplayName;
@@ -50,4 +51,4 @@
_semaphoreSlim.Release();
}
-}
\ No newline at end of file
+}
diff --git a/MatrixRoomUtils.Web/Shared/ModalWindow.razor b/MatrixRoomUtils.Web/Shared/ModalWindow.razor
index 216f1f3..b40d246 100644
--- a/MatrixRoomUtils.Web/Shared/ModalWindow.razor
+++ b/MatrixRoomUtils.Web/Shared/ModalWindow.razor
@@ -1,12 +1,12 @@
- @Title
+ @Title
-
- @ChildContent
-
+
+ @ChildContent
+
@code {
@@ -23,18 +23,30 @@
[Parameter]
public double Y { get; set; } = 60;
+ [Parameter]
+ public double MinWidth { get; set; } = 100;
+
[Parameter]
public Action OnCloseClicked { get; set; }
[Parameter]
public bool Collapsed { get; set; } = false;
+ private ElementReference _titleRef;
+
private double _x = 60;
private double _y = 60;
- protected override void OnInitialized() {
+ protected override async Task OnInitializedAsync() {
_x = X;
_y = Y;
+ await base.OnInitializedAsync();
+ }
+
+ protected override async Task OnAfterRenderAsync(bool firstRender) {
+ //set minwidth to title width
+ MinWidth = await JSRuntime.InvokeAsync("getWidth", _titleRef) + 75;
+ await base.OnAfterRenderAsync(firstRender);
}
private void WindowDrag(DragEventArgs obj) {
diff --git a/MatrixRoomUtils.Web/Shared/ModalWindow.razor.css b/MatrixRoomUtils.Web/Shared/ModalWindow.razor.css
index b25ab0e..6d08114 100644
--- a/MatrixRoomUtils.Web/Shared/ModalWindow.razor.css
+++ b/MatrixRoomUtils.Web/Shared/ModalWindow.razor.css
@@ -24,6 +24,7 @@
top: 0;
left: 0;
width: fit-content;
+ text-wrap: nowrap;
height: 100%;
line-height: 25px;
padding-left: 10px;
@@ -55,14 +56,13 @@
cursor: pointer;
}
-.r-modal > .content {
+.r-modal > .r-modal-content {
position: relative;
top: 25px;
left: 0;
width: fit-content;
height: fit-content;
min-width: 150px;
- min-height: 5px;
max-width: 75vw;
max-height: 75vh;
overflow: auto;
diff --git a/MatrixRoomUtils.Web/Shared/RoomList.razor b/MatrixRoomUtils.Web/Shared/RoomList.razor
index fadec1c..e3894a6 100644
--- a/MatrixRoomUtils.Web/Shared/RoomList.razor
+++ b/MatrixRoomUtils.Web/Shared/RoomList.razor
@@ -22,9 +22,12 @@ else {
public ProfileResponseEventData? GlobalProfile { get; set; }
Dictionary> RoomsWithTypes = new();
-
+
protected override async Task OnInitializedAsync() {
- GlobalProfile ??= await (await MRUStorage.GetCurrentSession())!.GetProfile((await MRUStorage.GetCurrentSession())!.WhoAmI.UserId);
+ var hs = await MRUStorage.GetCurrentSessionOrNavigate();
+ if (hs is null) return;
+
+ GlobalProfile ??= await hs.GetProfile(hs.WhoAmI.UserId);
if (RoomsWithTypes.Any()) return;
var tasks = Rooms.Select(ProcessRoom);
@@ -41,7 +44,7 @@ else {
_ => roomType
};
-
+
private static SemaphoreSlim _semaphoreSlim = new(8, 8);
private async Task ProcessRoom(RoomInfo room) {
await _semaphoreSlim.WaitAsync();
@@ -59,14 +62,14 @@ else {
catch (MatrixException e) {
roomType = $"Error: {e.ErrorCode}";
}
-
+
if (!RoomsWithTypes.ContainsKey(roomType)) {
RoomsWithTypes.Add(roomType, new List());
}
RoomsWithTypes[roomType].Add(room);
-
+
StateHasChanged();
_semaphoreSlim.Release();
}
-}
\ No newline at end of file
+}
diff --git a/MatrixRoomUtils.Web/Shared/SimpleComponents/LinkButton.razor b/MatrixRoomUtils.Web/Shared/SimpleComponents/LinkButton.razor
index 09b5c32..b800989 100644
--- a/MatrixRoomUtils.Web/Shared/SimpleComponents/LinkButton.razor
+++ b/MatrixRoomUtils.Web/Shared/SimpleComponents/LinkButton.razor
@@ -1,16 +1,20 @@
-
+ OnClick?.Invoke())"
+ style="background-color: @(Color ?? "#1b6ec2");">
@ChildContent
@code {
-
+
[Parameter]
- public string href { get; set; }
-
+ public string? href { get; set; }
+
[Parameter]
public RenderFragment ChildContent { get; set; }
[Parameter]
public string? Color { get; set; }
-}
\ No newline at end of file
+ [Parameter]
+ public Func? OnClick { get; set; }
+
+}
diff --git a/MatrixRoomUtils.Web/_Imports.razor b/MatrixRoomUtils.Web/_Imports.razor
index 85c015e..b1d9212 100644
--- a/MatrixRoomUtils.Web/_Imports.razor
+++ b/MatrixRoomUtils.Web/_Imports.razor
@@ -21,4 +21,5 @@
@inject MRUStorageWrapper MRUStorage
@inject HomeserverProviderService HomeserverProvider
@inject TieredStorageService TieredStorage
-@inject HomeserverResolverService HomeserverResolver
\ No newline at end of file
+@inject HomeserverResolverService HomeserverResolver
+@inject IJSRuntime JSRuntime
diff --git a/MatrixRoomUtils.Web/wwwroot/css/app.css b/MatrixRoomUtils.Web/wwwroot/css/app.css
index b3a8cf3..bcd8f5d 100644
--- a/MatrixRoomUtils.Web/wwwroot/css/app.css
+++ b/MatrixRoomUtils.Web/wwwroot/css/app.css
@@ -118,4 +118,4 @@ a, .btn-link {
pre {
font-family: JetBrainsMono, var(--bs-font-monospace);
-}
\ No newline at end of file
+}
diff --git a/MatrixRoomUtils.Web/wwwroot/index.html b/MatrixRoomUtils.Web/wwwroot/index.html
index 0598c4d..9a85530 100644
--- a/MatrixRoomUtils.Web/wwwroot/index.html
+++ b/MatrixRoomUtils.Web/wwwroot/index.html
@@ -34,10 +34,21 @@
if (element instanceof HTMLElement) {
console.log(element);
element.focus();
- } else if (element.__internalId) {
+ } else if (element.hasOwnProperty("__internalId")) {
console.log("Element is not an HTMLElement", element);
}
}
+ function getWidth(element) {
+ console.log("getWidth", element);
+ if (element == null) return 0;
+ if (element instanceof HTMLElement) {
+ return element.offsetWidth
+ } else if (element.hasOwnProperty("__internalId")) {
+ console.log("Element is not an HTMLElement", element);
+ return 0;
+ }
+ return 0;
+ }
function getWindowDimensions() {
return {
width: window.innerWidth,
--
cgit 1.4.1