illumio-pylo 0.3.12__py3-none-any.whl → 0.3.13__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.
- illumio_pylo/API/APIConnector.py +61 -14
- illumio_pylo/API/CredentialsManager.py +130 -3
- illumio_pylo/API/Explorer.py +619 -14
- illumio_pylo/API/JsonPayloadTypes.py +64 -4
- illumio_pylo/FilterQuery.py +892 -0
- illumio_pylo/LabelCommon.py +13 -3
- illumio_pylo/LabelDimension.py +109 -0
- illumio_pylo/LabelStore.py +97 -38
- illumio_pylo/WorkloadStore.py +58 -0
- illumio_pylo/__init__.py +9 -3
- illumio_pylo/cli/__init__.py +5 -2
- illumio_pylo/cli/commands/__init__.py +1 -0
- illumio_pylo/cli/commands/credential_manager.py +176 -0
- illumio_pylo/cli/commands/traffic_export.py +358 -0
- illumio_pylo/cli/commands/ui/credential_manager_ui/app.js +191 -2
- illumio_pylo/cli/commands/ui/credential_manager_ui/index.html +50 -1
- illumio_pylo/cli/commands/ui/credential_manager_ui/styles.css +179 -28
- illumio_pylo/cli/commands/update_pce_objects_cache.py +1 -2
- illumio_pylo/cli/commands/workload_export.py +29 -0
- {illumio_pylo-0.3.12.dist-info → illumio_pylo-0.3.13.dist-info}/METADATA +1 -1
- {illumio_pylo-0.3.12.dist-info → illumio_pylo-0.3.13.dist-info}/RECORD +24 -22
- illumio_pylo/Query.py +0 -331
- {illumio_pylo-0.3.12.dist-info → illumio_pylo-0.3.13.dist-info}/WHEEL +0 -0
- {illumio_pylo-0.3.12.dist-info → illumio_pylo-0.3.13.dist-info}/licenses/LICENSE +0 -0
- {illumio_pylo-0.3.12.dist-info → illumio_pylo-0.3.13.dist-info}/top_level.txt +0 -0
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
btnSubmit: document.getElementById('btn-submit'),
|
|
22
22
|
btnCloseTestModal: document.getElementById('btn-close-test-modal'),
|
|
23
23
|
btnCloseTest: document.getElementById('btn-close-test'),
|
|
24
|
+
themeToggle: document.getElementById('theme-toggle'),
|
|
24
25
|
|
|
25
26
|
// Modal
|
|
26
27
|
credentialModal: document.getElementById('credential-modal'),
|
|
@@ -57,23 +58,82 @@
|
|
|
57
58
|
deleteCredentialName: document.getElementById('delete-credential-name'),
|
|
58
59
|
btnCloseDeleteModal: document.getElementById('btn-close-delete-modal'),
|
|
59
60
|
btnCancelDelete: document.getElementById('btn-cancel-delete'),
|
|
60
|
-
btnConfirmDelete: document.getElementById('btn-confirm-delete')
|
|
61
|
+
btnConfirmDelete: document.getElementById('btn-confirm-delete'),
|
|
62
|
+
|
|
63
|
+
// Encrypt modal
|
|
64
|
+
encryptModal: document.getElementById('encrypt-modal'),
|
|
65
|
+
encryptCredentialName: document.getElementById('encrypt-credential-name'),
|
|
66
|
+
encryptSshKey: document.getElementById('encrypt-ssh-key'),
|
|
67
|
+
btnCloseEncryptModal: document.getElementById('btn-close-encrypt-modal'),
|
|
68
|
+
btnCancelEncrypt: document.getElementById('btn-cancel-encrypt'),
|
|
69
|
+
btnConfirmEncrypt: document.getElementById('btn-confirm-encrypt'),
|
|
70
|
+
|
|
71
|
+
// Shutdown modal
|
|
72
|
+
shutdownModal: document.getElementById('shutdown-modal'),
|
|
73
|
+
btnShutdown: document.getElementById('btn-shutdown'),
|
|
74
|
+
btnCloseShutdownModal: document.getElementById('btn-close-shutdown-modal'),
|
|
75
|
+
btnCancelShutdown: document.getElementById('btn-cancel-shutdown'),
|
|
76
|
+
btnConfirmShutdown: document.getElementById('btn-confirm-shutdown'),
|
|
77
|
+
shutdownMessage: document.getElementById('shutdown-message'),
|
|
78
|
+
shutdownCountdown: document.getElementById('shutdown-countdown'),
|
|
79
|
+
shutdownActions: document.getElementById('shutdown-actions')
|
|
61
80
|
};
|
|
62
81
|
|
|
63
82
|
// State
|
|
64
83
|
let sshKeys = [];
|
|
65
84
|
let encryptionAvailable = false;
|
|
66
85
|
let credentialToDelete = null;
|
|
86
|
+
let credentialToEncrypt = null;
|
|
87
|
+
let shutdownInProgress = false;
|
|
88
|
+
|
|
89
|
+
// Theme Management
|
|
90
|
+
function initTheme() {
|
|
91
|
+
// Check for saved theme preference or default to browser preference
|
|
92
|
+
const savedTheme = localStorage.getItem('theme');
|
|
93
|
+
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
94
|
+
|
|
95
|
+
if (savedTheme) {
|
|
96
|
+
setTheme(savedTheme);
|
|
97
|
+
} else if (prefersDark) {
|
|
98
|
+
setTheme('dark');
|
|
99
|
+
} else {
|
|
100
|
+
setTheme('light');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Listen for browser theme changes
|
|
104
|
+
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
|
|
105
|
+
if (!localStorage.getItem('theme')) {
|
|
106
|
+
setTheme(e.matches ? 'dark' : 'light');
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function setTheme(theme) {
|
|
112
|
+
document.documentElement.setAttribute('data-theme', theme);
|
|
113
|
+
elements.themeToggle.textContent = theme === 'dark' ? '☀️' : '🌙';
|
|
114
|
+
elements.themeToggle.title = theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function toggleTheme() {
|
|
118
|
+
const currentTheme = document.documentElement.getAttribute('data-theme');
|
|
119
|
+
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
|
|
120
|
+
setTheme(newTheme);
|
|
121
|
+
localStorage.setItem('theme', newTheme);
|
|
122
|
+
}
|
|
67
123
|
|
|
68
124
|
// Initialize the application
|
|
69
125
|
async function init() {
|
|
70
|
-
|
|
126
|
+
initTheme();
|
|
71
127
|
await checkEncryptionStatus();
|
|
128
|
+
await loadCredentials();
|
|
72
129
|
setupEventListeners();
|
|
73
130
|
}
|
|
74
131
|
|
|
75
132
|
// Setup event listeners
|
|
76
133
|
function setupEventListeners() {
|
|
134
|
+
// Theme toggle button
|
|
135
|
+
elements.themeToggle.addEventListener('click', toggleTheme);
|
|
136
|
+
|
|
77
137
|
// New credential button
|
|
78
138
|
elements.btnNewCredential.addEventListener('click', () => openCreateModal());
|
|
79
139
|
|
|
@@ -96,6 +156,15 @@
|
|
|
96
156
|
elements.btnCancelDelete.addEventListener('click', closeDeleteModal);
|
|
97
157
|
elements.btnConfirmDelete.addEventListener('click', confirmDelete);
|
|
98
158
|
|
|
159
|
+
// Encrypt modal buttons
|
|
160
|
+
elements.btnCloseEncryptModal.addEventListener('click', closeEncryptModal);
|
|
161
|
+
elements.btnCancelEncrypt.addEventListener('click', closeEncryptModal);
|
|
162
|
+
elements.btnConfirmEncrypt.addEventListener('click', confirmEncrypt);
|
|
163
|
+
|
|
164
|
+
// Shutdown modal buttons
|
|
165
|
+
elements.btnShutdown.addEventListener('click', openShutdownModal);
|
|
166
|
+
elements.btnConfirmShutdown.addEventListener('click', confirmShutdown);
|
|
167
|
+
|
|
99
168
|
// Close modals on background click
|
|
100
169
|
elements.credentialModal.addEventListener('click', (e) => {
|
|
101
170
|
if (e.target === elements.credentialModal) closeModal();
|
|
@@ -106,6 +175,9 @@
|
|
|
106
175
|
elements.deleteModal.addEventListener('click', (e) => {
|
|
107
176
|
if (e.target === elements.deleteModal) closeDeleteModal();
|
|
108
177
|
});
|
|
178
|
+
elements.encryptModal.addEventListener('click', (e) => {
|
|
179
|
+
if (e.target === elements.encryptModal) closeEncryptModal();
|
|
180
|
+
});
|
|
109
181
|
}
|
|
110
182
|
|
|
111
183
|
// API Functions
|
|
@@ -158,17 +230,22 @@
|
|
|
158
230
|
|
|
159
231
|
for (const cred of credentials) {
|
|
160
232
|
const row = document.createElement('tr');
|
|
233
|
+
// Show encrypt button only if encryption is available and key is not already encrypted
|
|
234
|
+
const showEncryptBtn = encryptionAvailable && sshKeys.length > 0 && !cred.api_key_encrypted;
|
|
235
|
+
console.log('Rendering credential:', cred.name, 'Show Encrypt Button:', showEncryptBtn, 'Encryption Available:', encryptionAvailable, 'SSH Keys:', sshKeys.length, 'API Key Encrypted:', cred.api_key_encrypted);
|
|
161
236
|
row.innerHTML = `
|
|
162
237
|
<td><strong>${escapeHtml(cred.name)}</strong></td>
|
|
163
238
|
<td>${escapeHtml(cred.fqdn)}</td>
|
|
164
239
|
<td>${cred.port}</td>
|
|
165
240
|
<td>${cred.org_id}</td>
|
|
166
241
|
<td>${escapeHtml(cred.api_user)}</td>
|
|
242
|
+
<td class="${cred.api_key_encrypted ? 'status-yes' : 'status-no'}">${cred.api_key_encrypted ? 'Yes' : 'No'}</td>
|
|
167
243
|
<td class="${cred.verify_ssl ? 'status-yes' : 'status-no'}">${cred.verify_ssl ? 'Yes' : 'No'}</td>
|
|
168
244
|
<td title="${escapeHtml(cred.originating_file)}">${truncatePath(cred.originating_file)}</td>
|
|
169
245
|
<td class="actions-cell">
|
|
170
246
|
<button class="btn btn-small btn-success btn-test" data-name="${escapeHtml(cred.name)}">Test</button>
|
|
171
247
|
<button class="btn btn-small btn-secondary btn-edit" data-name="${escapeHtml(cred.name)}">Edit</button>
|
|
248
|
+
${showEncryptBtn ? `<button class="btn btn-small btn-warning btn-encrypt" data-name="${escapeHtml(cred.name)}">Encrypt</button>` : ''}
|
|
172
249
|
<button class="btn btn-small btn-danger btn-delete" data-name="${escapeHtml(cred.name)}">Delete</button>
|
|
173
250
|
</td>
|
|
174
251
|
`;
|
|
@@ -182,6 +259,9 @@
|
|
|
182
259
|
document.querySelectorAll('.btn-edit').forEach(btn => {
|
|
183
260
|
btn.addEventListener('click', () => openEditModal(btn.dataset.name));
|
|
184
261
|
});
|
|
262
|
+
document.querySelectorAll('.btn-encrypt').forEach(btn => {
|
|
263
|
+
btn.addEventListener('click', () => openEncryptModal(btn.dataset.name));
|
|
264
|
+
});
|
|
185
265
|
document.querySelectorAll('.btn-delete').forEach(btn => {
|
|
186
266
|
btn.addEventListener('click', () => openDeleteModal(btn.dataset.name));
|
|
187
267
|
});
|
|
@@ -297,6 +377,115 @@
|
|
|
297
377
|
credentialToDelete = null;
|
|
298
378
|
}
|
|
299
379
|
|
|
380
|
+
// Open modal for encrypting credential
|
|
381
|
+
function openEncryptModal(name) {
|
|
382
|
+
credentialToEncrypt = name;
|
|
383
|
+
elements.encryptCredentialName.textContent = name;
|
|
384
|
+
// Populate SSH key dropdown
|
|
385
|
+
elements.encryptSshKey.innerHTML = '';
|
|
386
|
+
for (const key of sshKeys) {
|
|
387
|
+
const option = document.createElement('option');
|
|
388
|
+
option.value = key.index;
|
|
389
|
+
option.textContent = `${key.type} | ${key.fingerprint.substring(0, 16)}... | ${key.comment || 'No comment'}`;
|
|
390
|
+
elements.encryptSshKey.appendChild(option);
|
|
391
|
+
}
|
|
392
|
+
elements.encryptModal.classList.remove('hidden');
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Close encrypt modal
|
|
396
|
+
function closeEncryptModal() {
|
|
397
|
+
elements.encryptModal.classList.add('hidden');
|
|
398
|
+
credentialToEncrypt = null;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Open modal for shutting down the server
|
|
402
|
+
function openShutdownModal() {
|
|
403
|
+
// Reset modal to initial state
|
|
404
|
+
shutdownInProgress = false;
|
|
405
|
+
elements.shutdownMessage.textContent = 'This will stop the Credential Manager server program.';
|
|
406
|
+
elements.shutdownCountdown.classList.add('hidden');
|
|
407
|
+
elements.shutdownCountdown.textContent = '';
|
|
408
|
+
elements.shutdownActions.classList.remove('hidden');
|
|
409
|
+
elements.btnConfirmShutdown.disabled = false;
|
|
410
|
+
elements.btnConfirmShutdown.textContent = 'Stop Server';
|
|
411
|
+
elements.btnCancelShutdown.disabled = true;
|
|
412
|
+
elements.btnCloseShutdownModal.disabled = true;
|
|
413
|
+
elements.shutdownModal.classList.remove('hidden');
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Shutdown modal intentionally stays visible once opened.
|
|
417
|
+
|
|
418
|
+
// Confirm shutdown
|
|
419
|
+
async function confirmShutdown() {
|
|
420
|
+
if (shutdownInProgress) return;
|
|
421
|
+
|
|
422
|
+
shutdownInProgress = true;
|
|
423
|
+
elements.btnConfirmShutdown.disabled = true;
|
|
424
|
+
elements.btnConfirmShutdown.textContent = 'Stopping...';
|
|
425
|
+
elements.btnCancelShutdown.disabled = true;
|
|
426
|
+
elements.btnCloseShutdownModal.disabled = true;
|
|
427
|
+
|
|
428
|
+
try {
|
|
429
|
+
await apiCall('/api/shutdown', { method: 'POST' });
|
|
430
|
+
|
|
431
|
+
// Update modal to show success and countdown
|
|
432
|
+
elements.shutdownMessage.textContent = 'Server shutdown acknowledged. Closing window...';
|
|
433
|
+
elements.shutdownActions.classList.add('hidden');
|
|
434
|
+
elements.shutdownCountdown.classList.remove('hidden');
|
|
435
|
+
|
|
436
|
+
// Start countdown
|
|
437
|
+
let countdown = 10;
|
|
438
|
+
elements.shutdownCountdown.textContent = `Window will close in ${countdown} seconds...`;
|
|
439
|
+
elements.shutdownCountdown.className = 'countdown-text';
|
|
440
|
+
|
|
441
|
+
const countdownInterval = setInterval(() => {
|
|
442
|
+
countdown--;
|
|
443
|
+
if (countdown > 0) {
|
|
444
|
+
elements.shutdownCountdown.textContent = `Window will close in ${countdown} seconds...`;
|
|
445
|
+
} else {
|
|
446
|
+
clearInterval(countdownInterval);
|
|
447
|
+
elements.shutdownCountdown.textContent = 'Closing window...';
|
|
448
|
+
// Attempt to close the window/tab
|
|
449
|
+
window.close();
|
|
450
|
+
// If window.close() doesn't work (common in modern browsers), show a message
|
|
451
|
+
setTimeout(() => {
|
|
452
|
+
elements.shutdownCountdown.textContent = 'Please close this browser tab/window manually.';
|
|
453
|
+
}, 500);
|
|
454
|
+
}
|
|
455
|
+
}, 1000);
|
|
456
|
+
} catch (error) {
|
|
457
|
+
shutdownInProgress = false;
|
|
458
|
+
elements.btnConfirmShutdown.disabled = false;
|
|
459
|
+
elements.btnConfirmShutdown.textContent = 'Stop Server';
|
|
460
|
+
showNotification('Failed to stop server: ' + error.message, 'error');
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Confirm credential encryption
|
|
465
|
+
async function confirmEncrypt() {
|
|
466
|
+
if (!credentialToEncrypt) return;
|
|
467
|
+
|
|
468
|
+
elements.btnConfirmEncrypt.disabled = true;
|
|
469
|
+
elements.btnConfirmEncrypt.textContent = 'Encrypting...';
|
|
470
|
+
|
|
471
|
+
try {
|
|
472
|
+
await apiCall(`/api/credentials/${encodeURIComponent(credentialToEncrypt)}/encrypt`, {
|
|
473
|
+
method: 'POST',
|
|
474
|
+
body: JSON.stringify({
|
|
475
|
+
ssh_key_index: parseInt(elements.encryptSshKey.value)
|
|
476
|
+
})
|
|
477
|
+
});
|
|
478
|
+
showNotification('API key encrypted successfully!', 'success');
|
|
479
|
+
closeEncryptModal();
|
|
480
|
+
await loadCredentials();
|
|
481
|
+
} catch (error) {
|
|
482
|
+
showNotification('Failed to encrypt API key: ' + error.message, 'error');
|
|
483
|
+
} finally {
|
|
484
|
+
elements.btnConfirmEncrypt.disabled = false;
|
|
485
|
+
elements.btnConfirmEncrypt.textContent = 'Encrypt';
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
300
489
|
// Reset form
|
|
301
490
|
function resetForm() {
|
|
302
491
|
elements.credentialForm.reset();
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
<body>
|
|
10
10
|
<div class="container">
|
|
11
11
|
<header>
|
|
12
|
+
<button id="theme-toggle" class="theme-toggle" title="Toggle dark mode">🌙</button>
|
|
12
13
|
<h1>🔐 PYLO Credential Manager</h1>
|
|
13
14
|
<p class="subtitle">Manage your PCE credentials</p>
|
|
14
15
|
</header>
|
|
@@ -33,7 +34,8 @@
|
|
|
33
34
|
<th>Port</th>
|
|
34
35
|
<th>Org ID</th>
|
|
35
36
|
<th>API User</th>
|
|
36
|
-
<th>
|
|
37
|
+
<th>Encrypted Key</th>
|
|
38
|
+
<th>Verify SSL</th>
|
|
37
39
|
<th>File</th>
|
|
38
40
|
<th>Actions</th>
|
|
39
41
|
</tr>
|
|
@@ -111,6 +113,7 @@
|
|
|
111
113
|
<select id="form-ssh-key">
|
|
112
114
|
</select>
|
|
113
115
|
</div>
|
|
116
|
+
<p class="warning-text">You may be prompted by your SSH agent to unlock the selected SSH key when encrypting or using the key.</p>
|
|
114
117
|
</div>
|
|
115
118
|
|
|
116
119
|
<!-- Storage location (only for create) -->
|
|
@@ -161,6 +164,52 @@
|
|
|
161
164
|
</div>
|
|
162
165
|
</div>
|
|
163
166
|
</div>
|
|
167
|
+
|
|
168
|
+
<!-- Encrypt confirmation modal -->
|
|
169
|
+
<div id="encrypt-modal" class="modal hidden">
|
|
170
|
+
<div class="modal-content modal-small">
|
|
171
|
+
<div class="modal-header">
|
|
172
|
+
<h2>Encrypt API Key</h2>
|
|
173
|
+
<button class="btn-close" id="btn-close-encrypt-modal">×</button>
|
|
174
|
+
</div>
|
|
175
|
+
<div class="encrypt-confirmation">
|
|
176
|
+
<p>Encrypt the API key for "<strong id="encrypt-credential-name"></strong>" using an SSH key.</p>
|
|
177
|
+
<div class="form-group">
|
|
178
|
+
<label for="encrypt-ssh-key">Select SSH Key:</label>
|
|
179
|
+
<select id="encrypt-ssh-key">
|
|
180
|
+
</select>
|
|
181
|
+
</div>
|
|
182
|
+
<p class="warning-text">You may be prompted by your SSH agent to unlock the selected SSH key when encrypting the API key.</p>
|
|
183
|
+
</div>
|
|
184
|
+
<div class="form-actions">
|
|
185
|
+
<button type="button" class="btn btn-secondary" id="btn-cancel-encrypt">Cancel</button>
|
|
186
|
+
<button type="button" class="btn btn-primary" id="btn-confirm-encrypt">Encrypt</button>
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
<!-- Shutdown confirmation modal -->
|
|
192
|
+
<div id="shutdown-modal" class="modal hidden">
|
|
193
|
+
<div class="modal-content modal-small">
|
|
194
|
+
<div class="modal-header">
|
|
195
|
+
<h2>Close Program</h2>
|
|
196
|
+
<button class="btn-close" id="btn-close-shutdown-modal">×</button>
|
|
197
|
+
</div>
|
|
198
|
+
<div id="shutdown-content" class="shutdown-confirmation">
|
|
199
|
+
<p id="shutdown-message">This will stop the Credential Manager server program.</p>
|
|
200
|
+
<p id="shutdown-countdown" class="hidden"></p>
|
|
201
|
+
</div>
|
|
202
|
+
<div class="form-actions" id="shutdown-actions">
|
|
203
|
+
<button type="button" class="btn btn-secondary" id="btn-cancel-shutdown">Cancel</button>
|
|
204
|
+
<button type="button" class="btn btn-danger" id="btn-confirm-shutdown">Stop Server</button>
|
|
205
|
+
</div>
|
|
206
|
+
</div>
|
|
207
|
+
</div>
|
|
208
|
+
|
|
209
|
+
<!-- Shutdown button fixed at bottom -->
|
|
210
|
+
<div class="shutdown-button-container">
|
|
211
|
+
<button id="btn-shutdown" class="btn btn-shutdown">Job done, close program</button>
|
|
212
|
+
</div>
|
|
164
213
|
</div>
|
|
165
214
|
|
|
166
215
|
<script src="/static/app.js"></script>
|