177 lines
5 KiB
C#
177 lines
5 KiB
C#
using System.Text.Json;
|
|
using Blazored.LocalStorage;
|
|
using Iceshrimp.Frontend.Core.Schemas;
|
|
using Iceshrimp.Shared.Schemas.Web;
|
|
using Microsoft.JSInterop;
|
|
|
|
namespace Iceshrimp.Frontend.Core.Services;
|
|
|
|
internal class SessionService
|
|
{
|
|
public SessionService(
|
|
ApiService apiService, ISyncLocalStorageService localStorage, IJSRuntime js, ILogger<SessionService> logger
|
|
)
|
|
{
|
|
ApiService = apiService;
|
|
LocalStorage = localStorage;
|
|
Js = js;
|
|
Logger = logger;
|
|
try
|
|
{
|
|
Users = LocalStorage.GetItem<Dictionary<string, StoredUser>>("Users") ?? [];
|
|
}
|
|
catch (JsonException e)
|
|
{
|
|
Logger.LogError($"Stored users are malformed, reconstructing to a minimal form: {e.Message}");
|
|
Users = ReconstructUsers();
|
|
}
|
|
var lastUser = LocalStorage.GetItem<string?>("last_user");
|
|
if (lastUser != null)
|
|
{
|
|
SetSession(lastUser);
|
|
}
|
|
}
|
|
|
|
private ISyncLocalStorageService LocalStorage { get; }
|
|
private ApiService ApiService { get; }
|
|
private IJSRuntime Js { get; }
|
|
private ILogger<SessionService> Logger { get; }
|
|
public Dictionary<string, StoredUser> Users { get; }
|
|
public StoredUser? Current { get; private set; }
|
|
|
|
private void WriteUsers()
|
|
{
|
|
LocalStorage.SetItem("Users", Users);
|
|
}
|
|
|
|
public void AddUser(StoredUser user)
|
|
{
|
|
try
|
|
{
|
|
Users.Add(user.Id, user);
|
|
Logger.LogInformation($"User {user.Id} added.");
|
|
}
|
|
catch (ArgumentException) // Update user if it already exists.
|
|
{
|
|
Users[user.Id] = user;
|
|
Logger.LogInformation($"User {user.Id} updated.");
|
|
}
|
|
|
|
WriteUsers();
|
|
}
|
|
|
|
public async Task UpdateCurrentUserAsync()
|
|
{
|
|
if (Current == null) return;
|
|
var res = await ApiService.Auth.GetAuthStatusAsync();
|
|
if (res.Status is AuthStatusEnum.Authenticated)
|
|
{
|
|
var user = new StoredUser
|
|
{ // Token nor user will ever be null on an authenticated response
|
|
Id = res.User!.Id,
|
|
Username = res.User.Username,
|
|
DisplayName = res.User.DisplayName,
|
|
AvatarUrl = res.User.AvatarUrl,
|
|
BannerUrl = res.User.BannerUrl,
|
|
InstanceName = res.User.InstanceName,
|
|
InstanceIconUrl = res.User.InstanceIconUrl,
|
|
Token = res.Token!,
|
|
Host = res.User.Host,
|
|
IsAdmin = res.IsAdmin ?? false,
|
|
IsModerator = res.IsModerator ?? false,
|
|
Emojis = res.User.Emojis,
|
|
MovedTo = res.User.MovedTo
|
|
};
|
|
AddUser(user);
|
|
Current = user;
|
|
Logger.LogInformation($"Updated current user {user.Id}");
|
|
}
|
|
}
|
|
|
|
private Dictionary<string, StoredUser> ReconstructUsers()
|
|
{
|
|
var newUsers = new Dictionary<string, StoredUser>();
|
|
|
|
var users = new Dictionary<string, MinimalStoredUser>();
|
|
try
|
|
{
|
|
users = LocalStorage.GetItem<Dictionary<string, MinimalStoredUser>>("Users") ?? [];
|
|
}
|
|
catch (JsonException e)
|
|
{
|
|
Logger.LogWarning($"Users couldn't be reconstructed to a minimum state: {e.Message}");
|
|
}
|
|
|
|
foreach (var user in users.Values)
|
|
{
|
|
var stored = new StoredUser
|
|
{
|
|
Id = user.Id,
|
|
Username = user.Username,
|
|
Host = null,
|
|
DisplayName = null,
|
|
AvatarUrl = $"/identicon/{user.Id}",
|
|
AvatarAlt = null,
|
|
BannerUrl = null,
|
|
BannerAlt = null,
|
|
InstanceName = null,
|
|
InstanceIconUrl = null,
|
|
IsBot = false,
|
|
IsCat = false,
|
|
Emojis = [],
|
|
MovedTo = null,
|
|
Token = user.Token,
|
|
IsAdmin = false,
|
|
IsModerator = false
|
|
};
|
|
|
|
newUsers.Add(user.Id, stored);
|
|
}
|
|
|
|
LocalStorage.SetItem("Users", newUsers);
|
|
LocalStorage.RemoveItem("last_user");
|
|
return newUsers;
|
|
}
|
|
|
|
public void DeleteUser(string id)
|
|
{
|
|
if (id == Current?.Id) throw new ArgumentException("Cannot remove current user.");
|
|
Users.Remove(id);
|
|
WriteUsers();
|
|
}
|
|
|
|
private StoredUser GetUserById(string id)
|
|
{
|
|
var user = Users[id];
|
|
return user;
|
|
}
|
|
|
|
public void EndSession()
|
|
{
|
|
if (Current is { IsAdmin: true })
|
|
{
|
|
((IJSInProcessRuntime)Js).InvokeVoid("eval",
|
|
"document.cookie = \"admin_session=; path=/ ; Fri, 31 Dec 1000 23:59:59 GMT SameSite=Lax\"");
|
|
}
|
|
Current = null;
|
|
LocalStorage.RemoveItem("last_user");
|
|
}
|
|
|
|
public void SetSession(string id)
|
|
{
|
|
var user = GetUserById(id);
|
|
if (user == null) throw new Exception("Did not find User in Local Storage");
|
|
ApiService.SetBearerToken(user.Token);
|
|
Current = user;
|
|
LocalStorage.SetItem("last_user", user.Id);
|
|
var sessionsString = Users.Aggregate("", (current, el) => current + $"{el.Value.Token},").TrimEnd(',');
|
|
((IJSInProcessRuntime)Js).InvokeVoid("eval",
|
|
$"document.cookie = \"sessions={sessionsString}; path=/; expires=Fri, 31 Dec 9999 23:59:59 GMT; SameSite=Lax\"");
|
|
if (user.IsAdmin)
|
|
{
|
|
((IJSInProcessRuntime)Js).InvokeVoid("eval",
|
|
$"document.cookie = \"admin_session={user.Token}; path=/; expires=Fri, 31 Dec 9999 23:59:59 GMT; SameSite=Lax\"");
|
|
}
|
|
// Security implications of this need a second pass? user.Id should never be user controllable, but still.
|
|
}
|
|
}
|