Skip to content

Commit

Permalink
Add option to rename keys/secrets (#216)
Browse files Browse the repository at this point in the history
* Add option to rename secrets

* Address PR comments

Co-authored-by: Max Goedjen <[email protected]>
  • Loading branch information
Dylan Lundy and maxgoedjen authored Jun 1, 2021
1 parent cd965b9 commit 8114acf
Show file tree
Hide file tree
Showing 10 changed files with 159 additions and 57 deletions.
5 changes: 5 additions & 0 deletions SecretKit/Common/Erasers/AnySecretStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,12 @@ public class AnySecretStoreModifiable: AnySecretStore, SecretStoreModifiable {

private let _create: (String, Bool) throws -> Void
private let _delete: (AnySecret) throws -> Void
private let _update: (AnySecret, String) throws -> Void

public init<SecretStoreType>(modifiable secretStore: SecretStoreType) where SecretStoreType: SecretStoreModifiable {
_create = { try secretStore.create(name: $0, requiresAuthentication: $1) }
_delete = { try secretStore.delete(secret: $0.base as! SecretStoreType.SecretType) }
_update = { try secretStore.update(secret: $0.base as! SecretStoreType.SecretType, name: $1) }
super.init(secretStore)
}

Expand All @@ -64,4 +66,7 @@ public class AnySecretStoreModifiable: AnySecretStore, SecretStoreModifiable {
try _delete(secret)
}

public func update(secret: AnySecret, name: String) throws {
try _update(secret, name)
}
}
1 change: 1 addition & 0 deletions SecretKit/Common/Types/SecretStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public protocol SecretStoreModifiable: SecretStore {

func create(name: String, requiresAuthentication: Bool) throws
func delete(secret: SecretType) throws
func update(secret: SecretType, name: String) throws

}

Expand Down
19 changes: 18 additions & 1 deletion SecretKit/SecureEnclave/SecureEnclaveStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,31 @@ extension SecureEnclave {
let deleteAttributes = [
kSecClass: kSecClassKey,
kSecAttrApplicationLabel: secret.id as CFData
] as CFDictionary
] as CFDictionary
let status = SecItemDelete(deleteAttributes)
if status != errSecSuccess {
throw KeychainError(statusCode: status)
}
reloadSecrets()
}

public func update(secret: Secret, name: String) throws {
let updateQuery = [
kSecClass: kSecClassKey,
kSecAttrApplicationLabel: secret.id as CFData
] as CFDictionary

let updatedAttributes = [
kSecAttrLabel: name,
] as CFDictionary

let status = SecItemUpdate(updateQuery, updatedAttributes)
if status != errSecSuccess {
throw KeychainError(statusCode: status)
}
reloadSecrets()
}

