Fix connect-time username prompt never reaching SSH/RDP authentication

The username entered at the connect-time prompt (added for issue #21)
was updating SessionTab's own in-memory Profile copy, but
SshSessionBackend/RdpSessionBackend are constructed with -- and only
ever read from -- their own separate Profile copy on a worker thread,
which never saw that edit. Authentication was still built from the
original (blank) username regardless of what was typed into the
prompt.

SessionConnectOptions gains a username field, populated by SessionTab
on every connect attempt and threaded through the same way password
already is; both backends now prefer options.username over
profile().username. Covered by a new SSH regression test using an
exact-match fixture host that only succeeds for a specific
user@host target.

Bump version to v2026.9.16.5.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-16 08:40:34 -06:00
co-authored by Claude Sonnet 5
parent 0cdf930303
commit b3cedcfa48
8 changed files with 93 additions and 6 deletions
+7 -1
View File
@@ -1640,7 +1640,13 @@ void RdpSessionBackend::workerMain()
const Profile& p = profile();
const QString host = p.host.trimmed();
QString username = p.username.trimmed();
// m_activeOptions.username carries a value prompted for at connect time
// (see SessionTab::requestConnectOptions()) when the saved profile's
// own username was blank; profile().username never sees that edit
// since this backend's Profile copy was captured at construction time.
QString username = m_activeOptions.username.trimmed().isEmpty()
? p.username.trimmed()
: m_activeOptions.username.trimmed();
QString domain = p.domain.trimmed();
if (domain.isEmpty()) {
const int domainSeparator = username.indexOf(QLatin1Char('\\'));
+6
View File
@@ -12,6 +12,12 @@
class SessionConnectOptions
{
public:
// Only set when the profile's own username was blank and SessionTab
// prompted for one inline at connect time (see issue #21); empty means
// "use the backend's own profile().username" as before. SSH/RDP need
// this up front, unlike VNC's Apple auth which discovers the need for
// one mid-connection via usernameRequested()/provideUsername() instead.
QString username;
QString password;
QString privateKeyPath;
QString knownHostsPolicy;
+4
View File
@@ -998,6 +998,10 @@ void SessionTab::requestConnectOptions(
{
SessionConnectOptions baseOptions;
baseOptions.knownHostsPolicy = m_profile.knownHostsPolicy;
// The backend's own Profile copy was captured when it was constructed
// and never sees later edits to m_profile (e.g. the username prompt
// below) -- it has to travel through here instead.
baseOptions.username = m_profile.username.trimmed();
const bool isSsh = m_profile.protocol.compare(QStringLiteral("SSH"), Qt::CaseInsensitive) == 0;
const bool isRdp = m_profile.protocol.compare(QStringLiteral("RDP"), Qt::CaseInsensitive) == 0;
+9 -2
View File
@@ -388,9 +388,16 @@ bool SshSessionBackend::startSshProcess(const SessionConnectOptions& options)
<< QStringLiteral("PasswordAuthentication=no");
}
const QString target = p.username.trimmed().isEmpty()
// options.username carries a value prompted for at connect time (see
// SessionTab::requestConnectOptions()) when the saved profile's own
// username was blank; profile().username never sees that edit since
// the backend's Profile copy was captured at construction time.
const QString username = options.username.trimmed().isEmpty()
? p.username.trimmed()
: options.username.trimmed();
const QString target = username.isEmpty()
? p.host.trimmed()
: QStringLiteral("%1@%2").arg(p.username.trimmed(), p.host.trimmed());
: QStringLiteral("%1@%2").arg(username, p.host.trimmed());
args << target;
m_process->setProcessEnvironment(environment);