feat: add saved SSH keys and terminal workspace tools

This commit is contained in:
Jocay
2026-10-05 00:58:22 +08:00
parent 0ea184f8e1
commit 3580ded915
15 changed files with 1606 additions and 373 deletions
+113
View File
@@ -163,6 +163,119 @@ class TestConnectionStore(unittest.TestCase):
})
self.assertEqual('', profile['directory'])
def test_privatekey_is_encrypted_and_never_public(self):
profile = self.store.upsert('admin', {
'hostname': 'example.com', 'username': 'root',
'privatekey': 'private-key-material',
'passphrase': 'key-passphrase', 'save_passphrase': True,
'auth_type': 'privatekey'
})
raw = self.read_raw()
self.assertNotIn('private-key-material', raw)
self.assertNotIn('key-passphrase', raw)
self.assertTrue(profile['has_privatekey'])
self.assertTrue(profile['has_passphrase'])
for public in (profile, self.store.list('admin')[0]):
for field in ('privatekey', 'privatekey_enc', 'passphrase',
'passphrase_enc'):
self.assertNotIn(field, public)
self.assertEqual(
('private-key-material', 'key-passphrase'),
self.store.get_privatekey('admin', profile['id'])
)
self.assertEqual(
('', ''), self.store.get_privatekey('other', profile['id'])
)
other_id = self.store.make_id('admin', 'evil.example', 22, 'root')
self.assertEqual(('', ''),
self.store.get_privatekey('admin', other_id))
rotated = ConnectionStore(self.filename, 'other-secret')
self.assertEqual(('', ''),
rotated.get_privatekey('admin', profile['id']))
def test_credentials_can_be_replaced_and_cleared(self):
connection = {
'hostname': 'example.com', 'username': 'root',
'password': 'password-secret', 'privatekey': 'old-key',
'passphrase': 'key-secret', 'save_passphrase': True
}
profile = self.store.upsert('admin', connection)
connection.update(privatekey='new-key', save_passphrase=False)
self.store.upsert('admin', connection)
self.assertEqual(('new-key', ''),
self.store.get_privatekey('admin', profile['id']))
connection['save_credentials'] = False
public = self.store.upsert('admin', connection)
self.assertFalse(public['has_password'])
self.assertFalse(public['has_privatekey'])
self.assertFalse(public['has_passphrase'])
self.assertEqual('', self.store.get_password('admin', profile['id']))
self.assertEqual(('', ''),
self.store.get_privatekey('admin', profile['id']))
def test_privatekey_requires_encryption_secret(self):
store = ConnectionStore(self.filename)
with self.assertRaises(ValueError):
store.upsert('admin', {
'hostname': 'example.com', 'username': 'root',
'privatekey': 'private-key-material'
})
self.assertFalse(os.path.exists(self.filename))
def test_metadata_update_keeps_credentials_and_owner_boundary(self):
profile = self.store.upsert('admin', {
'hostname': 'example.com', 'username': 'root',
'password': 'secret', 'name': 'Production', 'group': 'Servers'
})
self.assertIsNone(self.store.update_metadata(
'other', profile['id'], {'name': 'stolen'}
))
updated = self.store.update_metadata('admin', profile['id'], {
'name': 'New name', 'group': 'Testing', 'notes': 'A note'
})
self.assertEqual('New name', updated['name'])
self.assertEqual('secret', self.store.get_password('admin', profile['id']))
def test_plain_export_omits_credentials_and_import_preserves_them(self):
profile = self.store.upsert('admin', {
'hostname': 'example.com', 'username': 'root', 'password': 'secret'
})
exported = self.store.export_connections('admin')
self.assertNotIn('password', exported[0])
self.assertNotIn('owner', exported[0])
exported[0].update(name='Imported', password='injected')
self.store.import_connections('admin', exported)
self.assertEqual('secret', self.store.get_password('admin', profile['id']))
self.assertEqual('Imported', self.store.list('admin')[0]['name'])
def test_invalid_import_is_atomic(self):
with self.assertRaises(ValueError):
self.store.import_connections('admin', [
{'hostname': 'example.com', 'username': 'root', 'port': 22},
{'hostname': 'example.com', 'username': 'root', 'port': -1}
])
self.assertEqual([], self.store.list('admin'))
with self.assertRaises(ValueError):
self.store.import_connections('admin', [
{'hostname': 'example.com', 'username': 'root', 'port': 0}
])
def test_encrypted_import_reencrypts_for_destination_server(self):
profile = self.store.upsert('admin', {
'hostname': 'example.com', 'username': 'root',
'privatekey': 'key-material', 'passphrase': 'key-secret',
'save_passphrase': True
})
exported = self.store.export_connections('admin', True)
destination = ConnectionStore(self.filename + '.new', 'new-server')
destination.import_connections('new-owner', exported, True)
new_id = destination.make_id('new-owner', 'example.com', 22, 'root')
self.assertEqual(('key-material', 'key-secret'),
destination.get_privatekey('new-owner', new_id))
with open(destination.filename, encoding='utf-8') as saved:
self.assertNotIn('key-material', saved.read())
self.assertEqual([], destination.list('admin'))
class TestSecretBox(unittest.TestCase):