mirror of
git://jb55.com/damus
synced 2024-10-01 17:30:44 +00:00
Replace Vault dependency with @KeychainStorage property wrapper
Changelog-Changed: replace Vault dependency with @KeychainStorage property wrapper Closes: #1076
This commit is contained in:
parent
27fb4e797d
commit
e4860f3ba8
@ -258,6 +258,8 @@
|
|||||||
4CFF8F6D29CD022E008DB934 /* WideEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6C29CD022E008DB934 /* WideEventView.swift */; };
|
4CFF8F6D29CD022E008DB934 /* WideEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6C29CD022E008DB934 /* WideEventView.swift */; };
|
||||||
4FE60CDD295E1C5E00105A1F /* Wallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE60CDC295E1C5E00105A1F /* Wallet.swift */; };
|
4FE60CDD295E1C5E00105A1F /* Wallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE60CDC295E1C5E00105A1F /* Wallet.swift */; };
|
||||||
50088DA129E8271A008A1FDF /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50088DA029E8271A008A1FDF /* WebSocket.swift */; };
|
50088DA129E8271A008A1FDF /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50088DA029E8271A008A1FDF /* WebSocket.swift */; };
|
||||||
|
501F8C802A0220E1001AFC1D /* KeychainStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 501F8C7F2A0220E1001AFC1D /* KeychainStorage.swift */; };
|
||||||
|
501F8C822A0224EB001AFC1D /* KeychainStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 501F8C812A0224EB001AFC1D /* KeychainStorageTests.swift */; };
|
||||||
50A50A8D29A09E1C00C01BE7 /* RequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */; };
|
50A50A8D29A09E1C00C01BE7 /* RequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */; };
|
||||||
50B5685329F97CB400A23243 /* CredentialHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B5685229F97CB400A23243 /* CredentialHandler.swift */; };
|
50B5685329F97CB400A23243 /* CredentialHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B5685229F97CB400A23243 /* CredentialHandler.swift */; };
|
||||||
5C42E78C29DB76D90086AAC1 /* EmptyUserSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C42E78B29DB76D90086AAC1 /* EmptyUserSearchView.swift */; };
|
5C42E78C29DB76D90086AAC1 /* EmptyUserSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C42E78B29DB76D90086AAC1 /* EmptyUserSearchView.swift */; };
|
||||||
@ -268,7 +270,6 @@
|
|||||||
643EA5C8296B764E005081BB /* RelayFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 643EA5C7296B764E005081BB /* RelayFilterView.swift */; };
|
643EA5C8296B764E005081BB /* RelayFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 643EA5C7296B764E005081BB /* RelayFilterView.swift */; };
|
||||||
647D9A8D2968520300A295DE /* SideMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 647D9A8C2968520300A295DE /* SideMenuView.swift */; };
|
647D9A8D2968520300A295DE /* SideMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 647D9A8C2968520300A295DE /* SideMenuView.swift */; };
|
||||||
64FBD06F296255C400D9D3B2 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64FBD06E296255C400D9D3B2 /* Theme.swift */; };
|
64FBD06F296255C400D9D3B2 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64FBD06E296255C400D9D3B2 /* Theme.swift */; };
|
||||||
6C7DE41F2955169800E66263 /* Vault in Frameworks */ = {isa = PBXBuildFile; productRef = 6C7DE41E2955169800E66263 /* Vault */; };
|
|
||||||
7C0F392F29B57CAF0039859C /* Binding+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C0F392E29B57CAF0039859C /* Binding+.swift */; };
|
7C0F392F29B57CAF0039859C /* Binding+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C0F392E29B57CAF0039859C /* Binding+.swift */; };
|
||||||
7C60CAEF298471A1009C80D6 /* CoreSVG.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C60CAEE298471A1009C80D6 /* CoreSVG.swift */; };
|
7C60CAEF298471A1009C80D6 /* CoreSVG.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C60CAEE298471A1009C80D6 /* CoreSVG.swift */; };
|
||||||
7C902AE32981D55B002AB16E /* ZoomableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C902AE22981D55B002AB16E /* ZoomableScrollView.swift */; };
|
7C902AE32981D55B002AB16E /* ZoomableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C902AE22981D55B002AB16E /* ZoomableScrollView.swift */; };
|
||||||
@ -681,6 +682,8 @@
|
|||||||
4CFF8F6C29CD022E008DB934 /* WideEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WideEventView.swift; sourceTree = "<group>"; };
|
4CFF8F6C29CD022E008DB934 /* WideEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WideEventView.swift; sourceTree = "<group>"; };
|
||||||
4FE60CDC295E1C5E00105A1F /* Wallet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Wallet.swift; sourceTree = "<group>"; };
|
4FE60CDC295E1C5E00105A1F /* Wallet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Wallet.swift; sourceTree = "<group>"; };
|
||||||
50088DA029E8271A008A1FDF /* WebSocket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebSocket.swift; sourceTree = "<group>"; };
|
50088DA029E8271A008A1FDF /* WebSocket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebSocket.swift; sourceTree = "<group>"; };
|
||||||
|
501F8C7F2A0220E1001AFC1D /* KeychainStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainStorage.swift; sourceTree = "<group>"; };
|
||||||
|
501F8C812A0224EB001AFC1D /* KeychainStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainStorageTests.swift; sourceTree = "<group>"; };
|
||||||
50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestTests.swift; sourceTree = "<group>"; };
|
50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestTests.swift; sourceTree = "<group>"; };
|
||||||
50B5685229F97CB400A23243 /* CredentialHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialHandler.swift; sourceTree = "<group>"; };
|
50B5685229F97CB400A23243 /* CredentialHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialHandler.swift; sourceTree = "<group>"; };
|
||||||
5C42E78B29DB76D90086AAC1 /* EmptyUserSearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyUserSearchView.swift; sourceTree = "<group>"; };
|
5C42E78B29DB76D90086AAC1 /* EmptyUserSearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyUserSearchView.swift; sourceTree = "<group>"; };
|
||||||
@ -720,7 +723,6 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */,
|
4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */,
|
||||||
6C7DE41F2955169800E66263 /* Vault in Frameworks */,
|
|
||||||
4C649881286E0EE300EAE2B3 /* secp256k1 in Frameworks */,
|
4C649881286E0EE300EAE2B3 /* secp256k1 in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@ -1035,6 +1037,7 @@
|
|||||||
4C363AA728297703006E126D /* InsertSort.swift */,
|
4C363AA728297703006E126D /* InsertSort.swift */,
|
||||||
4C477C9D282C3A4800033AA3 /* TipCounter.swift */,
|
4C477C9D282C3A4800033AA3 /* TipCounter.swift */,
|
||||||
4C285C8B28398BC6008A31F1 /* Keys.swift */,
|
4C285C8B28398BC6008A31F1 /* Keys.swift */,
|
||||||
|
501F8C7F2A0220E1001AFC1D /* KeychainStorage.swift */,
|
||||||
4C90BD19283AA67F008EE7EF /* Bech32.swift */,
|
4C90BD19283AA67F008EE7EF /* Bech32.swift */,
|
||||||
4C216F352870A9A700040376 /* InputDismissKeyboard.swift */,
|
4C216F352870A9A700040376 /* InputDismissKeyboard.swift */,
|
||||||
3169CAEC294FCCFC00EE4006 /* Constants.swift */,
|
3169CAEC294FCCFC00EE4006 /* Constants.swift */,
|
||||||
@ -1271,6 +1274,7 @@
|
|||||||
3A3040F229A91366008A0F29 /* ProfileViewTests.swift */,
|
3A3040F229A91366008A0F29 /* ProfileViewTests.swift */,
|
||||||
3A30410029AB12AA008A0F29 /* EventGroupViewTests.swift */,
|
3A30410029AB12AA008A0F29 /* EventGroupViewTests.swift */,
|
||||||
4C8D00D329E3C5D40036AF10 /* NIP19Tests.swift */,
|
4C8D00D329E3C5D40036AF10 /* NIP19Tests.swift */,
|
||||||
|
501F8C812A0224EB001AFC1D /* KeychainStorageTests.swift */,
|
||||||
);
|
);
|
||||||
path = damusTests;
|
path = damusTests;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -1393,7 +1397,6 @@
|
|||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
4C649880286E0EE300EAE2B3 /* secp256k1 */,
|
4C649880286E0EE300EAE2B3 /* secp256k1 */,
|
||||||
4C06670328FC7EC500038D2A /* Kingfisher */,
|
4C06670328FC7EC500038D2A /* Kingfisher */,
|
||||||
6C7DE41E2955169800E66263 /* Vault */,
|
|
||||||
);
|
);
|
||||||
productName = damus;
|
productName = damus;
|
||||||
productReference = 4CE6DEE327F7A08100C66700 /* damus.app */;
|
productReference = 4CE6DEE327F7A08100C66700 /* damus.app */;
|
||||||
@ -1498,7 +1501,6 @@
|
|||||||
packageReferences = (
|
packageReferences = (
|
||||||
4C64987F286E0EE300EAE2B3 /* XCRemoteSwiftPackageReference "secp256k1" */,
|
4C64987F286E0EE300EAE2B3 /* XCRemoteSwiftPackageReference "secp256k1" */,
|
||||||
4C06670228FC7EC500038D2A /* XCRemoteSwiftPackageReference "Kingfisher" */,
|
4C06670228FC7EC500038D2A /* XCRemoteSwiftPackageReference "Kingfisher" */,
|
||||||
6C7DE41D2955169800E66263 /* XCRemoteSwiftPackageReference "Vault" */,
|
|
||||||
);
|
);
|
||||||
productRefGroup = 4CE6DEE427F7A08100C66700 /* Products */;
|
productRefGroup = 4CE6DEE427F7A08100C66700 /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
@ -1789,6 +1791,7 @@
|
|||||||
4C75EFA427FA577B0006080F /* PostView.swift in Sources */,
|
4C75EFA427FA577B0006080F /* PostView.swift in Sources */,
|
||||||
4C30AC7229A5677A00E2BD5A /* NotificationsView.swift in Sources */,
|
4C30AC7229A5677A00E2BD5A /* NotificationsView.swift in Sources */,
|
||||||
4C1A9A2129DDD3E100516EAC /* KeySettingsView.swift in Sources */,
|
4C1A9A2129DDD3E100516EAC /* KeySettingsView.swift in Sources */,
|
||||||
|
501F8C802A0220E1001AFC1D /* KeychainStorage.swift in Sources */,
|
||||||
4C1A9A1D29DDCF9B00516EAC /* NotificationSettingsView.swift in Sources */,
|
4C1A9A1D29DDCF9B00516EAC /* NotificationSettingsView.swift in Sources */,
|
||||||
4C75EFB528049D790006080F /* Relay.swift in Sources */,
|
4C75EFB528049D790006080F /* Relay.swift in Sources */,
|
||||||
4CEE2AF1280B216B00AB5EEF /* EventDetailView.swift in Sources */,
|
4CEE2AF1280B216B00AB5EEF /* EventDetailView.swift in Sources */,
|
||||||
@ -1811,6 +1814,7 @@
|
|||||||
3A3040ED29A5CB86008A0F29 /* ReplyDescriptionTests.swift in Sources */,
|
3A3040ED29A5CB86008A0F29 /* ReplyDescriptionTests.swift in Sources */,
|
||||||
4C8D00D429E3C5D40036AF10 /* NIP19Tests.swift in Sources */,
|
4C8D00D429E3C5D40036AF10 /* NIP19Tests.swift in Sources */,
|
||||||
3A30410129AB12AA008A0F29 /* EventGroupViewTests.swift in Sources */,
|
3A30410129AB12AA008A0F29 /* EventGroupViewTests.swift in Sources */,
|
||||||
|
501F8C822A0224EB001AFC1D /* KeychainStorageTests.swift in Sources */,
|
||||||
3ACBCB78295FE5C70037388A /* TimeAgoTests.swift in Sources */,
|
3ACBCB78295FE5C70037388A /* TimeAgoTests.swift in Sources */,
|
||||||
DD597CBD2963D85A00C64D32 /* MarkdownTests.swift in Sources */,
|
DD597CBD2963D85A00C64D32 /* MarkdownTests.swift in Sources */,
|
||||||
3A3040EF29A8FEE9008A0F29 /* EventDetailBarTests.swift in Sources */,
|
3A3040EF29A8FEE9008A0F29 /* EventDetailBarTests.swift in Sources */,
|
||||||
@ -2307,14 +2311,6 @@
|
|||||||
revision = 40b4b38b3b1c83f7088c76189a742870e0ca06a9;
|
revision = 40b4b38b3b1c83f7088c76189a742870e0ca06a9;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
6C7DE41D2955169800E66263 /* XCRemoteSwiftPackageReference "Vault" */ = {
|
|
||||||
isa = XCRemoteSwiftPackageReference;
|
|
||||||
repositoryURL = "https://github.com/SparrowTek/Vault";
|
|
||||||
requirement = {
|
|
||||||
kind = upToNextMajorVersion;
|
|
||||||
minimumVersion = 1.0.0;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
/* End XCRemoteSwiftPackageReference section */
|
/* End XCRemoteSwiftPackageReference section */
|
||||||
|
|
||||||
/* Begin XCSwiftPackageProductDependency section */
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
@ -2328,11 +2324,6 @@
|
|||||||
package = 4C64987F286E0EE300EAE2B3 /* XCRemoteSwiftPackageReference "secp256k1" */;
|
package = 4C64987F286E0EE300EAE2B3 /* XCRemoteSwiftPackageReference "secp256k1" */;
|
||||||
productName = secp256k1;
|
productName = secp256k1;
|
||||||
};
|
};
|
||||||
6C7DE41E2955169800E66263 /* Vault */ = {
|
|
||||||
isa = XCSwiftPackageProductDependency;
|
|
||||||
package = 6C7DE41D2955169800E66263 /* XCRemoteSwiftPackageReference "Vault" */;
|
|
||||||
productName = Vault;
|
|
||||||
};
|
|
||||||
/* End XCSwiftPackageProductDependency section */
|
/* End XCSwiftPackageProductDependency section */
|
||||||
};
|
};
|
||||||
rootObject = 4CE6DEDB27F7A08100C66700 /* Project object */;
|
rootObject = 4CE6DEDB27F7A08100C66700 /* Project object */;
|
||||||
|
@ -16,15 +16,6 @@
|
|||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "40b4b38b3b1c83f7088c76189a742870e0ca06a9"
|
"revision" : "40b4b38b3b1c83f7088c76189a742870e0ca06a9"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "vault",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/SparrowTek/Vault",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "87db56c3c8b6421c65b0745f73e08b0dc56f79d4",
|
|
||||||
"version" : "1.0.3"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"version" : 2
|
"version" : 2
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import Vault
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
let fallback_zap_amount = 1000
|
let fallback_zap_amount = 1000
|
||||||
@ -160,16 +159,11 @@ class UserSettingsStore: ObservableObject {
|
|||||||
var deepl_plan: DeepLPlan
|
var deepl_plan: DeepLPlan
|
||||||
|
|
||||||
var deepl_api_key: String {
|
var deepl_api_key: String {
|
||||||
didSet {
|
get {
|
||||||
do {
|
return internal_deepl_api_key ?? ""
|
||||||
if deepl_api_key == "" {
|
}
|
||||||
try clearDeepLApiKey()
|
set {
|
||||||
} else {
|
internal_deepl_api_key = newValue == "" ? nil : newValue
|
||||||
try saveDeepLApiKey(deepl_api_key)
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// No-op.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,73 +173,34 @@ class UserSettingsStore: ObservableObject {
|
|||||||
@Setting(key: "libretranslate_url", default_value: "")
|
@Setting(key: "libretranslate_url", default_value: "")
|
||||||
var libretranslate_url: String
|
var libretranslate_url: String
|
||||||
|
|
||||||
@Setting(key: "libretranslate_api_key", default_value: "")
|
|
||||||
var libretranslate_api_key: String {
|
var libretranslate_api_key: String {
|
||||||
didSet {
|
get {
|
||||||
do {
|
return internal_libretranslate_api_key ?? ""
|
||||||
if libretranslate_api_key == "" {
|
}
|
||||||
try clearLibreTranslateApiKey()
|
set {
|
||||||
} else {
|
internal_libretranslate_api_key = newValue == "" ? nil : newValue
|
||||||
try saveLibreTranslateApiKey(libretranslate_api_key)
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// No-op.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Published var nokyctranslate_api_key: String {
|
var nokyctranslate_api_key: String {
|
||||||
didSet {
|
get {
|
||||||
do {
|
return internal_nokyctranslate_api_key ?? ""
|
||||||
if nokyctranslate_api_key == "" {
|
|
||||||
try clearNoKYCTranslateApiKey()
|
|
||||||
} else {
|
|
||||||
try saveNoKYCTranslateApiKey(nokyctranslate_api_key)
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// No-op.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
set {
|
||||||
|
internal_nokyctranslate_api_key = newValue == "" ? nil : newValue
|
||||||
init() {
|
|
||||||
do {
|
|
||||||
deepl_api_key = try Vault.getPrivateKey(keychainConfiguration: DamusDeepLKeychainConfiguration())
|
|
||||||
} catch {
|
|
||||||
deepl_api_key = ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
|
||||||
nokyctranslate_api_key = try Vault.getPrivateKey(keychainConfiguration: DamusNoKYCTranslateKeychainConfiguration())
|
|
||||||
} catch {
|
|
||||||
nokyctranslate_api_key = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private func saveLibreTranslateApiKey(_ apiKey: String) throws {
|
|
||||||
try Vault.savePrivateKey(apiKey, keychainConfiguration: DamusLibreTranslateKeychainConfiguration())
|
|
||||||
}
|
|
||||||
|
|
||||||
private func clearLibreTranslateApiKey() throws {
|
|
||||||
try Vault.deletePrivateKey(keychainConfiguration: DamusLibreTranslateKeychainConfiguration())
|
|
||||||
}
|
|
||||||
|
|
||||||
private func saveNoKYCTranslateApiKey(_ apiKey: String) throws {
|
|
||||||
try Vault.savePrivateKey(apiKey, keychainConfiguration: DamusNoKYCTranslateKeychainConfiguration())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func clearNoKYCTranslateApiKey() throws {
|
// These internal keys are necessary because entries in the keychain need to be Optional,
|
||||||
try Vault.deletePrivateKey(keychainConfiguration: DamusNoKYCTranslateKeychainConfiguration())
|
// but the translation view needs non-Optional String in order to use them as Bindings.
|
||||||
}
|
@KeychainStorage(account: "deepl_apikey")
|
||||||
|
var internal_deepl_api_key: String?
|
||||||
|
|
||||||
private func saveDeepLApiKey(_ apiKey: String) throws {
|
@KeychainStorage(account: "nokyctranslate_apikey")
|
||||||
try Vault.savePrivateKey(apiKey, keychainConfiguration: DamusDeepLKeychainConfiguration())
|
var internal_nokyctranslate_api_key: String?
|
||||||
}
|
|
||||||
|
@KeychainStorage(account: "libretranslate_apikey")
|
||||||
private func clearDeepLApiKey() throws {
|
var internal_libretranslate_api_key: String?
|
||||||
try Vault.deletePrivateKey(keychainConfiguration: DamusDeepLKeychainConfiguration())
|
|
||||||
}
|
|
||||||
|
|
||||||
var can_translate: Bool {
|
var can_translate: Bool {
|
||||||
switch translation_service {
|
switch translation_service {
|
||||||
@ -254,31 +209,13 @@ class UserSettingsStore: ObservableObject {
|
|||||||
case .libretranslate:
|
case .libretranslate:
|
||||||
return URLComponents(string: libretranslate_url) != nil
|
return URLComponents(string: libretranslate_url) != nil
|
||||||
case .deepl:
|
case .deepl:
|
||||||
return deepl_api_key != ""
|
return internal_deepl_api_key != nil
|
||||||
case .nokyctranslate:
|
case .nokyctranslate:
|
||||||
return nokyctranslate_api_key != ""
|
return internal_nokyctranslate_api_key != nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DamusLibreTranslateKeychainConfiguration: KeychainConfiguration {
|
|
||||||
var serviceName = "damus"
|
|
||||||
var accessGroup: String? = nil
|
|
||||||
var accountName = "libretranslate_apikey"
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DamusDeepLKeychainConfiguration: KeychainConfiguration {
|
|
||||||
var serviceName = "damus"
|
|
||||||
var accessGroup: String? = nil
|
|
||||||
var accountName = "deepl_apikey"
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DamusNoKYCTranslateKeychainConfiguration: KeychainConfiguration {
|
|
||||||
var serviceName = "damus"
|
|
||||||
var accessGroup: String? = nil
|
|
||||||
var accountName = "nokyctranslate_apikey"
|
|
||||||
}
|
|
||||||
|
|
||||||
func pk_setting_key(_ pubkey: String, key: String) -> String {
|
func pk_setting_key(_ pubkey: String, key: String) -> String {
|
||||||
return "\(pubkey)_\(key)"
|
return "\(pubkey)_\(key)"
|
||||||
}
|
}
|
||||||
|
73
damus/Util/KeychainStorage.swift
Normal file
73
damus/Util/KeychainStorage.swift
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
//
|
||||||
|
// KeychainStorage.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by Bryan Montz on 5/2/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Security
|
||||||
|
|
||||||
|
@propertyWrapper struct KeychainStorage {
|
||||||
|
let account: String
|
||||||
|
private let service = "damus"
|
||||||
|
|
||||||
|
var wrappedValue: String? {
|
||||||
|
get {
|
||||||
|
let query = [
|
||||||
|
kSecAttrService: service,
|
||||||
|
kSecAttrAccount: account,
|
||||||
|
kSecClass: kSecClassGenericPassword,
|
||||||
|
kSecReturnData: true,
|
||||||
|
kSecMatchLimit: kSecMatchLimitOne
|
||||||
|
] as [CFString: Any] as CFDictionary
|
||||||
|
|
||||||
|
var result: AnyObject?
|
||||||
|
let status = SecItemCopyMatching(query, &result)
|
||||||
|
|
||||||
|
if status == errSecSuccess, let data = result as? Data {
|
||||||
|
return String(data: data, encoding: .utf8)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if let newValue {
|
||||||
|
let query = [
|
||||||
|
kSecAttrService: service,
|
||||||
|
kSecAttrAccount: account,
|
||||||
|
kSecClass: kSecClassGenericPassword,
|
||||||
|
kSecValueData: newValue.data(using: .utf8) as Any
|
||||||
|
] as [CFString: Any] as CFDictionary
|
||||||
|
|
||||||
|
var status = SecItemAdd(query, nil)
|
||||||
|
|
||||||
|
if status == errSecDuplicateItem {
|
||||||
|
let query = [
|
||||||
|
kSecAttrService: service,
|
||||||
|
kSecAttrAccount: account,
|
||||||
|
kSecClass: kSecClassGenericPassword
|
||||||
|
] as [CFString: Any] as CFDictionary
|
||||||
|
|
||||||
|
let updates = [
|
||||||
|
kSecValueData: newValue.data(using: .utf8) as Any
|
||||||
|
] as CFDictionary
|
||||||
|
|
||||||
|
status = SecItemUpdate(query, updates)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let query = [
|
||||||
|
kSecAttrService: service,
|
||||||
|
kSecAttrAccount: account,
|
||||||
|
kSecClass: kSecClassGenericPassword
|
||||||
|
] as [CFString: Any] as CFDictionary
|
||||||
|
|
||||||
|
_ = SecItemDelete(query)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init(account: String) {
|
||||||
|
self.account = account
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import secp256k1
|
import secp256k1
|
||||||
import Vault
|
|
||||||
|
|
||||||
let PUBKEY_HRP = "npub"
|
let PUBKEY_HRP = "npub"
|
||||||
let PRIVKEY_HRP = "nsec"
|
let PRIVKEY_HRP = "nsec"
|
||||||
@ -44,12 +43,6 @@ enum Bech32Key {
|
|||||||
case sec(String)
|
case sec(String)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DamusKeychainConfiguration: KeychainConfiguration {
|
|
||||||
var serviceName = "damus"
|
|
||||||
var accessGroup: String? = nil
|
|
||||||
var accountName = "privkey"
|
|
||||||
}
|
|
||||||
|
|
||||||
func decode_bech32_key(_ key: String) -> Bech32Key? {
|
func decode_bech32_key(_ key: String) -> Bech32Key? {
|
||||||
guard let decoded = try? bech32_decode(key) else {
|
guard let decoded = try? bech32_decode(key) else {
|
||||||
return nil
|
return nil
|
||||||
@ -114,12 +107,17 @@ func save_pubkey(pubkey: String) {
|
|||||||
UserDefaults.standard.set(pubkey, forKey: "pubkey")
|
UserDefaults.standard.set(pubkey, forKey: "pubkey")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum Keys {
|
||||||
|
@KeychainStorage(account: "privkey")
|
||||||
|
static var privkey: String?
|
||||||
|
}
|
||||||
|
|
||||||
func save_privkey(privkey: String) throws {
|
func save_privkey(privkey: String) throws {
|
||||||
try Vault.savePrivateKey(privkey, keychainConfiguration: DamusKeychainConfiguration())
|
Keys.privkey = privkey
|
||||||
}
|
}
|
||||||
|
|
||||||
func clear_saved_privkey() throws {
|
func clear_saved_privkey() throws {
|
||||||
try Vault.deletePrivateKey(keychainConfiguration: DamusKeychainConfiguration())
|
Keys.privkey = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func clear_saved_pubkey() {
|
func clear_saved_pubkey() {
|
||||||
@ -154,7 +152,7 @@ func get_saved_pubkey() -> String? {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func get_saved_privkey() -> String? {
|
func get_saved_privkey() -> String? {
|
||||||
let mkey = try? Vault.getPrivateKey(keychainConfiguration: DamusKeychainConfiguration());
|
let mkey = Keys.privkey
|
||||||
return mkey.map { $0.trimmingCharacters(in: .whitespaces) }
|
return mkey.map { $0.trimmingCharacters(in: .whitespaces) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,9 +79,7 @@ struct TranslationSettingsView: View {
|
|||||||
if settings.translation_service != .none {
|
if settings.translation_service != .none {
|
||||||
Toggle(NSLocalizedString("Automatically translate notes", comment: "Toggle to automatically translate notes."), isOn: $settings.auto_translate)
|
Toggle(NSLocalizedString("Automatically translate notes", comment: "Toggle to automatically translate notes."), isOn: $settings.auto_translate)
|
||||||
.toggleStyle(.switch)
|
.toggleStyle(.switch)
|
||||||
}
|
|
||||||
|
|
||||||
if settings.translation_service != .none {
|
|
||||||
Toggle(NSLocalizedString("Translate DMs", comment: "Toggle to translate direct messages."), isOn: $settings.translate_dms)
|
Toggle(NSLocalizedString("Translate DMs", comment: "Toggle to translate direct messages."), isOn: $settings.translate_dms)
|
||||||
.toggleStyle(.switch)
|
.toggleStyle(.switch)
|
||||||
}
|
}
|
||||||
@ -92,81 +90,6 @@ struct TranslationSettingsView: View {
|
|||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var libretranslate_view: some View {
|
|
||||||
VStack {
|
|
||||||
Picker(NSLocalizedString("Server", comment: "Prompt selection of LibreTranslate server to perform machine translations on notes"), selection: $settings.libretranslate_server) {
|
|
||||||
ForEach(LibreTranslateServer.allCases, id: \.self) { server in
|
|
||||||
Text(server.model.displayName)
|
|
||||||
.tag(server.model.tag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TextField(NSLocalizedString("URL", comment: "Example URL to LibreTranslate server"), text: $settings.libretranslate_url)
|
|
||||||
.disableAutocorrection(true)
|
|
||||||
.disabled(settings.libretranslate_server != .custom)
|
|
||||||
.autocapitalization(UITextAutocapitalizationType.none)
|
|
||||||
HStack {
|
|
||||||
let libretranslate_api_key_placeholder = NSLocalizedString("API Key (optional)", comment: "Prompt for optional entry of API Key to use translation server.")
|
|
||||||
if show_api_key {
|
|
||||||
TextField(libretranslate_api_key_placeholder, text: $settings.libretranslate_api_key)
|
|
||||||
.disableAutocorrection(true)
|
|
||||||
.autocapitalization(UITextAutocapitalizationType.none)
|
|
||||||
if settings.libretranslate_api_key != "" {
|
|
||||||
Button(NSLocalizedString("Hide API Key", comment: "Button to hide the LibreTranslate server API key.")) {
|
|
||||||
show_api_key = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
SecureField(libretranslate_api_key_placeholder, text: $settings.libretranslate_api_key)
|
|
||||||
.disableAutocorrection(true)
|
|
||||||
.autocapitalization(UITextAutocapitalizationType.none)
|
|
||||||
if settings.libretranslate_api_key != "" {
|
|
||||||
Button(NSLocalizedString("Show API Key", comment: "Button to show the LibreTranslate server API key.")) {
|
|
||||||
show_api_key = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var deepl_view: some View {
|
|
||||||
VStack {
|
|
||||||
Picker(NSLocalizedString("Plan", comment: "Prompt selection of DeepL subscription plan to perform machine translations on notes"), selection: $settings.deepl_plan) {
|
|
||||||
ForEach(DeepLPlan.allCases, id: \.self) { server in
|
|
||||||
Text(server.model.displayName)
|
|
||||||
.tag(server.model.tag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HStack {
|
|
||||||
let deepl_api_key_placeholder = NSLocalizedString("API Key (required)", comment: "Prompt for required entry of API Key to use translation server.")
|
|
||||||
if show_api_key {
|
|
||||||
TextField(deepl_api_key_placeholder, text: $settings.deepl_api_key)
|
|
||||||
.disableAutocorrection(true)
|
|
||||||
.autocapitalization(UITextAutocapitalizationType.none)
|
|
||||||
if settings.deepl_api_key != "" {
|
|
||||||
Button(NSLocalizedString("Hide API Key", comment: "Button to hide the DeepL translation API key.")) {
|
|
||||||
show_api_key = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
SecureField(deepl_api_key_placeholder, text: $settings.deepl_api_key)
|
|
||||||
.disableAutocorrection(true)
|
|
||||||
.autocapitalization(UITextAutocapitalizationType.none)
|
|
||||||
if settings.deepl_api_key != "" {
|
|
||||||
Button(NSLocalizedString("Show API Key", comment: "Button to show the DeepL translation API key.")) {
|
|
||||||
show_api_key = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if settings.deepl_api_key == "" {
|
|
||||||
Link(NSLocalizedString("Get API Key", comment: "Button to navigate to DeepL website to get a translation API key."), destination: URL(string: "https://www.deepl.com/pro-api")!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TranslationSettingsView_Previews: PreviewProvider {
|
struct TranslationSettingsView_Previews: PreviewProvider {
|
||||||
|
46
damusTests/KeychainStorageTests.swift
Normal file
46
damusTests/KeychainStorageTests.swift
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
//
|
||||||
|
// KeychainStorageTests.swift
|
||||||
|
// damusTests
|
||||||
|
//
|
||||||
|
// Created by Bryan Montz on 5/3/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
@testable import damus
|
||||||
|
import Security
|
||||||
|
|
||||||
|
final class KeychainStorageTests: XCTestCase {
|
||||||
|
@KeychainStorage(account: "test-keyname")
|
||||||
|
var secret: String?
|
||||||
|
|
||||||
|
override func tearDownWithError() throws {
|
||||||
|
secret = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testWriteToKeychain() throws {
|
||||||
|
// write a secret to the keychain using the property wrapper's setter
|
||||||
|
secret = "super-secure-key"
|
||||||
|
|
||||||
|
// verify it exists in the keychain using the property wrapper's getter
|
||||||
|
XCTAssertEqual(secret, "super-secure-key")
|
||||||
|
|
||||||
|
// verify it exists in the keychain directly
|
||||||
|
let query = [
|
||||||
|
kSecAttrService: "damus",
|
||||||
|
kSecAttrAccount: "test-keyname",
|
||||||
|
kSecClass: kSecClassGenericPassword,
|
||||||
|
kSecReturnData: true,
|
||||||
|
kSecMatchLimit: kSecMatchLimitOne
|
||||||
|
] as [CFString: Any] as CFDictionary
|
||||||
|
|
||||||
|
var result: AnyObject?
|
||||||
|
let status = SecItemCopyMatching(query, &result)
|
||||||
|
XCTAssertEqual(status, errSecSuccess)
|
||||||
|
|
||||||
|
let data = try XCTUnwrap(result as? Data)
|
||||||
|
let the_secret = String(data: data, encoding: .utf8)
|
||||||
|
|
||||||
|
XCTAssertEqual(the_secret, "super-secure-key")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user