bitwarden-py 0.1.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.
- bitwarden_py-0.1.0/PKG-INFO +134 -0
- bitwarden_py-0.1.0/README.md +115 -0
- bitwarden_py-0.1.0/pyproject.toml +26 -0
- bitwarden_py-0.1.0/src/bitwarden_py/__init__.py +3 -0
- bitwarden_py-0.1.0/src/bitwarden_py/command_runner.py +14 -0
- bitwarden_py-0.1.0/src/bitwarden_py/commands.py +78 -0
- bitwarden_py-0.1.0/src/bitwarden_py/main.py +43 -0
- bitwarden_py-0.1.0/src/bitwarden_py/py.typed +0 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bitwarden-py
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A simple Python wrapper for the Bitwarden CLI — programmatically read and update passwords stored in your Bitwarden vault.
|
|
5
|
+
Keywords: bitwarden,bitwarden-cli
|
|
6
|
+
Author: SpaceShaman
|
|
7
|
+
Author-email: SpaceShaman <spaceshaman@tuta.io>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
14
|
+
Requires-Python: >=3.12
|
|
15
|
+
Project-URL: Documentation, https://github.com/SpaceShaman/bitwarden-py
|
|
16
|
+
Project-URL: Homepage, https://github.com/SpaceShaman/bitwarden-py
|
|
17
|
+
Project-URL: Repository, https://github.com/SpaceShaman/bitwarden-py
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# bitwarden-py
|
|
21
|
+
|
|
22
|
+
> A simple Python wrapper for the [Bitwarden CLI](https://bitwarden.com/help/cli/) — programmatically read and update passwords stored in your Bitwarden vault.
|
|
23
|
+
|
|
24
|
+
[](https://pypi.org/project/bitwarden-py)
|
|
25
|
+
[](https://pypi.org/project/bitwarden-py)
|
|
26
|
+
[](https://github.com/psf/black)
|
|
27
|
+
[](https://github.com/astral-sh/ruff)
|
|
28
|
+
[](https://github.com/astral-sh/uv)
|
|
29
|
+
[](https://github.com/SpaceShaman/bitwarden-py?tab=MIT-1-ov-file)
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Requirements
|
|
34
|
+
|
|
35
|
+
### Bitwarden CLI
|
|
36
|
+
|
|
37
|
+
`bitwarden-py` calls the official `bw` binary under the hood, so you must have it installed and available in your `PATH`.
|
|
38
|
+
|
|
39
|
+
Install it by following the official guide:
|
|
40
|
+
👉 [https://bitwarden.com/help/cli/#download-and-install](https://bitwarden.com/help/cli/#download-and-install)
|
|
41
|
+
|
|
42
|
+
Quick install options:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# macOS (Homebrew)
|
|
46
|
+
brew install bitwarden-cli
|
|
47
|
+
|
|
48
|
+
# Windows (Chocolatey)
|
|
49
|
+
choco install bitwarden-cli
|
|
50
|
+
|
|
51
|
+
# npm (cross-platform)
|
|
52
|
+
npm install -g @bitwarden/cli
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
After installation, verify it works:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
bw --version
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Installation
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pip install bitwarden-py
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Or with [uv](https://github.com/astral-sh/uv):
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
uv add bitwarden-py
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Quick Start
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from bitwarden_py import Bitwarden
|
|
81
|
+
|
|
82
|
+
bw = Bitwarden(
|
|
83
|
+
email="your@email.com",
|
|
84
|
+
password="your_master_password",
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# Fetch a password by item name
|
|
88
|
+
db_password = bw.get_password("My Database")
|
|
89
|
+
print(db_password) # → "s3cr3t!"
|
|
90
|
+
|
|
91
|
+
# Update the password of an item
|
|
92
|
+
bw.set_password("My Database", "n3wp4ssw0rd")
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Using a self-hosted server
|
|
96
|
+
|
|
97
|
+
By default the library points to `https://vault.bitwarden.eu`.
|
|
98
|
+
Pass `server_url` to connect to a different instance:
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
bw = Bitwarden(
|
|
102
|
+
email="your@email.com",
|
|
103
|
+
password="your_master_password",
|
|
104
|
+
server_url="https://bitwarden.yourcompany.com",
|
|
105
|
+
)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## API Reference
|
|
111
|
+
|
|
112
|
+
### `Bitwarden(email, password, server_url?)`
|
|
113
|
+
|
|
114
|
+
Creates a new client and authenticates with the vault.
|
|
115
|
+
|
|
116
|
+
| Parameter | Type | Default | Description |
|
|
117
|
+
|--------------|-------|----------------------------------|------------------------------------|
|
|
118
|
+
| `email` | `str` | — | Your Bitwarden account email |
|
|
119
|
+
| `password` | `str` | — | Your master password |
|
|
120
|
+
| `server_url` | `str` | `"https://vault.bitwarden.eu"` | Bitwarden server URL |
|
|
121
|
+
|
|
122
|
+
### `bw.get_password(item_name) → str`
|
|
123
|
+
|
|
124
|
+
Returns the password of the vault item matching `item_name`.
|
|
125
|
+
|
|
126
|
+
### `bw.set_password(item_name, new_password) → None`
|
|
127
|
+
|
|
128
|
+
Updates the password of the vault item matching `item_name`.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## License
|
|
133
|
+
|
|
134
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# bitwarden-py
|
|
2
|
+
|
|
3
|
+
> A simple Python wrapper for the [Bitwarden CLI](https://bitwarden.com/help/cli/) — programmatically read and update passwords stored in your Bitwarden vault.
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/bitwarden-py)
|
|
6
|
+
[](https://pypi.org/project/bitwarden-py)
|
|
7
|
+
[](https://github.com/psf/black)
|
|
8
|
+
[](https://github.com/astral-sh/ruff)
|
|
9
|
+
[](https://github.com/astral-sh/uv)
|
|
10
|
+
[](https://github.com/SpaceShaman/bitwarden-py?tab=MIT-1-ov-file)
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Requirements
|
|
15
|
+
|
|
16
|
+
### Bitwarden CLI
|
|
17
|
+
|
|
18
|
+
`bitwarden-py` calls the official `bw` binary under the hood, so you must have it installed and available in your `PATH`.
|
|
19
|
+
|
|
20
|
+
Install it by following the official guide:
|
|
21
|
+
👉 [https://bitwarden.com/help/cli/#download-and-install](https://bitwarden.com/help/cli/#download-and-install)
|
|
22
|
+
|
|
23
|
+
Quick install options:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# macOS (Homebrew)
|
|
27
|
+
brew install bitwarden-cli
|
|
28
|
+
|
|
29
|
+
# Windows (Chocolatey)
|
|
30
|
+
choco install bitwarden-cli
|
|
31
|
+
|
|
32
|
+
# npm (cross-platform)
|
|
33
|
+
npm install -g @bitwarden/cli
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
After installation, verify it works:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
bw --version
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install bitwarden-py
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Or with [uv](https://github.com/astral-sh/uv):
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
uv add bitwarden-py
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Quick Start
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from bitwarden_py import Bitwarden
|
|
62
|
+
|
|
63
|
+
bw = Bitwarden(
|
|
64
|
+
email="your@email.com",
|
|
65
|
+
password="your_master_password",
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# Fetch a password by item name
|
|
69
|
+
db_password = bw.get_password("My Database")
|
|
70
|
+
print(db_password) # → "s3cr3t!"
|
|
71
|
+
|
|
72
|
+
# Update the password of an item
|
|
73
|
+
bw.set_password("My Database", "n3wp4ssw0rd")
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Using a self-hosted server
|
|
77
|
+
|
|
78
|
+
By default the library points to `https://vault.bitwarden.eu`.
|
|
79
|
+
Pass `server_url` to connect to a different instance:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
bw = Bitwarden(
|
|
83
|
+
email="your@email.com",
|
|
84
|
+
password="your_master_password",
|
|
85
|
+
server_url="https://bitwarden.yourcompany.com",
|
|
86
|
+
)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## API Reference
|
|
92
|
+
|
|
93
|
+
### `Bitwarden(email, password, server_url?)`
|
|
94
|
+
|
|
95
|
+
Creates a new client and authenticates with the vault.
|
|
96
|
+
|
|
97
|
+
| Parameter | Type | Default | Description |
|
|
98
|
+
|--------------|-------|----------------------------------|------------------------------------|
|
|
99
|
+
| `email` | `str` | — | Your Bitwarden account email |
|
|
100
|
+
| `password` | `str` | — | Your master password |
|
|
101
|
+
| `server_url` | `str` | `"https://vault.bitwarden.eu"` | Bitwarden server URL |
|
|
102
|
+
|
|
103
|
+
### `bw.get_password(item_name) → str`
|
|
104
|
+
|
|
105
|
+
Returns the password of the vault item matching `item_name`.
|
|
106
|
+
|
|
107
|
+
### `bw.set_password(item_name, new_password) → None`
|
|
108
|
+
|
|
109
|
+
Updates the password of the vault item matching `item_name`.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## License
|
|
114
|
+
|
|
115
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "bitwarden-py"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "A simple Python wrapper for the Bitwarden CLI — programmatically read and update passwords stored in your Bitwarden vault."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
authors = [{ name = "SpaceShaman", email = "spaceshaman@tuta.io" }]
|
|
9
|
+
keywords = ["bitwarden", "bitwarden-cli"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Programming Language :: Python :: 3",
|
|
12
|
+
"Programming Language :: Python :: 3.12",
|
|
13
|
+
"Programming Language :: Python :: 3.13",
|
|
14
|
+
"Programming Language :: Python :: 3.14",
|
|
15
|
+
"Programming Language :: Python :: 3.15",
|
|
16
|
+
]
|
|
17
|
+
dependencies = []
|
|
18
|
+
|
|
19
|
+
[project.urls]
|
|
20
|
+
Documentation = "https://github.com/SpaceShaman/bitwarden-py"
|
|
21
|
+
Homepage = "https://github.com/SpaceShaman/bitwarden-py"
|
|
22
|
+
Repository = "https://github.com/SpaceShaman/bitwarden-py"
|
|
23
|
+
|
|
24
|
+
[build-system]
|
|
25
|
+
requires = ["uv_build>=0.11.14,<0.12.0"]
|
|
26
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def run_command(command: list[str]) -> str:
|
|
5
|
+
try:
|
|
6
|
+
return subprocess.run(
|
|
7
|
+
command,
|
|
8
|
+
capture_output=True,
|
|
9
|
+
text=True,
|
|
10
|
+
check=True,
|
|
11
|
+
stdin=subprocess.DEVNULL,
|
|
12
|
+
).stdout.strip()
|
|
13
|
+
except subprocess.CalledProcessError as e:
|
|
14
|
+
raise RuntimeError(e.stderr.strip()) from e
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Literal
|
|
6
|
+
|
|
7
|
+
from .command_runner import run_command
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class Status:
|
|
12
|
+
status: Literal["locked", "unlocked", "unauthenticated"]
|
|
13
|
+
server_url: str
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_status() -> Status:
|
|
17
|
+
output = run_command(["bw", "status"])
|
|
18
|
+
status_data = json.loads(output)
|
|
19
|
+
return Status(
|
|
20
|
+
status=status_data.get("status", "unauthenticated"),
|
|
21
|
+
server_url=status_data.get("serverUrl", ""),
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def login(email: str, password: str) -> None:
|
|
26
|
+
run_command(
|
|
27
|
+
[
|
|
28
|
+
"bw",
|
|
29
|
+
"login",
|
|
30
|
+
email,
|
|
31
|
+
password,
|
|
32
|
+
"--raw",
|
|
33
|
+
],
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def logout() -> None:
|
|
38
|
+
run_command(["bw", "logout"])
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def set_server_url(url: str) -> None:
|
|
42
|
+
run_command(["bw", "config", "server", url])
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def get_session(password: str) -> str:
|
|
46
|
+
os.environ.setdefault("BW_PASSWORD", password)
|
|
47
|
+
return run_command(["bw", "unlock", "--raw", "--passwordenv", "BW_PASSWORD"])
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def get_password(session: str, item_name: str) -> str:
|
|
51
|
+
return run_command(["bw", "get", "password", item_name, "--session", session])
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def set_password(session: str, item_name: str, new_password: str) -> None:
|
|
55
|
+
item = json.loads(
|
|
56
|
+
run_command(["bw", "get", "item", item_name, "--session", session])
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
if not item.get("login"):
|
|
60
|
+
raise RuntimeError(f"Item '{item_name}' is not a login item")
|
|
61
|
+
|
|
62
|
+
item["login"]["password"] = new_password
|
|
63
|
+
|
|
64
|
+
encoded_item = base64.b64encode(
|
|
65
|
+
json.dumps(item, separators=(",", ":")).encode()
|
|
66
|
+
).decode()
|
|
67
|
+
|
|
68
|
+
run_command(
|
|
69
|
+
[
|
|
70
|
+
"bw",
|
|
71
|
+
"edit",
|
|
72
|
+
"item",
|
|
73
|
+
item["id"],
|
|
74
|
+
encoded_item,
|
|
75
|
+
"--session",
|
|
76
|
+
session,
|
|
77
|
+
]
|
|
78
|
+
)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from .commands import (
|
|
2
|
+
get_password,
|
|
3
|
+
get_session,
|
|
4
|
+
get_status,
|
|
5
|
+
login,
|
|
6
|
+
logout,
|
|
7
|
+
set_password,
|
|
8
|
+
set_server_url,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Bitwarden:
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
email: str,
|
|
16
|
+
password: str,
|
|
17
|
+
server_url: str = "https://vault.bitwarden.eu",
|
|
18
|
+
):
|
|
19
|
+
self._email = email
|
|
20
|
+
self._password = password
|
|
21
|
+
self._server_url = server_url
|
|
22
|
+
self._session = None
|
|
23
|
+
self._setup()
|
|
24
|
+
|
|
25
|
+
def _setup(self) -> None:
|
|
26
|
+
status = get_status()
|
|
27
|
+
if status.status != "unauthenticated":
|
|
28
|
+
logout()
|
|
29
|
+
if status.server_url != self._server_url:
|
|
30
|
+
set_server_url(self._server_url)
|
|
31
|
+
status = get_status()
|
|
32
|
+
login(self._email, self._password)
|
|
33
|
+
self._session = get_session(self._password)
|
|
34
|
+
|
|
35
|
+
def get_password(self, item_name: str) -> str:
|
|
36
|
+
if not self._session:
|
|
37
|
+
raise RuntimeError("Bitwarden session is not established.")
|
|
38
|
+
return get_password(self._session, item_name)
|
|
39
|
+
|
|
40
|
+
def set_password(self, item_name: str, new_password: str) -> None:
|
|
41
|
+
if not self._session:
|
|
42
|
+
raise RuntimeError("Bitwarden session is not established.")
|
|
43
|
+
set_password(self._session, item_name, new_password)
|
|
File without changes
|