jwbmisc 0.0.4__py3-none-any.whl → 0.0.6__py3-none-any.whl
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.
- jwbmisc/_version.py +2 -2
- jwbmisc/exec.py +5 -0
- jwbmisc/keeper.py +49 -21
- jwbmisc/passwd.py +5 -7
- {jwbmisc-0.0.4.dist-info → jwbmisc-0.0.6.dist-info}/METADATA +1 -1
- jwbmisc-0.0.6.dist-info/RECORD +15 -0
- jwbmisc-0.0.4.dist-info/RECORD +0 -15
- {jwbmisc-0.0.4.dist-info → jwbmisc-0.0.6.dist-info}/WHEEL +0 -0
- {jwbmisc-0.0.4.dist-info → jwbmisc-0.0.6.dist-info}/licenses/LICENSE +0 -0
- {jwbmisc-0.0.4.dist-info → jwbmisc-0.0.6.dist-info}/top_level.txt +0 -0
jwbmisc/_version.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.0.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 0,
|
|
31
|
+
__version__ = version = '0.0.6'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 0, 6)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
jwbmisc/exec.py
CHANGED
|
@@ -9,6 +9,7 @@ def run_cmd(
|
|
|
9
9
|
stdin=None,
|
|
10
10
|
contains_sensitive_data=False,
|
|
11
11
|
timeout=20,
|
|
12
|
+
cwd=None,
|
|
12
13
|
decode=True,
|
|
13
14
|
dry_run=False,
|
|
14
15
|
):
|
|
@@ -22,6 +23,9 @@ def run_cmd(
|
|
|
22
23
|
|
|
23
24
|
cmd = [str(v) for v in cmd]
|
|
24
25
|
|
|
26
|
+
if cwd is not None:
|
|
27
|
+
cwd = str(cwd)
|
|
28
|
+
|
|
25
29
|
if dry_run:
|
|
26
30
|
print(cmd)
|
|
27
31
|
if capture:
|
|
@@ -35,6 +39,7 @@ def run_cmd(
|
|
|
35
39
|
env=env,
|
|
36
40
|
check=True,
|
|
37
41
|
timeout=timeout,
|
|
42
|
+
cwd=cwd,
|
|
38
43
|
input=stdin,
|
|
39
44
|
)
|
|
40
45
|
except sp.CalledProcessError as ex:
|
jwbmisc/keeper.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import json
|
|
2
|
-
from
|
|
2
|
+
from typing import Any
|
|
3
|
+
from keepercommander import vault
|
|
3
4
|
from keepercommander import api
|
|
4
5
|
from time import sleep
|
|
5
6
|
from keepercommander.params import KeeperParams
|
|
@@ -13,8 +14,10 @@ from logging import getLogger
|
|
|
13
14
|
|
|
14
15
|
logger = getLogger(__name__)
|
|
15
16
|
|
|
17
|
+
CONFIG_FILE = Path.home() / ".config" / "jwbmisc" / "keeper.json"
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
|
|
20
|
+
class MinimalKeeperUI:
|
|
18
21
|
def __init__(self):
|
|
19
22
|
self.waiting_for_sso_data_key = False
|
|
20
23
|
|
|
@@ -65,19 +68,45 @@ class _MinimalKeeperUI:
|
|
|
65
68
|
step.resume()
|
|
66
69
|
|
|
67
70
|
|
|
68
|
-
def
|
|
69
|
-
if isinstance(
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
def as_strings(vs: list[Any] | Any) -> list[str]:
|
|
72
|
+
if not isinstance(vs, list):
|
|
73
|
+
vs = [vs]
|
|
74
|
+
return [str(v) for v in vs]
|
|
75
|
+
|
|
73
76
|
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
def extract_record_field(record, field: str | None) -> str | None | list[dict[str, str | list[str]]]:
|
|
78
|
+
if isinstance(record, vault.PasswordRecord):
|
|
79
|
+
values = [
|
|
80
|
+
{"type": "password", "label": "login", "value": as_strings(record.login)},
|
|
81
|
+
{"type": "password", "label": "password", "value": as_strings(record.password)},
|
|
82
|
+
{"type": "password", "label": "link", "value": as_strings(record.link)},
|
|
83
|
+
] + [{"type": f.type, "label": f.name, "value": as_strings(f.value)} for f in record.custom]
|
|
76
84
|
|
|
85
|
+
elif isinstance(record, vault.TypedRecord):
|
|
86
|
+
fields = record.fields + record.custom
|
|
87
|
+
values = [{"type": f.type, "label": f.label, "value": as_strings(f.value)} for f in fields]
|
|
88
|
+
else:
|
|
89
|
+
raise TypeError("only TypedRecord & PasswordRecord are supported")
|
|
90
|
+
|
|
91
|
+
if not field:
|
|
92
|
+
return values
|
|
93
|
+
|
|
94
|
+
value = next(
|
|
95
|
+
(
|
|
96
|
+
v
|
|
97
|
+
for v in values
|
|
98
|
+
if (v["type"] and v["type"].lower() == field.lower())
|
|
99
|
+
or (v["label"] and v["label"].lower() == field.lower())
|
|
100
|
+
),
|
|
101
|
+
None,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
if value and value["value"]:
|
|
105
|
+
return value["value"][0]
|
|
77
106
|
return None
|
|
78
107
|
|
|
79
108
|
|
|
80
|
-
def
|
|
109
|
+
def perform_login(params):
|
|
81
110
|
if not params.user:
|
|
82
111
|
user = os.environ.get("KEEPER_USERNAME", None)
|
|
83
112
|
if user is None:
|
|
@@ -96,29 +125,28 @@ def _perform_keeper_login(params):
|
|
|
96
125
|
params.server = server
|
|
97
126
|
|
|
98
127
|
try:
|
|
99
|
-
api.login(params, login_ui=
|
|
128
|
+
api.login(params, login_ui=MinimalKeeperUI())
|
|
100
129
|
except KeyboardInterrupt:
|
|
101
130
|
raise KeyError("\nKeeper login cancelled by user.") from None
|
|
102
131
|
except Exception as e:
|
|
103
132
|
raise KeyError(f"Keeper login failed: {e}") from e
|
|
104
133
|
|
|
105
134
|
|
|
106
|
-
def
|
|
107
|
-
|
|
108
|
-
config_file.parent.mkdir(parents=True, exist_ok=True)
|
|
135
|
+
def get_password(record_uid: str, field_path: str | None) -> str:
|
|
136
|
+
CONFIG_FILE.parent.mkdir(parents=True, exist_ok=True)
|
|
109
137
|
|
|
110
|
-
params = KeeperParams(config_filename=str(
|
|
138
|
+
params = KeeperParams(config_filename=str(CONFIG_FILE))
|
|
111
139
|
|
|
112
|
-
if
|
|
140
|
+
if CONFIG_FILE.exists():
|
|
113
141
|
try:
|
|
114
|
-
params.config = json.loads(
|
|
142
|
+
params.config = json.loads(CONFIG_FILE.read_text())
|
|
115
143
|
loader.load_config_properties(params)
|
|
116
144
|
if not params.session_token:
|
|
117
145
|
raise ValueError("No session token")
|
|
118
146
|
except Exception:
|
|
119
|
-
|
|
147
|
+
perform_login(params)
|
|
120
148
|
else:
|
|
121
|
-
|
|
149
|
+
perform_login(params)
|
|
122
150
|
|
|
123
151
|
try:
|
|
124
152
|
api.sync_down(params)
|
|
@@ -126,11 +154,11 @@ def get_keeper_password(record_uid: str, field_path: str) -> str:
|
|
|
126
154
|
raise KeyError(f"Failed to sync Keeper vault: {e}") from e
|
|
127
155
|
|
|
128
156
|
try:
|
|
129
|
-
record =
|
|
157
|
+
record = vault.KeeperRecord.load(params, record_uid)
|
|
130
158
|
except Exception as e:
|
|
131
159
|
raise KeyError(f"Record {record_uid} not found: {e}") from e
|
|
132
160
|
|
|
133
|
-
value =
|
|
161
|
+
value = extract_record_field(record, field_path)
|
|
134
162
|
if value is None:
|
|
135
163
|
raise KeyError(f"Field '{field_path}' not found in record {record_uid}")
|
|
136
164
|
|
jwbmisc/passwd.py
CHANGED
|
@@ -40,11 +40,9 @@ def get_pass(*pass_keys: str):
|
|
|
40
40
|
|
|
41
41
|
if pass_key.startswith("keeper://"):
|
|
42
42
|
path = pass_key.removeprefix("keeper://")
|
|
43
|
-
if "/" not in path:
|
|
44
|
-
raise KeyError("Invalid keeper:// format. Expected: keeper://RECORD_UID/field/fieldname")
|
|
45
43
|
|
|
46
|
-
|
|
47
|
-
return _keeper_password(
|
|
44
|
+
parts = path.split("/")
|
|
45
|
+
return _keeper_password(*parts)
|
|
48
46
|
|
|
49
47
|
raise KeyError(f"Could not acquire password from one of {pass_keys}")
|
|
50
48
|
|
|
@@ -71,7 +69,7 @@ def _call_unix_pass(key, lnum=1):
|
|
|
71
69
|
return pw
|
|
72
70
|
|
|
73
71
|
|
|
74
|
-
def _keeper_password(record_uid: str, field_path: str) -> str:
|
|
75
|
-
from .keeper import
|
|
72
|
+
def _keeper_password(record_uid: str, field_path: str | None = None) -> str:
|
|
73
|
+
from .keeper import get_password as keeper_get_password
|
|
76
74
|
|
|
77
|
-
return
|
|
75
|
+
return keeper_get_password(record_uid, field_path)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
jwbmisc/__init__.py,sha256=5VXxKCMejjLWaiECh64309WJ2LpHHSULxaGOvCAOzyU,581
|
|
2
|
+
jwbmisc/_version.py,sha256=7MyqQ3iPP2mJruPfRYGCNCq1z7_Nk7c-eyYecYITxsY,704
|
|
3
|
+
jwbmisc/collection.py,sha256=mICHxBFfk2XY8ajdWkLvmQF1R6oo6d0torpRAeDvd1c,663
|
|
4
|
+
jwbmisc/exec.py,sha256=_3Xf0D342VDoVwtCZLjnyJ8N-WuHJfeIrBGFsL2qUW4,1260
|
|
5
|
+
jwbmisc/fs.py,sha256=Vf28qbOnBeHEbXNMUZjOQXtMWBurjkzD2KmfV2gJQXM,599
|
|
6
|
+
jwbmisc/interactive.py,sha256=qMgpQNHvUg4AYw-aAAy2qdxri1aKr-mffoLfUgh8TWE,423
|
|
7
|
+
jwbmisc/json.py,sha256=h3CBDNNZjcTDxydViyydPsQufXQLuxqP24wBBAT2nDs,1198
|
|
8
|
+
jwbmisc/keeper.py,sha256=mKhrUjNvlePxDv89tMcDs4dEgHWTVbtzMPH1jMRuylU,5201
|
|
9
|
+
jwbmisc/passwd.py,sha256=UKQk9sDJFYQSqBvTjPjtYLC1arAol82GlUdlc_PK4Eo,2433
|
|
10
|
+
jwbmisc/string.py,sha256=0_AvtAyXUlyVL6yw1iMjs3vMunubTTTWS0PSu_qKgi8,1232
|
|
11
|
+
jwbmisc-0.0.6.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
12
|
+
jwbmisc-0.0.6.dist-info/METADATA,sha256=7ixSPZaGjryN7NKhu88Q9AAoMaAkU1E_Q25_48eXfT0,14507
|
|
13
|
+
jwbmisc-0.0.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
14
|
+
jwbmisc-0.0.6.dist-info/top_level.txt,sha256=FqEYs8zdG3iGOJmC6cutDXfGQUNptzfYeKsaG43y1HE,8
|
|
15
|
+
jwbmisc-0.0.6.dist-info/RECORD,,
|
jwbmisc-0.0.4.dist-info/RECORD
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
jwbmisc/__init__.py,sha256=5VXxKCMejjLWaiECh64309WJ2LpHHSULxaGOvCAOzyU,581
|
|
2
|
-
jwbmisc/_version.py,sha256=QlXZ5JTjE_pgpDaeHk0GTExkc75xUZFmd0hA7kGYCJ0,704
|
|
3
|
-
jwbmisc/collection.py,sha256=mICHxBFfk2XY8ajdWkLvmQF1R6oo6d0torpRAeDvd1c,663
|
|
4
|
-
jwbmisc/exec.py,sha256=9g1Jc7iDkBj1Y-dn5VhnwH1JqvWSbrFe6Mmvnf-iqag,1177
|
|
5
|
-
jwbmisc/fs.py,sha256=Vf28qbOnBeHEbXNMUZjOQXtMWBurjkzD2KmfV2gJQXM,599
|
|
6
|
-
jwbmisc/interactive.py,sha256=qMgpQNHvUg4AYw-aAAy2qdxri1aKr-mffoLfUgh8TWE,423
|
|
7
|
-
jwbmisc/json.py,sha256=h3CBDNNZjcTDxydViyydPsQufXQLuxqP24wBBAT2nDs,1198
|
|
8
|
-
jwbmisc/keeper.py,sha256=mm3lVUL0UzXzxIBgT3ZM60By-I4Sckozl4t74O3mvQQ,4324
|
|
9
|
-
jwbmisc/passwd.py,sha256=cBCgy3NnLGa2-Fd7S4I_Qos9Taw7UpU-39k0RsKHs0U,2577
|
|
10
|
-
jwbmisc/string.py,sha256=0_AvtAyXUlyVL6yw1iMjs3vMunubTTTWS0PSu_qKgi8,1232
|
|
11
|
-
jwbmisc-0.0.4.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
12
|
-
jwbmisc-0.0.4.dist-info/METADATA,sha256=aVscIglVdplmuuHuZAfHjbl6gtWSIFegob1-FNbhHec,14507
|
|
13
|
-
jwbmisc-0.0.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
14
|
-
jwbmisc-0.0.4.dist-info/top_level.txt,sha256=FqEYs8zdG3iGOJmC6cutDXfGQUNptzfYeKsaG43y1HE,8
|
|
15
|
-
jwbmisc-0.0.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|