feat/folders #21

Merged
mhahl merged 25 commits from feat/folders into main 2026-09-01 22:53:28 +00:00
Owner
No description provided.
Introduce project-scoped secret folders: DB migration (0003_secret_folders.sql) adds api.folders, folder_id on api.secrets, indexes, RLS policies and helper functions (rbac_scope_chain, rbac_folder_binding_allows, rbac_secret_binding_allows, api.can_access_folder, api.can_access_secret_row, validate_binding_scope). Server-side: new secret_svc.folders (path parsing, materialize/delete helpers, visible folder detection), integrate folder handling into secret_ops (folder_id, conflict targets), add folder routes, views and UI partials, RBAC helper updates, and tests covering folders and migration. Secret creation now validates path syntax before DB use.
Add a SECURITY DEFINER function private.materialize_folder_path (migration 0004) that inserts/upserts into api.folders with row_security off so folder upserts invoked during secret creation won’t hit RLS InsufficientPrivilege for ancestor-scoped bindings. Python code in app/secret_svc/folders.py now calls the new function via SELECT and adjusts fetch logic. Grant EXECUTE to authenticator/authenticated and update tests/test_migrations.py to include the new migration.
Replace the badge-style folder list with a semantic <section> and table. Adds a "Folders" heading and table columns for Folder and Access, rendering each folder as a table row with the path link and a muted access_mode cell. Improves markup semantics, accessibility, and makes folder access visible in the UI.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Simplify the project secrets partial by removing the wrapping <section> and the "Folders" <h2> heading; the folder table is now rendered inline. This is a small markup/UX cleanup in app/templates/partials/project_secrets.html with no functional changes to data or access display.
Markup cleanup: wrap <h2> headings in a .section-head div for consistent styling in folder_view.html. In app/templates/partials/project_secrets.html the folders list was moved above the secrets section, its heading wrapped with .section-head, and a duplicate folders block removed. Layout-only changes; no functional behavior changed.
This change adds support for creating project folders from slash-separated paths, including the route and UI flow for creating folders from the secrets tab. It also updates the RBAC docs to describe folder-scoped access, inherit/restricted folder modes, and the fact that secret roles can be applied at folder scope. The result is a more complete folder model that behaves like a parent scope for secrets while preserving project-level access controls.
This change adds server-side SSH key generation for project secrets and exposes it in the secret creation UI. Users can select Ed25519, RSA, or ECDSA, generate a private key from the browser, and paste it into the SSH secret field without leaving the workflow.

It also simplifies folder loading in the project detail view by fetching all project folders instead of only visible paths, and refreshes the create-folder dialog markup to match the current form styling.
This change adds management API endpoints for listing, creating, and deleting folders and includes folder metadata in secret responses. It also adds an effective-access panel to the folder UI with search and pagination, and updates the folder scope label used in access reporting to show the folder path.
Update SSH key generation to return both private and public keys from the server (rename private_pem, import PublicFormat). Update secret_new.html to display a readonly Public key textarea, move the generate button to the form actions (labelled "Generate and save"), and update client JS to populate both private and public fields. Improves UX by showing the public key and allowing generation+save in one action.
Add extract_ssh_public_key to derive OpenSSH-format public keys from private PEMs (uses cryptography, returns empty string on error). Wire it into the secret view renderer and update secret_view.html to display the public key (view + edit) with copy buttons. Also adjust the secret form actions: replace save/cancel with Create, Generate (ssh-gen-btn) and a Cancel link. These changes surface SSH public keys for SSH-type secrets without altering other secret kinds.
The SSH key generation flow now creates the key, fills the form fields, and submits the secret form automatically so the generated key is saved immediately. The generate button was moved into the SSH field group, failure states reset correctly, and the secret view form now uses an SSH-specific save label and the proper cancel URL.
Try load_ssh_private_key first and fall back to load_pem_private_key on ValueError. Adds the necessary import and preserves returning an empty string for other exceptions. This allows extracting the public key from PEM-encoded private keys as well as OpenSSH-format keys.
Add migration 0006 to update private.machine_upsert_enc so its ON CONFLICT target matches the root-level unique index (includes "folder_id IS NULL AND deleted_at IS NULL"). This ensures machine upserts (which create root-level secrets) resolve conflicts against the correct partial index. Also add tests: include the new migration in the shipped order and add test_machine_upsert_conflict_target asserting the predicate and ON CONFLICT clause are present.
Add db/migrations/0007_machine_upsert_folder.sql which extends private.machine_upsert_enc to parse slash-separated keys, materialize folder hierarchy via private.materialize_folder_path, and store secrets with folder_id (matching web UI behavior). Grants EXECUTE on the function to authenticator. Also update tests/test_migrations.py to include the new 0007 migration in the ordered migration list.
0001_init.sql left both 8-arg and 9-arg overloads, so GRANT EXECUTE
without an argument list aborts migration 0006. Drop the stale 8-arg
function first and GRANT the 9-arg signature explicitly.
process_queue already logs deliveries with row['webhook_id'] but the
queue SELECT omitted that column. Include q.webhook_id so delivery
rows are recorded against the webhook that queued them.
Pass --no-control-socket so gunicorn does not try to bind a Unix
control socket that the non-root appuser cannot create.
Slash-separated keys (corvus-agent hosts/.../users/root) 500ed because
the 0007 function variable shadowed api.secrets.folder_id in ON CONFLICT.
Filter project folders to only those that contain non-deleted secrets (add EXISTS subquery). Change secret loading to return only root-level (folder_id IS NULL) secrets when no search query is provided (search still scans all secrets). Update folder view to display only the basename (last path component) of secret keys for a cleaner UI.
Lock the corvus-agent contract: omitting value with kind=ssh generates
a keypair and returns ssh_public_key plus private_key; plain PUTs
without value are still rejected.
Security: session rotation, DB as_user & HSM
Some checks failed
CI / tox py + lint (pull_request) Has been cancelled
eafcfd5141
Several security, cleanup and typing fixes across the app:
- db.as_user converted to a contextmanager that yields a connection and ensures JWT claims/ROLE are reset, rollback performed and connection closed.
- Rotate session cookie on login and 2FA start to mitigate session fixation.
- Admin HSM slot creation rejects inline PKCS#11 pin-values unless ALLOW_INSECURE_DEFAULTS is set.
- Add Subresource Integrity attrs for vendor scripts in base template.
- Minor import/typing reorders, small refactors and test adjustment for additional DB fetches.
mhahl merged commit 422239923c into main 2026-09-01 22:53:28 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Sigaint/corvus!21
No description provided.