diff --git a/Sources/Packages/Sources/SecretKit/PublicKeyStandinFileController.swift b/Sources/Packages/Sources/SecretKit/PublicKeyStandinFileController.swift index 3d84317..cbf40a6 100644 --- a/Sources/Packages/Sources/SecretKit/PublicKeyStandinFileController.swift +++ b/Sources/Packages/Sources/SecretKit/PublicKeyStandinFileController.swift @@ -15,15 +15,21 @@ public class PublicKeyFileStoreController { /// Writes out the keys specified to disk. /// - Parameter secrets: The Secrets to generate keys for. - /// - Parameter clear: Whether or not the directory should be erased before writing keys. + /// - Parameter clear: Whether or not any untracked files in the directory should be removed. public func generatePublicKeys(for secrets: [AnySecret], clear: Bool = false) throws { logger.log("Writing public keys to disk") if clear { - try? FileManager.default.removeItem(at: URL(fileURLWithPath: directory)) + let validPaths = Set(secrets.map { publicKeyPath(for: $0) }).union(Set(secrets.map { sshCertificatePath(for: $0) })) + let untracked = Set(try FileManager.default.contentsOfDirectory(atPath: directory) + .map { "\(directory)/\($0)" }) + .subtracting(validPaths) + for path in untracked { + try? FileManager.default.removeItem(at: URL(fileURLWithPath: path)) + } } try? FileManager.default.createDirectory(at: URL(fileURLWithPath: directory), withIntermediateDirectories: false, attributes: nil) for secret in secrets { - let path = path(for: secret) + let path = publicKeyPath(for: secret) guard let data = keyWriter.openSSHString(secret: secret).data(using: .utf8) else { continue } FileManager.default.createFile(atPath: path, contents: data, attributes: nil) } @@ -34,9 +40,18 @@ public class PublicKeyFileStoreController { /// - Parameter secret: The Secret to return the path for. /// - Returns: The path to the Secret's public key. /// - 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 path(for secret: SecretType) -> String { + public func publicKeyPath(for secret: SecretType) -> String { let minimalHex = keyWriter.openSSHMD5Fingerprint(secret: secret).replacingOccurrences(of: ":", with: "") return directory.appending("/").appending("\(minimalHex).pub") } + /// The path for a Secret's SSH Certificate public key. + /// - Parameter secret: The Secret to return the path for. + /// - Returns: The path to the SSH Certificate public key. + /// - 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(for secret: SecretType) -> String { + let minimalHex = keyWriter.openSSHMD5Fingerprint(secret: secret).replacingOccurrences(of: ":", with: "") + return directory.appending("/").appending("\(minimalHex)-cert.pub") + } + } diff --git a/Sources/Secretive/Views/SecretDetailView.swift b/Sources/Secretive/Views/SecretDetailView.swift index d756679..52978d7 100644 --- a/Sources/Secretive/Views/SecretDetailView.swift +++ b/Sources/Secretive/Views/SecretDetailView.swift @@ -21,7 +21,7 @@ struct SecretDetailView: View { CopyableView(title: "Public Key", image: Image(systemName: "key"), text: keyString) Spacer() .frame(height: 20) - CopyableView(title: "Public Key Path", image: Image(systemName: "lock.doc"), text: publicKeyFileStoreController.path(for: secret)) + CopyableView(title: "Public Key Path", image: Image(systemName: "lock.doc"), text: publicKeyFileStoreController.publicKeyPath(for: secret)) Spacer() } }