Skip to content

Commit 300ba59

Browse files
committed
Refactor
Signed-off-by: Milen Pivchev <[email protected]>
1 parent c2b1a59 commit 300ba59

File tree

6 files changed

+97
-75
lines changed

6 files changed

+97
-75
lines changed

Nextcloud.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
D5B6AA7827200C7200D49C24 /* NCActivityTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */; };
9696
F30E77E92EAB716900B1EFAB /* CertificatePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = F30E77E82EAB716900B1EFAB /* CertificatePicker.swift */; };
9797
F30E77EC2EAB7C9B00B1EFAB /* DocumentPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = F30E77EB2EAB7C9800B1EFAB /* DocumentPicker.swift */; };
98+
F30E77EF2EAF9BCD00B1EFAB /* CertificatePickerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F30E77EE2EAF9BC700B1EFAB /* CertificatePickerModel.swift */; };
9899
F310B1EF2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = F310B1EE2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift */; };
99100
F314F1142A30E2DE00BC7FAB /* View+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E8A390295DC5E0006CB2D0 /* View+Extension.swift */; };
100101
F321DA8A2B71205A00DDA0E6 /* NCTrashSelectTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = F321DA892B71205A00DDA0E6 /* NCTrashSelectTabBar.swift */; };
@@ -1384,6 +1385,7 @@
13841385
D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCActivityTableViewCell.swift; sourceTree = "<group>"; };
13851386
F30E77E82EAB716900B1EFAB /* CertificatePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CertificatePicker.swift; sourceTree = "<group>"; };
13861387
F30E77EB2EAB7C9800B1EFAB /* DocumentPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentPicker.swift; sourceTree = "<group>"; };
1388+
F30E77EE2EAF9BC700B1EFAB /* CertificatePickerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CertificatePickerModel.swift; sourceTree = "<group>"; };
13871389
F310B1EE2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCViewerMedia+VisionKit.swift"; sourceTree = "<group>"; };
13881390
F321DA892B71205A00DDA0E6 /* NCTrashSelectTabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCTrashSelectTabBar.swift; sourceTree = "<group>"; };
13891391
F32FADA82D1176DE007035E2 /* UIButton+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIButton+Extension.swift"; sourceTree = "<group>"; };
@@ -2264,6 +2266,7 @@
22642266
F30E77EA2EAB7C1700B1EFAB /* CertificatePicker */ = {
22652267
isa = PBXGroup;
22662268
children = (
2269+
F30E77EE2EAF9BC700B1EFAB /* CertificatePickerModel.swift */,
22672270
F30E77EB2EAB7C9800B1EFAB /* DocumentPicker.swift */,
22682271
F30E77E82EAB716900B1EFAB /* CertificatePicker.swift */,
22692272
);
@@ -4856,6 +4859,7 @@
48564859
F75D90212D2BE26F003E740B /* NCRecommendationsCell.swift in Sources */,
48574860
F7E98C1627E0D0FC001F9F19 /* NCManageDatabase+Video.swift in Sources */,
48584861
F7F4F11227ECDC52008676F9 /* UIFont+Extension.swift in Sources */,
4862+
F30E77EF2EAF9BCD00B1EFAB /* CertificatePickerModel.swift in Sources */,
48594863
F76882222C0DD1E7001CF441 /* NCCapabilitiesView.swift in Sources */,
48604864
F3CA337D2D0B2B6C00672333 /* AlbumModel.swift in Sources */,
48614865
AF93471A27E2361E002537EE /* NCShareHeader.swift in Sources */,

Nextcloud.xcodeproj/xcshareddata/xcschemes/Nextcloud.xcscheme

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@
7070
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
7171
shouldUseLaunchSchemeArgsEnv = "NO"
7272
enableThreadSanitizer = "YES"
73-
codeCoverageEnabled = "YES">
73+
disableMainThreadChecker = "YES"
74+
codeCoverageEnabled = "YES"
75+
disablePerformanceAntipatternChecker = "YES">
7476
<MacroExpansion>
7577
<BuildableReference
7678
BuildableIdentifier = "primary"
@@ -119,12 +121,14 @@
119121
buildConfiguration = "Debug"
120122
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
121123
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
124+
disableMainThreadChecker = "YES"
122125
launchStyle = "0"
123126
useCustomWorkingDirectory = "NO"
124127
ignoresPersistentStateOnLaunch = "NO"
125128
debugDocumentVersioning = "YES"
126129
debugServiceExtension = "internal"
127-
allowLocationSimulation = "NO">
130+
allowLocationSimulation = "NO"
131+
disablePerformanceAntipatternChecker = "YES">
128132
<BuildableProductRunnable
129133
runnableDebuggingMode = "0">
130134
<BuildableReference

iOSClient/AppDelegate.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
3737
Task {
3838
await NCAccount().deleteAllAccounts()
3939
}
40-
}
40+
}`
4141
let utilityFileSystem = NCUtilityFileSystem()
4242
let utility = NCUtility()
4343

iOSClient/CertificatePicker/CertificatePicker.swift

Lines changed: 11 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,19 @@ struct CertificatePicker: View {
5252
.textContentType(.password)
5353
.autocorrectionDisabled()
5454
.textInputAutocapitalization(.never)
55+
.submitLabel(.done)
56+
.onSubmit {
57+
if let url = pickedURL {
58+
model.handleCertificate(fileUrl: url, urlBase: urlBase, password: password)
59+
}
60+
}
5561
}
5662
}
5763
}
5864
.onAppear {
5965
model.delegate = delegate
6066
}
61-
.navigationTitle("_cert_navigation_title_")
67+
.navigationTitle(NSLocalizedString("_cert_navigation_title_", comment: ""))
6268
.navigationBarTitleDisplayMode(.inline)
6369
.toolbar {
6470
ToolbarItem(placement: .cancellationAction) {
@@ -76,6 +82,7 @@ struct CertificatePicker: View {
7682
} label: {
7783
Image(systemName: "checkmark")
7884
}
85+
.keyboardShortcut(.defaultAction)
7986
.disabled(pickedURL == nil || password.isEmpty)
8087
.tint(Color(NCBrandColor.shared.customer))
8188
}
@@ -88,81 +95,14 @@ struct CertificatePicker: View {
8895
}
8996
}
9097
}
91-
.alert("_client_cert_wrong_password_", isPresented: $model.isWrongPassword) {}
92-
}
93-
}
94-
}
95-
96-
protocol CertificatePickerDelegate: AnyObject {
97-
func certificatePickerDidImportIdentity(_ picker: CertificatePickerModel, for urlBase: String)
98-
}
99-
100-
@Observable class CertificatePickerModel: NSObject, UIDocumentPickerDelegate {
101-
var isWrongPassword = false
102-
@ObservationIgnored weak var delegate: CertificatePickerDelegate?
103-
104-
func handleCertificate(fileUrl: URL, urlBase: String, password: String) {
105-
if fileUrl.startAccessingSecurityScopedResource() {
106-
defer {
107-
fileUrl.stopAccessingSecurityScopedResource()
108-
}
109-
110-
if let identity = getIdentityFromP12(from: fileUrl, password: password) {
111-
let urlWithoutScheme = urlBase.replacingOccurrences(of: "https://", with: "").replacingOccurrences(of: "http://", with: "")
112-
let label = "client_identity_\(urlWithoutScheme)"
113-
storeIdentityInKeychain(identity: identity, label: label)
114-
delegate?.certificatePickerDidImportIdentity(self, for: urlBase)
115-
} else {
116-
isWrongPassword = true
98+
.alert(NSLocalizedString("_client_cert_wrong_password_", comment: ""), isPresented: $model.isWrongPassword) {}
99+
.onChange(of: model.isCertImportedSuccessfully) { _, newValue in
100+
if newValue { dismiss() }
117101
}
118102
}
119103
}
120-
121-
func getIdentityFromP12(from url: URL, password: String) -> SecIdentity? {
122-
guard let p12Data = try? Data(contentsOf: url) else { return nil }
123-
124-
let options = [kSecImportExportPassphrase as String: password]
125-
var items: CFArray?
126-
let status = SecPKCS12Import(p12Data as CFData, options as CFDictionary, &items)
127-
128-
if status == errSecSuccess,
129-
let array = items as? [[String: Any]] {
130-
// swiftlint:disable force_cast
131-
if let identity = array.first?[kSecImportItemIdentity as String] as! SecIdentity? {
132-
// swiftlint:enable force_cast
133-
return identity
134-
}
135-
}
136-
return nil
137-
}
138-
139-
func storeIdentityInKeychain(identity: SecIdentity, label: String) {
140-
let addQuery: [String: Any] = [
141-
kSecValueRef as String: identity,
142-
kSecClass as String: kSecClassIdentity,
143-
kSecAttrLabel as String: label,
144-
kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock
145-
]
146-
147-
let classes = [kSecClassIdentity, kSecClassCertificate, kSecClassKey]
148-
for secClass in classes {
149-
let deleteQuery: [String: Any] = [
150-
kSecClass as String: secClass,
151-
kSecAttrLabel as String: label,
152-
kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock
153-
]
154-
let status = SecItemDelete(deleteQuery as CFDictionary)
155-
print("Deleting \(secClass): \(status)")
156-
}
157-
158-
let addStatus = SecItemAdd(addQuery as CFDictionary, nil)
159-
print("Add status: \(addStatus)")
160-
161-
}
162-
163104
}
164105

165106
#Preview {
166107
CertificatePicker(urlBase: "test.com")
167108
}
168-
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// SPDX-FileCopyrightText: Nextcloud GmbH
2+
// SPDX-FileCopyrightText: 2025 Milen Pivchev
3+
// SPDX-License-Identifier: GPL-3.0-or-later
4+
5+
protocol CertificatePickerDelegate: AnyObject {
6+
func certificatePickerDidImportIdentity(_ picker: CertificatePickerModel, for urlBase: String)
7+
}
8+
9+
@Observable class CertificatePickerModel: NSObject, UIDocumentPickerDelegate {
10+
var isWrongPassword = false
11+
var isCertImportedSuccessfully = false
12+
@ObservationIgnored weak var delegate: CertificatePickerDelegate?
13+
14+
func handleCertificate(fileUrl: URL, urlBase: String, password: String) {
15+
if fileUrl.startAccessingSecurityScopedResource() {
16+
defer {
17+
fileUrl.stopAccessingSecurityScopedResource()
18+
}
19+
20+
if let identity = getIdentityFromP12(from: fileUrl, password: password) {
21+
let urlWithoutScheme = urlBase.replacingOccurrences(of: "https://", with: "").replacingOccurrences(of: "http://", with: "")
22+
let label = "client_identity_\(urlWithoutScheme)"
23+
storeIdentityInKeychain(identity: identity, label: label)
24+
delegate?.certificatePickerDidImportIdentity(self, for: urlBase)
25+
isCertImportedSuccessfully = true
26+
} else {
27+
isWrongPassword = true
28+
}
29+
}
30+
}
31+
32+
func getIdentityFromP12(from url: URL, password: String) -> SecIdentity? {
33+
guard let p12Data = try? Data(contentsOf: url) else { return nil }
34+
35+
let options = [kSecImportExportPassphrase as String: password]
36+
var items: CFArray?
37+
let status = SecPKCS12Import(p12Data as CFData, options as CFDictionary, &items)
38+
39+
if status == errSecSuccess,
40+
let array = items as? [[String: Any]] {
41+
// swiftlint:disable force_cast
42+
if let identity = array.first?[kSecImportItemIdentity as String] as! SecIdentity? {
43+
// swiftlint:enable force_cast
44+
return identity
45+
}
46+
}
47+
return nil
48+
}
49+
50+
func storeIdentityInKeychain(identity: SecIdentity, label: String) {
51+
let addQuery: [String: Any] = [
52+
kSecValueRef as String: identity,
53+
kSecClass as String: kSecClassIdentity,
54+
kSecAttrLabel as String: label,
55+
kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock
56+
]
57+
58+
let classes = [kSecClassIdentity, kSecClassCertificate, kSecClassKey]
59+
for secClass in classes {
60+
let deleteQuery: [String: Any] = [
61+
kSecClass as String: secClass,
62+
kSecAttrLabel as String: label,
63+
kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock
64+
]
65+
let status = SecItemDelete(deleteQuery as CFDictionary)
66+
print("Deleting \(secClass): \(status)")
67+
}
68+
69+
let addStatus = SecItemAdd(addQuery as CFDictionary, nil)
70+
print("Add status: \(addStatus)")
71+
72+
}
73+
74+
}

iOSClient/Login/NCLogin.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ extension NCLogin: ClientCertificateDelegate, CertificatePickerDelegate {
446446
#if DEBUG
447447
import Security
448448

449-
func clearKeychain() {
449+
private func clearKeychain() {
450450
let secItemClasses = [
451451
kSecClassGenericPassword,
452452
kSecClassInternetPassword,

0 commit comments

Comments
 (0)