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 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.4'
32
- __version_tuple__ = version_tuple = (0, 0, 4)
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 keepercommander import vault as keeper_vault
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
- class _MinimalKeeperUI:
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 _extract_keeper_field(record, field: str) -> str | None:
69
- if isinstance(record, keeper_vault.TypedRecord):
70
- value = record.get_typed_field(field)
71
- if value is None:
72
- value = next((f for f in record.custom if f.label == field), None)
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
- if value and value.value:
75
- return value.value[0] if isinstance(value.value, list) else str(value.value)
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 _perform_keeper_login(params):
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=_MinimalKeeperUI())
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 get_keeper_password(record_uid: str, field_path: str) -> str:
107
- config_file = Path.home() / ".config" / "keeper" / "config.json"
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(config_file))
138
+ params = KeeperParams(config_filename=str(CONFIG_FILE))
111
139
 
112
- if config_file.exists():
140
+ if CONFIG_FILE.exists():
113
141
  try:
114
- params.config = json.loads(config_file.read_text())
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
- _perform_keeper_login(params)
147
+ perform_login(params)
120
148
  else:
121
- _perform_keeper_login(params)
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 = keeper_vault.KeeperRecord.load(params, record_uid)
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 = _extract_keeper_field(record, field_path)
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
- record_uid, field_path = path.split("/", 1)
47
- return _keeper_password(record_uid, field_path)
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 get_keeper_password
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 get_keeper_password(record_uid, field_path)
75
+ return keeper_get_password(record_uid, field_path)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jwbmisc
3
- Version: 0.0.4
3
+ Version: 0.0.6
4
4
  Summary: Misc util functions of jwb
5
5
  Author-email: Joachim Bargsten <jw@bargsten.org>
6
6
  License: Apache License
@@ -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,,
@@ -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,,