Skip to content
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
5 changes: 2 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
module github.com/1password/onepassword-sdk-go

go 1.22.0

toolchain go1.22.5
go 1.24.0

require (
github.com/extism/go-sdk v1.7.0
Expand All @@ -18,6 +16,7 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834 // indirect
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
golang.org/x/sys v0.37.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZB
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
Expand Down
178 changes: 20 additions & 158 deletions internal/shared_lib_core.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,85 +3,26 @@ package internal
import (
"context"
"encoding/json"
"errors"
"fmt"
"log"
"os"
"path"
"runtime"
"unsafe"
)

/*
#cgo LDFLAGS: -ldl

#include <dlfcn.h>
#include <stdlib.h>
#include <stdint.h>

// Function pointer types matching Rust exports
typedef int32_t (*send_message_t)(
const uint8_t* msg_ptr,
size_t msg_len,
uint8_t** out_buf,
size_t* out_len,
size_t* out_cap
);

typedef void (*free_message_t)(
uint8_t* buf,
size_t len,
size_t cap
);

// Trampoline for calling `send_message`, as Go cannot call function pointers directly.
static inline int32_t call_send_message(
send_message_t fn,
const uint8_t* msg_ptr,
size_t msg_len,
uint8_t** out_buf,
size_t* out_len,
size_t* out_cap
) {
return fn(msg_ptr, msg_len, out_buf, out_len, out_cap);
}

// Trampoline for calling `free_message`, as Go cannot call function pointers directly.
static inline void call_free_message(
free_message_t fn,
uint8_t* buf,
size_t len,
size_t cap
) {
fn(buf, len, cap);
}

// dlopen wrapper
static void* open_library(const char* path) {
return dlopen(path, RTLD_NOW);
}

// dlsym wrapper
static void* load_symbol(void* handle, const char* name) {
return dlsym(handle, name);
}

// dlclose wrapper
static int close_library(void* handle) {
return dlclose(handle);
// Request/Response mirror your Unix file (kept identical)
type Request struct {
Kind string `json:"kind"`
AccountName string `json:"account_name"`
Payload []byte `json:"payload"`
}

*/
import "C"

type SharedLibCore struct {
accountName string
handle unsafe.Pointer
sendMessage C.send_message_t
freeResponse C.free_message_t
type Response struct {
Success bool `json:"success"`
Payload []byte `json:"payload"`
}

var coreLib *SharedLibCore
func (r Response) Error() string { return string(r.Payload) }

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

case "windows":
locations = []string{
path.Join(home, `AppData\Local\1Password\op_sdk_ipc_client.dll`),
`C:\Program Files\1Password\app\8\op_sdk_ipc_client.dll`,
`C:\Program Files (x86)\1Password\app\8\op_sdk_ipc_client.dll`,
path.Join(home, `AppData\Local\1Password\app\8\op_sdk_ipc_client.dll`),
}

default:
return "", fmt.Errorf("unsupported OS: %s", runtime.GOOS)
}
Expand All @@ -121,11 +70,11 @@ func find1PasswordLibPath() (string, error) {

func GetSharedLibCore(accountName string) (*CoreWrapper, error) {
if coreLib == nil {
path, err := find1PasswordLibPath()
libPath, err := find1PasswordLibPath()
if err != nil {
return nil, err
}
coreLib, err = loadCore(path)
coreLib, err = loadCore(libPath)
if err != nil {
return nil, err
}
Expand All @@ -137,40 +86,6 @@ func GetSharedLibCore(accountName string) (*CoreWrapper, error) {
return &coreWrapper, nil
}

func loadCore(path string) (*SharedLibCore, error) {
cPath := C.CString(path)
defer C.free(unsafe.Pointer(cPath))

handle := C.open_library(cPath)
if handle == nil {
return nil, errors.New("failed to open library")
}

symbol := C.CString("op_sdk_ipc_send_message")
defer C.free(unsafe.Pointer(symbol))

fnSend := C.load_symbol(handle, symbol)
if fnSend == nil {
C.close_library(handle)
return nil, errors.New("failed to load send_message")
}

symbolFree := C.CString("op_sdk_ipc_free_response")
defer C.free(unsafe.Pointer(symbolFree))

fnFree := C.load_symbol(handle, symbolFree)
if fnFree == nil {
C.close_library(handle)
return nil, errors.New("failed to load free_message")
}

return &SharedLibCore{
handle: handle,
sendMessage: (C.send_message_t)(fnSend),
freeResponse: (C.free_message_t)(fnFree),
}, nil
}

// InitClient creates a client instance in the current core module and returns its unique ID.
func (slc *SharedLibCore) InitClient(ctx context.Context, config []byte) ([]byte, error) {
const kind = "init_client"
Expand All @@ -192,6 +107,7 @@ func (slc *SharedLibCore) InitClient(ctx context.Context, config []byte) ([]byte
return res, nil
}

// Invoke performs an SDK operation.
func (slc *SharedLibCore) Invoke(ctx context.Context, invokeConfig []byte) ([]byte, error) {
const kind = "invoke"
request := Request{
Expand Down Expand Up @@ -220,57 +136,3 @@ func (slc *SharedLibCore) ReleaseClient(clientID []byte) {
log.Println("failed to release client")
}
}

func (slc *SharedLibCore) callSharedLibrary(input []byte) ([]byte, error) {
if len(input) == 0 {
return nil, errors.New("internal: empty input")
}

var outBuf *C.uint8_t
var outLen C.size_t
var outCap C.size_t

retCode := C.call_send_message(
slc.sendMessage,
(*C.uint8_t)(unsafe.Pointer(&input[0])),
C.size_t(len(input)),
&outBuf,
&outLen,
&outCap,
)

if retCode != 0 {
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))
}

resp := C.GoBytes(unsafe.Pointer(outBuf), C.int(outLen))
// Call trampoline with the function pointer
C.call_free_message(slc.freeResponse, outBuf, outLen, outCap)

var response Response
err := json.Unmarshal(resp, &response)
if err != nil {
return nil, err
}

if response.Success {
return response.Payload, nil
} else {
return nil, response
}
}

type Request struct {
Kind string `json:"kind"`
AccountName string `json:"account_name"`
Payload []byte `json:"payload"`
}

type Response struct {
Success bool `json:"success"`
Payload []byte `json:"payload"`
}

func (r Response) Error() string {
return string(r.Payload)
}
Loading
Loading