Public Access
Relay: end a user's sessions into a vault the moment they lose access to it
This commit is contained in:
1 parent
965c9a7043
commit
170caa1391
12 files changed
+281
-38
No files matched your search
@@ -2,6 +2,7 @@ using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using DodoSSH.Api.Features.Events;
|
||||
using DodoSSH.Contracts;
|
||||
using DodoSSH.Domain;
|
||||
using DodoSSH.Infrastructure;
|
||||
@@ -266,6 +267,56 @@ public sealed class RelayEndpointTests(ApiFixture fixture)
|
||||
.ShouldBe(RelayCloseReason.IdleTimeout);
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// A session is authorised once, when its ticket is issued, and used to run on for as long as it carried
|
||||
/// bytes — removing somebody from a team left their shells into its machines open for up to the session's
|
||||
/// maximum lifetime. The announcement that tells their event socket to re-read its vaults now has the
|
||||
/// relay ask again too.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Access is withdrawn here by deleting the vault rather than by a team removal, because what is under
|
||||
/// test is the relay hearing the announcement and asking the access model again — any way of making the
|
||||
/// answer change will do, and this one needs no team. The removal path announces through the same call;
|
||||
/// see <c>TeamService.RemoveMemberAsync</c>.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
[Fact]
|
||||
public async Task ASessionWhoseUserLosesAccess_IsClosedWithItsOwnCode()
|
||||
{
|
||||
using var target = LoopbackTarget.Echo();
|
||||
var (subject, hostId) = await SeedRoutedHostAsync(target, "10.40.8.1");
|
||||
var ticket = await IssueAsync(subject, hostId);
|
||||
|
||||
using var timeout = Timeout();
|
||||
using var socket = await ConnectAsync(ticket, timeout.Token);
|
||||
|
||||
await RoundTripAsync(socket, "still here", timeout.Token);
|
||||
|
||||
Guid userId;
|
||||
|
||||
await using (var scope = fixture.CreateScope())
|
||||
{
|
||||
var database = scope.ServiceProvider.GetRequiredService<DodoDbContext>();
|
||||
|
||||
userId = (await database.Users.SingleAsync(u => u.Subject == subject, timeout.Token)).Id;
|
||||
|
||||
var vaultId = (await database.Hosts.SingleAsync(h => h.Id == hostId, timeout.Token)).VaultId;
|
||||
var vault = await database.Vaults.SingleAsync(v => v.Id == vaultId, timeout.Token);
|
||||
|
||||
vault.DeletedAtUtc = Now;
|
||||
await database.SaveChangesAsync(timeout.Token);
|
||||
|
||||
scope.ServiceProvider.GetRequiredService<IVaultEventPublisher>().VaultAccessChanged(userId);
|
||||
}
|
||||
|
||||
await ReadUntilCloseAsync(socket, timeout.Token);
|
||||
|
||||
socket.CloseStatus.ShouldBe((WebSocketCloseStatus)RelayProtocol.AccessRevokedCloseCode);
|
||||
(await WaitForClosedAuditAsync(ticket.SessionId, timeout.Token)).CloseReason
|
||||
.ShouldBe(RelayCloseReason.AccessRevoked);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AUserAtTheCap_IsRefusedATicket_AndASocketOverItIsClosed()
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user