public func sign(data: Data, with secret: SecretType, for provenance: SigningRequestProvenance) throws -> Data {
let context = LAContext()
context.localizedReason = "sign a request from \"\(provenance.origin.displayName)\" using secret \"\(secret.name)\""
Expand Down
12 changes: 8 additions & 4 deletions Secretive.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
objects = {

/* Begin PBXBuildFile section */
2C4A9D2F2636FFD3008CC8E2 /* RenameSecretView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C4A9D2E2636FFD3008CC8E2 /* RenameSecretView.swift */; };
50020BB024064869003D4025 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50020BAF24064869003D4025 /* AppDelegate.swift */; };
50153E20250AFCB200525160 /* UpdateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50153E1F250AFCB200525160 /* UpdateView.swift */; };
50153E22250DECA300525160 /* SecretListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50153E21250DECA300525160 /* SecretListView.swift */; };
50153E22250DECA300525160 /* SecretListItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50153E21250DECA300525160 /* SecretListItemView.swift */; };
5018F54F24064786002EB505 /* Notifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5018F54E24064786002EB505 /* Notifier.swift */; };
501B7AE1251C56F700776EC7 /* SigningRequestProvenance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507CE4F32420A8C10029F750 /* SigningRequestProvenance.swift */; };
50524B442420969E008DBD97 /* OpenSSHWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50524B432420969D008DBD97 /* OpenSSHWriterTests.swift */; };
Expand Down Expand Up @@ -222,9 +223,10 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
2C4A9D2E2636FFD3008CC8E2 /* RenameSecretView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenameSecretView.swift; sourceTree = "<group>"; };
50020BAF24064869003D4025 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
50153E1F250AFCB200525160 /* UpdateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateView.swift; sourceTree = "<group>"; };
50153E21250DECA300525160 /* SecretListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretListView.swift; sourceTree = "<group>"; };
50153E21250DECA300525160 /* SecretListItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretListItemView.swift; sourceTree = "<group>"; };
5018F54E24064786002EB505 /* Notifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifier.swift; sourceTree = "<group>"; };
50524B432420969D008DBD97 /* OpenSSHWriterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenSSHWriterTests.swift; sourceTree = "<group>"; };
50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JustUpdatedChecker.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -541,10 +543,11 @@
children = (
50617D8423FCE48E0099B055 /* ContentView.swift */,
5079BA0E250F29BF00EA86F4 /* StoreListView.swift */,
50153E21250DECA300525160 /* SecretListView.swift */,
50153E21250DECA300525160 /* SecretListItemView.swift */,
50C385A42407A76D00AF2719 /* SecretDetailView.swift */,
5099A02323FD2AAA0062B6F2 /* CreateSecretView.swift */,
50B8550C24138C4F009958AC /* DeleteSecretView.swift */,
2C4A9D2E2636FFD3008CC8E2 /* RenameSecretView.swift */,
50BB046A2418AAAE00D6E079 /* EmptyStoreView.swift */,
506772C82425BB8500034DED /* NoStoresView.swift */,
50153E1F250AFCB200525160 /* UpdateView.swift */,
Expand Down Expand Up @@ -1005,6 +1008,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2C4A9D2F2636FFD3008CC8E2 /* RenameSecretView.swift in Sources */,
5091D2BC25183B830049FD9B /* ApplicationDirectoryController.swift in Sources */,
5066A6C22516F303004B5A36 /* SetupView.swift in Sources */,
50617D8523FCE48E0099B055 /* ContentView.swift in Sources */,
Expand All @@ -1022,7 +1026,7 @@
50BB046B2418AAAE00D6E079 /* EmptyStoreView.swift in Sources */,
50617D8323FCE48E0099B055 /* App.swift in Sources */,
506772C92425BB8500034DED /* NoStoresView.swift in Sources */,
50153E22250DECA300525160 /* SecretListView.swift in Sources */,
50153E22250DECA300525160 /* SecretListItemView.swift in Sources */,
508A58B5241ED48F0069DC07 /* PreviewAgentStatusChecker.swift in Sources */,
508A58AA241E06B40069DC07 /* PreviewUpdater.swift in Sources */,
);
Expand Down
5 changes: 3 additions & 2 deletions Secretive/Preview Content/PreviewStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,17 @@ extension Preview {
}

class StoreModifiable: Store, SecretStoreModifiable {

override var name: String { "Modifiable Preview Store" }

func create(name: String, requiresAuthentication: Bool) throws {
}

func delete(secret: Preview.Secret) throws {
}
}

func update(secret: Preview.Secret, name: String) throws {
}
}
}

extension Preview {
Expand Down
6 changes: 3 additions & 3 deletions Secretive/Views/DeleteSecretView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@ struct DeleteSecretView<StoreType: SecretStoreModifiable>: View {
TextField(secret.name, text: $confirm)
}
}
.onExitCommand {
dismissalBlock(false)
}
}
HStack {
Spacer()
Expand All @@ -47,6 +44,9 @@ struct DeleteSecretView<StoreType: SecretStoreModifiable>: View {
}
.padding()
.frame(minWidth: 400)
.onExitCommand {
dismissalBlock(false)
}
}

