51 lines
1.8 KiB
Swift
51 lines
1.8 KiB
Swift
// SPDX-FileCopyrightText: 2026 Kaede Akino
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
import Foundation
|
|
import TermindDomain
|
|
|
|
import struct TermindDomain.Host
|
|
|
|
/// A session-owned route snapshot. Hosts and login hints contain no secrets and
|
|
/// do not become Vault records until an explicit save.
|
|
public nonisolated struct SSHConnectionRoute: Hashable, Sendable {
|
|
public var hosts: [Host]
|
|
public var usernames: [Host.ID: String]
|
|
public var sourceHostIDs: [Host.ID: Host.ID]
|
|
|
|
public init(
|
|
hosts: [Host], usernames: [Host.ID: String] = [:], sourceHostIDs: [Host.ID: Host.ID] = [:]
|
|
) {
|
|
self.hosts = hosts
|
|
self.usernames = usernames
|
|
self.sourceHostIDs = sourceHostIDs
|
|
}
|
|
|
|
public var isValid: Bool {
|
|
guard !hosts.isEmpty, hosts.count <= 8, Set(hosts.map(\.id)).count == hosts.count else { return false }
|
|
return hosts.enumerated().allSatisfy { index, host in
|
|
host.jumpHostID == (index == 0 ? nil : hosts[index - 1].id)
|
|
}
|
|
}
|
|
|
|
public func host(_ id: Host.ID) -> Host? { hosts.first { $0.id == id } }
|
|
|
|
/// Login hints fill only unbound Hosts. An Identity still resolves its own
|
|
/// credentials, and an interactive submission always takes precedence.
|
|
public func submissions(
|
|
merging submissions: [Host.ID: SSHAuthenticationSubmission]
|
|
) -> [Host.ID: SSHAuthenticationSubmission] {
|
|
var result = submissions
|
|
for host in hosts where host.identityID == nil && result[host.id] == nil {
|
|
if let username = usernames[host.id] {
|
|
result[host.id] = SSHAuthenticationSubmission(username: username)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
public static var invalidRouteError: TermindSSHError {
|
|
.route(String(localized: "The Jump Host route is invalid or exceeds eight hosts."))
|
|
}
|
|
}
|