diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsSidebar.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsSidebar.razor index 08c0b652..8743c5d6 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsSidebar.razor +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsSidebar.razor @@ -35,7 +35,6 @@ @code { private Dictionary>? _groups; - private DocIndexItem? _selectedValue; private bool _expanded = true; [Parameter] diff --git a/samples/UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub.EFCore/uauthhub.db-shm b/samples/UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub.EFCore/uauthhub.db-shm index c6cedbd5..3b286565 100644 Binary files a/samples/UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub.EFCore/uauthhub.db-shm and b/samples/UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub.EFCore/uauthhub.db-shm differ diff --git a/samples/UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub.EFCore/uauthhub.db-wal b/samples/UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub.EFCore/uauthhub.db-wal index a23ad2d5..db39603e 100644 Binary files a/samples/UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub.EFCore/uauthhub.db-wal and b/samples/UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub.EFCore/uauthhub.db-wal differ diff --git a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/Components/Dialogs/CreateUserDialog.razor.cs b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/Components/Dialogs/CreateUserDialog.razor.cs index 820b1119..a06a2acd 100644 --- a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/Components/Dialogs/CreateUserDialog.razor.cs +++ b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/Components/Dialogs/CreateUserDialog.razor.cs @@ -18,7 +18,7 @@ public partial class CreateUserDialog private async Task CreateUserAsync() { - await _form.Validate(); + await _form.ValidateAsync(); if (!_form.IsValid) return; diff --git a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/Components/Dialogs/CredentialDialog.razor.cs b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/Components/Dialogs/CredentialDialog.razor.cs index 5f419abf..5e931ea6 100644 --- a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/Components/Dialogs/CredentialDialog.razor.cs +++ b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/Components/Dialogs/CredentialDialog.razor.cs @@ -31,7 +31,7 @@ private async Task ChangePasswordAsync() if (_form is null) return; - await _form.Validate(); + await _form.ValidateAsync(); if (!_form.IsValid) { Snackbar.Add("Form is not valid.", Severity.Error); diff --git a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/Components/Dialogs/ProfileDialog.razor.cs b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/Components/Dialogs/ProfileDialog.razor.cs index 955e8e98..f89b038e 100644 --- a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/Components/Dialogs/ProfileDialog.razor.cs +++ b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/Components/Dialogs/ProfileDialog.razor.cs @@ -70,7 +70,7 @@ private async Task SaveAsync() if (_form is not null) { - await _form.Validate(); + await _form.ValidateAsync(); if (!_form.IsValid) return; } diff --git a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/Components/Pages/Register.razor.cs b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/Components/Pages/Register.razor.cs index e8c16205..41eadd64 100644 --- a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/Components/Pages/Register.razor.cs +++ b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/Components/Pages/Register.razor.cs @@ -20,7 +20,7 @@ protected override async Task OnInitializedAsync() private async Task HandleRegisterAsync() { - await _form.Validate(); + await _form.ValidateAsync(); if (!_form.IsValid) return; diff --git a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/Components/Pages/ResetCredential.razor.cs b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/Components/Pages/ResetCredential.razor.cs index 21fad18a..71a4d93e 100644 --- a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/Components/Pages/ResetCredential.razor.cs +++ b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/Components/Pages/ResetCredential.razor.cs @@ -12,7 +12,7 @@ public partial class ResetCredential private async Task ResetPasswordAsync() { - await _form.Validate(); + await _form.ValidateAsync(); if (!_form.IsValid) { Snackbar.Add("Please fix the validation errors.", Severity.Error); diff --git a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/uauth.db b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/uauth.db index b911a93d..4e86411b 100644 Binary files a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/uauth.db and b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/uauth.db differ diff --git a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/uauth.db-shm b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/uauth.db-shm new file mode 100644 index 00000000..f64b9c90 Binary files /dev/null and b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/uauth.db-shm differ diff --git a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/uauth.db-wal b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/uauth.db-wal new file mode 100644 index 00000000..05f8b783 Binary files /dev/null and b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/uauth.db-wal differ diff --git a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Dialogs/CreateUserDialog.razor.cs b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Dialogs/CreateUserDialog.razor.cs index ec16e78f..af13ee27 100644 --- a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Dialogs/CreateUserDialog.razor.cs +++ b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Dialogs/CreateUserDialog.razor.cs @@ -18,7 +18,7 @@ public partial class CreateUserDialog private async Task CreateUserAsync() { - await _form.Validate(); + await _form.ValidateAsync(); if (!_form.IsValid) return; diff --git a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Dialogs/CredentialDialog.razor.cs b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Dialogs/CredentialDialog.razor.cs index ee48c215..887707c5 100644 --- a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Dialogs/CredentialDialog.razor.cs +++ b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Dialogs/CredentialDialog.razor.cs @@ -31,7 +31,7 @@ private async Task ChangePasswordAsync() if (_form is null) return; - await _form.Validate(); + await _form.ValidateAsync(); if (!_form.IsValid) { Snackbar.Add("Form is not valid.", Severity.Error); diff --git a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Dialogs/ProfileDialog.razor.cs b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Dialogs/ProfileDialog.razor.cs index 24fd603e..d2186593 100644 --- a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Dialogs/ProfileDialog.razor.cs +++ b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Dialogs/ProfileDialog.razor.cs @@ -70,7 +70,7 @@ private async Task SaveAsync() if (_form is not null) { - await _form.Validate(); + await _form.ValidateAsync(); if (!_form.IsValid) return; } diff --git a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Pages/Register.razor.cs b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Pages/Register.razor.cs index d1e67865..6adc7dc6 100644 --- a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Pages/Register.razor.cs +++ b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Pages/Register.razor.cs @@ -20,7 +20,7 @@ protected override async Task OnInitializedAsync() private async Task HandleRegisterAsync() { - await _form.Validate(); + await _form.ValidateAsync(); if (!_form.IsValid) return; diff --git a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Pages/ResetCredential.razor.cs b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Pages/ResetCredential.razor.cs index db40becc..9bcaf5f7 100644 --- a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Pages/ResetCredential.razor.cs +++ b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Pages/ResetCredential.razor.cs @@ -12,7 +12,7 @@ public partial class ResetCredential private async Task ResetPasswordAsync() { - await _form.Validate(); + await _form.ValidateAsync(); if (!_form.IsValid) { Snackbar.Add("Please fix the validation errors.", Severity.Error); diff --git a/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/Components/Dialogs/CreateUserDialog.razor.cs b/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/Components/Dialogs/CreateUserDialog.razor.cs index 61aa56fb..b10d7d9d 100644 --- a/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/Components/Dialogs/CreateUserDialog.razor.cs +++ b/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/Components/Dialogs/CreateUserDialog.razor.cs @@ -18,7 +18,7 @@ public partial class CreateUserDialog private async Task CreateUserAsync() { - await _form.Validate(); + await _form.ValidateAsync(); if (!_form.IsValid) return; diff --git a/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/Components/Dialogs/CredentialDialog.razor.cs b/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/Components/Dialogs/CredentialDialog.razor.cs index 926eba3d..a9c76d1c 100644 --- a/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/Components/Dialogs/CredentialDialog.razor.cs +++ b/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/Components/Dialogs/CredentialDialog.razor.cs @@ -31,7 +31,7 @@ private async Task ChangePasswordAsync() if (_form is null) return; - await _form.Validate(); + await _form.ValidateAsync(); if (!_form.IsValid) { Snackbar.Add("Form is not valid.", Severity.Error); diff --git a/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/Components/Dialogs/ProfileDialog.razor.cs b/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/Components/Dialogs/ProfileDialog.razor.cs index c0442702..c96497e8 100644 --- a/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/Components/Dialogs/ProfileDialog.razor.cs +++ b/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/Components/Dialogs/ProfileDialog.razor.cs @@ -72,7 +72,7 @@ private async Task SaveAsync() if (_form is not null) { - await _form.Validate(); + await _form.ValidateAsync(); if (!_form.IsValid) return; } diff --git a/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/Pages/Register.razor.cs b/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/Pages/Register.razor.cs index db73fd6a..8219cee3 100644 --- a/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/Pages/Register.razor.cs +++ b/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/Pages/Register.razor.cs @@ -20,7 +20,7 @@ protected override async Task OnInitializedAsync() private async Task HandleRegisterAsync() { - await _form.Validate(); + await _form.ValidateAsync(); if (!_form.IsValid) return; diff --git a/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/Pages/ResetCredential.razor.cs b/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/Pages/ResetCredential.razor.cs index 726c4864..b76e12b1 100644 --- a/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/Pages/ResetCredential.razor.cs +++ b/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/Pages/ResetCredential.razor.cs @@ -12,7 +12,7 @@ public partial class ResetCredential private async Task ResetPasswordAsync() { - await _form.Validate(); + await _form.ValidateAsync(); if (!_form.IsValid) { Snackbar.Add("Please fix the validation errors.", Severity.Error); diff --git a/src/CodeBeam.UltimateAuth.Server/Services/SessionApplicationService.cs b/src/CodeBeam.UltimateAuth.Server/Services/SessionApplicationService.cs index 4b62d6ab..d7640693 100644 --- a/src/CodeBeam.UltimateAuth.Server/Services/SessionApplicationService.cs +++ b/src/CodeBeam.UltimateAuth.Server/Services/SessionApplicationService.cs @@ -164,7 +164,9 @@ public async Task RevokeUserSessionAsync(AccessContext context, UserKey userKey, var expected = session.Version; var revoked = session.Revoke(now); - await store.SaveSessionAsync(revoked, expected); + await store.ExecuteAsync(async innerCt2 => { + await store.SaveSessionAsync(revoked, expected); + }); }); await _accessOrchestrator.ExecuteAsync(context, command, ct); @@ -176,7 +178,10 @@ public async Task RevokeUserChainAsync(AccessContext context, User { var isCurrent = context.ActorChainId == chainId; var store = _storeFactory.Create(context.ResourceTenant); - await store.RevokeChainCascadeAsync(chainId, _clock.UtcNow); + + await store.ExecuteAsync(async innerCt2 => { + await store.RevokeChainCascadeAsync(chainId, _clock.UtcNow); + }); return new RevokeResult { @@ -198,15 +203,18 @@ public async Task RevokeAllChainsAsync(AccessContext context, UserKey userKey, S var command = new AccessCommand(async innerCt => { var store = _storeFactory.Create(context.ResourceTenant); - var chains = await store.GetChainsByUserAsync(userKey); - foreach (var chain in chains) - { - if (exceptChainId.HasValue && chain.ChainId == exceptChainId.Value) - continue; + await store.ExecuteAsync(async innerCt2 => { + var chains = await store.GetChainsByUserAsync(userKey); - await store.RevokeChainCascadeAsync(chain.ChainId, _clock.UtcNow); - } + foreach (var chain in chains) + { + if (exceptChainId.HasValue && chain.ChainId == exceptChainId.Value) + continue; + + await store.RevokeChainCascadeAsync(chain.ChainId, _clock.UtcNow); + } + }); }); await _accessOrchestrator.ExecuteAsync(context, command, ct); @@ -220,7 +228,9 @@ public async Task LogoutDeviceAsync(AccessContext context, Session var store = _storeFactory.Create(context.ResourceTenant); var now = _clock.UtcNow; - await store.LogoutChainAsync(currentChainId, now, innerCt); + await store.ExecuteAsync(async innerCt2 => { + await store.LogoutChainAsync(currentChainId, now, innerCt2); + }); return new RevokeResult { @@ -239,7 +249,10 @@ public async Task LogoutOtherDevicesAsync(AccessContext context, UserKey userKey var store = _storeFactory.Create(context.ResourceTenant); var now = _clock.UtcNow; - await store.RevokeOtherSessionsAsync(userKey, currentChainId, now, innerCt); + await store.ExecuteAsync(async innerCt2 => { + await store.RevokeOtherSessionsAsync(userKey, currentChainId, now, innerCt2); + }); + }); await _accessOrchestrator.ExecuteAsync(context, command, ct); @@ -252,7 +265,9 @@ public async Task LogoutAllDevicesAsync(AccessContext context, UserKey userKey, var store = _storeFactory.Create(context.ResourceTenant); var now = _clock.UtcNow; - await store.RevokeAllSessionsAsync(userKey, now, innerCt); + await store.ExecuteAsync(async innerCt2 => { + await store.RevokeAllSessionsAsync(userKey, now, innerCt2); + }); }); await _accessOrchestrator.ExecuteAsync(context, command, ct); @@ -263,7 +278,10 @@ public async Task RevokeRootAsync(AccessContext context, UserKey userKey, Cancel var command = new AccessCommand(async innerCt => { var store = _storeFactory.Create(context.ResourceTenant); - await store.RevokeRootCascadeAsync(userKey, _clock.UtcNow); + + await store.ExecuteAsync(async innerCt2 => { + await store.RevokeRootCascadeAsync(userKey, _clock.UtcNow); + }); }); await _accessOrchestrator.ExecuteAsync(context, command, ct); diff --git a/src/sessions/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore/Stores/EfCoreSessionStore.cs b/src/sessions/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore/Stores/EfCoreSessionStore.cs index a1108ca4..99824242 100644 --- a/src/sessions/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore/Stores/EfCoreSessionStore.cs +++ b/src/sessions/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore/Stores/EfCoreSessionStore.cs @@ -11,6 +11,7 @@ internal sealed class EfCoreSessionStore : ISessionStore where TDbCo { private readonly TDbContext _db; private readonly TenantKey _tenant; + private bool _inExecution; public EfCoreSessionStore(TDbContext db, TenantExecutionContext tenant) { @@ -32,6 +33,7 @@ await strategy.ExecuteAsync(async () => try { + _inExecution = true; await action(ct); await _db.SaveChangesAsync(ct); await tx.CommitAsync(ct); @@ -46,6 +48,10 @@ await strategy.ExecuteAsync(async () => await tx.RollbackAsync(ct); throw; } + finally + { + _inExecution = false; + } }); } @@ -59,6 +65,7 @@ public async Task ExecuteAsync(Func ExecuteAsync(Func x.Tenant == _tenant && x.SessionId == session.SessionId); if (projection == null) @@ -122,6 +136,9 @@ public Task CreateSessionAsync(UAuthSession session, CancellationToken ct = defa { ct.ThrowIfCancellationRequested(); + if (!_inExecution) + throw new InvalidOperationException("Must be called inside ExecuteAsync"); + var projection = session.ToProjection(); if (session.Version != 0) @@ -136,6 +153,9 @@ public async Task RevokeSessionAsync(AuthSessionId sessionId, DateTimeOffs { ct.ThrowIfCancellationRequested(); + if (!_inExecution) + throw new InvalidOperationException("Must be called inside ExecuteAsync"); + var projection = await DbSetSession.SingleOrDefaultAsync(x => x.Tenant == _tenant && x.SessionId == sessionId, ct); if (projection is null || projection.RevokedAt is not null) @@ -152,6 +172,9 @@ public async Task RevokeAllSessionsAsync(UserKey user, DateTimeOffset at, Cancel { ct.ThrowIfCancellationRequested(); + if (!_inExecution) + throw new InvalidOperationException("Must be called inside ExecuteAsync"); + var chains = await DbSetChain .Where(x => x.Tenant == _tenant && x.UserKey == user) .ToListAsync(ct); @@ -188,6 +211,9 @@ public async Task RevokeOtherSessionsAsync(UserKey user, SessionChainId keepChai { ct.ThrowIfCancellationRequested(); + if (!_inExecution) + throw new InvalidOperationException("Must be called inside ExecuteAsync"); + var chains = await DbSetChain .Where(x => x.Tenant == _tenant && x.UserKey == user && x.ChainId != keepChain) .ToListAsync(ct); @@ -267,6 +293,9 @@ public async Task SaveChainAsync(UAuthSessionChain chain, long expectedVersion, { ct.ThrowIfCancellationRequested(); + if (!_inExecution) + throw new InvalidOperationException("Must be called inside ExecuteAsync"); + var projection = DbSetChain.Local.FirstOrDefault(x => x.Tenant == _tenant && x.ChainId == chain.ChainId); if (projection is null) @@ -289,6 +318,9 @@ public Task CreateChainAsync(UAuthSessionChain chain, CancellationToken ct = def { ct.ThrowIfCancellationRequested(); + if (!_inExecution) + throw new InvalidOperationException("Must be called inside ExecuteAsync"); + if (chain.Version != 0) throw new InvalidOperationException("New chain must have version 0."); @@ -304,6 +336,9 @@ public async Task RevokeChainAsync(SessionChainId chainId, DateTimeOffset at, Ca { ct.ThrowIfCancellationRequested(); + if (!_inExecution) + throw new InvalidOperationException("Must be called inside ExecuteAsync"); + var projection = await DbSetChain .SingleOrDefaultAsync(x => x.Tenant == _tenant && x.ChainId == chainId, ct); @@ -319,6 +354,9 @@ public async Task LogoutChainAsync(SessionChainId chainId, DateTimeOffset at, Ca { ct.ThrowIfCancellationRequested(); + if (!_inExecution) + throw new InvalidOperationException("Must be called inside ExecuteAsync"); + var chainProjection = await DbSetChain .SingleOrDefaultAsync(x => x.Tenant == _tenant && x.ChainId == chainId, ct); @@ -352,6 +390,9 @@ public async Task RevokeOtherChainsAsync(UserKey userKey, SessionChainId current { ct.ThrowIfCancellationRequested(); + if (!_inExecution) + throw new InvalidOperationException("Must be called inside ExecuteAsync"); + var projections = await DbSetChain .Where(x => x.Tenant == _tenant && @@ -372,6 +413,9 @@ public async Task RevokeAllChainsAsync(UserKey userKey, DateTimeOffset at, Cance { ct.ThrowIfCancellationRequested(); + if (!_inExecution) + throw new InvalidOperationException("Must be called inside ExecuteAsync"); + var projections = await DbSetChain .Where(x => x.Tenant == _tenant && @@ -402,6 +446,9 @@ public async Task SetActiveSessionIdAsync(SessionChainId chainId, AuthSessionId { ct.ThrowIfCancellationRequested(); + if (!_inExecution) + throw new InvalidOperationException("Must be called inside ExecuteAsync"); + var projection = DbSetChain.Local.FirstOrDefault(x => x.Tenant == _tenant && x.ChainId == chainId); if (projection is null) @@ -428,6 +475,9 @@ public async Task SaveRootAsync(UAuthSessionRoot root, long expectedVersion, Can { ct.ThrowIfCancellationRequested(); + if (!_inExecution) + throw new InvalidOperationException("Must be called inside ExecuteAsync"); + var projection = await DbSetRoot .SingleOrDefaultAsync(x => x.Tenant == _tenant && @@ -448,6 +498,9 @@ public Task CreateRootAsync(UAuthSessionRoot root, CancellationToken ct = defaul { ct.ThrowIfCancellationRequested(); + if (!_inExecution) + throw new InvalidOperationException("Must be called inside ExecuteAsync"); + if (root.Version != 0) throw new InvalidOperationException("New root must have version 0."); @@ -462,6 +515,9 @@ public async Task RevokeRootAsync(UserKey userKey, DateTimeOffset at, Cancellati { ct.ThrowIfCancellationRequested(); + if (!_inExecution) + throw new InvalidOperationException("Must be called inside ExecuteAsync"); + var projection = await DbSetRoot .SingleOrDefaultAsync(x => x.Tenant == _tenant && x.UserKey == userKey, ct); @@ -540,6 +596,9 @@ public async Task RemoveSessionAsync(AuthSessionId sessionId, CancellationToken { ct.ThrowIfCancellationRequested(); + if (!_inExecution) + throw new InvalidOperationException("Must be called inside ExecuteAsync"); + var projection = await DbSetSession.SingleOrDefaultAsync(x => x.Tenant == _tenant && x.SessionId == sessionId, ct); if (projection is null) @@ -552,6 +611,9 @@ public async Task RevokeChainCascadeAsync(SessionChainId chainId, DateTimeOffset { ct.ThrowIfCancellationRequested(); + if (!_inExecution) + throw new InvalidOperationException("Must be called inside ExecuteAsync"); + var chainProjection = await DbSetChain .SingleOrDefaultAsync(x => x.Tenant == _tenant && x.ChainId == chainId, ct); @@ -581,6 +643,9 @@ public async Task RevokeRootCascadeAsync(UserKey userKey, DateTimeOffset at, Can { ct.ThrowIfCancellationRequested(); + if (!_inExecution) + throw new InvalidOperationException("Must be called inside ExecuteAsync"); + var rootProjection = await DbSetRoot .SingleOrDefaultAsync(x => x.Tenant == _tenant && x.UserKey == userKey, ct); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreSessionStoreTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreSessionStoreTests.cs index 6eb7d7c9..504bcf49 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreSessionStoreTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreSessionStoreTests.cs @@ -261,6 +261,8 @@ public async Task Revoke_Session_Should_Work() ClaimsSnapshot.Empty, SessionMetadata.Empty); + bool revoked = false; + await store.ExecuteAsync(async ct => { await store.CreateRootAsync(root, ct); @@ -268,7 +270,10 @@ await store.ExecuteAsync(async ct => await store.CreateSessionAsync(session, ct); }); - var revoked = await store.RevokeSessionAsync(sessionId, DateTimeOffset.UtcNow); + await store.ExecuteAsync(async ct => + { + revoked = await store.RevokeSessionAsync(sessionId, DateTimeOffset.UtcNow); + }); Assert.True(revoked); }