Public Access
Let a session's transport say what it negotiated
ISshConnection and ISftpSession both carry Cipher now — the server-to-client algorithm off SSH.NET's own ConnectionInfo, captured once because a rekey is not an event that library raises — and TerminalWorkspace.GetSessionFacts hands that plus the host key's algorithm back per session, without ever handing over the connection itself. Nothing reads either yet; the status bar that will is the next commit.
This commit is contained in:
@@ -83,6 +83,9 @@ internal sealed class FakeSftpSession(SshConnectionRequest request) : ISftpSessi
|
||||
public HostKeyPresentation HostKey { get; } =
|
||||
new(request.Host, request.Port, "ssh-ed25519", "SHA256:fake");
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Cipher { get; } = "aes256-gcm@openssh.com";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string HomeDirectory => $"/home/{request.Username}";
|
||||
|
||||
@@ -139,6 +142,9 @@ internal sealed class FakeSshConnection(SshConnectionRequest request) : ISshConn
|
||||
public HostKeyPresentation HostKey { get; } =
|
||||
new(request.Host, request.Port, "ssh-ed25519", "SHA256:fake");
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Cipher { get; } = "aes256-gcm@openssh.com";
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ISshShellSession> OpenShellAsync(
|
||||
TerminalSize size,
|
||||
|
||||
@@ -36,6 +36,12 @@ public sealed class KeyAuthenticationTests(SshServerFixture fixture)
|
||||
|
||||
connection.IsConnected.ShouldBeTrue();
|
||||
|
||||
// The one place this suite checks Cipher against a real handshake rather than a fake's fixed string.
|
||||
// SSH.NET negotiates whatever the container's sshd offers first from its own preference list, so the
|
||||
// exact algorithm is not pinned here — only that ConnectionInfo.CurrentServerEncryption came back as
|
||||
// something rather than the empty string a stalled or pre-handshake read would produce.
|
||||
connection.Cipher.ShouldNotBeNullOrEmpty();
|
||||
|
||||
// Authenticated is not the same as usable: a channel has to open on the connection too.
|
||||
await using var shell = await connection.OpenShellAsync(TerminalSize.Default, Token);
|
||||
|
||||
|
||||
@@ -136,6 +136,9 @@ internal sealed class FakeConnection(SshConnectionRequest request, long bytesPer
|
||||
public HostKeyPresentation HostKey { get; } =
|
||||
new(request.Host, request.Port, "ssh-ed25519", "SHA256:fake");
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Cipher { get; } = "aes256-gcm@openssh.com";
|
||||
|
||||
/// <summary>The shell this connection opened, if it opened one.</summary>
|
||||
internal FakeShellSession? Shell { get; private set; }
|
||||
|
||||
|
||||
@@ -149,6 +149,31 @@ public sealed class TerminalWorkspaceTests
|
||||
workspace.IsSessionLive(second).ShouldBeTrue("closing one tab must not disturb another");
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// The shell's connect path reads these back once, right after <see cref="TerminalWorkspace.OpenSessionAsync"/>
|
||||
/// returns, to fill in the status bar's cipher and host-key facts — see <c>VaultViewModel.ConnectAndAnnounceAsync</c>.
|
||||
/// Asserted the same way <see cref="LivenessIsAnsweredPerSession"/> asserts liveness: per session, and
|
||||
/// null rather than thrown for an id this workspace never issued.
|
||||
/// </remarks>
|
||||
[Fact]
|
||||
public async Task SessionFactsAreReadPerSession()
|
||||
{
|
||||
var connections = new FakeConnectionFactory();
|
||||
|
||||
await using var workspace = CreateWorkspace(connections);
|
||||
|
||||
var sessionId = await workspace.OpenSessionAsync(
|
||||
Request(), TerminalSize.Default, TestContext.Current.CancellationToken);
|
||||
|
||||
var facts = workspace.GetSessionFacts(sessionId).ShouldNotBeNull();
|
||||
var connection = connections.Connections.ShouldHaveSingleItem();
|
||||
|
||||
facts.Cipher.ShouldBe(connection.Cipher);
|
||||
facts.HostKeyAlgorithm.ShouldBe(connection.HostKey.Algorithm);
|
||||
|
||||
workspace.GetSessionFacts(9999).ShouldBeNull("this workspace never issued that id");
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Inserting a snippet has to be able to say whether it arrived, and the transport cannot: it drops
|
||||
|
||||
@@ -23,6 +23,9 @@ internal sealed class FakeSftpSession : ISftpSession
|
||||
/// <inheritdoc />
|
||||
public HostKeyPresentation HostKey { get; } = new("host.internal", 22, "ssh-ed25519", "SHA256:fake");
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Cipher { get; } = "aes256-gcm@openssh.com";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string HomeDirectory => "/home/dodo";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user