func delete() {
Expand Down
50 changes: 50 additions & 0 deletions Secretive/Views/RenameSecretView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import SwiftUI
import SecretKit

struct RenameSecretView<StoreType: SecretStoreModifiable>: View {

@ObservedObject var store: StoreType
let secret: StoreType.SecretType
var dismissalBlock: (_ renamed: Bool) -> ()

@State private var newName = ""

var body: some View {
VStack {
HStack {
Image(nsImage: NSApp.applicationIconImage)
.resizable()
.frame(width: 64, height: 64)
.padding()
VStack {
HStack {
Text("Type your new name for \"\(secret.name)\" below.")
Spacer()
}
HStack {
TextField(secret.name, text: $newName).focusable()
}
}
}
HStack {
Spacer()
Button("Rename", action: rename)
.disabled(newName.count == 0)
.keyboardShortcut(.return)
Button("Cancel") {
dismissalBlock(false)
}.keyboardShortcut(.cancelAction)
}
}
.padding()
.frame(minWidth: 400)
.onExitCommand {
dismissalBlock(false)
}
}

func rename() {
try? store.update(secret: secret, name: newName)
dismissalBlock(true)
}
}
54 changes: 54 additions & 0 deletions Secretive/Views/SecretListItemView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import SwiftUI
import SecretKit

struct SecretListItemView: View {

@ObservedObject var store: AnySecretStore
var secret: AnySecret
@Binding var activeSecret: AnySecret.ID?

@State var isDeleting: Bool = false
@State var isRenaming: Bool = false

var deletedSecret: (AnySecret) -> Void
var renamedSecret: (AnySecret) -> Void

var body: some View {
let showingPopupWrapped = Binding(
get: { isDeleting || isRenaming },
set: { if $0 == false { isDeleting = false; isRenaming = false } }
)

return NavigationLink(destination: SecretDetailView(secret: secret), tag: secret.id, selection: $activeSecret) {
Text(secret.name)
}.contextMenu {
if store is AnySecretStoreModifiable {
Button(action: { isRenaming = true }) {
Text("Rename")
}
Button(action: { isDeleting = true }) {
Text("Delete")
}
}
}
.popover(isPresented: showingPopupWrapped) {
if let modifiable = store as? AnySecretStoreModifiable {
if isDeleting {
DeleteSecretView(store: modifiable, secret: secret) { deleted in
isDeleting = false
if deleted {
deletedSecret(secret)
}
}
} else if isRenaming {
RenameSecretView(store: modifiable, secret: secret) { renamed in
isRenaming = false
if renamed {
renamedSecret(secret)
}
}
}
}
}
}
}
41 changes: 0 additions & 41 deletions Secretive/Views/SecretListView.swift

This file was deleted.

23 changes: 17 additions & 6 deletions Secretive/Views/StoreListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,17 @@ struct StoreListView: View {
@Binding var showingCreation: Bool

@State private var activeSecret: AnySecret.ID?
@State private var deletingSecret: AnySecret?

@EnvironmentObject private var storeList: SecretStoreList

private func secretDeleted(secret: AnySecret) {
activeSecret = nextDefaultSecret
}

private func secretRenamed(secret: AnySecret) {
activeSecret = nextDefaultSecret
}

var body: some View {
NavigationView {
List(selection: $activeSecret) {
Expand All @@ -19,9 +26,15 @@ struct StoreListView: View {
if store.secrets.isEmpty {
EmptyStoreView(store: store, activeSecret: $activeSecret)
} else {
SecretListView(store: store, activeSecret: $activeSecret, deletingSecret: $deletingSecret, deletedSecret: { _ in
activeSecret = nextDefaultSecret
})
ForEach(store.secrets) { secret in
SecretListItemView(
store: store,
secret: secret,
activeSecret: $activeSecret,
deletedSecret: self.secretDeleted,
renamedSecret: self.secretRenamed
)
}
}
}
}
Expand All @@ -33,9 +46,7 @@ struct StoreListView: View {
}
.frame(minWidth: 100, idealWidth: 240)
}

}

}

extension StoreListView {
Expand Down

0 comments on commit 8114acf

Please sign in to comment.