using DodoSSH.Client.Domain; using DodoSSH.Client.Storage; namespace DodoSSH.Client.Sync; /// /// The SSH keys in a vault, decrypted, with unpushed local changes laid over them. /// /// /// /// Identical in shape to and identical in implementation, because both are /// facades over the same generic repository. The only thing that differs is the item kind, and with it /// the cipher, the merge, and the fact that a key sends the server no plaintext columns at all. /// /// /// A key listed here has its private key in memory. Listing is not a cheap metadata read: it /// decrypts every key in the vault, so the caller holds the material for as long as it holds the listing. /// That is the same bargain makes for passwords in notes and the reason /// SshKeySecret documents what managed strings do and do not give you — but it is worth stating /// where the decryption actually happens, which is here. /// /// public sealed class SshKeyRepository( ItemStore items, OutboxStore outbox, VaultKeyring keyring, IActivityLogSink? activity = null) { private readonly VaultItemRepository keys = new(SshKeyKind.Instance, items, outbox, keyring, activity); /// public Task> ListAsync( Guid vaultId, CancellationToken cancellationToken) => keys.ListAsync(vaultId, cancellationToken); /// public Task CreateAsync(Guid vaultId, SshKeySecret key, CancellationToken cancellationToken) => keys.CreateAsync(vaultId, key, cancellationToken); /// public Task UpdateAsync( Guid vaultId, Guid entityId, SshKeySecret key, CancellationToken cancellationToken) => keys.UpdateAsync(vaultId, entityId, key, cancellationToken); /// /// /// /// The same two writes a host's move is, and the reason a key needs one at all is what a vault is for: /// a key created in a personal vault before a team existed is the key the team's hosts authenticate /// with, and until this existed the only way to get it across was to paste the private half into a /// second item and delete the first — which is a private key on a clipboard, and two items nobody can /// tell apart afterwards. /// /// /// It lands with a new id, as everything moved does, so every host and group default naming the /// old one is left pointing at a tombstone. Re-aiming them is the caller's, because only the caller /// knows which of them it is allowed to rewrite — see VaultViewModel.ReAimAtAsync. /// /// public Task MoveAsync( Guid fromVaultId, Guid toVaultId, Guid entityId, SshKeySecret key, CancellationToken cancellationToken) => keys.MoveAsync(fromVaultId, toVaultId, entityId, key, cancellationToken); /// public Task DeleteAsync(Guid vaultId, Guid entityId, CancellationToken cancellationToken) => keys.DeleteAsync(vaultId, entityId, cancellationToken); }