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.
@@ -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
- await loadCredentials();
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>SSL</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">&times;</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">&times;</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>