This commit is contained in:
Max Goedjen 2025-09-01 15:09:27 -07:00
parent 9299bf343f
commit c8d90ba455
No known key found for this signature in database
6 changed files with 51 additions and 36 deletions

View File

@ -4,7 +4,7 @@ import OSLog
/// Manages storage and lookup for OpenSSH certificates.
public actor OpenSSHCertificateHandler: Sendable {
private let publicKeyFileStoreController = PublicKeyFileStoreController(homeDirectory: NSHomeDirectory())
private let publicKeyFileStoreController = PublicKeyFileStoreController(homeDirectory: URL.homeDirectory)
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "OpenSSHCertificateHandler")
private let writer = OpenSSHPublicKeyWriter()
private var keyBlobsAndNames: [AnySecret: (Data, Data)] = [:]

View File

@ -5,12 +5,12 @@ import OSLog
public final class PublicKeyFileStoreController: Sendable {
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "PublicKeyFileStoreController")
private let directory: String
private let directory: URL
private let keyWriter = OpenSSHPublicKeyWriter()
/// Initializes a PublicKeyFileStoreController.
public init(homeDirectory: String) {
directory = homeDirectory.appending("/PublicKeys")
public init(homeDirectory: URL) {
directory = homeDirectory.appending(component: "PublicKeys")
}
/// Writes out the keys specified to disk.
@ -20,7 +20,7 @@ public final class PublicKeyFileStoreController: Sendable {
logger.log("Writing public keys to disk")
if clear {
let validPaths = Set(secrets.map { publicKeyPath(for: $0) }).union(Set(secrets.map { sshCertificatePath(for: $0) }))
let contentsOfDirectory = (try? FileManager.default.contentsOfDirectory(atPath: directory)) ?? []
let contentsOfDirectory = (try? FileManager.default.contentsOfDirectory(atPath: directory.path())) ?? []
let fullPathContents = contentsOfDirectory.map { "\(directory)/\($0)" }
let untracked = Set(fullPathContents)
@ -29,7 +29,7 @@ public final class PublicKeyFileStoreController: Sendable {
try? FileManager.default.removeItem(at: URL(fileURLWithPath: path))
}
}
try? FileManager.default.createDirectory(at: URL(fileURLWithPath: directory), withIntermediateDirectories: false, attributes: nil)
try? FileManager.default.createDirectory(at: directory, withIntermediateDirectories: false, attributes: nil)
for secret in secrets {
let path = publicKeyPath(for: secret)
let data = Data(keyWriter.openSSHString(secret: secret).utf8)
@ -44,14 +44,14 @@ public final class PublicKeyFileStoreController: Sendable {
/// - Warning: This method returning a path does not imply that a key has been written to disk already. This method only describes where it will be written to.
public func publicKeyPath<SecretType: Secret>(for secret: SecretType) -> String {
let minimalHex = keyWriter.openSSHMD5Fingerprint(secret: secret).replacingOccurrences(of: ":", with: "")
return directory.appending("/").appending("\(minimalHex).pub")
return directory.appending(component: "\(minimalHex).pub").path()
}
/// Short-circuit check to ship enumerating a bunch of paths if there's nothing in the cert directory.
public var hasAnyCertificates: Bool {
do {
return try FileManager.default
.contentsOfDirectory(atPath: directory)
.contentsOfDirectory(atPath: directory.path())
.filter { $0.hasSuffix("-cert.pub") }
.isEmpty == false
} catch {
@ -65,7 +65,7 @@ public final class PublicKeyFileStoreController: Sendable {
/// - Warning: This method returning a path does not imply that a key has a SSH certificates. This method only describes where it will be.
public func sshCertificatePath<SecretType: Secret>(for secret: SecretType) -> String {
let minimalHex = keyWriter.openSSHMD5Fingerprint(secret: secret).replacingOccurrences(of: ":", with: "")
return directory.appending("/").appending("\(minimalHex)-cert.pub")
return directory.appending(component: "\(minimalHex)-cert.pub").path()
}
}

View File

@ -21,7 +21,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}()
private let updater = Updater(checkOnLaunch: true)
private let notifier = Notifier()
private let publicKeyFileStoreController = PublicKeyFileStoreController(homeDirectory: NSHomeDirectory())
private let publicKeyFileStoreController = PublicKeyFileStoreController(homeDirectory: URL.homeDirectory)
private lazy var agent: Agent = {
Agent(storeList: storeList, witness: notifier)
}()

View File

@ -71,11 +71,13 @@ struct IntegrationsView: View {
case .gettingStarted:
Text("TBD")
case .tool:
Section(selectedInstruction.tool) {
ConfigurationItemView(title: "Configuration File", value: selectedInstruction.configPath, action: .revealInFinder( selectedInstruction.configPath))
ConfigurationItemView(title: "Add This:", action: .copy(selectedInstruction.configText)) {
ForEach(selectedInstruction.steps) { stepGroup in
Section {
ConfigurationItemView(title: "Configuration File", value: stepGroup.path, action: .revealInFinder(stepGroup.path))
ForEach(stepGroup.steps, id: \.self) { step in
ConfigurationItemView(title: "Add This:", action: .copy(step)) {
HStack {
Text(selectedInstruction.configText)
Text(step)
.padding(8)
.font(.system(.subheadline, design: .monospaced))
Spacer()
@ -88,6 +90,8 @@ struct IntegrationsView: View {
}
}
}
}
}
if let url = selectedInstruction.website {
Section {
Link(destination: url) {
@ -140,27 +144,39 @@ struct ConfigurationGroup: Identifiable {
var instructions: [ConfigurationFileInstructions] = []
}
struct ConfigurationFileInstructions: Identifiable, Hashable {
struct ConfigurationFileInstructions: Hashable, Identifiable {
struct StepGroup: Hashable, Identifiable {
let path: String
let steps: [String]
var id: String { path }
}
var id: ID
var tool: String
var configPath: String
var configText: String
var steps: [StepGroup]
var website: URL?
var note: String?
init(tool: String, configPath: String, configText: String, website: URL? = nil) {
init(tool: String, configPath: String, configText: String, website: URL? = nil, note: String? = nil) {
self.id = .tool(tool)
self.tool = tool
self.configPath = configPath
self.configText = configText
self.steps = [StepGroup(path: configPath, steps: [configText])]
self.website = website
self.note = note
}
init(tool: String, steps: [StepGroup], website: URL? = nil, note: String? = nil) {
self.id = .tool(tool)
self.tool = tool
self.steps = steps
self.website = website
}
init(_ name: LocalizedStringResource, id: ID) {
self.id = id
tool = String(localized: name)
configPath = ""
configText = ""
self.steps = []
}
enum ID: Identifiable, Hashable {

View File

@ -6,7 +6,7 @@ struct SecretDetailView<SecretType: Secret>: View {
let secret: SecretType
private let keyWriter = OpenSSHPublicKeyWriter()
private let publicKeyFileStoreController = PublicKeyFileStoreController(homeDirectory: URL.agentHomePath)
private let publicKeyFileStoreController = PublicKeyFileStoreController(homeDirectory: URL.agentHomeURL)
var body: some View {
ScrollView {
@ -39,8 +39,8 @@ struct SecretDetailView<SecretType: Secret>: View {
extension URL {
static var agentHomePath: String {
URL.homeDirectory.path().replacingOccurrences(of: Bundle.hostBundleID, with: Bundle.agentBundleID)
static var agentHomeURL: URL {
URL(fileURLWithPath: URL.homeDirectory.path().replacingOccurrences(of: Bundle.hostBundleID, with: Bundle.agentBundleID))
}
}

View File

@ -141,7 +141,6 @@ extension SetupView {
}
#Preview {
SetupView(setupComplete: .constant(false))
}