diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index b9e608c..818ed32 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -27,7 +27,7 @@ jobs: run: | pip install pytest && pip install pytest-asyncio && - python -m pytest src/onepassword/*.py + python3 -m pytest sdk/onepassword/*.py - name: Lint with Ruff run: | diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a5fec87 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.pytest_cache +.idea +*__pycache__ +dist/ +onepassword.egg-info/ +.DS_Store +build/ \ No newline at end of file diff --git a/src/onepassword/example.py b/example/example.py similarity index 77% rename from src/onepassword/example.py rename to example/example.py index 3fc796a..cf1af08 100644 --- a/src/onepassword/example.py +++ b/example/example.py @@ -4,13 +4,13 @@ async def main(): # Your service account token here - token = os.environ("OP_SERVICE_ACCOUNT_TOKEN") + token = os.getenv("OP_SERVICE_ACCOUNT_TOKEN") # Connect to 1Password client = await Client.authenticate(auth=token, integration_name=DEFAULT_INTEGRATION_NAME, integration_version=DEFAULT_INTEGRATION_VERSION) # Retrieve secret from 1Password - value = await client.secrets.resolve("op://Test Login/test_username") + value = await client.secrets.resolve("op://Russian Blue Vault/hers/department") print(value) if __name__ == '__main__': diff --git a/lib/op_uniffi_core_linux_amd64/sdk-core-linux-amd64/__init__.py b/lib/op_uniffi_core_linux_amd64/sdk-core-linux-amd64/__init__.py new file mode 100644 index 0000000..6b7979b --- /dev/null +++ b/lib/op_uniffi_core_linux_amd64/sdk-core-linux-amd64/__init__.py @@ -0,0 +1 @@ +import op_uniffi_core \ No newline at end of file diff --git a/src/onepassword/libop_uniffi_core.so b/lib/op_uniffi_core_linux_amd64/sdk-core-linux-amd64/libop_uniffi_core.so similarity index 100% rename from src/onepassword/libop_uniffi_core.so rename to lib/op_uniffi_core_linux_amd64/sdk-core-linux-amd64/libop_uniffi_core.so diff --git a/src/onepassword/op_uniffi_core.py b/lib/op_uniffi_core_linux_amd64/sdk-core-linux-amd64/op_uniffi_core.py similarity index 100% rename from src/onepassword/op_uniffi_core.py rename to lib/op_uniffi_core_linux_amd64/sdk-core-linux-amd64/op_uniffi_core.py diff --git a/lib/op_uniffi_core_linux_amd64/setup.py b/lib/op_uniffi_core_linux_amd64/setup.py new file mode 100644 index 0000000..a58dca1 --- /dev/null +++ b/lib/op_uniffi_core_linux_amd64/setup.py @@ -0,0 +1,9 @@ +from setuptools import setup, find_packages + +setup( + name='sdk-core-linux-amd64', + version='0.1.0', + packages=find_packages(), + author='1Password', + url='https://github.com/1Password/onepassword-sdk-python', +) \ No newline at end of file diff --git a/lib/op_uniffi_core_mac_arm64/MANIFEST.in b/lib/op_uniffi_core_mac_arm64/MANIFEST.in new file mode 100644 index 0000000..a7d6343 --- /dev/null +++ b/lib/op_uniffi_core_mac_arm64/MANIFEST.in @@ -0,0 +1 @@ +include sdk_core_mac_arm64/libop_uniffi_core.dylib \ No newline at end of file diff --git a/lib/op_uniffi_core_mac_arm64/sdk_core_darwin_arm64.egg-info/PKG-INFO b/lib/op_uniffi_core_mac_arm64/sdk_core_darwin_arm64.egg-info/PKG-INFO new file mode 100644 index 0000000..ae6f7e3 --- /dev/null +++ b/lib/op_uniffi_core_mac_arm64/sdk_core_darwin_arm64.egg-info/PKG-INFO @@ -0,0 +1,5 @@ +Metadata-Version: 2.1 +Name: sdk_core_darwin_arm64 +Version: 0.1.0b1 +Home-page: https://github.com/1Password/onepassword-sdk-python +Author: 1Password diff --git a/lib/op_uniffi_core_mac_arm64/sdk_core_darwin_arm64.egg-info/SOURCES.txt b/lib/op_uniffi_core_mac_arm64/sdk_core_darwin_arm64.egg-info/SOURCES.txt new file mode 100644 index 0000000..40113cf --- /dev/null +++ b/lib/op_uniffi_core_mac_arm64/sdk_core_darwin_arm64.egg-info/SOURCES.txt @@ -0,0 +1,8 @@ +MANIFEST.in +setup.py +sdk_core_darwin_arm64/__init__.py +sdk_core_darwin_arm64/op_uniffi_core.py +sdk_core_darwin_arm64.egg-info/PKG-INFO +sdk_core_darwin_arm64.egg-info/SOURCES.txt +sdk_core_darwin_arm64.egg-info/dependency_links.txt +sdk_core_darwin_arm64.egg-info/top_level.txt \ No newline at end of file diff --git a/lib/op_uniffi_core_mac_arm64/sdk_core_darwin_arm64.egg-info/dependency_links.txt b/lib/op_uniffi_core_mac_arm64/sdk_core_darwin_arm64.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/lib/op_uniffi_core_mac_arm64/sdk_core_darwin_arm64.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/lib/op_uniffi_core_mac_arm64/sdk_core_darwin_arm64.egg-info/top_level.txt b/lib/op_uniffi_core_mac_arm64/sdk_core_darwin_arm64.egg-info/top_level.txt new file mode 100644 index 0000000..56fcd09 --- /dev/null +++ b/lib/op_uniffi_core_mac_arm64/sdk_core_darwin_arm64.egg-info/top_level.txt @@ -0,0 +1 @@ +sdk_core_darwin_arm64 diff --git a/lib/op_uniffi_core_mac_arm64/sdk_core_darwin_arm64/__init__.py b/lib/op_uniffi_core_mac_arm64/sdk_core_darwin_arm64/__init__.py new file mode 100644 index 0000000..6b7979b --- /dev/null +++ b/lib/op_uniffi_core_mac_arm64/sdk_core_darwin_arm64/__init__.py @@ -0,0 +1 @@ +import op_uniffi_core \ No newline at end of file diff --git a/src/onepassword/libop_uniffi_core.dylib b/lib/op_uniffi_core_mac_arm64/sdk_core_darwin_arm64/libop_uniffi_core.dylib similarity index 100% rename from src/onepassword/libop_uniffi_core.dylib rename to lib/op_uniffi_core_mac_arm64/sdk_core_darwin_arm64/libop_uniffi_core.dylib diff --git a/lib/op_uniffi_core_mac_arm64/sdk_core_darwin_arm64/op_uniffi_core.py b/lib/op_uniffi_core_mac_arm64/sdk_core_darwin_arm64/op_uniffi_core.py new file mode 100644 index 0000000..65b0d49 --- /dev/null +++ b/lib/op_uniffi_core_mac_arm64/sdk_core_darwin_arm64/op_uniffi_core.py @@ -0,0 +1,966 @@ + + +# This file was autogenerated by some hot garbage in the `uniffi` crate. +# Trust me, you don't want to mess with it! + +# Common helper code. +# +# Ideally this would live in a separate .py file where it can be unittested etc +# in isolation, and perhaps even published as a re-useable package. +# +# However, it's important that the details of how this helper code works (e.g. the +# way that different builtin types are passed across the FFI) exactly match what's +# expected by the rust code on the other side of the interface. In practice right +# now that means coming from the exact some version of `uniffi` that was used to +# compile the rust component. The easiest way to ensure this is to bundle the Python +# helpers directly inline like we're doing here. + +import os +import sys +import ctypes +import enum +import struct +import contextlib +import datetime +import typing +import asyncio +import platform + +# Used for default argument values +_DEFAULT = object() + + +class _UniffiRustBuffer(ctypes.Structure): + _fields_ = [ + ("capacity", ctypes.c_int32), + ("len", ctypes.c_int32), + ("data", ctypes.POINTER(ctypes.c_char)), + ] + + @staticmethod + def alloc(size): + return _rust_call(_UniffiLib.ffi_op_uniffi_core_rustbuffer_alloc, size) + + @staticmethod + def reserve(rbuf, additional): + return _rust_call(_UniffiLib.ffi_op_uniffi_core_rustbuffer_reserve, rbuf, additional) + + def free(self): + return _rust_call(_UniffiLib.ffi_op_uniffi_core_rustbuffer_free, self) + + def __str__(self): + return "_UniffiRustBuffer(capacity={}, len={}, data={})".format( + self.capacity, + self.len, + self.data[0:self.len] + ) + + @contextlib.contextmanager + def alloc_with_builder(*args): + """Context-manger to allocate a buffer using a _UniffiRustBufferBuilder. + + The allocated buffer will be automatically freed if an error occurs, ensuring that + we don't accidentally leak it. + """ + builder = _UniffiRustBufferBuilder() + try: + yield builder + except: + builder.discard() + raise + + @contextlib.contextmanager + def consume_with_stream(self): + """Context-manager to consume a buffer using a _UniffiRustBufferStream. + + The _UniffiRustBuffer will be freed once the context-manager exits, ensuring that we don't + leak it even if an error occurs. + """ + try: + s = _UniffiRustBufferStream.from_rust_buffer(self) + yield s + if s.remaining() != 0: + raise RuntimeError("junk data left in buffer at end of consume_with_stream") + finally: + self.free() + + @contextlib.contextmanager + def read_with_stream(self): + """Context-manager to read a buffer using a _UniffiRustBufferStream. + + This is like consume_with_stream, but doesn't free the buffer afterwards. + It should only be used with borrowed `_UniffiRustBuffer` data. + """ + s = _UniffiRustBufferStream.from_rust_buffer(self) + yield s + if s.remaining() != 0: + raise RuntimeError("junk data left in buffer at end of read_with_stream") + +class _UniffiForeignBytes(ctypes.Structure): + _fields_ = [ + ("len", ctypes.c_int32), + ("data", ctypes.POINTER(ctypes.c_char)), + ] + + def __str__(self): + return "_UniffiForeignBytes(len={}, data={})".format(self.len, self.data[0:self.len]) + + +class _UniffiRustBufferStream: + """ + Helper for structured reading of bytes from a _UniffiRustBuffer + """ + + def __init__(self, data, len): + self.data = data + self.len = len + self.offset = 0 + + @classmethod + def from_rust_buffer(cls, buf): + return cls(buf.data, buf.len) + + def remaining(self): + return self.len - self.offset + + def _unpack_from(self, size, format): + if self.offset + size > self.len: + raise InternalError("read past end of rust buffer") + value = struct.unpack(format, self.data[self.offset:self.offset+size])[0] + self.offset += size + return value + + def read(self, size): + if self.offset + size > self.len: + raise InternalError("read past end of rust buffer") + data = self.data[self.offset:self.offset+size] + self.offset += size + return data + + def read_i8(self): + return self._unpack_from(1, ">b") + + def read_u8(self): + return self._unpack_from(1, ">B") + + def read_i16(self): + return self._unpack_from(2, ">h") + + def read_u16(self): + return self._unpack_from(2, ">H") + + def read_i32(self): + return self._unpack_from(4, ">i") + + def read_u32(self): + return self._unpack_from(4, ">I") + + def read_i64(self): + return self._unpack_from(8, ">q") + + def read_u64(self): + return self._unpack_from(8, ">Q") + + def read_float(self): + v = self._unpack_from(4, ">f") + return v + + def read_double(self): + return self._unpack_from(8, ">d") + + def read_c_size_t(self): + return self._unpack_from(ctypes.sizeof(ctypes.c_size_t) , "@N") + +class _UniffiRustBufferBuilder: + """ + Helper for structured writing of bytes into a _UniffiRustBuffer. + """ + + def __init__(self): + self.rbuf = _UniffiRustBuffer.alloc(16) + self.rbuf.len = 0 + + def finalize(self): + rbuf = self.rbuf + self.rbuf = None + return rbuf + + def discard(self): + if self.rbuf is not None: + rbuf = self.finalize() + rbuf.free() + + @contextlib.contextmanager + def _reserve(self, num_bytes): + if self.rbuf.len + num_bytes > self.rbuf.capacity: + self.rbuf = _UniffiRustBuffer.reserve(self.rbuf, num_bytes) + yield None + self.rbuf.len += num_bytes + + def _pack_into(self, size, format, value): + with self._reserve(size): + # XXX TODO: I feel like I should be able to use `struct.pack_into` here but can't figure it out. + for i, byte in enumerate(struct.pack(format, value)): + self.rbuf.data[self.rbuf.len + i] = byte + + def write(self, value): + with self._reserve(len(value)): + for i, byte in enumerate(value): + self.rbuf.data[self.rbuf.len + i] = byte + + def write_i8(self, v): + self._pack_into(1, ">b", v) + + def write_u8(self, v): + self._pack_into(1, ">B", v) + + def write_i16(self, v): + self._pack_into(2, ">h", v) + + def write_u16(self, v): + self._pack_into(2, ">H", v) + + def write_i32(self, v): + self._pack_into(4, ">i", v) + + def write_u32(self, v): + self._pack_into(4, ">I", v) + + def write_i64(self, v): + self._pack_into(8, ">q", v) + + def write_u64(self, v): + self._pack_into(8, ">Q", v) + + def write_float(self, v): + self._pack_into(4, ">f", v) + + def write_double(self, v): + self._pack_into(8, ">d", v) + + def write_c_size_t(self, v): + self._pack_into(ctypes.sizeof(ctypes.c_size_t) , "@N", v) +# A handful of classes and functions to support the generated data structures. +# This would be a good candidate for isolating in its own ffi-support lib. + +class InternalError(Exception): + pass + +class _UniffiRustCallStatus(ctypes.Structure): + """ + Error runtime. + """ + _fields_ = [ + ("code", ctypes.c_int8), + ("error_buf", _UniffiRustBuffer), + ] + + # These match the values from the uniffi::rustcalls module + CALL_SUCCESS = 0 + CALL_ERROR = 1 + CALL_PANIC = 2 + + def __str__(self): + if self.code == _UniffiRustCallStatus.CALL_SUCCESS: + return "_UniffiRustCallStatus(CALL_SUCCESS)" + elif self.code == _UniffiRustCallStatus.CALL_ERROR: + return "_UniffiRustCallStatus(CALL_ERROR)" + elif self.code == _UniffiRustCallStatus.CALL_PANIC: + return "_UniffiRustCallStatus(CALL_PANIC)" + else: + return "_UniffiRustCallStatus()" + +def _rust_call(fn, *args): + # Call a rust function + return _rust_call_with_error(None, fn, *args) + +def _rust_call_with_error(error_ffi_converter, fn, *args): + # Call a rust function and handle any errors + # + # This function is used for rust calls that return Result<> and therefore can set the CALL_ERROR status code. + # error_ffi_converter must be set to the _UniffiConverter for the error class that corresponds to the result. + call_status = _UniffiRustCallStatus(code=_UniffiRustCallStatus.CALL_SUCCESS, error_buf=_UniffiRustBuffer(0, 0, None)) + + args_with_error = args + (ctypes.byref(call_status),) + result = fn(*args_with_error) + _uniffi_check_call_status(error_ffi_converter, call_status) + return result + +def _uniffi_check_call_status(error_ffi_converter, call_status): + if call_status.code == _UniffiRustCallStatus.CALL_SUCCESS: + pass + elif call_status.code == _UniffiRustCallStatus.CALL_ERROR: + if error_ffi_converter is None: + call_status.error_buf.free() + raise InternalError("_rust_call_with_error: CALL_ERROR, but error_ffi_converter is None") + else: + raise error_ffi_converter.lift(call_status.error_buf) + elif call_status.code == _UniffiRustCallStatus.CALL_PANIC: + # When the rust code sees a panic, it tries to construct a _UniffiRustBuffer + # with the message. But if that code panics, then it just sends back + # an empty buffer. + if call_status.error_buf.len > 0: + msg = _UniffiConverterString.lift(call_status.error_buf) + else: + msg = "Unknown rust panic" + raise InternalError(msg) + else: + raise InternalError("Invalid _UniffiRustCallStatus code: {}".format( + call_status.code)) + +# A function pointer for a callback as defined by UniFFI. +# Rust definition `fn(handle: u64, method: u32, args: _UniffiRustBuffer, buf_ptr: *mut _UniffiRustBuffer) -> int` +_UNIFFI_FOREIGN_CALLBACK_T = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_ulonglong, ctypes.c_ulong, ctypes.POINTER(ctypes.c_char), ctypes.c_int, ctypes.POINTER(_UniffiRustBuffer)) + +# UniFFI future continuation +_UNIFFI_FUTURE_CONTINUATION_T = ctypes.CFUNCTYPE(None, ctypes.c_size_t, ctypes.c_int8) + +class _UniffiPointerManagerCPython: + """ + Manage giving out pointers to Python objects on CPython + + This class is used to generate opaque pointers that reference Python objects to pass to Rust. + It assumes a CPython platform. See _UniffiPointerManagerGeneral for the alternative. + """ + + def new_pointer(self, obj): + """ + Get a pointer for an object as a ctypes.c_size_t instance + + Each call to new_pointer() must be balanced with exactly one call to release_pointer() + + This returns a ctypes.c_size_t. This is always the same size as a pointer and can be + interchanged with pointers for FFI function arguments and return values. + """ + # IncRef the object since we're going to pass a pointer to Rust + ctypes.pythonapi.Py_IncRef(ctypes.py_object(obj)) + # id() is the object address on CPython + # (https://docs.python.org/3/library/functions.html#id) + return id(obj) + + def release_pointer(self, address): + py_obj = ctypes.cast(address, ctypes.py_object) + obj = py_obj.value + ctypes.pythonapi.Py_DecRef(py_obj) + return obj + + def lookup(self, address): + return ctypes.cast(address, ctypes.py_object).value + +class _UniffiPointerManagerGeneral: + """ + Manage giving out pointers to Python objects on non-CPython platforms + + This has the same API as _UniffiPointerManagerCPython, but doesn't assume we're running on + CPython and is slightly slower. + + Instead of using real pointers, it maps integer values to objects and returns the keys as + c_size_t values. + """ + + def __init__(self): + self._map = {} + self._lock = threading.Lock() + self._current_handle = 0 + + def new_pointer(self, obj): + with self._lock: + handle = self._current_handle + self._current_handle += 1 + self._map[handle] = obj + return handle + + def release_pointer(self, handle): + with self._lock: + return self._map.pop(handle) + + def lookup(self, handle): + with self._lock: + return self._map[handle] + +# Pick an pointer manager implementation based on the platform +if platform.python_implementation() == 'CPython': + _UniffiPointerManager = _UniffiPointerManagerCPython # type: ignore +else: + _UniffiPointerManager = _UniffiPointerManagerGeneral # type: ignore +# Types conforming to `_UniffiConverterPrimitive` pass themselves directly over the FFI. +class _UniffiConverterPrimitive: + @classmethod + def lift(cls, value): + return value + + @classmethod + def lower(cls, value): + return value + +class _UniffiConverterPrimitiveInt(_UniffiConverterPrimitive): + @classmethod + def check_lower(cls, value): + try: + value = value.__index__() + except Exception: + raise TypeError("'{}' object cannot be interpreted as an integer".format(type(value).__name__)) + if not isinstance(value, int): + raise TypeError("__index__ returned non-int (type {})".format(type(value).__name__)) + if not cls.VALUE_MIN <= value < cls.VALUE_MAX: + raise ValueError("{} requires {} <= value < {}".format(cls.CLASS_NAME, cls.VALUE_MIN, cls.VALUE_MAX)) + +class _UniffiConverterPrimitiveFloat(_UniffiConverterPrimitive): + @classmethod + def check_lower(cls, value): + try: + value = value.__float__() + except Exception: + raise TypeError("must be real number, not {}".format(type(value).__name__)) + if not isinstance(value, float): + raise TypeError("__float__ returned non-float (type {})".format(type(value).__name__)) + +# Helper class for wrapper types that will always go through a _UniffiRustBuffer. +# Classes should inherit from this and implement the `read` and `write` static methods. +class _UniffiConverterRustBuffer: + @classmethod + def lift(cls, rbuf): + with rbuf.consume_with_stream() as stream: + return cls.read(stream) + + @classmethod + def lower(cls, value): + with _UniffiRustBuffer.alloc_with_builder() as builder: + cls.write(value, builder) + return builder.finalize() + +# Contains loading, initialization code, and the FFI Function declarations. +# Define some ctypes FFI types that we use in the library + +""" +Function pointer for a Rust task, which a callback function that takes a opaque pointer +""" +_UNIFFI_RUST_TASK = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_int8) + +def _uniffi_future_callback_t(return_type): + """ + Factory function to create callback function types for async functions + """ + return ctypes.CFUNCTYPE(None, ctypes.c_size_t, return_type, _UniffiRustCallStatus) + +def _uniffi_load_indirect(): + """ + This is how we find and load the dynamic library provided by the component. + For now we just look it up by name. + """ + if sys.platform == "darwin": + libname = "lib{}.dylib" + elif sys.platform.startswith("win"): + # As of python3.8, ctypes does not seem to search $PATH when loading DLLs. + # We could use `os.add_dll_directory` to configure the search path, but + # it doesn't feel right to mess with application-wide settings. Let's + # assume that the `.dll` is next to the `.py` file and load by full path. + libname = os.path.join( + os.path.dirname(__file__), + "{}.dll", + ) + else: + # Anything else must be an ELF platform - Linux, *BSD, Solaris/illumos + libname = "lib{}.so" + + libname = libname.format("op_uniffi_core") + path = os.path.join(os.path.dirname(__file__), libname) + lib = ctypes.cdll.LoadLibrary(path) + return lib + +def _uniffi_check_contract_api_version(lib): + # Get the bindings contract version from our ComponentInterface + bindings_contract_version = 25 + # Get the scaffolding contract version by calling the into the dylib + scaffolding_contract_version = lib.ffi_op_uniffi_core_uniffi_contract_version() + if bindings_contract_version != scaffolding_contract_version: + raise InternalError("UniFFI contract version mismatch: try cleaning and rebuilding your project") + +def _uniffi_check_api_checksums(lib): + if lib.uniffi_op_uniffi_core_checksum_func_init_client() != 45066: + raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + if lib.uniffi_op_uniffi_core_checksum_func_invoke() != 29143: + raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + if lib.uniffi_op_uniffi_core_checksum_func_release_client() != 57155: + raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + +# A ctypes library to expose the extern-C FFI definitions. +# This is an implementation detail which will be called internally by the public API. + +_UniffiLib = _uniffi_load_indirect() +_UniffiLib.uniffi_op_uniffi_core_fn_func_init_client.argtypes = ( + _UniffiRustBuffer, +) +_UniffiLib.uniffi_op_uniffi_core_fn_func_init_client.restype = ctypes.c_void_p +_UniffiLib.uniffi_op_uniffi_core_fn_func_invoke.argtypes = ( + _UniffiRustBuffer, +) +_UniffiLib.uniffi_op_uniffi_core_fn_func_invoke.restype = ctypes.c_void_p +_UniffiLib.uniffi_op_uniffi_core_fn_func_release_client.argtypes = ( + _UniffiRustBuffer, + ctypes.POINTER(_UniffiRustCallStatus), +) +_UniffiLib.uniffi_op_uniffi_core_fn_func_release_client.restype = None +_UniffiLib.ffi_op_uniffi_core_rustbuffer_alloc.argtypes = ( + ctypes.c_int32, + ctypes.POINTER(_UniffiRustCallStatus), +) +_UniffiLib.ffi_op_uniffi_core_rustbuffer_alloc.restype = _UniffiRustBuffer +_UniffiLib.ffi_op_uniffi_core_rustbuffer_from_bytes.argtypes = ( + _UniffiForeignBytes, + ctypes.POINTER(_UniffiRustCallStatus), +) +_UniffiLib.ffi_op_uniffi_core_rustbuffer_from_bytes.restype = _UniffiRustBuffer +_UniffiLib.ffi_op_uniffi_core_rustbuffer_free.argtypes = ( + _UniffiRustBuffer, + ctypes.POINTER(_UniffiRustCallStatus), +) +_UniffiLib.ffi_op_uniffi_core_rustbuffer_free.restype = None +_UniffiLib.ffi_op_uniffi_core_rustbuffer_reserve.argtypes = ( + _UniffiRustBuffer, + ctypes.c_int32, + ctypes.POINTER(_UniffiRustCallStatus), +) +_UniffiLib.ffi_op_uniffi_core_rustbuffer_reserve.restype = _UniffiRustBuffer +_UniffiLib.ffi_op_uniffi_core_rust_future_poll_u8.argtypes = ( + ctypes.c_void_p, + _UNIFFI_FUTURE_CONTINUATION_T, + ctypes.c_size_t, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_poll_u8.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_cancel_u8.argtypes = ( + ctypes.c_void_p, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_cancel_u8.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_free_u8.argtypes = ( + ctypes.c_void_p, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_free_u8.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_complete_u8.argtypes = ( + ctypes.c_void_p, + ctypes.POINTER(_UniffiRustCallStatus), +) +_UniffiLib.ffi_op_uniffi_core_rust_future_complete_u8.restype = ctypes.c_uint8 +_UniffiLib.ffi_op_uniffi_core_rust_future_poll_i8.argtypes = ( + ctypes.c_void_p, + _UNIFFI_FUTURE_CONTINUATION_T, + ctypes.c_size_t, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_poll_i8.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_cancel_i8.argtypes = ( + ctypes.c_void_p, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_cancel_i8.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_free_i8.argtypes = ( + ctypes.c_void_p, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_free_i8.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_complete_i8.argtypes = ( + ctypes.c_void_p, + ctypes.POINTER(_UniffiRustCallStatus), +) +_UniffiLib.ffi_op_uniffi_core_rust_future_complete_i8.restype = ctypes.c_int8 +_UniffiLib.ffi_op_uniffi_core_rust_future_poll_u16.argtypes = ( + ctypes.c_void_p, + _UNIFFI_FUTURE_CONTINUATION_T, + ctypes.c_size_t, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_poll_u16.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_cancel_u16.argtypes = ( + ctypes.c_void_p, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_cancel_u16.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_free_u16.argtypes = ( + ctypes.c_void_p, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_free_u16.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_complete_u16.argtypes = ( + ctypes.c_void_p, + ctypes.POINTER(_UniffiRustCallStatus), +) +_UniffiLib.ffi_op_uniffi_core_rust_future_complete_u16.restype = ctypes.c_uint16 +_UniffiLib.ffi_op_uniffi_core_rust_future_poll_i16.argtypes = ( + ctypes.c_void_p, + _UNIFFI_FUTURE_CONTINUATION_T, + ctypes.c_size_t, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_poll_i16.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_cancel_i16.argtypes = ( + ctypes.c_void_p, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_cancel_i16.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_free_i16.argtypes = ( + ctypes.c_void_p, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_free_i16.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_complete_i16.argtypes = ( + ctypes.c_void_p, + ctypes.POINTER(_UniffiRustCallStatus), +) +_UniffiLib.ffi_op_uniffi_core_rust_future_complete_i16.restype = ctypes.c_int16 +_UniffiLib.ffi_op_uniffi_core_rust_future_poll_u32.argtypes = ( + ctypes.c_void_p, + _UNIFFI_FUTURE_CONTINUATION_T, + ctypes.c_size_t, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_poll_u32.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_cancel_u32.argtypes = ( + ctypes.c_void_p, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_cancel_u32.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_free_u32.argtypes = ( + ctypes.c_void_p, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_free_u32.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_complete_u32.argtypes = ( + ctypes.c_void_p, + ctypes.POINTER(_UniffiRustCallStatus), +) +_UniffiLib.ffi_op_uniffi_core_rust_future_complete_u32.restype = ctypes.c_uint32 +_UniffiLib.ffi_op_uniffi_core_rust_future_poll_i32.argtypes = ( + ctypes.c_void_p, + _UNIFFI_FUTURE_CONTINUATION_T, + ctypes.c_size_t, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_poll_i32.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_cancel_i32.argtypes = ( + ctypes.c_void_p, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_cancel_i32.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_free_i32.argtypes = ( + ctypes.c_void_p, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_free_i32.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_complete_i32.argtypes = ( + ctypes.c_void_p, + ctypes.POINTER(_UniffiRustCallStatus), +) +_UniffiLib.ffi_op_uniffi_core_rust_future_complete_i32.restype = ctypes.c_int32 +_UniffiLib.ffi_op_uniffi_core_rust_future_poll_u64.argtypes = ( + ctypes.c_void_p, + _UNIFFI_FUTURE_CONTINUATION_T, + ctypes.c_size_t, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_poll_u64.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_cancel_u64.argtypes = ( + ctypes.c_void_p, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_cancel_u64.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_free_u64.argtypes = ( + ctypes.c_void_p, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_free_u64.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_complete_u64.argtypes = ( + ctypes.c_void_p, + ctypes.POINTER(_UniffiRustCallStatus), +) +_UniffiLib.ffi_op_uniffi_core_rust_future_complete_u64.restype = ctypes.c_uint64 +_UniffiLib.ffi_op_uniffi_core_rust_future_poll_i64.argtypes = ( + ctypes.c_void_p, + _UNIFFI_FUTURE_CONTINUATION_T, + ctypes.c_size_t, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_poll_i64.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_cancel_i64.argtypes = ( + ctypes.c_void_p, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_cancel_i64.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_free_i64.argtypes = ( + ctypes.c_void_p, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_free_i64.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_complete_i64.argtypes = ( + ctypes.c_void_p, + ctypes.POINTER(_UniffiRustCallStatus), +) +_UniffiLib.ffi_op_uniffi_core_rust_future_complete_i64.restype = ctypes.c_int64 +_UniffiLib.ffi_op_uniffi_core_rust_future_poll_f32.argtypes = ( + ctypes.c_void_p, + _UNIFFI_FUTURE_CONTINUATION_T, + ctypes.c_size_t, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_poll_f32.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_cancel_f32.argtypes = ( + ctypes.c_void_p, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_cancel_f32.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_free_f32.argtypes = ( + ctypes.c_void_p, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_free_f32.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_complete_f32.argtypes = ( + ctypes.c_void_p, + ctypes.POINTER(_UniffiRustCallStatus), +) +_UniffiLib.ffi_op_uniffi_core_rust_future_complete_f32.restype = ctypes.c_float +_UniffiLib.ffi_op_uniffi_core_rust_future_poll_f64.argtypes = ( + ctypes.c_void_p, + _UNIFFI_FUTURE_CONTINUATION_T, + ctypes.c_size_t, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_poll_f64.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_cancel_f64.argtypes = ( + ctypes.c_void_p, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_cancel_f64.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_free_f64.argtypes = ( + ctypes.c_void_p, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_free_f64.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_complete_f64.argtypes = ( + ctypes.c_void_p, + ctypes.POINTER(_UniffiRustCallStatus), +) +_UniffiLib.ffi_op_uniffi_core_rust_future_complete_f64.restype = ctypes.c_double +_UniffiLib.ffi_op_uniffi_core_rust_future_poll_pointer.argtypes = ( + ctypes.c_void_p, + _UNIFFI_FUTURE_CONTINUATION_T, + ctypes.c_size_t, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_poll_pointer.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_cancel_pointer.argtypes = ( + ctypes.c_void_p, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_cancel_pointer.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_free_pointer.argtypes = ( + ctypes.c_void_p, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_free_pointer.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_complete_pointer.argtypes = ( + ctypes.c_void_p, + ctypes.POINTER(_UniffiRustCallStatus), +) +_UniffiLib.ffi_op_uniffi_core_rust_future_complete_pointer.restype = ctypes.c_void_p +_UniffiLib.ffi_op_uniffi_core_rust_future_poll_rust_buffer.argtypes = ( + ctypes.c_void_p, + _UNIFFI_FUTURE_CONTINUATION_T, + ctypes.c_size_t, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_poll_rust_buffer.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_cancel_rust_buffer.argtypes = ( + ctypes.c_void_p, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_cancel_rust_buffer.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_free_rust_buffer.argtypes = ( + ctypes.c_void_p, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_free_rust_buffer.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_complete_rust_buffer.argtypes = ( + ctypes.c_void_p, + ctypes.POINTER(_UniffiRustCallStatus), +) +_UniffiLib.ffi_op_uniffi_core_rust_future_complete_rust_buffer.restype = _UniffiRustBuffer +_UniffiLib.ffi_op_uniffi_core_rust_future_poll_void.argtypes = ( + ctypes.c_void_p, + _UNIFFI_FUTURE_CONTINUATION_T, + ctypes.c_size_t, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_poll_void.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_cancel_void.argtypes = ( + ctypes.c_void_p, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_cancel_void.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_free_void.argtypes = ( + ctypes.c_void_p, +) +_UniffiLib.ffi_op_uniffi_core_rust_future_free_void.restype = None +_UniffiLib.ffi_op_uniffi_core_rust_future_complete_void.argtypes = ( + ctypes.c_void_p, + ctypes.POINTER(_UniffiRustCallStatus), +) +_UniffiLib.ffi_op_uniffi_core_rust_future_complete_void.restype = None +_UniffiLib.uniffi_op_uniffi_core_checksum_func_init_client.argtypes = ( +) +_UniffiLib.uniffi_op_uniffi_core_checksum_func_init_client.restype = ctypes.c_uint16 +_UniffiLib.uniffi_op_uniffi_core_checksum_func_invoke.argtypes = ( +) +_UniffiLib.uniffi_op_uniffi_core_checksum_func_invoke.restype = ctypes.c_uint16 +_UniffiLib.uniffi_op_uniffi_core_checksum_func_release_client.argtypes = ( +) +_UniffiLib.uniffi_op_uniffi_core_checksum_func_release_client.restype = ctypes.c_uint16 +_UniffiLib.ffi_op_uniffi_core_uniffi_contract_version.argtypes = ( +) +_UniffiLib.ffi_op_uniffi_core_uniffi_contract_version.restype = ctypes.c_uint32 +_uniffi_check_contract_api_version(_UniffiLib) +_uniffi_check_api_checksums(_UniffiLib) + +# Async support# RustFuturePoll values +_UNIFFI_RUST_FUTURE_POLL_READY = 0 +_UNIFFI_RUST_FUTURE_POLL_MAYBE_READY = 1 + +# Stores futures for _uniffi_continuation_callback +_UniffiContinuationPointerManager = _UniffiPointerManager() + +# Continuation callback for async functions +# lift the return value or error and resolve the future, causing the async function to resume. +@_UNIFFI_FUTURE_CONTINUATION_T +def _uniffi_continuation_callback(future_ptr, poll_code): + (eventloop, future) = _UniffiContinuationPointerManager.release_pointer(future_ptr) + eventloop.call_soon_threadsafe(_uniffi_set_future_result, future, poll_code) + +def _uniffi_set_future_result(future, poll_code): + if not future.cancelled(): + future.set_result(poll_code) + +async def _uniffi_rust_call_async(rust_future, ffi_poll, ffi_complete, ffi_free, lift_func, error_ffi_converter): + try: + eventloop = asyncio.get_running_loop() + + # Loop and poll until we see a _UNIFFI_RUST_FUTURE_POLL_READY value + while True: + future = eventloop.create_future() + ffi_poll( + rust_future, + _uniffi_continuation_callback, + _UniffiContinuationPointerManager.new_pointer((eventloop, future)), + ) + poll_code = await future + if poll_code == _UNIFFI_RUST_FUTURE_POLL_READY: + break + + return lift_func( + _rust_call_with_error(error_ffi_converter, ffi_complete, rust_future) + ) + finally: + ffi_free(rust_future) + +# Public interface members begin here. + + +class _UniffiConverterString: + @staticmethod + def check_lower(value): + if not isinstance(value, str): + raise TypeError("argument must be str, not {}".format(type(value).__name__)) + return value + + @staticmethod + def read(buf): + size = buf.read_i32() + if size < 0: + raise InternalError("Unexpected negative string length") + utf8_bytes = buf.read(size) + return utf8_bytes.decode("utf-8") + + @staticmethod + def write(value, buf): + utf8_bytes = value.encode("utf-8") + buf.write_i32(len(utf8_bytes)) + buf.write(utf8_bytes) + + @staticmethod + def lift(buf): + with buf.consume_with_stream() as stream: + return stream.read(stream.remaining()).decode("utf-8") + + @staticmethod + def lower(value): + with _UniffiRustBuffer.alloc_with_builder() as builder: + builder.write(value.encode("utf-8")) + return builder.finalize() + + +# Error +# We want to define each variant as a nested class that's also a subclass, +# which is tricky in Python. To accomplish this we're going to create each +# class separately, then manually add the child classes to the base class's +# __dict__. All of this happens in dummy class to avoid polluting the module +# namespace. +class Error(Exception): + """ + Error type sent over the FFI by UniFFI. + + `uniffi::Error` only supports errors that are enums, so we need to have a single-variant enum here. + """ + + pass + +_UniffiTempError = Error + +class Error: # type: ignore + class Error(_UniffiTempError): + """ + Any error ocurring in the SDK + """ + + + def __init__(self, msg): + super().__init__(", ".join([ + "msg={!r}".format(msg), + ])) + self.msg = msg + def __repr__(self): + return "Error.Error({})".format(str(self)) + _UniffiTempError.Error = Error # type: ignore + +Error = _UniffiTempError # type: ignore +del _UniffiTempError + + +class _UniffiConverterTypeError(_UniffiConverterRustBuffer): + @staticmethod + def read(buf): + variant = buf.read_i32() + if variant == 1: + return Error.Error( + msg=_UniffiConverterString.read(buf), + ) + raise InternalError("Raw enum value doesn't match any cases") + + @staticmethod + def check_lower(value): + if isinstance(value, Error.Error): + _UniffiConverterString.check_lower(value.msg) + return + + @staticmethod + def write(value, buf): + if isinstance(value, Error.Error): + buf.write_i32(1) + _UniffiConverterString.write(value.msg, buf) + +def init_client(client_config: "str"): + _UniffiConverterString.check_lower(client_config) + + return _uniffi_rust_call_async( + _UniffiLib.uniffi_op_uniffi_core_fn_func_init_client( + _UniffiConverterString.lower(client_config)), + _UniffiLib.ffi_op_uniffi_core_rust_future_poll_rust_buffer, + _UniffiLib.ffi_op_uniffi_core_rust_future_complete_rust_buffer, + _UniffiLib.ffi_op_uniffi_core_rust_future_free_rust_buffer, + # lift function + _UniffiConverterString.lift, + # Error FFI converter + _UniffiConverterTypeError, + ) + +def invoke(invocation: "str"): + _UniffiConverterString.check_lower(invocation) + + return _uniffi_rust_call_async( + _UniffiLib.uniffi_op_uniffi_core_fn_func_invoke( + _UniffiConverterString.lower(invocation)), + _UniffiLib.ffi_op_uniffi_core_rust_future_poll_rust_buffer, + _UniffiLib.ffi_op_uniffi_core_rust_future_complete_rust_buffer, + _UniffiLib.ffi_op_uniffi_core_rust_future_free_rust_buffer, + # lift function + _UniffiConverterString.lift, + # Error FFI converter + _UniffiConverterTypeError, + ) + +def release_client(client_id: "str"): + _UniffiConverterString.check_lower(client_id) + + _rust_call_with_error(_UniffiConverterTypeError,_UniffiLib.uniffi_op_uniffi_core_fn_func_release_client, + _UniffiConverterString.lower(client_id)) + + +__all__ = [ + "InternalError", + "Error", + "init_client", + "invoke", + "release_client", +] + diff --git a/lib/op_uniffi_core_mac_arm64/setup.py b/lib/op_uniffi_core_mac_arm64/setup.py new file mode 100644 index 0000000..a80ba0b --- /dev/null +++ b/lib/op_uniffi_core_mac_arm64/setup.py @@ -0,0 +1,10 @@ +from setuptools import setup, find_packages + +setup( + name='sdk_core_darwin_arm64', + version='0.1.0-beta.01', + packages=find_packages(), + author='1Password', + url='https://github.com/1Password/onepassword-sdk-python', + include_package_data=True +) \ No newline at end of file diff --git a/sdk/onepassword/__init__.py b/sdk/onepassword/__init__.py new file mode 100644 index 0000000..fe39ab1 --- /dev/null +++ b/sdk/onepassword/__init__.py @@ -0,0 +1 @@ +import onepassword.client \ No newline at end of file diff --git a/src/onepassword/client.py b/sdk/onepassword/client.py similarity index 72% rename from src/onepassword/client.py rename to sdk/onepassword/client.py index 800e15a..6596a1d 100644 --- a/src/onepassword/client.py +++ b/sdk/onepassword/client.py @@ -1,14 +1,14 @@ import sys import platform -from src.onepassword.core import InitClient, ReleaseClient -from src.onepassword.secrets_api import Secrets +from onepassword.core import _init_client, _release_client +from onepassword.secrets_api import Secrets import weakref -SDK_LANGUAGE = "Go" +SDK_LANGUAGE = "Python" SDK_VERSION = "0010001" # v0.1.0 DEFAULT_INTEGRATION_NAME = "Unknown" DEFAULT_INTEGRATION_VERSION = "Unknown" -DEFAULT_REQUEST_LIBRARY = "net/http" +DEFAULT_REQUEST_LIBRARY = "Reqwest" DEFAULT_OS_VERSION = "0.0.0" @@ -19,9 +19,9 @@ class Client: async def authenticate(cls, auth, integration_name, integration_version): self = cls() self.config = new_default_config(auth=auth, integration_name=integration_name, integration_version=integration_version) - self.client_id = int(await InitClient(self.config)) - self.secrets = Secrets(client_id=self.client_id) - self._finalizer = weakref.finalize(self, ReleaseClient, self.client_id) + client_id = int(await _init_client(self.config)) + self.secrets = Secrets(client_id) + self._finalizer = weakref.finalize(self, _release_client, client_id) return self @@ -35,8 +35,8 @@ def new_default_config(auth, integration_name, integration_version): "integrationVersion": integration_version, "requestLibraryName": DEFAULT_REQUEST_LIBRARY, "requestLibraryVersion": str(sys.version_info[0]) + "." + str(sys.version_info[1]) + "." + str(sys.version_info[2]), - "os": platform.system(), + "os": platform.system().lower(), "osVersion": DEFAULT_OS_VERSION, - "architecture": platform.architecture()[0], + "architecture": platform.machine(), } return client_config_dict diff --git a/sdk/onepassword/core.py b/sdk/onepassword/core.py new file mode 100644 index 0000000..890a613 --- /dev/null +++ b/sdk/onepassword/core.py @@ -0,0 +1,24 @@ +import json +import importlib.util +import platform + +def import_core(): + lib_name = "sdk_core_{}_{}".format(platform.system().lower(), platform.machine()) + spec = importlib.util.find_spec(lib_name) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + +core = import_core() + +# InitClient creates a client instance in the current core module and returns its unique ID. +async def _init_client(client_config): + return await core.init_client(json.dumps(client_config)) + +# Invoke calls specified business logic from core. +async def _invoke(invoke_config): + return await core.invoke(json.dumps(invoke_config)) + +# ReleaseClient releases memory in the core associated with the given client ID. +def _release_client(client_id): + return core.release_client(json.dumps(client_id)) diff --git a/src/onepassword/secrets_api.py b/sdk/onepassword/secrets_api.py similarity index 86% rename from src/onepassword/secrets_api.py rename to sdk/onepassword/secrets_api.py index ffc7b78..25e6898 100644 --- a/src/onepassword/secrets_api.py +++ b/sdk/onepassword/secrets_api.py @@ -1,4 +1,4 @@ -from src.onepassword.core import Invoke +from onepassword.core import _invoke """Secrets represents all operations the SDK client can perform on 1Password secrets.""" class Secrets: @@ -7,7 +7,7 @@ def __init__(self, client_id): """resolve returns the secret the provided reference points to.""" async def resolve(self, reference): - response = await Invoke({ + response = await _invoke({ "clientId": self.client_id, "invocation": { "name": "Resolve", diff --git a/sdk/onepassword/test_client.py b/sdk/onepassword/test_client.py new file mode 100644 index 0000000..bb1fc1c --- /dev/null +++ b/sdk/onepassword/test_client.py @@ -0,0 +1,68 @@ +import sys +from onepassword.client import Client, new_default_config, DEFAULT_INTEGRATION_NAME, DEFAULT_INTEGRATION_VERSION, SDK_LANGUAGE, SDK_VERSION, DEFAULT_OS_VERSION, DEFAULT_REQUEST_LIBRARY +import os +import platform +import pytest + +TOKEN = os.getenv('OP_SERVICE_ACCOUNT_TOKEN') + +## test resolve function + +# valid +@pytest.mark.asyncio +async def test_valid_resolve(): + client = await Client.authenticate(auth=TOKEN, integration_name=DEFAULT_INTEGRATION_NAME, integration_version=DEFAULT_INTEGRATION_VERSION) + result = await client.secrets.resolve(reference="op://gowwbvgow7kxocrfmfvtwni6vi/6ydrn7ne6mwnqc2prsbqx4i4aq/password") + assert(result == "test_password_42") + +# invalid +@pytest.mark.asyncio +async def test_invalid_resolve(): + client = await Client.authenticate(auth=TOKEN, integration_name=DEFAULT_INTEGRATION_NAME, integration_version=DEFAULT_INTEGRATION_VERSION) + with pytest.raises(Exception, match="error resolving secret reference: secret reference is not prefixed with \"op://\""): + await client.secrets.resolve(reference="invalid_reference") + +## test client constructor + +# valid +@pytest.mark.asyncio +async def test_good_client_construction(): + client = await Client.authenticate(auth=TOKEN, integration_name=DEFAULT_INTEGRATION_NAME, integration_version=DEFAULT_INTEGRATION_VERSION) + assert(client.config['serviceAccountToken'] == TOKEN) + assert(client.config['integrationName'] == DEFAULT_INTEGRATION_NAME) + assert(client.config['integrationVersion'] == DEFAULT_INTEGRATION_VERSION) + +# invalid +@pytest.mark.asyncio +async def test_client_construction_no_auth(): + with pytest.raises(Exception, match='invalid client configuration: encountered the following errors: service account token was not specified; service account token had invalid format'): + await Client.authenticate(auth="", integration_name=DEFAULT_INTEGRATION_NAME, integration_version=DEFAULT_INTEGRATION_VERSION) + +# invalid +@pytest.mark.asyncio +async def test_client_construction_no_name(): + with pytest.raises(Exception, match='invalid client configuration: encountered the following errors: integration name was not specified'): + await Client.authenticate(auth=TOKEN, integration_name="", integration_version=DEFAULT_INTEGRATION_VERSION) + +# invalid +@pytest.mark.asyncio +async def test_client_construction_no_version(): + with pytest.raises(Exception, match='invalid client configuration: encountered the following errors: integration version was not specified'): + await Client.authenticate(auth=TOKEN, integration_name=DEFAULT_INTEGRATION_NAME, integration_version="") + +## test config function + +# valid +def test_good_new_default_config(): + config = new_default_config(auth=TOKEN, integration_name=DEFAULT_INTEGRATION_NAME, integration_version=DEFAULT_INTEGRATION_VERSION) + + assert(config["serviceAccountToken"] == TOKEN) + assert(config["programmingLanguage"] == SDK_LANGUAGE) + assert(config["sdkVersion"] == SDK_VERSION) + assert(config["integrationName"] == DEFAULT_INTEGRATION_NAME) + assert(config["integrationVersion"] == DEFAULT_INTEGRATION_VERSION) + assert(config["requestLibraryName"] == DEFAULT_REQUEST_LIBRARY) + assert(config["requestLibraryVersion"] == str(sys.version_info[0]) + "." + str(sys.version_info[1]) + "." + str(sys.version_info[2])) + assert(config["os"] == platform.system().lower()) + assert(config["osVersion"] == DEFAULT_OS_VERSION) + assert(config["architecture"] == platform.machine()) diff --git a/sdk/pyproject.toml b/sdk/pyproject.toml new file mode 100644 index 0000000..d0e6f80 --- /dev/null +++ b/sdk/pyproject.toml @@ -0,0 +1,8 @@ +[build-system] +requires = ["setuptools"] + +[project] +dependencies = [ + 'sdk_core_darwin_arm64 @ git+ssh://github.com/1password/onepassword-sdk-python@andi/packaging&subdirectory=lib/op_uniffi_core_mac_arm64; platform_system=="Darwin" and platform_machine=="arm64"', + 'sdk_core_linux_amd64 @ git+ssh://github.com/1password/onepassword-sdk-python@andi/packaging&subdirectory=lib/op_uniffi_core_linux_amd64; platform_system=="Linux" and platform_machine=="x86_64"', +] \ No newline at end of file diff --git a/sdk/setup.py b/sdk/setup.py new file mode 100644 index 0000000..73ebffc --- /dev/null +++ b/sdk/setup.py @@ -0,0 +1,10 @@ +from setuptools import setup, find_packages + +setup( + name='onepassword', + version='0.1.0-beta.1', + packages=find_packages(), + author='1Password', + description="The 1Password Python SDK offers programmatic read access to your secrets in 1Password in an interface native to Python.", + url='https://github.com/1Password/onepassword-sdk-python', +) \ No newline at end of file diff --git a/src/onepassword/__init__.py b/src/onepassword/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/onepassword/core.py b/src/onepassword/core.py deleted file mode 100644 index 23fb8c2..0000000 --- a/src/onepassword/core.py +++ /dev/null @@ -1,14 +0,0 @@ -import json -import src.onepassword.op_uniffi_core as op_uniffi_core - -# InitClient creates a client instance in the current core module and returns its unique ID. -async def InitClient(client_config): - return await op_uniffi_core.init_client(json.dumps(client_config)) - -# Invoke calls specified business logic from core. -async def Invoke(invoke_config): - return await op_uniffi_core.invoke(json.dumps(invoke_config)) - -# ReleaseClient releases memory in the core associated with the given client ID. -def ReleaseClient(client_id): - return op_uniffi_core.release_client(json.dumps(client_id)) diff --git a/src/onepassword/test_client.py b/src/onepassword/test_client.py deleted file mode 100644 index 1d467cf..0000000 --- a/src/onepassword/test_client.py +++ /dev/null @@ -1,68 +0,0 @@ -import sys -import src.onepassword.client as onepassword -import os -import platform -import pytest - -TOKEN = os.environ['OP_SERVICE_ACCOUNT_TOKEN'] - -## test resolve function - -# valid -@pytest.mark.asyncio -async def test_valid_resolve(): - client = await onepassword.Client.authenticate(auth=TOKEN, integration_name=onepassword.DEFAULT_INTEGRATION_NAME, integration_version=onepassword.DEFAULT_INTEGRATION_VERSION) - result = await client.secrets.resolve(reference="op://gowwbvgow7kxocrfmfvtwni6vi/6ydrn7ne6mwnqc2prsbqx4i4aq/password") - assert(result == "test_password_42") - -# invalid -@pytest.mark.asyncio -async def test_invalid_resolve(): - client = await onepassword.Client.authenticate(auth=TOKEN, integration_name=onepassword.DEFAULT_INTEGRATION_NAME, integration_version=onepassword.DEFAULT_INTEGRATION_VERSION) - with pytest.raises(Exception, match="error resolving secret reference: secret reference is not prefixed with \"op://\""): - await client.secrets.resolve(reference="invalid_reference") - -## test client constructor - -# valid -@pytest.mark.asyncio -async def test_good_client_construction(): - client = await onepassword.Client.authenticate(auth=TOKEN, integration_name=onepassword.DEFAULT_INTEGRATION_NAME, integration_version=onepassword.DEFAULT_INTEGRATION_VERSION) - assert(client.config['serviceAccountToken'] == TOKEN) - assert(client.config['integrationName'] == onepassword.DEFAULT_INTEGRATION_NAME) - assert(client.config['integrationVersion'] == onepassword.DEFAULT_INTEGRATION_VERSION) - -# invalid -@pytest.mark.asyncio -async def test_client_construction_no_auth(): - with pytest.raises(Exception, match='invalid client configuration: encountered the following errors: service account token was not specified; service account token had invalid format'): - await onepassword.Client.authenticate(auth="", integration_name=onepassword.DEFAULT_INTEGRATION_NAME, integration_version=onepassword.DEFAULT_INTEGRATION_VERSION) - -# invalid -@pytest.mark.asyncio -async def test_client_construction_no_name(): - with pytest.raises(Exception, match='invalid client configuration: encountered the following errors: integration name was not specified'): - await onepassword.Client.authenticate(auth=TOKEN, integration_name="", integration_version=onepassword.DEFAULT_INTEGRATION_VERSION) - -# invalid -@pytest.mark.asyncio -async def test_client_construction_no_version(): - with pytest.raises(Exception, match='invalid client configuration: encountered the following errors: integration version was not specified'): - await onepassword.Client.authenticate(auth=TOKEN, integration_name=onepassword.DEFAULT_INTEGRATION_NAME, integration_version="") - -## test config function - -# valid -def test_good_new_default_config(): - config = onepassword.new_default_config(auth=TOKEN, integration_name=onepassword.DEFAULT_INTEGRATION_NAME, integration_version=onepassword.DEFAULT_INTEGRATION_VERSION) - - assert(config["serviceAccountToken"] == TOKEN) - assert(config["programmingLanguage"] == onepassword.SDK_LANGUAGE) - assert(config["sdkVersion"] == onepassword.SDK_VERSION) - assert(config["integrationName"] == onepassword.DEFAULT_INTEGRATION_NAME) - assert(config["integrationVersion"] == onepassword.DEFAULT_INTEGRATION_VERSION) - assert(config["requestLibraryName"] == onepassword.DEFAULT_REQUEST_LIBRARY) - assert(config["requestLibraryVersion"] == str(sys.version_info[0]) + "." + str(sys.version_info[1]) + "." + str(sys.version_info[2])) - assert(config["os"] == platform.system()) - assert(config["osVersion"] == onepassword.DEFAULT_OS_VERSION) - assert(config["architecture"] == platform.architecture()[0])