Skip to content

Commit b82e66b

Browse files
author
Carlos Cabanero
committed
DefaultAgent with a few interface improvements
- Adding state as the agent can be unavailable. - Differentiate when Settings do not exist (nil), from where there are issues reading them.
1 parent 33aec3c commit b82e66b

File tree

4 files changed

+64
-35
lines changed

4 files changed

+64
-35
lines changed

Blink/Commands/ssh/SSHAgentAdd.swift

+8-5
Original file line numberDiff line numberDiff line change
@@ -98,15 +98,18 @@ public class BlinkSSHAgentAdd: NSObject {
9898
let currentRunLoop = RunLoop.current
9999

100100
public func start(_ argc: Int32, argv: [String], session: MCPSession) -> Int32 {
101-
do {
102-
command = try BlinkSSHAgentAddCommand.parse(Array(argv[1...]))
101+
do {
102+
command = try BlinkSSHAgentAddCommand.parse(Array(argv[1...]))
103103
} catch {
104104
let message = BlinkSSHAgentAddCommand.message(for: error)
105105
print(message, to: &stderr)
106106
return -1
107107
}
108108

109-
let _ = SSHDefaultAgent.instance
109+
guard let defaultAgent = SSHDefaultAgent.instance else {
110+
print("Default Agent is not available.", to: &stderr)
111+
return -1
112+
}
110113

111114
if command.remove {
112115
let keyName = command.keyName ?? "id_rsa"
@@ -121,7 +124,7 @@ public class BlinkSSHAgentAdd: NSObject {
121124
}
122125

123126
if command.list {
124-
for key in SSHDefaultAgent.instance.ring {
127+
for key in defaultAgent.ring {
125128
let str = BKPubKey.withID(key.name)?.publicKey ?? ""
126129
print("\(str) \(key.name)", to: &stdout)
127130
}
@@ -137,7 +140,7 @@ public class BlinkSSHAgentAdd: NSObject {
137140
return -1;
138141
}
139142

140-
for key in SSHDefaultAgent.instance.ring {
143+
for key in defaultAgent.ring {
141144
if let blob = try? key.signer.publicKey.encode()[4...],
142145
let sshkey = try? SSHKey(fromPublicBlob: blob)
143146
{

Blink/Commands/ssh/SSHConfigProvider.swift

+6-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,12 @@ extension SSHClientConfigProvider {
142142
}
143143

144144
// Link to Default Agent
145-
agent.linkTo(agent: SSHDefaultAgent.instance)
145+
if let defaultAgent = SSHDefaultAgent.instance {
146+
agent.linkTo(agent: defaultAgent)
147+
} else {
148+
printLn("Default agent is not available.")
149+
}
150+
146151
return agent
147152
}
148153

Blink/Commands/ssh/SSHDefaultAgent.swift

+49-28
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import SSH
3535

3636

3737
final class SSHDefaultAgent {
38-
public static var instance: SSHAgent {
38+
public static var instance: SSHAgent? {
3939
if let agent = Self._instance {
4040
return agent
4141
} else {
@@ -44,6 +44,7 @@ final class SSHDefaultAgent {
4444
}
4545
private static var _instance: SSHAgent? = nil
4646
private init() {}
47+
// The pool is responsible for the location of Agents.
4748
private static let defaultAgentFile: URL = BlinkPaths.blinkAgentSettingsURL().appendingPathComponent("default")
4849

4950
enum Error: Swift.Error, LocalizedError {
@@ -57,30 +58,41 @@ final class SSHDefaultAgent {
5758
}
5859
}
5960

60-
private static func load() -> SSHAgent {
61-
let instance = SSHAgent()
62-
Self._instance = instance
63-
// If the Settings are not available, the agent is initialized with the default configuration.
64-
// If there is a problem with the location, it is safe to assume that it will persist.
65-
if let settings = try? getSettings() {
66-
try? applySettings(settings)
67-
} else {
68-
try? setSettings(BKAgentSettings(prompt: .Confirm, keys: []))
61+
// Load the (default) agent in the pool. If the Agent cannot be loaded, it will be unavailable (nil).
62+
// If the agent doesn't exist, it will be initialized (default only).
63+
private static func load() -> SSHAgent? {
64+
do {
65+
if let settings = try getSettings() {
66+
try setAgentInstance(with: settings)
67+
} else {
68+
try setSettings(BKAgentSettings())
69+
}
70+
} catch {
71+
return nil
6972
}
70-
return instance
73+
74+
return Self._instance
7175
}
7276

77+
// Create the settings for the agent and load it in the pool.
78+
// NOTE: For non-default agents, this would be the main initialization method.
7379
static func setSettings(_ settings: BKAgentSettings) throws {
7480
try BKAgentSettings.save(settings: settings, to: defaultAgentFile)
75-
try applySettings(settings)
81+
try setAgentInstance(with: settings)
7682
}
7783

78-
private static func applySettings(_ settings: BKAgentSettings) throws {
79-
let agent = Self.instance
80-
agent.clear()
81-
84+
private static func setAgentInstance(with settings: BKAgentSettings) throws {
8285
let bkConfig = try BKConfig()
8386

87+
let agent: SSHAgent
88+
if let _instance = Self._instance {
89+
agent = _instance
90+
agent.clear()
91+
} else {
92+
agent = SSHAgent()
93+
Self._instance = agent
94+
}
95+
8496
settings.keys.forEach { key in
8597
if let (signer, name) = bkConfig.signer(forIdentity: key) {
8698
if let constraints = settings.constraints() {
@@ -90,18 +102,21 @@ final class SSHDefaultAgent {
90102
}
91103
}
92104

93-
static func getSettings() throws -> BKAgentSettings {
94-
try BKAgentSettings.load(from: defaultAgentFile)
105+
static func getSettings() throws -> BKAgentSettings? {
106+
try BKAgentSettings.read(from: defaultAgentFile)
95107
}
96108

97109
// Applying settings clears the agent first. Adding a key doesn't modify or reset previous constraints.
98110
static func addKey(named keyName: String) throws {
99-
let settings = try getSettings()
111+
guard let agent = Self.instance,
112+
let settings = try getSettings() else {
113+
return
114+
}
115+
100116
if settings.keys.contains(keyName) {
101117
return
102118
}
103119

104-
let agent = Self.instance
105120
let bkConfig = try BKConfig()
106121

107122
if let (signer, name) = bkConfig.signer(forIdentity: keyName) {
@@ -124,16 +139,17 @@ final class SSHDefaultAgent {
124139

125140
static func removeKey(named keyName: String) throws -> Signer? {
126141
// Remove from settings and apply
127-
let settings = try getSettings()
128-
guard settings.keys.contains(keyName) else {
142+
guard let agent = Self.instance,
143+
let settings = try getSettings(),
144+
settings.keys.contains(keyName) else {
129145
return nil
130146
}
131147

132148
var keys = settings.keys
133149
keys.removeAll(where: { $0 == keyName })
134150
try BKAgentSettings.save(settings: BKAgentSettings(prompt: settings.prompt, keys: keys), to: defaultAgentFile)
135151

136-
return Self.instance.removeKey(keyName)
152+
return agent.removeKey(keyName)
137153
}
138154
}
139155

@@ -151,17 +167,22 @@ struct BKAgentSettings: Codable, Equatable {
151167
let prompt: BKAgentSettingsPrompt
152168
let keys: [String]
153169

154-
// init(prompt: BKAgentSettingsPrompt, keys: [String]) {
155-
// self.prompt = prompt
156-
// self.keys = keys
157-
// }
170+
init(prompt: BKAgentSettingsPrompt, keys: [String]) {
171+
self.prompt = prompt
172+
self.keys = keys
173+
}
174+
175+
init() { self = Self(prompt: .Confirm, keys: []) }
158176

159177
static func save(settings: BKAgentSettings, to file: URL) throws {
160178
let data = try JSONEncoder().encode(settings)
161179
try data.write(to: file)
162180
}
163181

164-
static func load(from file: URL) throws -> BKAgentSettings {
182+
fileprivate static func read(from file: URL) throws -> BKAgentSettings? {
183+
guard FileManager.default.fileExists(atPath: file.path) else {
184+
return nil
185+
}
165186
let data = try Data(contentsOf: file)
166187
return try JSONDecoder().decode(BKAgentSettings.self, from: data)
167188
}

Settings/ViewControllers/AgentSettings/AgentSettingsView.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ struct DefaultAgentSettingsView: View {
6363
if agentSettings == nil {
6464
print("Init settings")
6565
do {
66-
let agentSettings = try SSHDefaultAgent.getSettings()
66+
let agentSettings = try SSHDefaultAgent.getSettings() ?? BKAgentSettings()
6767
self.agentSettings = agentSettings
6868
} catch {
6969
self.alertMessage = "Failed to get settings: \(error.localizedDescription)"

0 commit comments

Comments
 (0)