-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
✨ Create a tooltip for clipevery to interact with keychain (#24)
- Loading branch information
1 parent
264424d
commit 95070bf
Showing
7 changed files
with
162 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
22 changes: 22 additions & 0 deletions
22
composeApp/src/desktopMain/kotlin/com/clipevery/macos/MacosKeychainHelper.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package com.clipevery.macos | ||
|
||
import com.clipevery.macos.api.MacosApi | ||
|
||
object MacosKeychainHelper { | ||
|
||
fun getPassword(service: String, account: String): String? { | ||
return MacosApi.INSTANCE.getPassword(service, account) | ||
} | ||
|
||
fun setPassword(service: String, account: String, password: String): Boolean { | ||
return MacosApi.INSTANCE.setPassword(service, account, password) | ||
} | ||
|
||
fun updatePassword(service: String, account: String, password: String): Boolean { | ||
return MacosApi.INSTANCE.updatePassword(service, account, password) | ||
} | ||
|
||
fun deletePassword(service: String, account: String): Boolean { | ||
return MacosApi.INSTANCE.deletePassword(service, account) | ||
} | ||
} |
13 changes: 0 additions & 13 deletions
13
composeApp/src/desktopMain/kotlin/com/clipevery/macos/api/MacClipboardApi.kt
This file was deleted.
Oops, something went wrong.
21 changes: 21 additions & 0 deletions
21
composeApp/src/desktopMain/kotlin/com/clipevery/macos/api/MacosApi.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package com.clipevery.macos.api | ||
|
||
import com.sun.jna.Library | ||
import com.sun.jna.Native | ||
|
||
interface MacosApi : Library { | ||
|
||
fun getClipboardChangeCount(): Int | ||
|
||
fun getPassword(service: String, account: String): String? | ||
|
||
fun setPassword(service: String, account: String, password: String): Boolean | ||
|
||
fun updatePassword(service: String, account: String, password: String): Boolean | ||
|
||
fun deletePassword(service: String, account: String): Boolean | ||
|
||
companion object { | ||
val INSTANCE: MacosApi = Native.load("MacosApi", MacosApi::class.java) | ||
} | ||
} |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import Cocoa | ||
import Foundation | ||
import Security | ||
|
||
@_cdecl("getClipboardChangeCount") | ||
public func getClipboardChangeCount() -> Int { | ||
let pasteboard = NSPasteboard.general | ||
return pasteboard.changeCount | ||
} | ||
|
||
@_cdecl("getPassword") | ||
public func getPassword(service: UnsafePointer<CChar>, account: UnsafePointer<CChar>) -> UnsafePointer<CChar>? { | ||
let serviceString = String(cString: service) | ||
let accountString = String(cString: account) | ||
|
||
let query: [String: Any] = [ | ||
kSecClass as String: kSecClassGenericPassword, | ||
kSecAttrService as String: serviceString, | ||
kSecAttrAccount as String: accountString, | ||
kSecReturnData as String: kCFBooleanTrue!, | ||
kSecMatchLimit as String: kSecMatchLimitOne | ||
] | ||
|
||
var item: CFTypeRef? | ||
let status = SecItemCopyMatching(query as CFDictionary, &item) | ||
|
||
guard status == errSecSuccess else { return nil } | ||
|
||
if let data = item as? Data, let password = String(data: data, encoding: .utf8) { | ||
return UnsafePointer<CChar>(strdup(password)) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
@_cdecl("setPassword") | ||
public func setPassword(service: UnsafePointer<CChar>, account: UnsafePointer<CChar>, password: UnsafePointer<CChar>) -> Bool { | ||
let serviceString = String(cString: service) | ||
let accountString = String(cString: account) | ||
let passwordString = String(cString: password) | ||
|
||
let passwordData = passwordString.data(using: .utf8)! | ||
|
||
let query: [String: Any] = [ | ||
kSecClass as String: kSecClassGenericPassword, | ||
kSecAttrService as String: serviceString, | ||
kSecAttrAccount as String: accountString, | ||
kSecValueData as String: passwordData | ||
] | ||
|
||
// Try to add the item to the keychain | ||
let status = SecItemAdd(query as CFDictionary, nil) | ||
|
||
// Check the result | ||
return status == errSecSuccess | ||
} | ||
|
||
@_cdecl("updatePassword") | ||
public func updatePassword(service: UnsafePointer<CChar>, account: UnsafePointer<CChar>, newPassword: UnsafePointer<CChar>) -> Bool { | ||
let serviceString = String(cString: service) | ||
let accountString = String(cString: account) | ||
let newPasswordString = String(cString: newPassword) | ||
|
||
let query: [String: Any] = [ | ||
kSecClass as String: kSecClassGenericPassword, | ||
kSecAttrService as String: serviceString, | ||
kSecAttrAccount as String: accountString | ||
] | ||
|
||
let updateFields: [String: Any] = [ | ||
kSecValueData as String: newPasswordString.data(using: .utf8)! | ||
] | ||
|
||
let status = SecItemUpdate(query as CFDictionary, updateFields as CFDictionary) | ||
|
||
return status == errSecSuccess | ||
} | ||
|
||
@_cdecl("deletePassword") | ||
public func deletePassword(service: UnsafePointer<CChar>, account: UnsafePointer<CChar>) -> Bool { | ||
let serviceString = String(cString: service) | ||
let accountString = String(cString: account) | ||
|
||
let query: [String: Any] = [ | ||
kSecClass as String: kSecClassGenericPassword, | ||
kSecAttrService as String: serviceString, | ||
kSecAttrAccount as String: accountString | ||
] | ||
|
||
let status = SecItemDelete(query as CFDictionary) | ||
|
||
return status == errSecSuccess | ||
} |
24 changes: 24 additions & 0 deletions
24
composeApp/src/desktopTest/kotlin/com/clipevery/macos/MacosKeychainHelperTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package com.clipevery.macos | ||
|
||
import com.clipevery.platform.currentPlatform | ||
import org.junit.Test | ||
import kotlin.test.assertEquals | ||
import kotlin.test.assertTrue | ||
|
||
class MacosKeychainHelperTest { | ||
|
||
@Test | ||
fun testKeychain() { | ||
if (currentPlatform().isMacos()) { | ||
MacosKeychainHelper.setPassword("com.clipevery", "test", "test") | ||
var password = MacosKeychainHelper.getPassword("com.clipevery", "test") | ||
assertEquals("test", password) | ||
MacosKeychainHelper.updatePassword("com.clipevery", "test", "test1") | ||
password = MacosKeychainHelper.getPassword("com.clipevery", "test") | ||
assertEquals("test1", password) | ||
assertTrue(MacosKeychainHelper.deletePassword("com.clipevery", "test")) | ||
val password1 = MacosKeychainHelper.getPassword("com.clipevery", "test") | ||
assertTrue(password1 == null) | ||
} | ||
} | ||
} |