vw-cli 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vw_cli-0.2.0/LICENSE +9 -0
- vw_cli-0.2.0/PKG-INFO +199 -0
- vw_cli-0.2.0/README.md +171 -0
- vw_cli-0.2.0/pyproject.toml +39 -0
- vw_cli-0.2.0/setup.cfg +4 -0
- vw_cli-0.2.0/tests/test_cli.py +83 -0
- vw_cli-0.2.0/tests/test_client.py +537 -0
- vw_cli-0.2.0/tests/test_crypto.py +205 -0
- vw_cli-0.2.0/vw_cli/__init__.py +12 -0
- vw_cli-0.2.0/vw_cli/__main__.py +3 -0
- vw_cli-0.2.0/vw_cli/auth.py +123 -0
- vw_cli-0.2.0/vw_cli/cli.py +123 -0
- vw_cli-0.2.0/vw_cli/crypto.py +112 -0
- vw_cli-0.2.0/vw_cli/error.py +2 -0
- vw_cli-0.2.0/vw_cli/vault.py +175 -0
- vw_cli-0.2.0/vw_cli.egg-info/PKG-INFO +199 -0
- vw_cli-0.2.0/vw_cli.egg-info/SOURCES.txt +19 -0
- vw_cli-0.2.0/vw_cli.egg-info/dependency_links.txt +1 -0
- vw_cli-0.2.0/vw_cli.egg-info/entry_points.txt +2 -0
- vw_cli-0.2.0/vw_cli.egg-info/requires.txt +3 -0
- vw_cli-0.2.0/vw_cli.egg-info/top_level.txt +1 -0
vw_cli-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright © 2026 robertanrbrandao[at]gmail[dot]com
|
|
4
|
+
|
|
5
|
+
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:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
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.
|
vw_cli-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vw-cli
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Lightweight Vaultwarden (Bitwarden-compatible) CLI in Python
|
|
5
|
+
Author-email: Roberta Brandao <roberta@betabrandao.com.br>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://gitlab.com/betabrandao/vaultvarden-cli
|
|
8
|
+
Project-URL: Repository, https://gitlab.com/betabrandao/vaultvarden-cli
|
|
9
|
+
Keywords: bitwarden,vaultwarden,cli,password-manager,alpine
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: System Administrators
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Security :: Cryptography
|
|
20
|
+
Classifier: Topic :: Utilities
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: requests>=2.31
|
|
25
|
+
Requires-Dist: cryptography>=41
|
|
26
|
+
Requires-Dist: argon2-cffi>=23
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
|
|
29
|
+
# vw-cli
|
|
30
|
+
|
|
31
|
+
Lightweight Vaultwarden (Bitwarden-compatible) CLI written in Python.
|
|
32
|
+
Authenticates via API key, syncs the vault, derives the encryption key
|
|
33
|
+
locally, and prints decrypted passwords or items — all in a single command.
|
|
34
|
+
|
|
35
|
+
Designed for use in Alpine‑based Docker containers where the official
|
|
36
|
+
`bw` Node.js CLI is impractical.
|
|
37
|
+
|
|
38
|
+
## Quick start
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
pip install vw-cli
|
|
42
|
+
|
|
43
|
+
export VW_SERVER=https://vault.example.com
|
|
44
|
+
export VW_CLIENTID=user.aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
|
|
45
|
+
export VW_CLIENTSECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
46
|
+
export VW_PASSWORD="your master password"
|
|
47
|
+
|
|
48
|
+
vw-cli get password "My Website"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Usage
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
usage: vw-cli <command> [args]
|
|
55
|
+
|
|
56
|
+
commands:
|
|
57
|
+
login authenticate with API key
|
|
58
|
+
sync sync vault and print raw JSON
|
|
59
|
+
unlock unlock vault (derive encryption key)
|
|
60
|
+
list list all item names
|
|
61
|
+
get password <item> print password for <item>
|
|
62
|
+
get item <item> print full decrypted <item> as JSON
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Commands
|
|
66
|
+
|
|
67
|
+
| command | description |
|
|
68
|
+
|---------|-------------|
|
|
69
|
+
| `login` | Authenticate with the API key. Verifies credentials work. |
|
|
70
|
+
| `sync` | Pull the full vault (profile + all ciphers) and print raw JSON. |
|
|
71
|
+
| `unlock` | Derive the master key locally and decrypt the stored symmetric key. |
|
|
72
|
+
| `list` | Print every item name (lowercased), one per line. |
|
|
73
|
+
| `get password <item>` | Print the password for the named item (empty string if none). |
|
|
74
|
+
| `get item <item>` | Print the full decrypted item as JSON. |
|
|
75
|
+
|
|
76
|
+
Item lookup is case‑insensitive and supports substring matching.
|
|
77
|
+
|
|
78
|
+
## Environment variables
|
|
79
|
+
|
|
80
|
+
| variable | required | default | description |
|
|
81
|
+
|----------|----------|---------|-------------|
|
|
82
|
+
| `VW_SERVER` | no | `https://vault.bitwarden.com` | Vaultwarden or Bitwarden server URL |
|
|
83
|
+
| `VW_CLIENTID` | yes | — | API client ID (`user.xxx` or `org.xxx`) |
|
|
84
|
+
| `VW_CLIENTSECRET` | yes | — | API client secret |
|
|
85
|
+
| `VW_PASSWORD` | yes | — | Master password |
|
|
86
|
+
|
|
87
|
+
When `VW_SERVER` contains `bitwarden` the official Bitwarden identity
|
|
88
|
+
and API endpoints are used automatically; otherwise the same URL is used
|
|
89
|
+
for both identity and API paths.
|
|
90
|
+
|
|
91
|
+
## Docker example
|
|
92
|
+
|
|
93
|
+
```Dockerfile
|
|
94
|
+
FROM alpine:3.19
|
|
95
|
+
RUN apk add --no-cache python3 py3-pip
|
|
96
|
+
RUN pip install vw-cli
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
```sh
|
|
100
|
+
docker run --rm \
|
|
101
|
+
-e VW_SERVER=https://vault.example.com \
|
|
102
|
+
-e VW_CLIENTID=user.xxx \
|
|
103
|
+
-e VW_CLIENTSECRET=xxx \
|
|
104
|
+
-e VW_PASSWORD="..." \
|
|
105
|
+
my-image vw-cli get password "Database"
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## How it works
|
|
109
|
+
|
|
110
|
+
1. **Login** — sends a `client_credentials` grant with the API key to
|
|
111
|
+
`{identity_url}/connect/token` and receives a bearer token.
|
|
112
|
+
2. **Sync** — fetches `GET /api/sync` which returns the user profile
|
|
113
|
+
(email, KDF parameters, encrypted symmetric key) and all ciphers.
|
|
114
|
+
3. **Unlock** — derives the 32‑byte master key using the password and
|
|
115
|
+
email (PBKDF2‑SHA256 or Argon2id, depending on the profile KDF), then
|
|
116
|
+
decrypts the 64‑byte symmetric key stored in the profile.
|
|
117
|
+
4. **Decrypt** — splits the symmetric key into an AES‑256‑CBC encryption
|
|
118
|
+
key (first 32 bytes) and an HMAC‑SHA256 MAC key (last 32 bytes), then
|
|
119
|
+
decrypts individual cipher fields.
|
|
120
|
+
|
|
121
|
+
All key derivation happens **locally** — no unlock endpoint is called.
|
|
122
|
+
|
|
123
|
+
## Architecture
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
vw_cli/
|
|
127
|
+
├── __init__.py # package entry, re‑exports
|
|
128
|
+
├── __main__.py # python -m vw_cli support
|
|
129
|
+
├── error.py # VwError exception class
|
|
130
|
+
├── crypto.py # VwCryptoKey, key derivation, AES-CBC, HMAC
|
|
131
|
+
├── auth.py # VwAuth – API key login, session management
|
|
132
|
+
├── vault.py # VwVault – sync, unlock, cipher operations
|
|
133
|
+
└── cli.py # CLI argument parsing, orchestration
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Separation of concerns:
|
|
137
|
+
|
|
138
|
+
| module | responsibility |
|
|
139
|
+
|--------|---------------|
|
|
140
|
+
| `crypto.py` | Pure functions: key derivation, encryption/decryption, data types. No I/O. |
|
|
141
|
+
| `auth.py` | `VwAuth` class: HTTP session, token acquisition, request helper. |
|
|
142
|
+
| `vault.py` | `VwVault` class: sync, unlock, name cache, find/get/list operations. |
|
|
143
|
+
| `cli.py` | Environment variable reading, argument parsing, command dispatch. |
|
|
144
|
+
|
|
145
|
+
## Development
|
|
146
|
+
|
|
147
|
+
### Virtualenv (recommended)
|
|
148
|
+
|
|
149
|
+
```sh
|
|
150
|
+
python -m venv venv
|
|
151
|
+
source venv/bin/activate
|
|
152
|
+
pip install -e .
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
The package is installed in **editable mode** (`-e`), so source changes take
|
|
156
|
+
effect immediately — no need to reinstall.
|
|
157
|
+
|
|
158
|
+
### Run without pip
|
|
159
|
+
|
|
160
|
+
You don't need to `pip install` at all. The `__main__.py` entry point lets you
|
|
161
|
+
run the CLI directly from the checkout:
|
|
162
|
+
|
|
163
|
+
```sh
|
|
164
|
+
python -m vw_cli get password "My Website"
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Or set up a shell alias for convenience:
|
|
168
|
+
|
|
169
|
+
```sh
|
|
170
|
+
alias vw-cli='python -m vw_cli'
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Test
|
|
174
|
+
|
|
175
|
+
```sh
|
|
176
|
+
python -m pytest tests/ -v
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Testing
|
|
180
|
+
|
|
181
|
+
Three test files covering all layers:
|
|
182
|
+
|
|
183
|
+
| test file | coverage |
|
|
184
|
+
|-----------|----------|
|
|
185
|
+
| `tests/test_crypto.py` | `VwCryptoKey`, `_safe_int`, key derivation, AES-CBC, HMAC, `decrypt`, `decrypt_bytes`, `decode_encrypted` |
|
|
186
|
+
| `tests/test_client.py` | `VwAuth` (login, errors), `VwVault` (sync, unlock, find, get, list), `_setup_urls`, network errors, timeout |
|
|
187
|
+
| `tests/test_cli.py` | CLI argument parsing, help/version output, error handling, exit codes |
|
|
188
|
+
|
|
189
|
+
Tests use mocks and never touch the network.
|
|
190
|
+
|
|
191
|
+
### Dependencies
|
|
192
|
+
|
|
193
|
+
- `requests` — HTTP client
|
|
194
|
+
- `cryptography` — AES‑256‑CBC via OpenSSL bindings
|
|
195
|
+
- `argon2-cffi` — Argon2id KDF (optional; falls back gracefully)
|
|
196
|
+
|
|
197
|
+
## License
|
|
198
|
+
|
|
199
|
+
MIT
|
vw_cli-0.2.0/README.md
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# vw-cli
|
|
2
|
+
|
|
3
|
+
Lightweight Vaultwarden (Bitwarden-compatible) CLI written in Python.
|
|
4
|
+
Authenticates via API key, syncs the vault, derives the encryption key
|
|
5
|
+
locally, and prints decrypted passwords or items — all in a single command.
|
|
6
|
+
|
|
7
|
+
Designed for use in Alpine‑based Docker containers where the official
|
|
8
|
+
`bw` Node.js CLI is impractical.
|
|
9
|
+
|
|
10
|
+
## Quick start
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
pip install vw-cli
|
|
14
|
+
|
|
15
|
+
export VW_SERVER=https://vault.example.com
|
|
16
|
+
export VW_CLIENTID=user.aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
|
|
17
|
+
export VW_CLIENTSECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
18
|
+
export VW_PASSWORD="your master password"
|
|
19
|
+
|
|
20
|
+
vw-cli get password "My Website"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
usage: vw-cli <command> [args]
|
|
27
|
+
|
|
28
|
+
commands:
|
|
29
|
+
login authenticate with API key
|
|
30
|
+
sync sync vault and print raw JSON
|
|
31
|
+
unlock unlock vault (derive encryption key)
|
|
32
|
+
list list all item names
|
|
33
|
+
get password <item> print password for <item>
|
|
34
|
+
get item <item> print full decrypted <item> as JSON
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Commands
|
|
38
|
+
|
|
39
|
+
| command | description |
|
|
40
|
+
|---------|-------------|
|
|
41
|
+
| `login` | Authenticate with the API key. Verifies credentials work. |
|
|
42
|
+
| `sync` | Pull the full vault (profile + all ciphers) and print raw JSON. |
|
|
43
|
+
| `unlock` | Derive the master key locally and decrypt the stored symmetric key. |
|
|
44
|
+
| `list` | Print every item name (lowercased), one per line. |
|
|
45
|
+
| `get password <item>` | Print the password for the named item (empty string if none). |
|
|
46
|
+
| `get item <item>` | Print the full decrypted item as JSON. |
|
|
47
|
+
|
|
48
|
+
Item lookup is case‑insensitive and supports substring matching.
|
|
49
|
+
|
|
50
|
+
## Environment variables
|
|
51
|
+
|
|
52
|
+
| variable | required | default | description |
|
|
53
|
+
|----------|----------|---------|-------------|
|
|
54
|
+
| `VW_SERVER` | no | `https://vault.bitwarden.com` | Vaultwarden or Bitwarden server URL |
|
|
55
|
+
| `VW_CLIENTID` | yes | — | API client ID (`user.xxx` or `org.xxx`) |
|
|
56
|
+
| `VW_CLIENTSECRET` | yes | — | API client secret |
|
|
57
|
+
| `VW_PASSWORD` | yes | — | Master password |
|
|
58
|
+
|
|
59
|
+
When `VW_SERVER` contains `bitwarden` the official Bitwarden identity
|
|
60
|
+
and API endpoints are used automatically; otherwise the same URL is used
|
|
61
|
+
for both identity and API paths.
|
|
62
|
+
|
|
63
|
+
## Docker example
|
|
64
|
+
|
|
65
|
+
```Dockerfile
|
|
66
|
+
FROM alpine:3.19
|
|
67
|
+
RUN apk add --no-cache python3 py3-pip
|
|
68
|
+
RUN pip install vw-cli
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
```sh
|
|
72
|
+
docker run --rm \
|
|
73
|
+
-e VW_SERVER=https://vault.example.com \
|
|
74
|
+
-e VW_CLIENTID=user.xxx \
|
|
75
|
+
-e VW_CLIENTSECRET=xxx \
|
|
76
|
+
-e VW_PASSWORD="..." \
|
|
77
|
+
my-image vw-cli get password "Database"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## How it works
|
|
81
|
+
|
|
82
|
+
1. **Login** — sends a `client_credentials` grant with the API key to
|
|
83
|
+
`{identity_url}/connect/token` and receives a bearer token.
|
|
84
|
+
2. **Sync** — fetches `GET /api/sync` which returns the user profile
|
|
85
|
+
(email, KDF parameters, encrypted symmetric key) and all ciphers.
|
|
86
|
+
3. **Unlock** — derives the 32‑byte master key using the password and
|
|
87
|
+
email (PBKDF2‑SHA256 or Argon2id, depending on the profile KDF), then
|
|
88
|
+
decrypts the 64‑byte symmetric key stored in the profile.
|
|
89
|
+
4. **Decrypt** — splits the symmetric key into an AES‑256‑CBC encryption
|
|
90
|
+
key (first 32 bytes) and an HMAC‑SHA256 MAC key (last 32 bytes), then
|
|
91
|
+
decrypts individual cipher fields.
|
|
92
|
+
|
|
93
|
+
All key derivation happens **locally** — no unlock endpoint is called.
|
|
94
|
+
|
|
95
|
+
## Architecture
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
vw_cli/
|
|
99
|
+
├── __init__.py # package entry, re‑exports
|
|
100
|
+
├── __main__.py # python -m vw_cli support
|
|
101
|
+
├── error.py # VwError exception class
|
|
102
|
+
├── crypto.py # VwCryptoKey, key derivation, AES-CBC, HMAC
|
|
103
|
+
├── auth.py # VwAuth – API key login, session management
|
|
104
|
+
├── vault.py # VwVault – sync, unlock, cipher operations
|
|
105
|
+
└── cli.py # CLI argument parsing, orchestration
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Separation of concerns:
|
|
109
|
+
|
|
110
|
+
| module | responsibility |
|
|
111
|
+
|--------|---------------|
|
|
112
|
+
| `crypto.py` | Pure functions: key derivation, encryption/decryption, data types. No I/O. |
|
|
113
|
+
| `auth.py` | `VwAuth` class: HTTP session, token acquisition, request helper. |
|
|
114
|
+
| `vault.py` | `VwVault` class: sync, unlock, name cache, find/get/list operations. |
|
|
115
|
+
| `cli.py` | Environment variable reading, argument parsing, command dispatch. |
|
|
116
|
+
|
|
117
|
+
## Development
|
|
118
|
+
|
|
119
|
+
### Virtualenv (recommended)
|
|
120
|
+
|
|
121
|
+
```sh
|
|
122
|
+
python -m venv venv
|
|
123
|
+
source venv/bin/activate
|
|
124
|
+
pip install -e .
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
The package is installed in **editable mode** (`-e`), so source changes take
|
|
128
|
+
effect immediately — no need to reinstall.
|
|
129
|
+
|
|
130
|
+
### Run without pip
|
|
131
|
+
|
|
132
|
+
You don't need to `pip install` at all. The `__main__.py` entry point lets you
|
|
133
|
+
run the CLI directly from the checkout:
|
|
134
|
+
|
|
135
|
+
```sh
|
|
136
|
+
python -m vw_cli get password "My Website"
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Or set up a shell alias for convenience:
|
|
140
|
+
|
|
141
|
+
```sh
|
|
142
|
+
alias vw-cli='python -m vw_cli'
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Test
|
|
146
|
+
|
|
147
|
+
```sh
|
|
148
|
+
python -m pytest tests/ -v
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Testing
|
|
152
|
+
|
|
153
|
+
Three test files covering all layers:
|
|
154
|
+
|
|
155
|
+
| test file | coverage |
|
|
156
|
+
|-----------|----------|
|
|
157
|
+
| `tests/test_crypto.py` | `VwCryptoKey`, `_safe_int`, key derivation, AES-CBC, HMAC, `decrypt`, `decrypt_bytes`, `decode_encrypted` |
|
|
158
|
+
| `tests/test_client.py` | `VwAuth` (login, errors), `VwVault` (sync, unlock, find, get, list), `_setup_urls`, network errors, timeout |
|
|
159
|
+
| `tests/test_cli.py` | CLI argument parsing, help/version output, error handling, exit codes |
|
|
160
|
+
|
|
161
|
+
Tests use mocks and never touch the network.
|
|
162
|
+
|
|
163
|
+
### Dependencies
|
|
164
|
+
|
|
165
|
+
- `requests` — HTTP client
|
|
166
|
+
- `cryptography` — AES‑256‑CBC via OpenSSL bindings
|
|
167
|
+
- `argon2-cffi` — Argon2id KDF (optional; falls back gracefully)
|
|
168
|
+
|
|
169
|
+
## License
|
|
170
|
+
|
|
171
|
+
MIT
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=64"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "vw-cli"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Lightweight Vaultwarden (Bitwarden-compatible) CLI in Python"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
authors = [
|
|
12
|
+
{name = "Roberta Brandao", email = "roberta@betabrandao.com.br"},
|
|
13
|
+
]
|
|
14
|
+
keywords = ["bitwarden", "vaultwarden", "cli", "password-manager", "alpine"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Environment :: Console",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Intended Audience :: System Administrators",
|
|
20
|
+
"License :: OSI Approved :: MIT License",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Topic :: Security :: Cryptography",
|
|
26
|
+
"Topic :: Utilities",
|
|
27
|
+
]
|
|
28
|
+
requires-python = ">=3.10"
|
|
29
|
+
dependencies = ["requests>=2.31", "cryptography>=41", "argon2-cffi>=23"]
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://gitlab.com/betabrandao/vaultvarden-cli"
|
|
33
|
+
Repository = "https://gitlab.com/betabrandao/vaultvarden-cli"
|
|
34
|
+
|
|
35
|
+
[project.scripts]
|
|
36
|
+
vw-cli = "vw_cli.cli:main"
|
|
37
|
+
|
|
38
|
+
[tool.setuptools.packages.find]
|
|
39
|
+
include = ["vw_cli*"]
|
vw_cli-0.2.0/setup.cfg
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
import requests
|
|
5
|
+
|
|
6
|
+
from vw_cli.cli import VERSION, main
|
|
7
|
+
from vw_cli.error import VwError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TestHelp:
|
|
11
|
+
def test_help_exits_zero(self):
|
|
12
|
+
sys.argv = ["vw-cli", "--help"]
|
|
13
|
+
with pytest.raises(SystemExit) as exc:
|
|
14
|
+
main()
|
|
15
|
+
assert exc.value.code == 0
|
|
16
|
+
|
|
17
|
+
def test_help_with_no_args(self):
|
|
18
|
+
sys.argv = ["vw-cli"]
|
|
19
|
+
with pytest.raises(SystemExit) as exc:
|
|
20
|
+
main()
|
|
21
|
+
assert exc.value.code == 0
|
|
22
|
+
|
|
23
|
+
def test_help_with_h_flag(self):
|
|
24
|
+
sys.argv = ["vw-cli", "-h"]
|
|
25
|
+
with pytest.raises(SystemExit) as exc:
|
|
26
|
+
main()
|
|
27
|
+
assert exc.value.code == 0
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class TestVersion:
|
|
31
|
+
def test_version_output(self, capsys):
|
|
32
|
+
sys.argv = ["vw-cli", "--version"]
|
|
33
|
+
with pytest.raises(SystemExit):
|
|
34
|
+
main()
|
|
35
|
+
out, _ = capsys.readouterr()
|
|
36
|
+
assert "vw-cli" in out
|
|
37
|
+
assert VERSION in out
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class TestUnknownCommand:
|
|
41
|
+
def test_unknown_command_exits_1(self):
|
|
42
|
+
sys.argv = ["vw-cli", "nonsense"]
|
|
43
|
+
with pytest.raises(SystemExit) as exc:
|
|
44
|
+
main()
|
|
45
|
+
assert exc.value.code == 1
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class TestVwErrorCaught:
|
|
49
|
+
def test_bwerror_prints_and_exits_1(self, monkeypatch):
|
|
50
|
+
def fail(*a):
|
|
51
|
+
raise VwError("boom")
|
|
52
|
+
|
|
53
|
+
monkeypatch.setattr("vw_cli.auth.VwAuth.login", fail)
|
|
54
|
+
sys.argv = ["vw-cli", "login"]
|
|
55
|
+
with pytest.raises(SystemExit) as exc:
|
|
56
|
+
main()
|
|
57
|
+
assert exc.value.code == 1
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class TestRequestExceptionCaught:
|
|
61
|
+
def test_network_error_prints_and_exits_1(self, monkeypatch):
|
|
62
|
+
def fail(*a):
|
|
63
|
+
raise requests.ConnectionError("timeout")
|
|
64
|
+
|
|
65
|
+
monkeypatch.setattr("vw_cli.auth.VwAuth.login", fail)
|
|
66
|
+
sys.argv = ["vw-cli", "login"]
|
|
67
|
+
with pytest.raises(SystemExit) as exc:
|
|
68
|
+
main()
|
|
69
|
+
assert exc.value.code == 1
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class TestGetMissingArgs:
|
|
73
|
+
def test_get_without_subcommand(self):
|
|
74
|
+
sys.argv = ["vw-cli", "get"]
|
|
75
|
+
with pytest.raises(SystemExit) as exc:
|
|
76
|
+
main()
|
|
77
|
+
assert exc.value.code == 1
|
|
78
|
+
|
|
79
|
+
def test_get_without_name(self):
|
|
80
|
+
sys.argv = ["vw-cli", "get", "password"]
|
|
81
|
+
with pytest.raises(SystemExit) as exc:
|
|
82
|
+
main()
|
|
83
|
+
assert exc.value.code == 1
|