From 4032af006b8217116159e0b25cc16d62bdd6baae Mon Sep 17 00:00:00 2001 From: jillianwilson Date: Wed, 2 Dec 2020 16:13:05 -0400 Subject: [PATCH] Initial 1Password Connect Python SDK Commit --- .gitignore | 146 +++++ LICENSE.md | 21 + README.md | 189 +++++++ poetry.lock | 302 ++++++++++ pyproject.toml | 18 + src/onepasswordconnectsdk/__init__.py | 8 + src/onepasswordconnectsdk/client.py | 523 ++++++++++++++++++ src/onepasswordconnectsdk/config.py | 242 ++++++++ src/onepasswordconnectsdk/models/__init__.py | 20 + src/onepasswordconnectsdk/models/constants.py | 4 + src/onepasswordconnectsdk/models/error.py | 140 +++++ src/onepasswordconnectsdk/models/full_item.py | 456 +++++++++++++++ .../models/full_item_all_of.py | 139 +++++ .../models/full_item_all_of_fields.py | 312 +++++++++++ .../models/full_item_all_of_section.py | 113 ++++ .../models/full_item_all_of_sections.py | 138 +++++ src/onepasswordconnectsdk/models/item.py | 404 ++++++++++++++ src/onepasswordconnectsdk/models/item_urls.py | 139 +++++ .../models/item_vault.py | 113 ++++ .../models/parsed_field.py | 7 + .../models/parsed_item.py | 10 + src/onepasswordconnectsdk/models/vault.py | 334 +++++++++++ src/tests/__init__.py | 0 src/tests/test_client_items.py | 218 ++++++++ src/tests/test_client_vaults.py | 63 +++ src/tests/test_config.py | 123 ++++ 26 files changed, 4182 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 poetry.lock create mode 100644 pyproject.toml create mode 100644 src/onepasswordconnectsdk/__init__.py create mode 100644 src/onepasswordconnectsdk/client.py create mode 100644 src/onepasswordconnectsdk/config.py create mode 100644 src/onepasswordconnectsdk/models/__init__.py create mode 100644 src/onepasswordconnectsdk/models/constants.py create mode 100644 src/onepasswordconnectsdk/models/error.py create mode 100644 src/onepasswordconnectsdk/models/full_item.py create mode 100644 src/onepasswordconnectsdk/models/full_item_all_of.py create mode 100644 src/onepasswordconnectsdk/models/full_item_all_of_fields.py create mode 100644 src/onepasswordconnectsdk/models/full_item_all_of_section.py create mode 100644 src/onepasswordconnectsdk/models/full_item_all_of_sections.py create mode 100644 src/onepasswordconnectsdk/models/item.py create mode 100644 src/onepasswordconnectsdk/models/item_urls.py create mode 100644 src/onepasswordconnectsdk/models/item_vault.py create mode 100644 src/onepasswordconnectsdk/models/parsed_field.py create mode 100644 src/onepasswordconnectsdk/models/parsed_item.py create mode 100644 src/onepasswordconnectsdk/models/vault.py create mode 100644 src/tests/__init__.py create mode 100644 src/tests/test_client_items.py create mode 100644 src/tests/test_client_vaults.py create mode 100644 src/tests/test_config.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..476e0c8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,146 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# VScode +.vscode/ + +# Goland +.idea/ + +.DS_Store \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..d844620 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) [2020] [1Password] + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e4a6907 --- /dev/null +++ b/README.md @@ -0,0 +1,189 @@ +# 1Password Connect Python SDK + +The 1Password Connect SDK provides access to the 1Password via 1Password Connect hosted on your infrastructure. The library is intended to be used by Python applications to simplify accessing `items` in 1Password `vaults`. + +## Prerequisites: + +- 1Password Connect deployed in your infrastructure. +## Installation + +`pip install https://github.com/1Password/connect-sdk-python/archive/v{version}.zip` + +## Usage + +**Import 1Password Connect Python SDK** + +```python +import onepasswordconnectsdk +``` + +**Environment Variables** + +- **OP_CONNECT_TOKEN** – The token to be used to authenticate with the 1Password Connect API. +- **OP_VAULT** - The default vault to fetch items from if not specified. + +**Creating a Client** + +There are two methods available for creating a client: + +- `new_client_from_environment`: Builds a new client for interacting with 1Password Connect using the `OP_CONNECT_TOKEN` *environment variable (ie a 1Password Connect API token)* and the hostname of 1Password Connect. +- `new_client`: Builds a new client for interacting with 1Password Connect. Accepts the hostname of 1Password Connect and the API token generated for the application. + +```python +:from onepasswordconnectsdk.client import ( + Client, + new_client_from_environment +) + +# creating client using OP_CONNECT_TOKEN environment variable +client: Client = new_client_from_environment( + "{1Password_Connect_Host}") + +# creating client by supplying hostname and 1Password Connect API token +client: Client = new_client_from_environment( + "{1Password_Connect_Host}", + "{1Password_Connect_API_Token}") +``` + +**Get Item** + +Get a specific item by item and vault uuids: + +```python +client.get_item("{item_id}", "{vault_id}") +``` + +**Get Item By Title** + +Get item by item title and vault id + +```python +client.get_item_by_title("{item_title}", "{vault_id}") +``` + +**Get Items** + +Get a summarized list of all items for a specified vault + +```python +client.get_items("{vault_id}") +``` + +**Delete Item** + +Delete an item by item and vault ids: + +```python +client.delete_item("{item_id}", "{vault_id}") +``` + +**Create Item** + +Create an item in the specified vault. + +```python +# Example item creation. Create an item with your desired arguments. +item = onepasswordconnectsdk.models.FullItem(vault=ItemVault(id="av223f76ydutdngislnkbz6z5u"), + id="kp2td65r4wbuhocwhhijpdbfqq", + title="newtitle", + category="LOGIN", + tags=["1password-connect"], + fields=[FullItemAllOfFields(value="new_user", + purpose="USERNAME")], + ) +client.create_item("{vault_id}", item) +``` + +**Update Item** + +Item the item with the specified item and vault ids. The existing item will be overwritten with the newly supplied item. + +```python +# Example item creation. Create an item with your desired arguments. +item = onepasswordconnectsdk.models.FullItem(vault=ItemVault(id="av223f76ydutdngislnkbz6z5u"), + id="kp2td65r4wbuhocwhhijpdbfqq", + title="newtitle", + category="LOGIN", + tags=["1password-connect"], + fields=[FullItemAllOfFields(value="new_user", + purpose="USERNAME")], + ) +client.update_item("{item_id}", "{vault_id}", item) +``` + +**Get Vault** + +Get vault by vault id + +```python +client.get_vault("{vault_id}") +``` + +**Get Vaults** + +Retrieve all vaults available to the service account. + +```python +client.get_vaults() +``` + +**Load Configuration** + +Users can create `classes` or `dicts` that describe fields they wish to get the values from in 1Password. Two convienience methods are provided that will handle the fetching of values for these fields: + +- **load_dict**: Takes a dictionary with keys specifying the user desired naming scheme of the values to return. Each key's value is a dictionary that includes information on where to find the item field value in 1Password. This returns a dictionary of user specified keys with values retrieved from 1Password +- **load**: Takes an object with class attributes annotated with tags describing where to find desired fields in 1Password. Manipulates given object and fills attributes in with 1Password item field values. + +```python +# example dict configuration for onepasswordconnectsdk.load_dict(CONFIG) +CONFIG = { + "server": { + "opitem": "My database item", + "opfield": "specific_section.hostname", + "opvault": "some_vault_id", + }, + "database": { + "opitem": "My database item", + "opfield": ".database", + }, + "username": { + "opitem": "My database item", + "opfield": ".username", + }, + "password": { + "opitem": "My database item", + "opfield": ".password", + }, +} + +values_dict = onepasswordconnectsdk.load_dict(CONFIG) +``` + +```python +# example class configuration for onepasswordconnectsdk.load(CONFIG) +class Config: + server: 'opitem:"My database item" opvault:some_vault_id opfield:specific_section.hostname' = None + database: 'opitem:"My database item" opfield:.database' = None + username: 'opitem:"My database item" opfield:.username' = None + password: 'opitem:"My database item" opfield:.password' = None + +CONFIG = Config() + +values_object = onepasswordconnectsdk.load(CONFIG) +``` + +## Development + +**Testing** + +```bash +$ pytest +``` + +## Security + +1Password requests you practice responsible disclosure if you discover a vulnerability. + +Please file requests via [**BugCrowd**](https://bugcrowd.com/agilebits). + +For information about security practices, please visit our [Security homepage](https://bugcrowd.com/agilebits). diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..28f0d39 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,302 @@ +[[package]] +category = "dev" +description = "Atomic file writes." +marker = "sys_platform == \"win32\"" +name = "atomicwrites" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.4.0" + +[[package]] +category = "dev" +description = "Classes Without Boilerplate" +name = "attrs" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "20.3.0" + +[package.extras] +dev = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"] +docs = ["furo", "sphinx", "zope.interface"] +tests = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] +tests_no_zope = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] + +[[package]] +category = "main" +description = "Python package for providing Mozilla's CA Bundle." +name = "certifi" +optional = false +python-versions = "*" +version = "2020.11.8" + +[[package]] +category = "main" +description = "Universal encoding detector for Python 2 and 3" +name = "chardet" +optional = false +python-versions = "*" +version = "3.0.4" + +[[package]] +category = "dev" +description = "Cross-platform colored terminal text." +marker = "sys_platform == \"win32\"" +name = "colorama" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "0.4.4" + +[[package]] +category = "main" +description = "Internationalized Domain Names in Applications (IDNA)" +name = "idna" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "2.10" + +[[package]] +category = "dev" +description = "Read metadata from Python packages" +marker = "python_version < \"3.8\"" +name = "importlib-metadata" +optional = false +python-versions = ">=3.6" +version = "3.1.1" + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] + +[[package]] +category = "dev" +description = "iniconfig: brain-dead simple config-ini parsing" +name = "iniconfig" +optional = false +python-versions = "*" +version = "1.1.1" + +[[package]] +category = "dev" +description = "Core utilities for Python packages" +name = "packaging" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "20.7" + +[package.dependencies] +pyparsing = ">=2.0.2" + +[[package]] +category = "dev" +description = "plugin and hook calling mechanisms for python" +name = "pluggy" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "0.13.1" + +[package.dependencies] +[package.dependencies.importlib-metadata] +python = "<3.8" +version = ">=0.12" + +[package.extras] +dev = ["pre-commit", "tox"] + +[[package]] +category = "dev" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +name = "py" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.9.0" + +[[package]] +category = "dev" +description = "Python parsing module" +name = "pyparsing" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +version = "2.4.7" + +[[package]] +category = "dev" +description = "pytest: simple powerful testing with Python" +name = "pytest" +optional = false +python-versions = ">=3.5" +version = "6.1.2" + +[package.dependencies] +atomicwrites = ">=1.0" +attrs = ">=17.4.0" +colorama = "*" +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<1.0" +py = ">=1.8.2" +toml = "*" + +[package.dependencies.importlib-metadata] +python = "<3.8" +version = ">=0.12" + +[package.extras] +checkqa_mypy = ["mypy (0.780)"] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] + +[[package]] +category = "main" +description = "Extensions to the standard Python datetime module" +name = "python-dateutil" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +version = "2.8.1" + +[package.dependencies] +six = ">=1.5" + +[[package]] +category = "main" +description = "Python HTTP for Humans." +name = "requests" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "2.25.0" + +[package.dependencies] +certifi = ">=2017.4.17" +chardet = ">=3.0.2,<4" +idna = ">=2.5,<3" +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] +socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] + +[[package]] +category = "main" +description = "Python 2 and 3 compatibility utilities" +name = "six" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +version = "1.15.0" + +[[package]] +category = "dev" +description = "Python Library for Tom's Obvious, Minimal Language" +name = "toml" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +version = "0.10.2" + +[[package]] +category = "main" +description = "HTTP library with thread-safe connection pooling, file post, and more." +name = "urllib3" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" +version = "1.26.2" + +[package.extras] +brotli = ["brotlipy (>=0.6.0)"] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] + +[[package]] +category = "dev" +description = "Backport of pathlib-compatible object wrapper for zip files" +marker = "python_version < \"3.8\"" +name = "zipp" +optional = false +python-versions = ">=3.6" +version = "3.4.0" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] + +[metadata] +content-hash = "820009d29e46fdb9a3615762f9743d73abd7e67d4f1db841b0ab68472247714d" +lock-version = "1.0" +python-versions = "^3.7" + +[metadata.files] +atomicwrites = [ + {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, + {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, +] +attrs = [ + {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"}, + {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, +] +certifi = [ + {file = "certifi-2020.11.8-py2.py3-none-any.whl", hash = "sha256:1f422849db327d534e3d0c5f02a263458c3955ec0aae4ff09b95f195c59f4edd"}, + {file = "certifi-2020.11.8.tar.gz", hash = "sha256:f05def092c44fbf25834a51509ef6e631dc19765ab8a57b4e7ab85531f0a9cf4"}, +] +chardet = [ + {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, + {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, +] +colorama = [ + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, +] +idna = [ + {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, + {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, +] +importlib-metadata = [ + {file = "importlib_metadata-3.1.1-py3-none-any.whl", hash = "sha256:6112e21359ef8f344e7178aa5b72dc6e62b38b0d008e6d3cb212c5b84df72013"}, + {file = "importlib_metadata-3.1.1.tar.gz", hash = "sha256:b0c2d3b226157ae4517d9625decf63591461c66b3a808c2666d538946519d170"}, +] +iniconfig = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, +] +packaging = [ + {file = "packaging-20.7-py2.py3-none-any.whl", hash = "sha256:eb41423378682dadb7166144a4926e443093863024de508ca5c9737d6bc08376"}, + {file = "packaging-20.7.tar.gz", hash = "sha256:05af3bb85d320377db281cf254ab050e1a7ebcbf5410685a9a407e18a1f81236"}, +] +pluggy = [ + {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, + {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, +] +py = [ + {file = "py-1.9.0-py2.py3-none-any.whl", hash = "sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2"}, + {file = "py-1.9.0.tar.gz", hash = "sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342"}, +] +pyparsing = [ + {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, + {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, +] +pytest = [ + {file = "pytest-6.1.2-py3-none-any.whl", hash = "sha256:4288fed0d9153d9646bfcdf0c0428197dba1ecb27a33bb6e031d002fa88653fe"}, + {file = "pytest-6.1.2.tar.gz", hash = "sha256:c0a7e94a8cdbc5422a51ccdad8e6f1024795939cc89159a0ae7f0b316ad3823e"}, +] +python-dateutil = [ + {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"}, + {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, +] +requests = [ + {file = "requests-2.25.0-py2.py3-none-any.whl", hash = "sha256:e786fa28d8c9154e6a4de5d46a1d921b8749f8b74e28bde23768e5e16eece998"}, + {file = "requests-2.25.0.tar.gz", hash = "sha256:7f1a0b932f4a60a1a65caa4263921bb7d9ee911957e0ae4a23a6dd08185ad5f8"}, +] +six = [ + {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, + {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, +] +toml = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] +urllib3 = [ + {file = "urllib3-1.26.2-py2.py3-none-any.whl", hash = "sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473"}, + {file = "urllib3-1.26.2.tar.gz", hash = "sha256:19188f96923873c92ccb987120ec4acaa12f0461fa9ce5d3d0772bc965a39e08"}, +] +zipp = [ + {file = "zipp-3.4.0-py3-none-any.whl", hash = "sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108"}, + {file = "zipp-3.4.0.tar.gz", hash = "sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb"}, +] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..f81a380 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,18 @@ +[tool.poetry] +name = "onepasswordconnectsdk" +version = "0.0.1" +description = "" +authors = ["1Password"] + +[tool.poetry.dependencies] +python = "^3.7" +requests = "^2.24.0" +six = "^1.10" +python-dateutil = "^2.8.1" + +[tool.poetry.dev-dependencies] +pytest = "^6.0" + +[build-system] +requires = ["poetry>=0.12"] +build-backend = "poetry.masonry.api" \ No newline at end of file diff --git a/src/onepasswordconnectsdk/__init__.py b/src/onepasswordconnectsdk/__init__.py new file mode 100644 index 0000000..ab7dcd4 --- /dev/null +++ b/src/onepasswordconnectsdk/__init__.py @@ -0,0 +1,8 @@ +# coding: utf-8 + +# flake8: noqa + +from onepasswordconnectsdk.config import load +from onepasswordconnectsdk.config import load_dict +from onepasswordconnectsdk.client import new_client +from onepasswordconnectsdk.client import new_client_from_environment diff --git a/src/onepasswordconnectsdk/client.py b/src/onepasswordconnectsdk/client.py new file mode 100644 index 0000000..54ce283 --- /dev/null +++ b/src/onepasswordconnectsdk/client.py @@ -0,0 +1,523 @@ +"""Python Client for connecting to 1Password Connect""" +from dateutil.parser import parse +import json +import os +import re +import six +import requests +import datetime +from requests.exceptions import HTTPError +import onepasswordconnectsdk +from onepasswordconnectsdk.models import FullItem, ItemVault + + +ENV_SERVICE_ACCOUNT_JWT_VARIABLE = "OP_CONNECT_TOKEN" + + +class Client: + + PRIMITIVE_TYPES = (float, bool, bytes, six.text_type) + six.integer_types + NATIVE_TYPES_MAPPING = { + "int": int, + "long": int if six.PY3 else long, # type: ignore # noqa: F821 + "float": float, + "str": str, + "bool": bool, + "date": datetime.date, + "datetime": datetime.datetime, + "object": object, + } + + """Python Client Class""" + + def __init__(self, url: str, token: str): + """Initialize client""" + self.url = url + self.token = token + self.session = self.create_session() + + def create_session(self): + session = requests.Session() + session.headers.update(self.build_headers()) + return session + + def build_headers(self): + """Builds the headers needed to make a request to the server + Parameters: + + Returns: + dict: The 1Password Connect API request headers + """ + + headers = {} + headers["Authorization"] = f"Bearer {self.token}" + headers["Content-Type"] = "application/json" + return headers + + def get_item(self, item_id: str, vault_id: str): + """Get a specific item by uuid + Parameters: + item_id (str): The id of the item to be fetched + vault_id (str): The id of the vault in which to get the item from + + Returns: + Item object: The found item + """ + url = f"/v1/vaults/{vault_id}/items/{item_id}" + + response = self.build_request("GET", url) + try: + response.raise_for_status() + except HTTPError: + raise FailedToRetrieveItemException( + f"Unable to retrieve item. Received {response.status_code}\ + for {url} with message: {response.json().get('message')}" + ) + return self.deserialize(response.content, "FullItem") + + def get_item_by_title(self, title: str, vault_id: str): + """Get a specific item by title + Parameters: + title (str): The title of the item to be fetched + vault_id (str): The id of the vault in which to get the item from + + Returns: + Item object: A summary of the found item + """ + filter_query = f'title eq "{title}"' + url = f"/v1/vaults/{vault_id}/items?filter={filter_query}" + + response = self.build_request("GET", url) + try: + response.raise_for_status() + except HTTPError: + raise FailedToRetrieveItemException( + f"Unable to retrieve items. Received {response.status_code} \ + for {url} with message: {response.json().get('message')}" + ) + + if len(response.json()) != 1: + raise FailedToRetrieveItemException( + f"Found {len(response.json())} items in vault {vault_id} with \ + title {title}" + ) + + return self.deserialize(response.content, "list[Item]")[0] + + def get_items(self, vault_id: str): + """Returns a list of item summaries for the specified vault + + Args: + vault_id (str): The id of the vault in which to get the items from + + Raises: + FailedToRetrieveItemException: Thrown when a HTTP error is returned + from the 1Password Connect API + + Returns: + List[Item]: A list of summarized items + """ + url = f"/v1/vaults/{vault_id}/items" + + response = self.build_request("GET", url) + try: + response.raise_for_status() + except HTTPError: + raise FailedToRetrieveItemException( + f"Unable to retrieve items. Received {response.status_code} \ + for {url} with message: {response.json().get('message')}" + ) + + return self.deserialize(response.content, "list[Item]") + + def delete_item(self, item_id: str, vault_id: str): + """Deletes a specified item from a specified vault + + Args: + item_id (str): The id of the item in which to delete the item from + vault_id (str): The id of the vault in which to delete the item + from + + Raises: + FailedToRetrieveItemException: Thrown when a HTTP error is returned + from the 1Password Connect API + """ + url = f"/v1/vaults/{vault_id}/items/{item_id}" + + response = self.build_request("DELETE", url) + try: + response.raise_for_status() + except HTTPError: + raise FailedToRetrieveItemException( + f"Unable to delete item. Received {response.status_code}\ + for {url} with message: {response.json().get('message')}" + ) + + def create_item(self, vault_id: str, item: FullItem): + """Creates an item at the specified vault + + Args: + vault_id (str): The id of the vault in which add the item to + item (FullItem): The item to create + + Raises: + FailedToRetrieveItemException: Thrown when a HTTP error is returned + from the 1Password Connect API + + Returns: + FullItem: The created item + """ + + url = f"/v1/vaults/{vault_id}/items" + + response: requests.Response = self.build_request("POST", url, item) + try: + response.raise_for_status() + except HTTPError: + raise FailedToRetrieveItemException( + f"Unable to post item. Received {response.status_code}\ + for {url} with message: {response.json().get('message')}" + ) + return self.deserialize(response.content, "FullItem") + + def update_item(self, item_uuid: str, vault_id: str, item: FullItem): + """Update the specified item at the specified vault. + + Args: + item_uuid (str): The id of the item in which to update + vault_id (str): The id of the vault in which to update the item + item (FullItem): The updated item + + Raises: + FailedToRetrieveItemException: Thrown when a HTTP error is returned + from the 1Password Connect API + + Returns: + FullItem: The updated item + """ + url = f"/v1/vaults/{vault_id}/items/{item_uuid}" + item.id = item_uuid + item.vault = ItemVault(id=vault_id) + + response: requests.Response = self.build_request("PUT", url, item) + try: + response.raise_for_status() + except HTTPError: + raise FailedToRetrieveItemException( + f"Unable to post item. Received {response.status_code}\ + for {url} with message: {response.json().get('message')}" + ) + return self.deserialize(response.content, "FullItem") + + def get_vault(self, vault_id: str): + """Returns the vault with the given vault_id + + Args: + vault_id (str): The id of the vault in which to fetch + + Raises: + FailedToRetrieveVaultException: Thrown when a HTTP error is + returned from the 1Password Connect API + + Returns: + Vault: The specified vault + """ + url = f"/v1/vaults/{vault_id}" + response = self.build_request("GET", url) + try: + response.raise_for_status() + except HTTPError: + raise FailedToRetrieveVaultException( + f"Unable to retrieve vault. Received {response.status_code} \ + for {url} with message {response.json().get('message')}" + ) + + return self.deserialize(response.content, "Vault") + + def get_vaults(self): + """Returns all vaults for service account set in client + + Raises: + FailedToRetrieveVaultException: Thrown when a HTTP error is + returned from the 1Password Connect API + + Returns: + List[Vault]: All vaults for the service account in use + """ + url = "/v1/vaults" + response = self.build_request("GET", url) + + try: + response.raise_for_status() + except HTTPError: + raise FailedToRetrieveVaultException( + f"Unable to retrieve vaults. Received {response.status_code} \ + for {url} with message {response.json().get('message')}" + ) + + return self.deserialize(response.content, "list[Vault]") + + def build_request(self, method: str, path: str, body=None): + """Builds a http request + Parameters: + method (str): The rest method to be used + path (str): The request path + body (str): The request body + + Returns: + Response object: The request response + """ + url = f"{self.url}{path}" + + if body: + serialized_body = json.dumps(self.sanitize_for_serialization(body)) + response = self.session.request(method, url, data=serialized_body) + else: + response = self.session.request(method, url) + return response + + def deserialize(self, response, response_type): + """Deserializes response into an object. + + :param response: RESTResponse object to be deserialized. + :param response_type: class literal for + deserialized object, or string of class name. + + :return: deserialized object. + """ + # fetch data from response object + try: + data = json.loads(response) + except ValueError: + data = response + + return self.__deserialize(data, response_type) + + def sanitize_for_serialization(self, obj): + """Builds a JSON POST object. + + If obj is None, return None. + If obj is str, int, long, float, bool, return directly. + If obj is datetime.datetime, datetime.date convert to string + in iso8601 format. + If obj is list, sanitize each element in the list. + If obj is dict, return the dict. + If obj is OpenAPI model, return the properties dict. + + :param obj: The data to serialize. + :return: The serialized form of data. + """ + if obj is None: + return None + elif isinstance(obj, self.PRIMITIVE_TYPES): + return obj + elif isinstance(obj, list): + return [self.sanitize_for_serialization(sub_obj) for sub_obj in obj] # noqa: E501 + elif isinstance(obj, tuple): + return tuple(self.sanitize_for_serialization(sub_obj) for sub_obj in obj) # noqa: E501 + elif isinstance(obj, (datetime.datetime, datetime.date)): + return obj.isoformat() + + if isinstance(obj, dict): + obj_dict = obj + else: + # Convert model obj to dict except + # attributes `openapi_types`, `attribute_map` + # and attributes which value is not None. + # Convert attribute name to json key in + # model definition for request. + obj_dict = { + obj.attribute_map[attr]: getattr(obj, attr) + for attr, _ in six.iteritems(obj.openapi_types) + if getattr(obj, attr) is not None + } + + return { + key: self.sanitize_for_serialization(val) + for key, val in six.iteritems(obj_dict) + } + + def __deserialize(self, data, klass): + """Deserializes dict, list, str into an object. + + :param data: dict, list or str. + :param klass: class literal, or string of class name. + + :return: object. + """ + if data is None: + return None + + if type(klass) == str: + if klass.startswith("list["): + sub_kls = re.match(r"list\[(.*)\]", klass).group(1) + return [self.__deserialize(sub_data, sub_kls) for sub_data in data] # noqa: E501 + + if klass.startswith("dict("): + sub_kls = re.match(r"dict\(([^,]*), (.*)\)", klass).group(2) + return { + k: self.__deserialize(v, sub_kls) for k, v in six.iteritems(data) # noqa: E501 + } + + # convert str to class + if klass in self.NATIVE_TYPES_MAPPING: + klass = self.NATIVE_TYPES_MAPPING[klass] + else: + klass = getattr(onepasswordconnectsdk.models, klass) + + if klass in self.PRIMITIVE_TYPES: + return self.__deserialize_primitive(data, klass) + elif klass == object: + return self.__deserialize_object(data) + elif klass == datetime.date: + return self.__deserialize_date(data) + elif klass == datetime.datetime: + return self.__deserialize_datetime(data) + else: + return self.__deserialize_model(data, klass) + + def __deserialize_primitive(self, data, klass): + """Deserializes string to primitive type. + + :param data: str. + :param klass: class literal. + + :return: int, long, float, str, bool. + """ + try: + return klass(data) + except UnicodeEncodeError: + return six.text_type(data) + except TypeError: + return data + + def __deserialize_object(self, value): + """Return an original value. + + :return: object. + """ + return value + + def __deserialize_date(self, string): + """Deserializes string to date. + + :param string: str. + :return: date. + """ + try: + return parse(string).date() + except ImportError: + return string + except ValueError: + raise FailedToDeserializeException( + f'Failed to parse `{0}`\ + as date object".format(string)' + ) + + def __deserialize_datetime(self, string): + """Deserializes string to datetime. + + The string should be in iso8601 datetime format. + + :param string: str. + :return: datetime. + """ + try: + return parse(string) + except ImportError: + return string + except ValueError: + raise FailedToDeserializeException( + f'Failed to parse `{0}`\ + as date object".format(string)' + ) + + def __deserialize_model(self, data, klass): + """Deserializes list or dict to model. + + :param data: dict, list. + :param klass: class literal. + :return: model object. + """ + has_discriminator = False + if ( + hasattr(klass, "get_real_child_model") + and klass.discriminator_value_class_map + ): + has_discriminator = True + + if not klass.openapi_types and has_discriminator is False: + return data + + kwargs = {} + if ( + data is not None + and klass.openapi_types is not None + and isinstance(data, (list, dict)) + ): + for attr, attr_type in six.iteritems(klass.openapi_types): + if klass.attribute_map[attr] in data: + value = data[klass.attribute_map[attr]] + kwargs[attr] = self.__deserialize(value, attr_type) + + instance = klass(**kwargs) + + if has_discriminator: + klass_name = instance.get_real_child_model(data) + if klass_name: + instance = self.__deserialize(data, klass_name) + return instance + + +def new_client(url: str, token: str): + """Builds a new client for interacting with 1Password Connect + Parameters: + url: The url of the 1Password Connect API + token: The 1Password Service Account token + + Returns: + Client: The 1Password Connect client + """ + return Client(url=url, token=token) + + +def new_client_from_environment(url: str): + """Builds a new client for interacting with 1Password Connect + using the OP_TOKEN environment variable + + Parameters: + url: The url of the 1Password Connect API + token: The 1Password Service Account token + + Returns: + Client: The 1Password Connect client + """ + token = os.environ.get(ENV_SERVICE_ACCOUNT_JWT_VARIABLE) + + if token is None: + raise EnvironmentTokenNotSetException( + "There is no token available in the " + f"{ENV_SERVICE_ACCOUNT_JWT_VARIABLE} variable" + ) + + return Client(url=url, token=token) + + +class OnePasswordConnectSDKError(RuntimeError): + pass + + +class EnvironmentTokenNotSetException(OnePasswordConnectSDKError, TypeError): + pass + + +class FailedToRetrieveItemException(OnePasswordConnectSDKError): + pass + + +class FailedToRetrieveVaultException(OnePasswordConnectSDKError): + pass + + +class FailedToDeserializeException(OnePasswordConnectSDKError, TypeError): + pass diff --git a/src/onepasswordconnectsdk/config.py b/src/onepasswordconnectsdk/config.py new file mode 100644 index 0000000..4b00635 --- /dev/null +++ b/src/onepasswordconnectsdk/config.py @@ -0,0 +1,242 @@ +import os +import shlex +from typing import List, Dict +from onepasswordconnectsdk.client import Client +from onepasswordconnectsdk.models import ( + Item, + FullItem, + ParsedField, + ParsedItem, + FullItemAllOfSections, +) +from onepasswordconnectsdk.models.constants import ( + ITEM_TAG, + FIELD_TAG, + VAULT_TAG, + VAULT_ID_ENV_VARIABLE, +) + + +def load_dict(client: Client, config: dict): + """Load: Takes a dictionary with keys specifiying the user + desired naming scheme of the values to return. Each key's + value is a dictionary that includes information on where + to find the item field value in 1Password. + + Example dictionary + { + "foo": { + "opitem": "Name of item in 1Password", # noqa: RST301 + "opfield": "section_name.field_name" + }, # noqa: RST203 + "bar": { + "opitem": "Name of another item in 1Password", # noqa: RST301 + "opfield": ".field_name", + "opvault" "some_vault_id" + } # noqa: RST201 + } # noqa: RST201 + + opitem (required): describes the name of the item to access from 1Password + + offield (required): describes the name of the field to access within + the specified item + + opvault: Only required if OP_VAULT is not set. Used to decribe the + vault in which to fetch the item + + Args: + client (Client): An instantied 1Password Connect client. + + config (dict): A dict with user specfied names for keys. Each key's + value is a dictionary that includes information on where to find + the item field value in 1Password. + + Returns: + dict: A dictionary of user specified keys with values retrieved + from 1Password + """ + + items: dict = {} + config_values: Dict[str, str] = {} + + for field, tags in config.items(): + item_tag = tags.get(ITEM_TAG) + field_tag = tags.get(FIELD_TAG) + vault_tag = tags.get(VAULT_TAG) + item_vault = _vault_uuid_for_field(field=field, vault_tag=vault_tag) + key = f"{item_vault}/{item_tag}" + item: ParsedItem = items.get(key) # type: ignore + + if item: + item.fields.append(ParsedField(field, field_tag)) + else: + item = ParsedItem( + vault_uuid=item_vault, + item_title=item_tag, + fields=[ParsedField(field, field_tag)], + ) + items[key] = item + for item_key, item in items.items(): + _set_values_for_item(client=client, + parsed_item=item, + config_dict=config_values) + + return config_values + + +def load(client: Client, config: object): + """Load: Takes a an object with class attributes annotated with tags + describing where to find desired fields in 1Password. Manipulates given object + and fills attributes in with 1Password item field values. + + Example class object + class Foo(): + foo: 'opitem:"Name of Item" opfield:section_name.field_name' # noqa: E501, RST301 + bar: 'opitem:"Name of another item" opfield:.field_name opvault:vault_id' # noqa: E501 + + opitem (required): describes the name of the item to access from 1Password + + opfield (required): describes the name of the field to access + within the specified item + + opvault: Only required if OP_VAULT is not set. Used to decribe + the vault in which to fetch the item + + Args: + client (Client): An instantied 1Password Connect client. + config (object): An object of a custom annoted class. + """ + items: dict = {} + annotations = config.__class__.__annotations__ + + for field, tags in annotations.items(): + parsed_tags = _parse_tags(tags) + item_tag = parsed_tags.get(ITEM_TAG) + field_tag = parsed_tags.get(FIELD_TAG) + vault_tag = parsed_tags.get(VAULT_TAG) + item_vault = _vault_uuid_for_field(field=field, vault_tag=vault_tag) + key = f"{item_vault}/{item_tag}" + item: ParsedItem = items.get(key) # type: ignore + + if item: + item.fields.append(ParsedField(field, field_tag)) + else: + item = ParsedItem( + vault_uuid=item_vault, + item_title=item_tag, + fields=[ParsedField(field, field_tag)], + ) + items[key] = item + + for item_key, item in items.items(): + _set_values_for_item(client=client, + parsed_item=item, + config_object=config) + + return config + + +def _parse_tags(tags: str): + """Takes a string of whitespace seperated tags + and assigns them to a dict. Expects each tag to + be of the format 'tag_name:tag_value' + + Args: + tags (str): The tags to parse + + Returns: + dict: The parsed tags + """ + return dict(item.split(":") for item in shlex.split(tags)) # type: ignore + + +def _vault_uuid_for_field(field: str, vault_tag: dict): + env_vault_uuid = os.environ.get(VAULT_ID_ENV_VARIABLE) + if vault_tag: + return vault_tag + elif env_vault_uuid: + return env_vault_uuid + raise NoVaultSetForFieldException( + f"There \ + is no vault for {field} field" + ) + + +def _set_values_for_item( + client: Client, + parsed_item: ParsedItem, + config_dict={}, + config_object: object = None, +): + # Retrieves a summary item + summary_item: Item = client.get_item_by_title( + parsed_item.item_title, parsed_item.vault_uuid + ) + # Fetching the full item + item: FullItem = client.get_item(summary_item.id, parsed_item.vault_uuid) + + sections = _convert_sections_to_dict(item.sections) + + for parsed_field in parsed_item.fields: + if parsed_field.tag is None: + raise NoFieldTagSetForFieldException( + f"There is no {FIELD_TAG} specified \ + for {parsed_field.name}" + ) + + path_parts = parsed_field.tag.split(".") + if len(path_parts) != 2: + raise InvalidFieldPathException( + f"Invalid field path format for \ + {parsed_field.name}" + ) + + value_found = False + for field in item.fields: + try: + section_id = field.section.id + except AttributeError: + section_id = None + + if field.label == path_parts[1] and ( + section_id is None or section_id == sections[path_parts[0]] + ): + value_found = True + + if config_object: + setattr(config_object, parsed_field.name, field.value) + else: + config_dict[parsed_field.name] = field.value + break + if not value_found: + raise UnknownSectionAndFieldTag( + f"There is no section {path_parts[0]} \ + for field {path_parts[1]}" + ) + + +def _convert_sections_to_dict(sections: List[FullItemAllOfSections]): + if not sections: + return {} + section_dict = {section.label: section.id for section in sections} + return section_dict + + +class ConfigurationError(RuntimeError): + pass + + +class NoVaultSetForFieldException(ConfigurationError): + pass + + +class NoFieldTagSetForFieldException(ConfigurationError): + pass + + +class InvalidFieldPathException(ConfigurationError): + pass + + +class UnknownSectionAndFieldTag(ConfigurationError): + pass diff --git a/src/onepasswordconnectsdk/models/__init__.py b/src/onepasswordconnectsdk/models/__init__.py new file mode 100644 index 0000000..9d3b29c --- /dev/null +++ b/src/onepasswordconnectsdk/models/__init__.py @@ -0,0 +1,20 @@ +# coding: utf-8 + +# flake8: noqa + + +from __future__ import absolute_import + +# import models into model package +from onepasswordconnectsdk.models.error import Error +from onepasswordconnectsdk.models.full_item import FullItem +from onepasswordconnectsdk.models.full_item_all_of import FullItemAllOf +from onepasswordconnectsdk.models.full_item_all_of_fields import FullItemAllOfFields +from onepasswordconnectsdk.models.full_item_all_of_section import FullItemAllOfSection +from onepasswordconnectsdk.models.full_item_all_of_sections import FullItemAllOfSections +from onepasswordconnectsdk.models.item import Item +from onepasswordconnectsdk.models.item_urls import ItemUrls +from onepasswordconnectsdk.models.item_vault import ItemVault +from onepasswordconnectsdk.models.parsed_field import ParsedField +from onepasswordconnectsdk.models.parsed_item import ParsedItem +from onepasswordconnectsdk.models.vault import Vault diff --git a/src/onepasswordconnectsdk/models/constants.py b/src/onepasswordconnectsdk/models/constants.py new file mode 100644 index 0000000..065a65e --- /dev/null +++ b/src/onepasswordconnectsdk/models/constants.py @@ -0,0 +1,4 @@ +FIELD_TAG = "opfield" +ITEM_TAG = "opitem" +VAULT_ID_ENV_VARIABLE = "OP_VAULT" +VAULT_TAG = "opvault" diff --git a/src/onepasswordconnectsdk/models/error.py b/src/onepasswordconnectsdk/models/error.py new file mode 100644 index 0000000..b1fc6ae --- /dev/null +++ b/src/onepasswordconnectsdk/models/error.py @@ -0,0 +1,140 @@ +# coding: utf-8 + +""" + 1Password Connect + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) # noqa: E501 + + The version of the OpenAPI document: 0.2.1 + Generated by: https://openapi-generator.tech +""" + + +import pprint +import re # noqa: F401 + +import six + + +class Error(object): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + """ + + """ + Attributes: + openapi_types (dict): The key is attribute name + and the value is attribute type. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + """ + openapi_types = {"status": "int", "message": "str"} + + attribute_map = {"status": "status", "message": "message"} + + def __init__(self, status=None, message=None): # noqa: E501 + self._status = None + self._message = None + self.discriminator = None + + if status is not None: + self.status = status + if message is not None: + self.message = message + + @property + def status(self): + """Gets the status of this Error. # noqa: E501 + + HTTP Status Code # noqa: E501 + + :return: The status of this Error. # noqa: E501 + :rtype: int + """ + return self._status + + @status.setter + def status(self, status): + """Sets the status of this Error. + + HTTP Status Code # noqa: E501 + + :param status: The status of this Error. # noqa: E501 + :type: int + """ + + self._status = status + + @property + def message(self): + """Gets the message of this Error. # noqa: E501 + + A message detailing the error # noqa: E501 + + :return: The message of this Error. # noqa: E501 + :rtype: str + """ + return self._message + + @message.setter + def message(self, message): + """Sets the message of this Error. + + A message detailing the error # noqa: E501 + + :param message: The message of this Error. # noqa: E501 + :type: str + """ + + self._message = message + + def to_dict(self): + """Returns the model properties as a dict""" + result = {} + + for attr, _ in six.iteritems(self.openapi_types): + value = getattr(self, attr) + if isinstance(value, list): + result[attr] = list( + map(lambda x: x.to_dict() if hasattr(x, "to_dict") + else x, value) + ) + elif hasattr(value, "to_dict"): + result[attr] = value.to_dict() + elif isinstance(value, dict): + result[attr] = dict( + map( + lambda item: (item[0], item[1].to_dict()) + if hasattr(item[1], "to_dict") + else item, + value.items(), + ) + ) + else: + result[attr] = value + + return result + + def to_str(self): + """Returns the string representation of the model""" + return pprint.pformat(self.to_dict()) + + def __repr__(self): + """For `print` and `pprint`""" + return self.to_str() + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, Error): + return False + + return self.to_dict() == other.to_dict() + + def __ne__(self, other): + """Returns true if both objects are not equal""" + if not isinstance(other, Error): + return True + + return self.to_dict() != other.to_dict() diff --git a/src/onepasswordconnectsdk/models/full_item.py b/src/onepasswordconnectsdk/models/full_item.py new file mode 100644 index 0000000..95bd9cd --- /dev/null +++ b/src/onepasswordconnectsdk/models/full_item.py @@ -0,0 +1,456 @@ +# coding: utf-8 + +""" + 1Password Connect + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) # noqa: E501 + + The version of the OpenAPI document: 0.2.1 + Generated by: https://openapi-generator.tech +""" + + +import pprint +import re # noqa: F401 + +import six + + +class FullItem(object): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + """ + + """ + Attributes: + openapi_types (dict): The key is attribute name + and the value is attribute type. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + """ + openapi_types = { + 'id': 'str', + 'title': 'str', + 'vault': 'ItemVault', + 'category': 'str', + 'urls': 'list[ItemUrls]', + 'favorite': 'bool', + 'tags': 'list[str]', + 'version': 'int', + 'trashed': 'bool', + 'created_at': 'datetime', + 'updated_at': 'datetime', + 'last_edited_by': 'str', + 'sections': 'list[FullItemAllOfSections]', + 'fields': 'list[FullItemAllOfFields]' + } + + attribute_map = { + 'id': 'id', + 'title': 'title', + 'vault': 'vault', + 'category': 'category', + 'urls': 'urls', + 'favorite': 'favorite', + 'tags': 'tags', + 'version': 'version', + 'trashed': 'trashed', + 'created_at': 'createdAt', + 'updated_at': 'updatedAt', + 'last_edited_by': 'lastEditedBy', + 'sections': 'sections', + 'fields': 'fields' + } + + def __init__(self, id=None, title=None, vault=None, category=None, urls=None, favorite=False, tags=None, version=None, trashed=False, created_at=None, updated_at=None, last_edited_by=None, sections=None, fields=None): # noqa: E501 + self._id = None + self._title = None + self._vault = None + self._category = None + self._urls = None + self._favorite = None + self._tags = None + self._version = None + self._trashed = None + self._created_at = None + self._updated_at = None + self._last_edited_by = None + self._sections = None + self._fields = None + self.discriminator = None + + if id is not None: + self.id = id + if title is not None: + self.title = title + self.vault = vault + if category is not None: + self.category = category + if urls is not None: + self.urls = urls + if favorite is not None: + self.favorite = favorite + if tags is not None: + self.tags = tags + if version is not None: + self.version = version + if trashed is not None: + self.trashed = trashed + if created_at is not None: + self.created_at = created_at + if updated_at is not None: + self.updated_at = updated_at + if last_edited_by is not None: + self.last_edited_by = last_edited_by + if sections is not None: + self.sections = sections + if fields is not None: + self.fields = fields + + @property + def id(self): + """Gets the id of this FullItem. # noqa: E501 + + + :return: The id of this FullItem. # noqa: E501 + :rtype: str + """ + return self._id + + @id.setter + def id(self, id): + """Sets the id of this FullItem. + + + :param id: The id of this FullItem. # noqa: E501 + :type: str + """ + + self._id = id + + @property + def title(self): + """Gets the title of this FullItem. # noqa: E501 + + + :return: The title of this FullItem. # noqa: E501 + :rtype: str + """ + return self._title + + @title.setter + def title(self, title): + """Sets the title of this FullItem. + + + :param title: The title of this FullItem. # noqa: E501 + :type: str + """ + + self._title = title + + @property + def vault(self): + """Gets the vault of this FullItem. # noqa: E501 + + + :return: The vault of this FullItem. # noqa: E501 + :rtype: ItemVault + """ + return self._vault + + @vault.setter + def vault(self, vault): + """Sets the vault of this FullItem. + + + :param vault: The vault of this FullItem. # noqa: E501 + :type: ItemVault + """ + + self._vault = vault + + @property + def category(self): + """Gets the category of this FullItem. # noqa: E501 + + + :return: The category of this FullItem. # noqa: E501 + :rtype: str + """ + return self._category + + @category.setter + def category(self, category): + """Sets the category of this FullItem. + + + :param category: The category of this FullItem. # noqa: E501 + :type: str + """ + allowed_values = ["LOGIN", "PASSWORD", "SERVER", "DATABASE", "CREDIT_CARD", "MEMBERSHIP", "PASSPORT", "SOFTWARE_LICENSE", "OUTDOOR_LICENSE", "SECURE_NOTE", "WIRELESS_ROUTER", "BANK_ACCOUNT", "DRIVER_LICENSE", "IDENTITY", "REWARD_PROGRAM", "DOCUMENT", "EMAIL_ACCOUNT", "SOCIAL_SECURITY_NUMBER", "CUSTOM"] # noqa: E501 + if category not in allowed_values: # noqa: E501 + raise ValueError( + "Invalid value for `category` ({0}), must be one of {1}" # noqa: E501 + .format(category, allowed_values) + ) + + self._category = category + + @property + def urls(self): + """Gets the urls of this FullItem. # noqa: E501 + + + :return: The urls of this FullItem. # noqa: E501 + :rtype: list[ItemUrls] + """ + return self._urls + + @urls.setter + def urls(self, urls): + """Sets the urls of this FullItem. + + + :param urls: The urls of this FullItem. # noqa: E501 + :type: list[ItemUrls] + """ + + self._urls = urls + + @property + def favorite(self): + """Gets the favorite of this FullItem. # noqa: E501 + + + :return: The favorite of this FullItem. # noqa: E501 + :rtype: bool + """ + return self._favorite + + @favorite.setter + def favorite(self, favorite): + """Sets the favorite of this FullItem. + + + :param favorite: The favorite of this FullItem. # noqa: E501 + :type: bool + """ + + self._favorite = favorite + + @property + def tags(self): + """Gets the tags of this FullItem. # noqa: E501 + + + :return: The tags of this FullItem. # noqa: E501 + :rtype: list[str] + """ + return self._tags + + @tags.setter + def tags(self, tags): + """Sets the tags of this FullItem. + + + :param tags: The tags of this FullItem. # noqa: E501 + :type: list[str] + """ + + self._tags = tags + + @property + def version(self): + """Gets the version of this FullItem. # noqa: E501 + + + :return: The version of this FullItem. # noqa: E501 + :rtype: int + """ + return self._version + + @version.setter + def version(self, version): + """Sets the version of this FullItem. + + + :param version: The version of this FullItem. # noqa: E501 + :type: int + """ + + self._version = version + + @property + def trashed(self): + """Gets the trashed of this FullItem. # noqa: E501 + + + :return: The trashed of this FullItem. # noqa: E501 + :rtype: bool + """ + return self._trashed + + @trashed.setter + def trashed(self, trashed): + """Sets the trashed of this FullItem. + + + :param trashed: The trashed of this FullItem. # noqa: E501 + :type: bool + """ + + self._trashed = trashed + + @property + def created_at(self): + """Gets the created_at of this FullItem. # noqa: E501 + + + :return: The created_at of this FullItem. # noqa: E501 + :rtype: datetime + """ + return self._created_at + + @created_at.setter + def created_at(self, created_at): + """Sets the created_at of this FullItem. + + + :param created_at: The created_at of this FullItem. # noqa: E501 + :type: datetime + """ + + self._created_at = created_at + + @property + def updated_at(self): + """Gets the updated_at of this FullItem. # noqa: E501 + + + :return: The updated_at of this FullItem. # noqa: E501 + :rtype: datetime + """ + return self._updated_at + + @updated_at.setter + def updated_at(self, updated_at): + """Sets the updated_at of this FullItem. + + + :param updated_at: The updated_at of this FullItem. # noqa: E501 + :type: datetime + """ + + self._updated_at = updated_at + + @property + def last_edited_by(self): + """Gets the last_edited_by of this FullItem. # noqa: E501 + + + :return: The last_edited_by of this FullItem. # noqa: E501 + :rtype: str + """ + return self._last_edited_by + + @last_edited_by.setter + def last_edited_by(self, last_edited_by): + """Sets the last_edited_by of this FullItem. + + + :param last_edited_by: The last_edited_by of this FullItem. # noqa: E501 + :type: str + """ + + self._last_edited_by = last_edited_by + + @property + def sections(self): + """Gets the sections of this FullItem. # noqa: E501 + + + :return: The sections of this FullItem. # noqa: E501 + :rtype: list[FullItemAllOfSections] + """ + return self._sections + + @sections.setter + def sections(self, sections): + """Sets the sections of this FullItem. + + + :param sections: The sections of this FullItem. # noqa: E501 + :type: list[FullItemAllOfSections] + """ + + self._sections = sections + + @property + def fields(self): + """Gets the fields of this FullItem. # noqa: E501 + + + :return: The fields of this FullItem. # noqa: E501 + :rtype: list[FullItemAllOfFields] + """ + return self._fields + + @fields.setter + def fields(self, fields): + """Sets the fields of this FullItem. + + + :param fields: The fields of this FullItem. # noqa: E501 + :type: list[FullItemAllOfFields] + """ + + self._fields = fields + + def to_dict(self): + """Returns the model properties as a dict""" + result = {} + + for attr, _ in six.iteritems(self.openapi_types): + value = getattr(self, attr) + if isinstance(value, list): + result[attr] = list(map( + lambda x: x.to_dict() if hasattr(x, "to_dict") else x, + value + )) + elif hasattr(value, "to_dict"): + result[attr] = value.to_dict() + elif isinstance(value, dict): + result[attr] = dict(map( + lambda item: (item[0], item[1].to_dict()) + if hasattr(item[1], "to_dict") else item, + value.items() + )) + else: + result[attr] = value + + return result + + def to_str(self): + """Returns the string representation of the model""" + return pprint.pformat(self.to_dict()) + + def __repr__(self): + """For `print` and `pprint`""" + return self.to_str() + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, FullItem): + return False + + return self.to_dict() == other.to_dict() + + def __ne__(self, other): + """Returns true if both objects are not equal""" + if not isinstance(other, FullItem): + return True + + return self.to_dict() != other.to_dict() diff --git a/src/onepasswordconnectsdk/models/full_item_all_of.py b/src/onepasswordconnectsdk/models/full_item_all_of.py new file mode 100644 index 0000000..4823ea3 --- /dev/null +++ b/src/onepasswordconnectsdk/models/full_item_all_of.py @@ -0,0 +1,139 @@ +# coding: utf-8 + +""" + 1Password Connect + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) # noqa: E501 + + The version of the OpenAPI document: 0.2.1 + Generated by: https://openapi-generator.tech +""" + + +import pprint +import re # noqa: F401 + +import six + + +class FullItemAllOf(object): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + """ + + """ + Attributes: + openapi_types (dict): The key is attribute name + and the value is attribute type. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + """ + openapi_types = { + 'sections': 'list[FullItemAllOfSections]', + 'fields': 'list[FullItemAllOfFields]' + } + + attribute_map = { + 'sections': 'sections', + 'fields': 'fields' + } + + def __init__(self, sections=None, fields=None): # noqa: E501 + self._sections = None + self._fields = None + self.discriminator = None + + if sections is not None: + self.sections = sections + if fields is not None: + self.fields = fields + + @property + def sections(self): + """Gets the sections of this FullItemAllOf. # noqa: E501 + + + :return: The sections of this FullItemAllOf. # noqa: E501 + :rtype: list[FullItemAllOfSections] + """ + return self._sections + + @sections.setter + def sections(self, sections): + """Sets the sections of this FullItemAllOf. + + + :param sections: The sections of this FullItemAllOf. # noqa: E501 + :type: list[FullItemAllOfSections] + """ + + self._sections = sections + + @property + def fields(self): + """Gets the fields of this FullItemAllOf. # noqa: E501 + + + :return: The fields of this FullItemAllOf. # noqa: E501 + :rtype: list[FullItemAllOfFields] + """ + return self._fields + + @fields.setter + def fields(self, fields): + """Sets the fields of this FullItemAllOf. + + + :param fields: The fields of this FullItemAllOf. # noqa: E501 + :type: list[FullItemAllOfFields] + """ + + self._fields = fields + + def to_dict(self): + """Returns the model properties as a dict""" + result = {} + + for attr, _ in six.iteritems(self.openapi_types): + value = getattr(self, attr) + if isinstance(value, list): + result[attr] = list(map( + lambda x: x.to_dict() if hasattr(x, "to_dict") else x, + value + )) + elif hasattr(value, "to_dict"): + result[attr] = value.to_dict() + elif isinstance(value, dict): + result[attr] = dict(map( + lambda item: (item[0], item[1].to_dict()) + if hasattr(item[1], "to_dict") else item, + value.items() + )) + else: + result[attr] = value + + return result + + def to_str(self): + """Returns the string representation of the model""" + return pprint.pformat(self.to_dict()) + + def __repr__(self): + """For `print` and `pprint`""" + return self.to_str() + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, FullItemAllOf): + return False + + return self.to_dict() == other.to_dict() + + def __ne__(self, other): + """Returns true if both objects are not equal""" + if not isinstance(other, FullItemAllOf): + return True + + return self.to_dict() != other.to_dict() diff --git a/src/onepasswordconnectsdk/models/full_item_all_of_fields.py b/src/onepasswordconnectsdk/models/full_item_all_of_fields.py new file mode 100644 index 0000000..cc5b804 --- /dev/null +++ b/src/onepasswordconnectsdk/models/full_item_all_of_fields.py @@ -0,0 +1,312 @@ +# coding: utf-8 + +""" + 1Password Connect + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) # noqa: E501 + + The version of the OpenAPI document: 0.2.1 + Generated by: https://openapi-generator.tech +""" + + +import pprint +import re # noqa: F401 + +import six + + +class FullItemAllOfFields(object): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + """ + + """ + Attributes: + openapi_types (dict): The key is attribute name + and the value is attribute type. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + """ + openapi_types = { + 'id': 'str', + 'section': 'FullItemAllOfSection', + 'type': 'str', + 'purpose': 'str', + 'label': 'str', + 'value': 'str', + 'generate': 'bool', + 'entropy': 'float' + } + + attribute_map = { + 'id': 'id', + 'section': 'section', + 'type': 'type', + 'purpose': 'purpose', + 'label': 'label', + 'value': 'value', + 'generate': 'generate', + 'entropy': 'entropy' + } + + def __init__(self, id=None, section=None, type='STRING', purpose=None, label=None, value=None, generate=False, entropy=None): # noqa: E501 + self._id = None + self._section = None + self._type = None + self._purpose = None + self._label = None + self._value = None + self._generate = None + self._entropy = None + self.discriminator = None + + self.id = id + if section is not None: + self.section = section + if type is not None: + self.type = type + if purpose is not None: + self.purpose = purpose + if label is not None: + self.label = label + if value is not None: + self.value = value + if generate is not None: + self.generate = generate + if entropy is not None: + self.entropy = entropy + + @property + def id(self): + """Gets the id of this FullItemAllOfFields. # noqa: E501 + + + :return: The id of this FullItemAllOfFields. # noqa: E501 + :rtype: str + """ + return self._id + + @id.setter + def id(self, id): + """Sets the id of this FullItemAllOfFields. + + + :param id: The id of this FullItemAllOfFields. # noqa: E501 + :type: str + """ + + self._id = id + + @property + def section(self): + """Gets the section of this FullItemAllOfFields. # noqa: E501 + + + :return: The section of this FullItemAllOfFields. # noqa: E501 + :rtype: FullItemAllOfSection + """ + return self._section + + @section.setter + def section(self, section): + """Sets the section of this FullItemAllOfFields. + + + :param section: The section of this FullItemAllOfFields. # noqa: E501 + :type: FullItemAllOfSection + """ + + self._section = section + + @property + def type(self): + """Gets the type of this FullItemAllOfFields. # noqa: E501 + + + :return: The type of this FullItemAllOfFields. # noqa: E501 + :rtype: str + """ + return self._type + + @type.setter + def type(self, type): + """Sets the type of this FullItemAllOfFields. + + + :param type: The type of this FullItemAllOfFields. # noqa: E501 + :type: str + """ + allowed_values = ["STRING", "EMAIL", "CONCEALED", "URL", "TOTP", "DATE", "MONTH_YEAR", "MENU"] # noqa: E501 + if type not in allowed_values: # noqa: E501 + raise ValueError( + "Invalid value for `type` ({0}), must be one of {1}" # noqa: E501 + .format(type, allowed_values) + ) + + self._type = type + + @property + def purpose(self): + """Gets the purpose of this FullItemAllOfFields. # noqa: E501 + + Some item types, Login and Password, have fields used for autofill. This property indicates that purpose. # noqa: E501 + + :return: The purpose of this FullItemAllOfFields. # noqa: E501 + :rtype: str + """ + return self._purpose + + @purpose.setter + def purpose(self, purpose): + """Sets the purpose of this FullItemAllOfFields. + + Some item types, Login and Password, have fields used for autofill. This property indicates that purpose. # noqa: E501 + + :param purpose: The purpose of this FullItemAllOfFields. # noqa: E501 + :type: str + """ + allowed_values = ["", "USERNAME", "PASSWORD", "NOTES"] # noqa: E501 + if purpose not in allowed_values: # noqa: E501 + raise ValueError( + "Invalid value for `purpose` ({0}), must be one of {1}" # noqa: E501 + .format(purpose, allowed_values) + ) + + self._purpose = purpose + + @property + def label(self): + """Gets the label of this FullItemAllOfFields. # noqa: E501 + + + :return: The label of this FullItemAllOfFields. # noqa: E501 + :rtype: str + """ + return self._label + + @label.setter + def label(self, label): + """Sets the label of this FullItemAllOfFields. + + + :param label: The label of this FullItemAllOfFields. # noqa: E501 + :type: str + """ + + self._label = label + + @property + def value(self): + """Gets the value of this FullItemAllOfFields. # noqa: E501 + + + :return: The value of this FullItemAllOfFields. # noqa: E501 + :rtype: str + """ + return self._value + + @value.setter + def value(self, value): + """Sets the value of this FullItemAllOfFields. + + + :param value: The value of this FullItemAllOfFields. # noqa: E501 + :type: str + """ + + self._value = value + + @property + def generate(self): + """Gets the generate of this FullItemAllOfFields. # noqa: E501 + + If value is not present then a new value should be generated for this field # noqa: E501 + + :return: The generate of this FullItemAllOfFields. # noqa: E501 + :rtype: bool + """ + return self._generate + + @generate.setter + def generate(self, generate): + """Sets the generate of this FullItemAllOfFields. + + If value is not present then a new value should be generated for this field # noqa: E501 + + :param generate: The generate of this FullItemAllOfFields. # noqa: E501 + :type: bool + """ + + self._generate = generate + + @property + def entropy(self): + """Gets the entropy of this FullItemAllOfFields. # noqa: E501 + + For fields with a purpose of `PASSWORD` this is the entropy of the value # noqa: E501 + + :return: The entropy of this FullItemAllOfFields. # noqa: E501 + :rtype: float + """ + return self._entropy + + @entropy.setter + def entropy(self, entropy): + """Sets the entropy of this FullItemAllOfFields. + + For fields with a purpose of `PASSWORD` this is the entropy of the value # noqa: E501 + + :param entropy: The entropy of this FullItemAllOfFields. # noqa: E501 + :type: float + """ + + self._entropy = entropy + + def to_dict(self): + """Returns the model properties as a dict""" + result = {} + + for attr, _ in six.iteritems(self.openapi_types): + value = getattr(self, attr) + if isinstance(value, list): + result[attr] = list(map( + lambda x: x.to_dict() if hasattr(x, "to_dict") else x, + value + )) + elif hasattr(value, "to_dict"): + result[attr] = value.to_dict() + elif isinstance(value, dict): + result[attr] = dict(map( + lambda item: (item[0], item[1].to_dict()) + if hasattr(item[1], "to_dict") else item, + value.items() + )) + else: + result[attr] = value + + return result + + def to_str(self): + """Returns the string representation of the model""" + return pprint.pformat(self.to_dict()) + + def __repr__(self): + """For `print` and `pprint`""" + return self.to_str() + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, FullItemAllOfFields): + return False + + return self.to_dict() == other.to_dict() + + def __ne__(self, other): + """Returns true if both objects are not equal""" + if not isinstance(other, FullItemAllOfFields): + return True + + return self.to_dict() != other.to_dict() diff --git a/src/onepasswordconnectsdk/models/full_item_all_of_section.py b/src/onepasswordconnectsdk/models/full_item_all_of_section.py new file mode 100644 index 0000000..4630851 --- /dev/null +++ b/src/onepasswordconnectsdk/models/full_item_all_of_section.py @@ -0,0 +1,113 @@ +# coding: utf-8 + +""" + 1Password Connect + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) # noqa: E501 + + The version of the OpenAPI document: 0.2.1 + Generated by: https://openapi-generator.tech +""" + + +import pprint +import re # noqa: F401 + +import six + + +class FullItemAllOfSection(object): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + """ + + """ + Attributes: + openapi_types (dict): The key is attribute name + and the value is attribute type. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + """ + openapi_types = { + 'id': 'str' + } + + attribute_map = { + 'id': 'id' + } + + def __init__(self, id=None): # noqa: E501 + self._id = None + self.discriminator = None + + if id is not None: + self.id = id + + @property + def id(self): + """Gets the id of this FullItemAllOfSection. # noqa: E501 + + + :return: The id of this FullItemAllOfSection. # noqa: E501 + :rtype: str + """ + return self._id + + @id.setter + def id(self, id): + """Sets the id of this FullItemAllOfSection. + + + :param id: The id of this FullItemAllOfSection. # noqa: E501 + :type: str + """ + + self._id = id + + def to_dict(self): + """Returns the model properties as a dict""" + result = {} + + for attr, _ in six.iteritems(self.openapi_types): + value = getattr(self, attr) + if isinstance(value, list): + result[attr] = list(map( + lambda x: x.to_dict() if hasattr(x, "to_dict") else x, + value + )) + elif hasattr(value, "to_dict"): + result[attr] = value.to_dict() + elif isinstance(value, dict): + result[attr] = dict(map( + lambda item: (item[0], item[1].to_dict()) + if hasattr(item[1], "to_dict") else item, + value.items() + )) + else: + result[attr] = value + + return result + + def to_str(self): + """Returns the string representation of the model""" + return pprint.pformat(self.to_dict()) + + def __repr__(self): + """For `print` and `pprint`""" + return self.to_str() + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, FullItemAllOfSection): + return False + + return self.to_dict() == other.to_dict() + + def __ne__(self, other): + """Returns true if both objects are not equal""" + if not isinstance(other, FullItemAllOfSection): + return True + + return self.to_dict() != other.to_dict() diff --git a/src/onepasswordconnectsdk/models/full_item_all_of_sections.py b/src/onepasswordconnectsdk/models/full_item_all_of_sections.py new file mode 100644 index 0000000..d74af07 --- /dev/null +++ b/src/onepasswordconnectsdk/models/full_item_all_of_sections.py @@ -0,0 +1,138 @@ +# coding: utf-8 + +""" + 1Password Connect + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) # noqa: E501 + + The version of the OpenAPI document: 0.2.1 + Generated by: https://openapi-generator.tech +""" + + +import pprint +import re # noqa: F401 + +import six + + +class FullItemAllOfSections(object): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + """ + + """ + Attributes: + openapi_types (dict): The key is attribute name + and the value is attribute type. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + """ + openapi_types = { + 'id': 'str', + 'label': 'str' + } + + attribute_map = { + 'id': 'id', + 'label': 'label' + } + + def __init__(self, id=None, label=None): # noqa: E501 + self._id = None + self._label = None + self.discriminator = None + + self.id = id + if label is not None: + self.label = label + + @property + def id(self): + """Gets the id of this FullItemAllOfSections. # noqa: E501 + + + :return: The id of this FullItemAllOfSections. # noqa: E501 + :rtype: str + """ + return self._id + + @id.setter + def id(self, id): + """Sets the id of this FullItemAllOfSections. + + + :param id: The id of this FullItemAllOfSections. # noqa: E501 + :type: str + """ + + self._id = id + + @property + def label(self): + """Gets the label of this FullItemAllOfSections. # noqa: E501 + + + :return: The label of this FullItemAllOfSections. # noqa: E501 + :rtype: str + """ + return self._label + + @label.setter + def label(self, label): + """Sets the label of this FullItemAllOfSections. + + + :param label: The label of this FullItemAllOfSections. # noqa: E501 + :type: str + """ + + self._label = label + + def to_dict(self): + """Returns the model properties as a dict""" + result = {} + + for attr, _ in six.iteritems(self.openapi_types): + value = getattr(self, attr) + if isinstance(value, list): + result[attr] = list(map( + lambda x: x.to_dict() if hasattr(x, "to_dict") else x, + value + )) + elif hasattr(value, "to_dict"): + result[attr] = value.to_dict() + elif isinstance(value, dict): + result[attr] = dict(map( + lambda item: (item[0], item[1].to_dict()) + if hasattr(item[1], "to_dict") else item, + value.items() + )) + else: + result[attr] = value + + return result + + def to_str(self): + """Returns the string representation of the model""" + return pprint.pformat(self.to_dict()) + + def __repr__(self): + """For `print` and `pprint`""" + return self.to_str() + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, FullItemAllOfSections): + return False + + return self.to_dict() == other.to_dict() + + def __ne__(self, other): + """Returns true if both objects are not equal""" + if not isinstance(other, FullItemAllOfSections): + return True + + return self.to_dict() != other.to_dict() diff --git a/src/onepasswordconnectsdk/models/item.py b/src/onepasswordconnectsdk/models/item.py new file mode 100644 index 0000000..518e3fe --- /dev/null +++ b/src/onepasswordconnectsdk/models/item.py @@ -0,0 +1,404 @@ +# coding: utf-8 + +""" + 1Password Connect + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) # noqa: E501 + + The version of the OpenAPI document: 0.2.1 + Generated by: https://openapi-generator.tech +""" + + +import pprint +import re # noqa: F401 + +import six + + +class Item(object): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + """ + + """ + Attributes: + openapi_types (dict): The key is attribute name + and the value is attribute type. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + """ + openapi_types = { + 'id': 'str', + 'title': 'str', + 'vault': 'ItemVault', + 'category': 'str', + 'urls': 'list[ItemUrls]', + 'favorite': 'bool', + 'tags': 'list[str]', + 'version': 'int', + 'trashed': 'bool', + 'created_at': 'datetime', + 'updated_at': 'datetime', + 'last_edited_by': 'str' + } + + attribute_map = { + 'id': 'id', + 'title': 'title', + 'vault': 'vault', + 'category': 'category', + 'urls': 'urls', + 'favorite': 'favorite', + 'tags': 'tags', + 'version': 'version', + 'trashed': 'trashed', + 'created_at': 'createdAt', + 'updated_at': 'updatedAt', + 'last_edited_by': 'lastEditedBy' + } + + def __init__(self, id=None, title=None, vault=None, category=None, urls=None, favorite=False, tags=None, version=None, trashed=False, created_at=None, updated_at=None, last_edited_by=None): # noqa: E501 + self._id = None + self._title = None + self._vault = None + self._category = None + self._urls = None + self._favorite = None + self._tags = None + self._version = None + self._trashed = None + self._created_at = None + self._updated_at = None + self._last_edited_by = None + self.discriminator = None + + if id is not None: + self.id = id + if title is not None: + self.title = title + self.vault = vault + if category is not None: + self.category = category + if urls is not None: + self.urls = urls + if favorite is not None: + self.favorite = favorite + if tags is not None: + self.tags = tags + if version is not None: + self.version = version + if trashed is not None: + self.trashed = trashed + if created_at is not None: + self.created_at = created_at + if updated_at is not None: + self.updated_at = updated_at + if last_edited_by is not None: + self.last_edited_by = last_edited_by + + @property + def id(self): + """Gets the id of this Item. # noqa: E501 + + + :return: The id of this Item. # noqa: E501 + :rtype: str + """ + return self._id + + @id.setter + def id(self, id): + """Sets the id of this Item. + + + :param id: The id of this Item. # noqa: E501 + :type: str + """ + + self._id = id + + @property + def title(self): + """Gets the title of this Item. # noqa: E501 + + + :return: The title of this Item. # noqa: E501 + :rtype: str + """ + return self._title + + @title.setter + def title(self, title): + """Sets the title of this Item. + + + :param title: The title of this Item. # noqa: E501 + :type: str + """ + + self._title = title + + @property + def vault(self): + """Gets the vault of this Item. # noqa: E501 + + + :return: The vault of this Item. # noqa: E501 + :rtype: ItemVault + """ + return self._vault + + @vault.setter + def vault(self, vault): + """Sets the vault of this Item. + + + :param vault: The vault of this Item. # noqa: E501 + :type: ItemVault + """ + + self._vault = vault + + @property + def category(self): + """Gets the category of this Item. # noqa: E501 + + + :return: The category of this Item. # noqa: E501 + :rtype: str + """ + return self._category + + @category.setter + def category(self, category): + """Sets the category of this Item. + + + :param category: The category of this Item. # noqa: E501 + :type: str + """ + allowed_values = ["LOGIN", "PASSWORD", "SERVER", "DATABASE", "CREDIT_CARD", "MEMBERSHIP", "PASSPORT", "SOFTWARE_LICENSE", "OUTDOOR_LICENSE", "SECURE_NOTE", "WIRELESS_ROUTER", "BANK_ACCOUNT", "DRIVER_LICENSE", "IDENTITY", "REWARD_PROGRAM", "DOCUMENT", "EMAIL_ACCOUNT", "SOCIAL_SECURITY_NUMBER", "CUSTOM"] # noqa: E501 + if category not in allowed_values: # noqa: E501 + raise ValueError( + "Invalid value for `category` ({0}), must be one of {1}" # noqa: E501 + .format(category, allowed_values) + ) + + self._category = category + + @property + def urls(self): + """Gets the urls of this Item. # noqa: E501 + + + :return: The urls of this Item. # noqa: E501 + :rtype: list[ItemUrls] + """ + return self._urls + + @urls.setter + def urls(self, urls): + """Sets the urls of this Item. + + + :param urls: The urls of this Item. # noqa: E501 + :type: list[ItemUrls] + """ + + self._urls = urls + + @property + def favorite(self): + """Gets the favorite of this Item. # noqa: E501 + + + :return: The favorite of this Item. # noqa: E501 + :rtype: bool + """ + return self._favorite + + @favorite.setter + def favorite(self, favorite): + """Sets the favorite of this Item. + + + :param favorite: The favorite of this Item. # noqa: E501 + :type: bool + """ + + self._favorite = favorite + + @property + def tags(self): + """Gets the tags of this Item. # noqa: E501 + + + :return: The tags of this Item. # noqa: E501 + :rtype: list[str] + """ + return self._tags + + @tags.setter + def tags(self, tags): + """Sets the tags of this Item. + + + :param tags: The tags of this Item. # noqa: E501 + :type: list[str] + """ + + self._tags = tags + + @property + def version(self): + """Gets the version of this Item. # noqa: E501 + + + :return: The version of this Item. # noqa: E501 + :rtype: int + """ + return self._version + + @version.setter + def version(self, version): + """Sets the version of this Item. + + + :param version: The version of this Item. # noqa: E501 + :type: int + """ + + self._version = version + + @property + def trashed(self): + """Gets the trashed of this Item. # noqa: E501 + + + :return: The trashed of this Item. # noqa: E501 + :rtype: bool + """ + return self._trashed + + @trashed.setter + def trashed(self, trashed): + """Sets the trashed of this Item. + + + :param trashed: The trashed of this Item. # noqa: E501 + :type: bool + """ + + self._trashed = trashed + + @property + def created_at(self): + """Gets the created_at of this Item. # noqa: E501 + + + :return: The created_at of this Item. # noqa: E501 + :rtype: datetime + """ + return self._created_at + + @created_at.setter + def created_at(self, created_at): + """Sets the created_at of this Item. + + + :param created_at: The created_at of this Item. # noqa: E501 + :type: datetime + """ + + self._created_at = created_at + + @property + def updated_at(self): + """Gets the updated_at of this Item. # noqa: E501 + + + :return: The updated_at of this Item. # noqa: E501 + :rtype: datetime + """ + return self._updated_at + + @updated_at.setter + def updated_at(self, updated_at): + """Sets the updated_at of this Item. + + + :param updated_at: The updated_at of this Item. # noqa: E501 + :type: datetime + """ + + self._updated_at = updated_at + + @property + def last_edited_by(self): + """Gets the last_edited_by of this Item. # noqa: E501 + + + :return: The last_edited_by of this Item. # noqa: E501 + :rtype: str + """ + return self._last_edited_by + + @last_edited_by.setter + def last_edited_by(self, last_edited_by): + """Sets the last_edited_by of this Item. + + + :param last_edited_by: The last_edited_by of this Item. # noqa: E501 + :type: str + """ + + self._last_edited_by = last_edited_by + + def to_dict(self): + """Returns the model properties as a dict""" + result = {} + + for attr, _ in six.iteritems(self.openapi_types): + value = getattr(self, attr) + if isinstance(value, list): + result[attr] = list(map( + lambda x: x.to_dict() if hasattr(x, "to_dict") else x, + value + )) + elif hasattr(value, "to_dict"): + result[attr] = value.to_dict() + elif isinstance(value, dict): + result[attr] = dict(map( + lambda item: (item[0], item[1].to_dict()) + if hasattr(item[1], "to_dict") else item, + value.items() + )) + else: + result[attr] = value + + return result + + def to_str(self): + """Returns the string representation of the model""" + return pprint.pformat(self.to_dict()) + + def __repr__(self): + """For `print` and `pprint`""" + return self.to_str() + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, Item): + return False + + return self.to_dict() == other.to_dict() + + def __ne__(self, other): + """Returns true if both objects are not equal""" + if not isinstance(other, Item): + return True + + return self.to_dict() != other.to_dict() diff --git a/src/onepasswordconnectsdk/models/item_urls.py b/src/onepasswordconnectsdk/models/item_urls.py new file mode 100644 index 0000000..d529b7b --- /dev/null +++ b/src/onepasswordconnectsdk/models/item_urls.py @@ -0,0 +1,139 @@ +# coding: utf-8 + +""" + 1Password Connect + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) # noqa: E501 + + The version of the OpenAPI document: 0.2.1 + Generated by: https://openapi-generator.tech +""" + + +import pprint +import re # noqa: F401 + +import six + + +class ItemUrls(object): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + """ + + """ + Attributes: + openapi_types (dict): The key is attribute name + and the value is attribute type. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + """ + openapi_types = { + 'primary': 'bool', + 'href': 'str' + } + + attribute_map = { + 'primary': 'primary', + 'href': 'href' + } + + def __init__(self, primary=None, href=None): # noqa: E501 + """ItemUrls - a model defined in OpenAPI""" # noqa: E501 + self._primary = None + self._href = None + self.discriminator = None + + if primary is not None: + self.primary = primary + self.href = href + + @property + def primary(self): + """Gets the primary of this ItemUrls. # noqa: E501 + + + :return: The primary of this ItemUrls. # noqa: E501 + :rtype: bool + """ + return self._primary + + @primary.setter + def primary(self, primary): + """Sets the primary of this ItemUrls. + + + :param primary: The primary of this ItemUrls. # noqa: E501 + :type: bool + """ + + self._primary = primary + + @property + def href(self): + """Gets the href of this ItemUrls. # noqa: E501 + + + :return: The href of this ItemUrls. # noqa: E501 + :rtype: str + """ + return self._href + + @href.setter + def href(self, href): + """Sets the href of this ItemUrls. + + + :param href: The href of this ItemUrls. # noqa: E501 + :type: str + """ + + self._href = href + + def to_dict(self): + """Returns the model properties as a dict""" + result = {} + + for attr, _ in six.iteritems(self.openapi_types): + value = getattr(self, attr) + if isinstance(value, list): + result[attr] = list(map( + lambda x: x.to_dict() if hasattr(x, "to_dict") else x, + value + )) + elif hasattr(value, "to_dict"): + result[attr] = value.to_dict() + elif isinstance(value, dict): + result[attr] = dict(map( + lambda item: (item[0], item[1].to_dict()) + if hasattr(item[1], "to_dict") else item, + value.items() + )) + else: + result[attr] = value + + return result + + def to_str(self): + """Returns the string representation of the model""" + return pprint.pformat(self.to_dict()) + + def __repr__(self): + """For `print` and `pprint`""" + return self.to_str() + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, ItemUrls): + return False + + return self.to_dict() == other.to_dict() + + def __ne__(self, other): + """Returns true if both objects are not equal""" + if not isinstance(other, ItemUrls): + return True + + return self.to_dict() != other.to_dict() diff --git a/src/onepasswordconnectsdk/models/item_vault.py b/src/onepasswordconnectsdk/models/item_vault.py new file mode 100644 index 0000000..609c57e --- /dev/null +++ b/src/onepasswordconnectsdk/models/item_vault.py @@ -0,0 +1,113 @@ +# coding: utf-8 + +""" + 1Password Connect + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) # noqa: E501 + + The version of the OpenAPI document: 0.2.1 + Generated by: https://openapi-generator.tech +""" + + +import pprint +import re # noqa: F401 + +import six + + +class ItemVault(object): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + """ + + """ + Attributes: + openapi_types (dict): The key is attribute name + and the value is attribute type. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + """ + openapi_types = { + 'id': 'str' + } + + attribute_map = { + 'id': 'id' + } + + def __init__(self, id=None): # noqa: E501 + """ItemVault - a model defined in OpenAPI""" # noqa: E501 + self._id = None + self.discriminator = None + + self.id = id + + @property + def id(self): + """Gets the id of this ItemVault. # noqa: E501 + + + :return: The id of this ItemVault. # noqa: E501 + :rtype: str + """ + return self._id + + @id.setter + def id(self, id): + """Sets the id of this ItemVault. + + + :param id: The id of this ItemVault. # nsoqa: E501 + :type: str + """ + + self._id = id + + def to_dict(self): + """Returns the model properties as a dict""" + result = {} + + for attr, _ in six.iteritems(self.openapi_types): + value = getattr(self, attr) + if isinstance(value, list): + result[attr] = list(map( + lambda x: x.to_dict() if hasattr(x, "to_dict") else x, + value + )) + elif hasattr(value, "to_dict"): + result[attr] = value.to_dict() + elif isinstance(value, dict): + result[attr] = dict(map( + lambda item: (item[0], item[1].to_dict()) + if hasattr(item[1], "to_dict") else item, + value.items() + )) + else: + result[attr] = value + + return result + + def to_str(self): + """Returns the string representation of the model""" + return pprint.pformat(self.to_dict()) + + def __repr__(self): + """For `print` and `pprint`""" + return self.to_str() + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, ItemVault): + return False + + return self.to_dict() == other.to_dict() + + def __ne__(self, other): + """Returns true if both objects are not equal""" + if not isinstance(other, ItemVault): + return True + + return self.to_dict() != other.to_dict() diff --git a/src/onepasswordconnectsdk/models/parsed_field.py b/src/onepasswordconnectsdk/models/parsed_field.py new file mode 100644 index 0000000..cae0bef --- /dev/null +++ b/src/onepasswordconnectsdk/models/parsed_field.py @@ -0,0 +1,7 @@ +from dataclasses import dataclass + + +@dataclass +class ParsedField: + name: str + tag: str diff --git a/src/onepasswordconnectsdk/models/parsed_item.py b/src/onepasswordconnectsdk/models/parsed_item.py new file mode 100644 index 0000000..8f3c9a8 --- /dev/null +++ b/src/onepasswordconnectsdk/models/parsed_item.py @@ -0,0 +1,10 @@ +from dataclasses import dataclass +from typing import List +from onepasswordconnectsdk.models import ParsedField + + +@dataclass +class ParsedItem: + vault_uuid: str + item_title: str + fields: List[ParsedField] diff --git a/src/onepasswordconnectsdk/models/vault.py b/src/onepasswordconnectsdk/models/vault.py new file mode 100644 index 0000000..255e58f --- /dev/null +++ b/src/onepasswordconnectsdk/models/vault.py @@ -0,0 +1,334 @@ +# coding: utf-8 + +""" + 1Password Connect + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) # noqa: E501 + + The version of the OpenAPI document: 0.2.1 + Generated by: https://openapi-generator.tech +""" + + +import pprint +import re # noqa: F401 + +import six + + +class Vault(object): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + """ + + """ + Attributes: + openapi_types (dict): The key is attribute name + and the value is attribute type. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + """ + openapi_types = { + 'id': 'str', + 'name': 'str', + 'description': 'str', + 'attribute_version': 'int', + 'content_version': 'int', + 'items': 'int', + 'type': 'str', + 'created_at': 'datetime', + 'updated_at': 'datetime' + } + + attribute_map = { + 'id': 'id', + 'name': 'name', + 'description': 'description', + 'attribute_version': 'attributeVersion', + 'content_version': 'contentVersion', + 'items': 'items', + 'type': 'type', + 'created_at': 'createdAt', + 'updated_at': 'updatedAt' + } + + def __init__(self, id=None, name=None, description=None, attribute_version=None, content_version=None, items=None, type=None, created_at=None, updated_at=None): # noqa: E501 + """Vault - a model defined in OpenAPI""" # noqa: E501 + self._id = None + self._name = None + self._description = None + self._attribute_version = None + self._content_version = None + self._items = None + self._type = None + self._created_at = None + self._updated_at = None + self.discriminator = None + + if id is not None: + self.id = id + if name is not None: + self.name = name + if description is not None: + self.description = description + if attribute_version is not None: + self.attribute_version = attribute_version + if content_version is not None: + self.content_version = content_version + if items is not None: + self.items = items + if type is not None: + self.type = type + if created_at is not None: + self.created_at = created_at + if updated_at is not None: + self.updated_at = updated_at + + @property + def id(self): + """Gets the id of this Vault. # noqa: E501 + + + :return: The id of this Vault. # noqa: E501 + :rtype: str + """ + return self._id + + @id.setter + def id(self, id): + """Sets the id of this Vault. + + + :param id: The id of this Vault. # noqa: E501 + :type: str + """ + + self._id = id + + @property + def name(self): + """Gets the name of this Vault. # noqa: E501 + + + :return: The name of this Vault. # noqa: E501 + :rtype: str + """ + return self._name + + @name.setter + def name(self, name): + """Sets the name of this Vault. + + + :param name: The name of this Vault. # noqa: E501 + :type: str + """ + + self._name = name + + @property + def description(self): + """Gets the description of this Vault. # noqa: E501 + + + :return: The description of this Vault. # noqa: E501 + :rtype: str + """ + return self._description + + @description.setter + def description(self, description): + """Sets the description of this Vault. + + + :param description: The description of this Vault. # noqa: E501 + :type: str + """ + + self._description = description + + @property + def attribute_version(self): + """Gets the attribute_version of this Vault. # noqa: E501 + + The vault version # noqa: E501 + + :return: The attribute_version of this Vault. # noqa: E501 + :rtype: int + """ + return self._attribute_version + + @attribute_version.setter + def attribute_version(self, attribute_version): + """Sets the attribute_version of this Vault. + + The vault version # noqa: E501 + + :param attribute_version: The attribute_version of this Vault. # noqa: E501 + :type: int + """ + + self._attribute_version = attribute_version + + @property + def content_version(self): + """Gets the content_version of this Vault. # noqa: E501 + + The version of the vault contents # noqa: E501 + + :return: The content_version of this Vault. # noqa: E501 + :rtype: int + """ + return self._content_version + + @content_version.setter + def content_version(self, content_version): + """Sets the content_version of this Vault. + + The version of the vault contents # noqa: E501 + + :param content_version: The content_version of this Vault. # noqa: E501 + :type: int + """ + + self._content_version = content_version + + @property + def items(self): + """Gets the items of this Vault. # noqa: E501 + + Number of active items in the vault # noqa: E501 + + :return: The items of this Vault. # noqa: E501 + :rtype: int + """ + return self._items + + @items.setter + def items(self, items): + """Sets the items of this Vault. + + Number of active items in the vault # noqa: E501 + + :param items: The items of this Vault. # noqa: E501 + :type: int + """ + + self._items = items + + @property + def type(self): + """Gets the type of this Vault. # noqa: E501 + + + :return: The type of this Vault. # noqa: E501 + :rtype: str + """ + return self._type + + @type.setter + def type(self, type): + """Sets the type of this Vault. + + + :param type: The type of this Vault. # noqa: E501 + :type: str + """ + allowed_values = ["USER_CREATED", "PERSONAL", "EVERYONE", "TRANSFER"] # noqa: E501 + if type not in allowed_values: # noqa: E501 + raise ValueError( + "Invalid value for `type` ({0}), must be one of {1}" # noqa: E501 + .format(type, allowed_values) + ) + + self._type = type + + @property + def created_at(self): + """Gets the created_at of this Vault. # noqa: E501 + + + :return: The created_at of this Vault. # noqa: E501 + :rtype: datetime + """ + return self._created_at + + @created_at.setter + def created_at(self, created_at): + """Sets the created_at of this Vault. + + + :param created_at: The created_at of this Vault. # noqa: E501 + :type: datetime + """ + + self._created_at = created_at + + @property + def updated_at(self): + """Gets the updated_at of this Vault. # noqa: E501 + + + :return: The updated_at of this Vault. # noqa: E501 + :rtype: datetime + """ + return self._updated_at + + @updated_at.setter + def updated_at(self, updated_at): + """Sets the updated_at of this Vault. + + + :param updated_at: The updated_at of this Vault. # noqa: E501 + :type: datetime + """ + + self._updated_at = updated_at + + def to_dict(self): + """Returns the model properties as a dict""" + result = {} + + for attr, _ in six.iteritems(self.openapi_types): + value = getattr(self, attr) + if isinstance(value, list): + result[attr] = list(map( + lambda x: x.to_dict() if hasattr(x, "to_dict") else x, + value + )) + elif hasattr(value, "to_dict"): + result[attr] = value.to_dict() + elif isinstance(value, dict): + result[attr] = dict(map( + lambda item: (item[0], item[1].to_dict()) + if hasattr(item[1], "to_dict") else item, + value.items() + )) + else: + result[attr] = value + + return result + + def to_str(self): + """Returns the string representation of the model""" + return pprint.pformat(self.to_dict()) + + def __repr__(self): + """For `print` and `pprint`""" + return self.to_str() + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, Vault): + return False + + return self.to_dict() == other.to_dict() + + def __ne__(self, other): + """Returns true if both objects are not equal""" + if not isinstance(other, Vault): + return True + + return self.to_dict() != other.to_dict() diff --git a/src/tests/__init__.py b/src/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/tests/test_client_items.py b/src/tests/test_client_items.py new file mode 100644 index 0000000..bee7eea --- /dev/null +++ b/src/tests/test_client_items.py @@ -0,0 +1,218 @@ +import json +from requests import Session, Response +from unittest.mock import Mock, patch +import pytest +from onepasswordconnectsdk import client, models + +VAULT_ID = "some_vault_id" +ITEM_ID = "some_item_id" +HOST = "mock_host" +TOKEN = "jwt_token" +SS_CLIENT = client.new_client(HOST, TOKEN) + +@patch.object(Session, 'request') +def test_get_item(mock): + expected_item = get_item() + expected_path = f"{HOST}/v1/vaults/{VAULT_ID}/items/{ITEM_ID}" + + mock.return_value.ok = True + response = Response() + response.status_code = 200 + response._content = json.dumps(expected_item) + mock.return_value = response + + item = SS_CLIENT.get_item(ITEM_ID, VAULT_ID) + compare_items(expected_item, item) + mock.assert_called_with("GET", expected_path) + +@patch.object(Session, 'request') +def test_get_items(mock): + expected_items = get_items() + expected_path = f"{HOST}/v1/vaults/{VAULT_ID}/items" + + mock.return_value.ok = True + response = Response() + response.status_code = 200 + response._content = json.dumps(expected_items) + mock.return_value = response + + items = SS_CLIENT.get_items(VAULT_ID) + assert len(expected_items) == len(items) + compare_summary_items(expected_items[0], items[0]) + mock.assert_called_with("GET", expected_path) + +@patch.object(Session, 'request') +def test_delete_item(mock): + expected_items = get_items() + expected_path = f"{HOST}/v1/vaults/{VAULT_ID}/items/{ITEM_ID}" + + mock.return_value.ok = True + response = Response() + response.status_code = 200 + response._content = json.dumps(expected_items) + mock.return_value = response + + SS_CLIENT.delete_item(ITEM_ID, VAULT_ID) + mock.assert_called_with("DELETE", expected_path) + +@patch.object(Session, 'request') +def test_create_item(mock): + mock.return_value.ok = True + mock.side_effect = create_item_side_effect + + item = generate_full_item() + + created_item = SS_CLIENT.create_item(VAULT_ID, item) + assert mock.called + compare_full_items(item, created_item) + +@patch.object(Session, 'request') +def test_update_item(mock): + mock.return_value.ok = True + mock.side_effect = create_item_side_effect + + item = generate_full_item() + + updated_item = SS_CLIENT.update_item(ITEM_ID, VAULT_ID, item) + assert mock.called + compare_full_items(item, updated_item) + +def compare_full_items(expected_item, returned_item): + assert expected_item.id == returned_item.id + assert expected_item.title == returned_item.title + assert expected_item.vault.id == returned_item.vault.id + assert expected_item.category == returned_item.category + assert expected_item.last_edited_by == returned_item.last_edited_by + assert expected_item.created_at == returned_item.created_at + assert expected_item.updated_at == returned_item.updated_at + + assert len(expected_item.sections) == len(returned_item.sections) + for i in range(len(expected_item.sections)): + compare_full_item_sections(expected_item.sections[i], returned_item.sections[i]) + + assert len(expected_item.fields) == len(returned_item.fields) + for i in range(len(expected_item.fields)): + compare_full_item_fields(expected_item.fields[i], returned_item.fields[i]) + +def create_item_side_effect(method, url, data): + response = Response() + response.status_code = 200 + response._content = data + return response + +def compare_full_item_fields(expected_field, returned_field): + assert expected_field.id == returned_field.id + assert expected_field.label == returned_field.label + assert expected_field.value == returned_field.value + assert expected_field.purpose == returned_field.purpose + assert expected_field.section.id == returned_field.section.id + assert expected_field.type == returned_field.type + +def compare_full_item_sections(expected_section, returned_section): + assert expected_section.id == returned_section.id + assert expected_section.label == returned_section.label + +def compare_summary_items(expected_item, returned_item): + assert expected_item["id"] == returned_item.id + assert expected_item["title"] == returned_item.title + assert expected_item["vault"]["id"] == returned_item.vault.id + assert expected_item["category"] == returned_item.category + assert expected_item["version"] == returned_item.version + +def compare_items(expected_item, returned_item): + compare_summary_items(expected_item, returned_item) + assert expected_item["lastEditedBy"] == returned_item.last_edited_by + + assert len(expected_item["sections"]) == len(returned_item.sections) + for i in range(len(expected_item["sections"])): + compare_sections(expected_item["sections"][i], returned_item.sections[i]) + + assert len(expected_item["fields"]) == len(returned_item.fields) + for i in range(len(expected_item["fields"])): + compare_fields(expected_item["fields"][i], returned_item.fields[i]) + +def compare_fields(expected_field, returned_field): + assert expected_field["id"] == returned_field.id + assert expected_field["label"] == returned_field.label + assert expected_field["value"] == returned_field.value + assert expected_field["purpose"] == returned_field.purpose + assert expected_field["section"]["id"] == returned_field.section.id + assert expected_field["type"] == returned_field.type + +def compare_sections(expected_section, returned_section): + assert expected_section["id"] == returned_section.id + assert expected_section["label"] == returned_section.label + +def get_items(): + return [{ + "id": "wepiqdxdzncjtnvmv5fegud4qy", + "title": "Example Login Default", + "version": 21, + "vault": { + "id": "hfnjvi6aymbsnfc2xeeoheizda" + }, + "category": "LOGIN", + "lastEditedBy": "DOIHOHSV2NHK5HMSOLCWJUXFDM", + "createdAt": "2020-10-29T17:52:17Z", + "updatedAt": "2020-11-10T14:05:53Z" + }] + +def get_item(): + return { + "id": "wepiqdxdzncjtnvmv5fegud4qy", + "title": "Test Login", + "version": 21, + "vault": { + "id": "hfnjvi6aymbsnfc2xeeoheizda" + }, + "category": "LOGIN", + "sections": [ + { + "id": "linked items", + "label": "Related Items" + }, + { + "id": "Section_47DC4DDBF26640AB8B8618DA36D5A492", + "label": "section" + } + ], + "fields": [ + { + "id": "password", + "type": "CONCEALED", + "purpose": "PASSWORD", + "label": "password", + "value": "Z9gKLhP{zxDJPGbYWFAtApzg", + "entropy": 130.0688473607018, + "section": { + "id": "Section_47DC4DDBF26640AB8B8618DA36D5A499" + }, + }, + { + "id": "716C5B0E95A84092B2FE2CC402E0DDDF", + "section": { + "id": "Section_47DC4DDBF26640AB8B8618DA36D5A492" + }, + "purpose": "USERNAME", + "type": "STRING", + "label": "something", + "value": "test" + } + ], + "lastEditedBy": "DOIHOHSV2NHK5HMSOLCWJUXFDM", + "createdAt": "2020-10-29T17:52:17Z", + "updatedAt": "2020-11-10T14:05:53Z" +} + +def generate_full_item(): + item = models.FullItem(vault=models.ItemVault(id="av223f76ydutdngislnkbz6z5u"), + id="kp2td65r4wbuhocwhhijpdbfqq", + title="newtitle", + category="LOGIN", + tags=["secret-service"], + fields=[models.FullItemAllOfFields(value="new_user", + purpose="USERNAME", + type="STRING", + section=models.FullItemAllOfSection(id="Section_47DC4DDBF26640AB8B8618DA36D5A499"))], + sections=[models.FullItemAllOfSections(id="id", label="label")]) + return item \ No newline at end of file diff --git a/src/tests/test_client_vaults.py b/src/tests/test_client_vaults.py new file mode 100644 index 0000000..ba1033e --- /dev/null +++ b/src/tests/test_client_vaults.py @@ -0,0 +1,63 @@ +import json +from requests import Session, Response +from unittest.mock import Mock, patch +import pytest +from onepasswordconnectsdk import client, models + +VAULT_ID = "some_vault_id" +HOST = "mock_host" +TOKEN = "jwt_token" +SS_CLIENT = client.new_client(HOST, TOKEN) + +@patch.object(Session, 'request') +def test_get_vaults(mock): + expected_vaults = list_vaults() + expected_path = f"{HOST}/v1/vaults" + + mock.return_value.ok = True + response = Response() + response.status_code = 200 + response._content = json.dumps(expected_vaults) + mock.return_value = response + + vaults = SS_CLIENT.get_vaults() + compare_vaults(expected_vaults[0], vaults[0]) + mock.assert_called_with("GET", expected_path) + +@patch.object(Session, 'request') +def test_get_vault(mock): + expected_vault = get_vault() + expected_path = f"{HOST}/v1/vaults/{VAULT_ID}" + + mock.return_value.ok = True + response = Response() + response.status_code = 200 + response._content = json.dumps(expected_vault) + mock.return_value = response + + vault = SS_CLIENT.get_vault(VAULT_ID) + compare_vaults(expected_vault, vault) + mock.assert_called_with("GET", expected_path) + +def list_vaults(): + return [ + get_vault() + ] + +def get_vault(): + return { + "id": "hfnjvi6aymbsnfc2xeeoheizda", + "name": "VaultA", + "attributeVersion": 2, + "contentVersion": 196, + "items": 2, + "type": "USER_CREATED", + } + +def compare_vaults(expected_vault, returned_vault): + assert expected_vault["id"] == returned_vault.id + assert expected_vault["name"] == returned_vault.name + assert expected_vault["attributeVersion"] == returned_vault.attribute_version + assert expected_vault["contentVersion"] == returned_vault.content_version + assert expected_vault["items"] == returned_vault.items + assert expected_vault["type"] == returned_vault.type \ No newline at end of file diff --git a/src/tests/test_config.py b/src/tests/test_config.py new file mode 100644 index 0000000..f0ceda5 --- /dev/null +++ b/src/tests/test_config.py @@ -0,0 +1,123 @@ +import json +from requests import Session, Response +from unittest.mock import Mock, patch +import pytest +import onepasswordconnectsdk +from onepasswordconnectsdk import client, models + +VAULT_ID = "some_vault_id" +ITEM_NAME1 = "TEST USER" +ITEM_NAME2 = "Another User" +HOST = "mock_host" +TOKEN = "jwt_token" +SS_CLIENT = client.new_client(HOST, TOKEN) + +USERNAME_VALUE = "new_user" +PASSWORD_VALUE = "password" +HOST_VALUE = "http://somehost" + +class Config: + username: f'opitem:"{ITEM_NAME1}" opfield:.username opvault:{VAULT_ID}' = None + password: f'opitem:"{ITEM_NAME1}" opfield:section1.password opvault:{VAULT_ID}' = None + host: f'opitem:"{ITEM_NAME2}" opfield:.host opvault:{VAULT_ID}' = None + + +CONFIG_CLASS = Config() + +@patch.object(Session, 'request') +def test_load(mock): + mock.return_value.ok = True + mock.side_effect = get_item_side_effect + + config_with_values = onepasswordconnectsdk.load(SS_CLIENT, CONFIG_CLASS) + assert mock.called + assert config_with_values.username == USERNAME_VALUE + assert config_with_values.password == PASSWORD_VALUE + assert config_with_values.host == HOST_VALUE + +@patch.object(Session, 'request') +def test_load_dict(mock): + config_dict = { + "username": { + "opitem": ITEM_NAME1, + "opfield": ".username", + "opvault":VAULT_ID + }, + "password": { + "opitem": ITEM_NAME1, + "opfield": "section1.password", + "opvault": VAULT_ID + } + } + mock.return_value.ok = True + mock.side_effect = get_item_side_effect + + config_with_values = onepasswordconnectsdk.load_dict(SS_CLIENT, config_dict) + assert mock.called + assert config_with_values['username'] == USERNAME_VALUE + assert config_with_values['password'] == PASSWORD_VALUE + +def get_item_side_effect(method, url): + response = Response() + response.status_code = 200 + + item = { + "id": ITEM_NAME1, + "title": ITEM_NAME1, + "vault": { + "id": VAULT_ID + }, + "category": "LOGIN", + "sections": [ + { + "id": "section1", + "label": "section1" + } + ], + "fields": [ + { + "id": "password", + "label": "password", + "value": PASSWORD_VALUE, + "section": { + "id": "section1" + } + }, + { + "id": "716C5B0E95A84092B2FE2CC402E0DDDF", + "label": "username", + "value": USERNAME_VALUE + } + ] + } + + item2 = { + "id": ITEM_NAME2, + "title": ITEM_NAME2, + "vault": { + "id": VAULT_ID + }, + "category": "LOGIN", + "fields": [ + { + "id": "716C5B0E95A84092B2FE2CC402E0DDDF", + "label": "host", + "value": HOST_VALUE + } + ] + } + + if ITEM_NAME1 in url: + if "eq" in url: + item = [item] + else: + item = item + elif ITEM_NAME2 in url: + if "eq" in url: + item = [item2] + else: + item = item2 + + response._content=str.encode(json.dumps(item)) + + return response