├── src
└── unopass
│ ├── __init__.py
│ └── unopass.py
├── requirements.txt
├── pyproject.toml
├── example.py
├── setup.cfg
├── LICENSE
├── .gitignore
└── README.md
/src/unopass/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | unopass
2 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = [
3 | "setuptools>=42",
4 | "wheel"
5 | ]
6 | build-backend = "setuptools.build_meta"
7 |
--------------------------------------------------------------------------------
/example.py:
--------------------------------------------------------------------------------
1 | from unopass import unopass as secret
2 |
3 |
4 | """
5 | secret.unopass({VAULT}, {ITEM}, {FIELD})
6 |
7 | OPTIONAL: signout of the 1Password CLI session:
8 | call secret.signout() with deauthorize=True to end of the 1Password session
9 | """
10 |
11 | username = secret.unopass("personal", "server", "username")
12 | password = secret.unopass("personal", "server", "password")
13 |
14 | secret.signout(deauthorize=True)
15 |
16 |
17 | print(f"user: {username}\npass: {password}")
18 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [metadata]
2 | name = unopass
3 | version = 0.0.21
4 | author = Amado Tejada
5 | author_email = tejada.amado@gmail.com
6 | description = unopass is a convenient python module that allows you to retrieve secrets from the 1Password CLI
7 | long_description = file: README.md
8 | long_description_content_type = text/markdown
9 | url = https://github.com/amadotejada/unopass
10 | classifiers =
11 | Programming Language :: Python :: 3
12 | License :: OSI Approved :: MIT License
13 | Operating System :: OS Independent
14 |
15 | [options]
16 | package_dir =
17 | = src
18 | packages = find:
19 | python_requires = >=3.6
20 |
21 | [options.packages.find]
22 | where = src
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Amado Tejada
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/unopass/unopass.py:
--------------------------------------------------------------------------------
1 | import shutil
2 | import subprocess
3 |
4 |
5 | def op_path() -> str:
6 | """
7 | It checks if the op command is installed and returns the path to the op command.
8 | :return: The location of the op binary.
9 | """
10 | try:
11 | cmd = "op"
12 | locate = shutil.which(cmd)
13 | if locate is not None:
14 | return locate
15 | else:
16 | raise FileNotFoundError("error: op cli not found\nhttps://github.com/amadotejada/unopass")
17 | except Exception as e:
18 | print(e)
19 | exit(1)
20 |
21 |
22 | def unopass(vault, item, field, deauthorize=None) -> str:
23 | """
24 | `Requires the 1Password CLI v2.4.1 or higher with Biometrics enabled`
25 |
26 | `unopass` is a function that takes a vault, item, and field as arguments and returns the value of
27 | the field
28 |
29 | :param vault: The name of the vault you want to access
30 | :param item: The name of the item you want to retrieve
31 | :param field: The field you want to retrieve
32 | :param deauthorize: The boolean value that determines if 1Password should signout
33 | :return: The results of the command.
34 | """
35 | try:
36 | op = op_path()
37 | cmd = (op, "read", f"op://{vault}/{item}/{field}")
38 | results = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
39 | signout(deauthorize)
40 | if results.strip().decode("utf-8") is not None:
41 | return results.strip().decode("utf-8")
42 | except subprocess.CalledProcessError as e:
43 | signout(deauthorize)
44 | print((e.output).decode("utf-8"))
45 | exit(1)
46 |
47 |
48 | def signout(deauthorize=None) -> None:
49 | """
50 | It signs out of the 1Password CLI session
51 |
52 | :param deauthorize: The boolean value that determines if 1Password should signout
53 | :return: The output of the command.
54 | """
55 | try:
56 | if deauthorize:
57 | op = op_path()
58 | results = subprocess.check_output([op, "signout"], stderr=subprocess.STDOUT)
59 | return results.strip().decode("utf-8")
60 | except subprocess.CalledProcessError as e:
61 | print(e.output.decode("utf-8"))
62 | exit(1)
63 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # macOS
10 | *.DS_Store
11 |
12 | # Distribution / packaging
13 | .Python
14 | build/
15 | develop-eggs/
16 | dist/
17 | downloads/
18 | eggs/
19 | .eggs/
20 | lib/
21 | lib64/
22 | parts/
23 | sdist/
24 | var/
25 | wheels/
26 | pip-wheel-metadata/
27 | share/python-wheels/
28 | *.egg-info/
29 | .installed.cfg
30 | *.egg
31 | MANIFEST
32 |
33 | # PyInstaller
34 | # Usually these files are written by a python script from a template
35 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
36 | *.manifest
37 | *.spec
38 |
39 | # Installer logs
40 | pip-log.txt
41 | pip-delete-this-directory.txt
42 |
43 | # Unit test / coverage reports
44 | htmlcov/
45 | .tox/
46 | .nox/
47 | .coverage
48 | .coverage.*
49 | .cache
50 | nosetests.xml
51 | coverage.xml
52 | *.cover
53 | *.py,cover
54 | .hypothesis/
55 | .pytest_cache/
56 |
57 | # Translations
58 | *.mo
59 | *.pot
60 |
61 | # Django stuff:
62 | *.log
63 | local_settings.py
64 | db.sqlite3
65 | db.sqlite3-journal
66 |
67 | # Flask stuff:
68 | instance/
69 | .webassets-cache
70 |
71 | # Scrapy stuff:
72 | .scrapy
73 |
74 | # Sphinx documentation
75 | docs/_build/
76 |
77 | # PyBuilder
78 | target/
79 |
80 | # Jupyter Notebook
81 | .ipynb_checkpoints
82 |
83 | # IPython
84 | profile_default/
85 | ipython_config.py
86 |
87 | # pyenv
88 | .python-version
89 |
90 | # pipenv
91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
94 | # install all needed dependencies.
95 | #Pipfile.lock
96 |
97 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
98 | __pypackages__/
99 |
100 | # Celery stuff
101 | celerybeat-schedule
102 | celerybeat.pid
103 |
104 | # SageMath parsed files
105 | *.sage.py
106 |
107 | # Environments
108 | .env
109 | .venv
110 | env/
111 | venv/
112 | ENV/
113 | env.bak/
114 | venv.bak/
115 |
116 | # Spyder project settings
117 | .spyderproject
118 | .spyproject
119 |
120 | # Rope project settings
121 | .ropeproject
122 |
123 | # mkdocs documentation
124 | /site
125 |
126 | # mypy
127 | .mypy_cache/
128 | .dmypy.json
129 | dmypy.json
130 |
131 | # Pyre type checker
132 | .pyre/
133 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # unopass
2 |
3 | ##### Written by [Amado Tejada](https://www.linkedin.com/in/amadotejada/)
4 |
5 | ##
6 | *unopass* is a convenient python module that allows you to retrieve secrets from the 1Password CLI at runtime using your biometrics. (E.g. Touch ID, etc.)
7 |
8 | This eliminates the need of storing secrets in env, or conf files and provides a more secured local workflow.
9 |
10 |
Go version: [HERE](https://github.com/amadotejada/unopass-go)
11 |
12 | Common use cases:
13 | * Access AWS CLI, Google Cloud Platform secrets
14 | * Access API secrets in your scripts
15 | * Use with other tools like GAM, see: [GAMpass](https://github.com/amadotejada/GAMpass)
16 | ##
17 |
18 |
19 |
20 | Note: *unopass* is early alpha software and should be tested extensively.
21 |
22 | #### Requirements:
23 | * [ 1Password CLI ](https://developer.1password.com/docs/cli/get-started#install) v2.4.1 or higher
24 | * [ 1Password App ](https://1password.com/downloads/) v8.7.1 or higher
25 | * [Biometrics](https://developer.1password.com/docs/cli/get-started#turn-on-biometric-unlock) enabled
26 |
27 | ### Security
28 | Authorization expires after 10 minutes of inactivity in the session. There's a hard limit of 12 hours, after which you must reauthorize.
29 |
30 | Learn about 1Password Biometrics [Security](https://developer.1password.com/docs/cli/biometric-security)
31 |
32 | ### Install
33 |
34 | [pypi repo](https://pypi.org/project/unopass/):
35 | ```bash
36 | pip3 install unopass
37 | ```
38 |
39 | You can also import *unopass* from source.
40 |
41 | ### Example
42 |
43 | ```python
44 | from unopass import unopass as secret
45 |
46 |
47 | """
48 | secret.unopass({VAULT}, {ITEM}, {FIELD})
49 |
50 | OPTIONAL: signout of the 1Password CLI session:
51 | call secret.signout() with deauthorize=True to end of the 1Password session
52 | """
53 |
54 | username = secret.unopass("personal", "server", "username")
55 | password = secret.unopass("personal", "server", "password")
56 |
57 | secret.signout(deauthorize=True)
58 |
59 |
60 | print(f"user: {username}\npass: {password}")
61 | ```
62 |
63 | ##
64 | ### Disclaimer
65 |
66 | This software {*unopass*} has not been endorsed or supported by 1Password, AgileBits Inc. and is in no way associated with them and/or its subsidiaries or affiliate.
67 |
68 | ### License
69 |
70 | *unopass* is released under the [MIT License](https://github.com/amadotejada/unopass/blob/main/LICENSE)
71 | ####
72 |
--------------------------------------------------------------------------------