Skip to content

Commit 67faaef

Browse files
authored
Enables the Go SDK to interact with the 1Password app on Windows. (#221)
1 parent 9f3bbde commit 67faaef

File tree

5 files changed

+272
-161
lines changed

5 files changed

+272
-161
lines changed

go.mod

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
module github.com/1password/onepassword-sdk-go
22

3-
go 1.22.0
4-
5-
toolchain go1.22.5
3+
go 1.24.0
64

75
require (
86
github.com/extism/go-sdk v1.7.0
@@ -18,6 +16,7 @@ require (
1816
github.com/pmezard/go-difflib v1.0.0 // indirect
1917
github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834 // indirect
2018
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
19+
golang.org/x/sys v0.37.0 // indirect
2120
google.golang.org/protobuf v1.34.2 // indirect
2221
gopkg.in/yaml.v3 v3.0.1 // indirect
2322
)

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZB
2020
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
2121
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
2222
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
23+
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
24+
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
2325
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
2426
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
2527
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=

internal/shared_lib_core.go

Lines changed: 20 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -3,85 +3,26 @@ package internal
33
import (
44
"context"
55
"encoding/json"
6-
"errors"
76
"fmt"
87
"log"
98
"os"
109
"path"
1110
"runtime"
12-
"unsafe"
1311
)
1412

15-
/*
16-
#cgo LDFLAGS: -ldl
17-
18-
#include <dlfcn.h>
19-
#include <stdlib.h>
20-
#include <stdint.h>
21-
22-
// Function pointer types matching Rust exports
23-
typedef int32_t (*send_message_t)(
24-
const uint8_t* msg_ptr,
25-
size_t msg_len,
26-
uint8_t** out_buf,
27-
size_t* out_len,
28-
size_t* out_cap
29-
);
30-
31-
typedef void (*free_message_t)(
32-
uint8_t* buf,
33-
size_t len,
34-
size_t cap
35-
);
36-
37-
// Trampoline for calling `send_message`, as Go cannot call function pointers directly.
38-
static inline int32_t call_send_message(
39-
send_message_t fn,
40-
const uint8_t* msg_ptr,
41-
size_t msg_len,
42-
uint8_t** out_buf,
43-
size_t* out_len,
44-
size_t* out_cap
45-
) {
46-
return fn(msg_ptr, msg_len, out_buf, out_len, out_cap);
47-
}
48-
49-
// Trampoline for calling `free_message`, as Go cannot call function pointers directly.
50-
static inline void call_free_message(
51-
free_message_t fn,
52-
uint8_t* buf,
53-
size_t len,
54-
size_t cap
55-
) {
56-
fn(buf, len, cap);
57-
}
58-
59-
// dlopen wrapper
60-
static void* open_library(const char* path) {
61-
return dlopen(path, RTLD_NOW);
62-
}
63-
64-
// dlsym wrapper
65-
static void* load_symbol(void* handle, const char* name) {
66-
return dlsym(handle, name);
67-
}
68-
69-
// dlclose wrapper
70-
static int close_library(void* handle) {
71-
return dlclose(handle);
13+
// Request/Response mirror your Unix file (kept identical)
14+
type Request struct {
15+
Kind string `json:"kind"`
16+
AccountName string `json:"account_name"`
17+
Payload []byte `json:"payload"`
7218
}
7319

74-
*/
75-
import "C"
76-
77-
type SharedLibCore struct {
78-
accountName string
79-
handle unsafe.Pointer
80-
sendMessage C.send_message_t
81-
freeResponse C.free_message_t
20+
type Response struct {
21+
Success bool `json:"success"`
22+
Payload []byte `json:"payload"`
8223
}
8324

84-
var coreLib *SharedLibCore
25+
func (r Response) Error() string { return string(r.Payload) }
8526

8627
// find1PasswordLibPath returns the path to the 1Password shared library
8728
// (libop_sdk_ipc_client.dylib/.so/.dll) depending on OS.
@@ -107,6 +48,14 @@ func find1PasswordLibPath() (string, error) {
10748
"/snap/bin/1password/libop_sdk_ipc_client.so",
10849
}
10950

51+
case "windows":
52+
locations = []string{
53+
path.Join(home, `AppData\Local\1Password\op_sdk_ipc_client.dll`),
54+
`C:\Program Files\1Password\app\8\op_sdk_ipc_client.dll`,
55+
`C:\Program Files (x86)\1Password\app\8\op_sdk_ipc_client.dll`,
56+
path.Join(home, `AppData\Local\1Password\app\8\op_sdk_ipc_client.dll`),
57+
}
58+
11059
default:
11160
return "", fmt.Errorf("unsupported OS: %s", runtime.GOOS)
11261
}
@@ -121,11 +70,11 @@ func find1PasswordLibPath() (string, error) {
12170

12271
func GetSharedLibCore(accountName string) (*CoreWrapper, error) {
12372
if coreLib == nil {
124-
path, err := find1PasswordLibPath()
73+
libPath, err := find1PasswordLibPath()
12574
if err != nil {
12675
return nil, err
12776
}
128-
coreLib, err = loadCore(path)
77+
coreLib, err = loadCore(libPath)
12978
if err != nil {
13079
return nil, err
13180
}
@@ -137,40 +86,6 @@ func GetSharedLibCore(accountName string) (*CoreWrapper, error) {
13786
return &coreWrapper, nil
13887
}
13988

140-
func loadCore(path string) (*SharedLibCore, error) {
141-
cPath := C.CString(path)
142-
defer C.free(unsafe.Pointer(cPath))
143-
144-
handle := C.open_library(cPath)
145-
if handle == nil {
146-
return nil, errors.New("failed to open library")
147-
}
148-
149-
symbol := C.CString("op_sdk_ipc_send_message")
150-
defer C.free(unsafe.Pointer(symbol))
151-
152-
fnSend := C.load_symbol(handle, symbol)
153-
if fnSend == nil {
154-
C.close_library(handle)
155-
return nil, errors.New("failed to load send_message")
156-
}
157-
158-
symbolFree := C.CString("op_sdk_ipc_free_response")
159-
defer C.free(unsafe.Pointer(symbolFree))
160-
161-
fnFree := C.load_symbol(handle, symbolFree)
162-
if fnFree == nil {
163-
C.close_library(handle)
164-
return nil, errors.New("failed to load free_message")
165-
}
166-
167-
return &SharedLibCore{
168-
handle: handle,
169-
sendMessage: (C.send_message_t)(fnSend),
170-
freeResponse: (C.free_message_t)(fnFree),
171-
}, nil
172-
}
173-
17489
// InitClient creates a client instance in the current core module and returns its unique ID.
17590
func (slc *SharedLibCore) InitClient(ctx context.Context, config []byte) ([]byte, error) {
17691
const kind = "init_client"
@@ -192,6 +107,7 @@ func (slc *SharedLibCore) InitClient(ctx context.Context, config []byte) ([]byte
192107
return res, nil
193108
}
194109

110+
// Invoke performs an SDK operation.
195111
func (slc *SharedLibCore) Invoke(ctx context.Context, invokeConfig []byte) ([]byte, error) {
196112
const kind = "invoke"
197113
request := Request{
@@ -220,57 +136,3 @@ func (slc *SharedLibCore) ReleaseClient(clientID []byte) {
220136
log.Println("failed to release client")
221137
}
222138
}
223-
224-
func (slc *SharedLibCore) callSharedLibrary(input []byte) ([]byte, error) {
225-
if len(input) == 0 {
226-
return nil, errors.New("internal: empty input")
227-
}
228-
229-
var outBuf *C.uint8_t
230-
var outLen C.size_t
231-
var outCap C.size_t
232-
233-
retCode := C.call_send_message(
234-
slc.sendMessage,
235-
(*C.uint8_t)(unsafe.Pointer(&input[0])),
236-
C.size_t(len(input)),
237-
&outBuf,
238-
&outLen,
239-
&outCap,
240-
)
241-
242-
if retCode != 0 {
243-
return nil, fmt.Errorf("failed to send message to Desktop App. Please make sure the integrations is enabled or otherwise contact 1Password support. Return code: %d", int(retCode))
244-
}
245-
246-
resp := C.GoBytes(unsafe.Pointer(outBuf), C.int(outLen))
247-
// Call trampoline with the function pointer
248-
C.call_free_message(slc.freeResponse, outBuf, outLen, outCap)
249-
250-
var response Response
251-
err := json.Unmarshal(resp, &response)
252-
if err != nil {
253-
return nil, err
254-
}
255-
256-
if response.Success {
257-
return response.Payload, nil
258-
} else {
259-
return nil, response
260-
}
261-
}
262-
263-
type Request struct {
264-
Kind string `json:"kind"`
265-
AccountName string `json:"account_name"`
266-
Payload []byte `json:"payload"`
267-
}
268-
269-
type Response struct {
270-
Success bool `json:"success"`
271-
Payload []byte `json:"payload"`
272-
}
273-
274-
func (r Response) Error() string {
275-
return string(r.Payload)
276-
}

0 commit comments

Comments
 (0)