Initial commit

This commit is contained in:
2026-06-02 10:51:14 +02:00
committed by kawa
commit d19092c84e
182 changed files with 13757 additions and 0 deletions
@@ -0,0 +1,14 @@
using SharepointToolbox.Web.Core.Models;
namespace SharepointToolbox.Web.Services.Session;
/// <summary>Stores OAuth tokens in ProtectedSessionStorage (browser-side, encrypted).
/// Nothing written to server disk.</summary>
public interface ISessionCredentialStore
{
Task<SessionTokens?> GetAsync();
Task SetAsync(SessionTokens tokens);
Task UpdateRefreshTokenAsync(string newRefreshToken);
Task ClearAsync();
Task<bool> HasCredentialsAsync();
}
+16
View File
@@ -0,0 +1,16 @@
using SharepointToolbox.Web.Core.Models;
namespace SharepointToolbox.Web.Services.Session;
/// <summary>Scoped per Blazor circuit. Set once on circuit init from auth state.</summary>
public interface IUserContextAccessor
{
string Email { get; }
string DisplayName { get; }
UserRole Role { get; }
bool IsAuthenticated { get; }
event Action? Initialized;
void Initialize(AppUser user);
}
+19
View File
@@ -0,0 +1,19 @@
using SharepointToolbox.Web.Core.Models;
namespace SharepointToolbox.Web.Services.Session;
/// <summary>
/// Scoped per Blazor circuit. Holds the active tenant profile for the current user.
/// All feature pages read the profile from here instead of asking the user per-request.
/// </summary>
public interface IUserSessionService
{
TenantProfile? CurrentProfile { get; }
bool HasProfile { get; }
AppSettings Settings { get; }
void SetProfile(TenantProfile profile);
Task ClearSessionAsync();
void UpdateSettings(AppSettings settings);
event Action? ProfileChanged;
}
@@ -0,0 +1,42 @@
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
using SharepointToolbox.Web.Core.Models;
namespace SharepointToolbox.Web.Services.Session;
public class SessionCredentialStore : ISessionCredentialStore
{
private const string Key = "sp-session-tokens";
private readonly ProtectedSessionStorage _storage;
public SessionCredentialStore(ProtectedSessionStorage storage) { _storage = storage; }
public async Task<SessionTokens?> GetAsync()
{
try
{
var result = await _storage.GetAsync<SessionTokens>(Key);
return result.Success ? result.Value : null;
}
catch { return null; }
}
public async Task SetAsync(SessionTokens tokens) =>
await _storage.SetAsync(Key, tokens);
public async Task UpdateRefreshTokenAsync(string newRefreshToken)
{
var tokens = await GetAsync();
if (tokens is null) return;
tokens.RefreshToken = newRefreshToken;
await _storage.SetAsync(Key, tokens);
}
public async Task ClearAsync() =>
await _storage.DeleteAsync(Key);
public async Task<bool> HasCredentialsAsync()
{
var tokens = await GetAsync();
return tokens is not null && !string.IsNullOrEmpty(tokens.RefreshToken);
}
}
+21
View File
@@ -0,0 +1,21 @@
using SharepointToolbox.Web.Core.Models;
namespace SharepointToolbox.Web.Services.Session;
public class UserContextAccessor : IUserContextAccessor
{
private AppUser? _user;
public string Email => _user?.Email ?? string.Empty;
public string DisplayName => _user?.DisplayName ?? string.Empty;
public UserRole Role => _user?.Role ?? UserRole.TechN0;
public bool IsAuthenticated => _user is not null;
public event Action? Initialized;
public void Initialize(AppUser user)
{
_user = user;
Initialized?.Invoke();
}
}
+52
View File
@@ -0,0 +1,52 @@
using SharepointToolbox.Web.Core.Models;
using SharepointToolbox.Web.Infrastructure.Persistence;
namespace SharepointToolbox.Web.Services.Session;
public class UserSessionService : IUserSessionService
{
private readonly ISessionManager _sessionManager;
private readonly SettingsRepository _settingsRepo;
private TenantProfile? _currentProfile;
private AppSettings _settings = new();
public TenantProfile? CurrentProfile => _currentProfile;
public bool HasProfile => _currentProfile is not null;
public AppSettings Settings => _settings;
public event Action? ProfileChanged;
public UserSessionService(ISessionManager sessionManager, SettingsRepository settingsRepo)
{
_sessionManager = sessionManager;
_settingsRepo = settingsRepo;
_ = LoadSettingsAsync();
}
public void SetProfile(TenantProfile profile)
{
_currentProfile = profile;
ProfileChanged?.Invoke();
}
public async Task ClearSessionAsync()
{
if (_currentProfile is not null)
await _sessionManager.ClearSessionAsync(_currentProfile.TenantUrl);
_currentProfile = null;
ProfileChanged?.Invoke();
}
public void UpdateSettings(AppSettings settings)
{
_settings = settings;
_ = _settingsRepo.SaveAsync(settings);
}
private async Task LoadSettingsAsync()
{
try { _settings = await _settingsRepo.LoadAsync(); }
catch { /* use defaults */ }
}
}