Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Create a tooltip for clipevery to interact with keychain #24

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.clipevery.macos

import com.clipevery.clip.ClipboardService
import com.clipevery.macos.api.MacClipboard
import com.clipevery.macos.api.MacosApi
import java.awt.Toolkit
import java.awt.datatransfer.Clipboard
import java.awt.datatransfer.Transferable
Expand All @@ -22,7 +22,7 @@ class MacosClipboardService

override fun run() {
try {
MacClipboard.INSTANCE.clipboardChangeCount.let { currentChangeCount ->
MacosApi.INSTANCE.getClipboardChangeCount().let { currentChangeCount ->
if (changeCount == currentChangeCount) {
return
}
Expand Down
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)
}
}

This file was deleted.

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.
93 changes: 93 additions & 0 deletions composeApp/src/desktopMain/swift/MacosApi.swift
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
}
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)
}
}
}
Loading