cloudx-proxy 0.4.1__py3-none-any.whl → 0.4.2__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.
- cloudx_proxy/_1password.py +208 -0
- cloudx_proxy/_version.py +2 -2
- {cloudx_proxy-0.4.1.dist-info → cloudx_proxy-0.4.2.dist-info}/METADATA +1 -1
- cloudx_proxy-0.4.2.dist-info/RECORD +12 -0
- cloudx_proxy-0.4.1.dist-info/RECORD +0 -11
- {cloudx_proxy-0.4.1.dist-info → cloudx_proxy-0.4.2.dist-info}/LICENSE +0 -0
- {cloudx_proxy-0.4.1.dist-info → cloudx_proxy-0.4.2.dist-info}/WHEEL +0 -0
- {cloudx_proxy-0.4.1.dist-info → cloudx_proxy-0.4.2.dist-info}/entry_points.txt +0 -0
- {cloudx_proxy-0.4.1.dist-info → cloudx_proxy-0.4.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,208 @@
|
|
1
|
+
import os
|
2
|
+
import json
|
3
|
+
import subprocess
|
4
|
+
from pathlib import Path
|
5
|
+
|
6
|
+
def check_1password_cli() -> tuple:
|
7
|
+
"""Check if 1Password CLI is installed and authenticated.
|
8
|
+
|
9
|
+
Returns:
|
10
|
+
tuple: (installed: bool, authenticated: bool, version: str)
|
11
|
+
"""
|
12
|
+
try:
|
13
|
+
# Check if 1Password CLI is installed
|
14
|
+
result = subprocess.run(
|
15
|
+
['op', '--version'],
|
16
|
+
capture_output=True,
|
17
|
+
text=True,
|
18
|
+
check=False
|
19
|
+
)
|
20
|
+
|
21
|
+
if result.returncode != 0:
|
22
|
+
return False, False, ""
|
23
|
+
|
24
|
+
version = result.stdout.strip()
|
25
|
+
|
26
|
+
# Check if 1Password CLI is authenticated
|
27
|
+
result = subprocess.run(
|
28
|
+
['op', 'account', 'list'],
|
29
|
+
capture_output=True,
|
30
|
+
text=True,
|
31
|
+
check=False
|
32
|
+
)
|
33
|
+
|
34
|
+
if result.returncode != 0:
|
35
|
+
return True, False, version
|
36
|
+
|
37
|
+
return True, True, version
|
38
|
+
|
39
|
+
except FileNotFoundError:
|
40
|
+
return False, False, ""
|
41
|
+
except Exception:
|
42
|
+
return False, False, ""
|
43
|
+
|
44
|
+
def check_ssh_agent(agent_sock_path: str) -> bool:
|
45
|
+
"""Check if 1Password SSH agent is running.
|
46
|
+
|
47
|
+
Args:
|
48
|
+
agent_sock_path: Path to the SSH agent socket
|
49
|
+
|
50
|
+
Returns:
|
51
|
+
bool: True if agent is running
|
52
|
+
"""
|
53
|
+
try:
|
54
|
+
# Check if the socket file exists
|
55
|
+
if not os.path.exists(agent_sock_path):
|
56
|
+
return False
|
57
|
+
|
58
|
+
# Check if agent is active
|
59
|
+
env = os.environ.copy()
|
60
|
+
env['SSH_AUTH_SOCK'] = agent_sock_path
|
61
|
+
|
62
|
+
result = subprocess.run(
|
63
|
+
['ssh-add', '-l'],
|
64
|
+
env=env,
|
65
|
+
capture_output=True,
|
66
|
+
text=True,
|
67
|
+
check=False
|
68
|
+
)
|
69
|
+
|
70
|
+
if "Could not open a connection to your authentication agent" in result.stderr:
|
71
|
+
return False
|
72
|
+
|
73
|
+
return True
|
74
|
+
|
75
|
+
except Exception:
|
76
|
+
return False
|
77
|
+
|
78
|
+
def list_ssh_keys() -> list:
|
79
|
+
"""List SSH keys stored in 1Password.
|
80
|
+
|
81
|
+
Returns:
|
82
|
+
list: List of SSH key items
|
83
|
+
"""
|
84
|
+
try:
|
85
|
+
# List SSH keys in 1Password using the correct command - confusingly, the category is "SSH Key" where the OUTPUT show "SSH_KEY"
|
86
|
+
result = subprocess.run(
|
87
|
+
['op', 'item', 'list', '--categories', 'SSH Key', '--format=json'],
|
88
|
+
capture_output=True,
|
89
|
+
text=True,
|
90
|
+
check=False
|
91
|
+
)
|
92
|
+
|
93
|
+
if result.returncode != 0:
|
94
|
+
return []
|
95
|
+
|
96
|
+
items = json.loads(result.stdout)
|
97
|
+
return items
|
98
|
+
|
99
|
+
except Exception:
|
100
|
+
return []
|
101
|
+
|
102
|
+
def create_ssh_key(title: str, vault: str) -> tuple:
|
103
|
+
"""Create a new SSH key in 1Password.
|
104
|
+
|
105
|
+
Args:
|
106
|
+
title: Title for the SSH key
|
107
|
+
vault: Vault ID to store the key in
|
108
|
+
|
109
|
+
Returns:
|
110
|
+
tuple: (success: bool, public_key: str, item_id: str)
|
111
|
+
"""
|
112
|
+
try:
|
113
|
+
# Create a new SSH key in 1Password
|
114
|
+
result = subprocess.run(
|
115
|
+
[
|
116
|
+
'op', 'item', 'create',
|
117
|
+
'--category=ssh-key', # and yet a different way to specify the category
|
118
|
+
f'--title={title}',
|
119
|
+
f'--vault={vault}'
|
120
|
+
],
|
121
|
+
capture_output=True,
|
122
|
+
text=True,
|
123
|
+
check=False
|
124
|
+
)
|
125
|
+
|
126
|
+
if result.returncode != 0:
|
127
|
+
return False, "", ""
|
128
|
+
|
129
|
+
# Parse the output to extract the public key and item ID
|
130
|
+
output_lines = result.stdout.strip().split('\n')
|
131
|
+
item_id = ""
|
132
|
+
public_key = ""
|
133
|
+
|
134
|
+
for idx, line in enumerate(output_lines):
|
135
|
+
if line.startswith("ID:"):
|
136
|
+
item_id = line.split(":", 1)[1].strip()
|
137
|
+
|
138
|
+
# Check for "public key:" in the line
|
139
|
+
if "public key:" in line.lower():
|
140
|
+
public_key = line.split(":", 1)[1].strip()
|
141
|
+
|
142
|
+
# If we got the item ID but not the public key, try to get it separately
|
143
|
+
if item_id and not public_key:
|
144
|
+
result = subprocess.run(
|
145
|
+
['op', 'item', 'get', item_id, '--fields', 'public key'],
|
146
|
+
capture_output=True,
|
147
|
+
text=True,
|
148
|
+
check=False
|
149
|
+
)
|
150
|
+
|
151
|
+
if result.returncode == 0:
|
152
|
+
public_key = result.stdout.strip()
|
153
|
+
|
154
|
+
return item_id and public_key, public_key, item_id
|
155
|
+
|
156
|
+
except Exception:
|
157
|
+
return False, "", ""
|
158
|
+
|
159
|
+
def get_vaults() -> list:
|
160
|
+
"""Get list of available 1Password vaults.
|
161
|
+
|
162
|
+
Returns:
|
163
|
+
list: List of vault objects with 'id' and 'name' keys
|
164
|
+
"""
|
165
|
+
try:
|
166
|
+
result = subprocess.run(
|
167
|
+
['op', 'vault', 'list', '--format=json'],
|
168
|
+
capture_output=True,
|
169
|
+
text=True,
|
170
|
+
check=False
|
171
|
+
)
|
172
|
+
|
173
|
+
if result.returncode != 0:
|
174
|
+
return []
|
175
|
+
|
176
|
+
vaults = json.loads(result.stdout)
|
177
|
+
return vaults
|
178
|
+
|
179
|
+
except Exception:
|
180
|
+
return []
|
181
|
+
|
182
|
+
def save_public_key(public_key: str, key_path: str) -> bool:
|
183
|
+
"""Save the public key to a file with proper permissions.
|
184
|
+
|
185
|
+
Args:
|
186
|
+
public_key: The public key content
|
187
|
+
key_path: Path to save the public key
|
188
|
+
|
189
|
+
Returns:
|
190
|
+
bool: True if successful
|
191
|
+
"""
|
192
|
+
try:
|
193
|
+
# Create parent directories if needed
|
194
|
+
os.makedirs(os.path.dirname(key_path), exist_ok=True)
|
195
|
+
|
196
|
+
# Write the public key
|
197
|
+
with open(key_path, 'w') as f:
|
198
|
+
f.write(public_key)
|
199
|
+
|
200
|
+
# Set permissions (644) on Unix-like systems
|
201
|
+
if os.name != 'nt': # Not Windows
|
202
|
+
import stat
|
203
|
+
os.chmod(key_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IROTH | stat.S_IRGRP)
|
204
|
+
|
205
|
+
return True
|
206
|
+
|
207
|
+
except Exception:
|
208
|
+
return False
|
cloudx_proxy/_version.py
CHANGED
@@ -0,0 +1,12 @@
|
|
1
|
+
cloudx_proxy/_1password.py,sha256=uxyCfVvO1eQrOfYRojst_LN2DV4fIwxM5moaQTn3wQY,5853
|
2
|
+
cloudx_proxy/__init__.py,sha256=ZZ2O_m9OFJm18AxMSuYJt4UjSuSqyJlYRaZMoets498,61
|
3
|
+
cloudx_proxy/_version.py,sha256=_F8vLxUxrAtC2alXNPGVa9l3P6_vLpQAzemS6QlnPGQ,511
|
4
|
+
cloudx_proxy/cli.py,sha256=kdrZydxL94BJrv6NnjIcceRqhoonBzMIx4vfm1Wl7qc,4104
|
5
|
+
cloudx_proxy/core.py,sha256=RF3bX5MQiokRKjYEPnfWdKywGdtoVUvV2xZqm9uOl1g,8135
|
6
|
+
cloudx_proxy/setup.py,sha256=jvv7ibJQ8svyjYYeVKwGa70L7RV2W7yS7JXEvKed3wI,33339
|
7
|
+
cloudx_proxy-0.4.2.dist-info/LICENSE,sha256=i7P2OR4zsJYsMWcCUDe_B9ZfGi9bU0K5I2nKfDrW_N8,1068
|
8
|
+
cloudx_proxy-0.4.2.dist-info/METADATA,sha256=YAHtMfsqZ1aDk1FryZhgV-Q_vr4GcdBO_mzs-gbEpK8,14037
|
9
|
+
cloudx_proxy-0.4.2.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
|
10
|
+
cloudx_proxy-0.4.2.dist-info/entry_points.txt,sha256=HGt743N2lVlKd7O1qWq3C0aEHyS5PjPnxzDHh7hwtSg,54
|
11
|
+
cloudx_proxy-0.4.2.dist-info/top_level.txt,sha256=2wtEote1db21j-VvkCJFfT-dLlauuG5indjggYh3xDg,13
|
12
|
+
cloudx_proxy-0.4.2.dist-info/RECORD,,
|
@@ -1,11 +0,0 @@
|
|
1
|
-
cloudx_proxy/__init__.py,sha256=ZZ2O_m9OFJm18AxMSuYJt4UjSuSqyJlYRaZMoets498,61
|
2
|
-
cloudx_proxy/_version.py,sha256=yF2DwGUoQKNnLhAbpZX8kCQKjw77EZzhRk7_OTftets,511
|
3
|
-
cloudx_proxy/cli.py,sha256=kdrZydxL94BJrv6NnjIcceRqhoonBzMIx4vfm1Wl7qc,4104
|
4
|
-
cloudx_proxy/core.py,sha256=RF3bX5MQiokRKjYEPnfWdKywGdtoVUvV2xZqm9uOl1g,8135
|
5
|
-
cloudx_proxy/setup.py,sha256=jvv7ibJQ8svyjYYeVKwGa70L7RV2W7yS7JXEvKed3wI,33339
|
6
|
-
cloudx_proxy-0.4.1.dist-info/LICENSE,sha256=i7P2OR4zsJYsMWcCUDe_B9ZfGi9bU0K5I2nKfDrW_N8,1068
|
7
|
-
cloudx_proxy-0.4.1.dist-info/METADATA,sha256=wFL6lV-shLLfI6cn8H5ZItIF944-TrX67LJC0Ml-muc,14037
|
8
|
-
cloudx_proxy-0.4.1.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
|
9
|
-
cloudx_proxy-0.4.1.dist-info/entry_points.txt,sha256=HGt743N2lVlKd7O1qWq3C0aEHyS5PjPnxzDHh7hwtSg,54
|
10
|
-
cloudx_proxy-0.4.1.dist-info/top_level.txt,sha256=2wtEote1db21j-VvkCJFfT-dLlauuG5indjggYh3xDg,13
|
11
|
-
cloudx_proxy-0.4.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|