mirror of
https://github.com/Dvorinka/Primora.git
synced 2026-10-28 15:51:23 +00:00
Local vault (apps/cli): clean-room PRMVLT01 format — Argon2id → XChaCha20-Poly1305, AEAD-bound header, atomic writes. vault:* / secrets:* / inject (primora:// refs) / agent credential broker (dummy tokens in the child, real secret on the wire) / TTL sessions / stack:* operator group. Project vault (backend): core.project_secrets, AES-256-GCM via the platform encryptor. Metadata-only list, audit-logged reveal, and secret://NAME refs resolved at job delivery — refs stored, values never. Surfaces: dashboard Vault page (+ local-vault card under the Tauri shell), desktop vault window via IPC to the primora binary, secrets:* --remote CLI mode, metadata/write-only MCP tools so agents consume secrets via secret:// refs without holding plaintext. Authored By: TDvorak <[email protected]> Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
88 lines
2.2 KiB
Go
88 lines
2.2 KiB
Go
package handlers
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/jackc/pgx/v5"
|
|
|
|
"github.com/tdvorak/primora/apps/backend/internal/middleware"
|
|
apperrors "github.com/tdvorak/primora/apps/backend/internal/response"
|
|
"github.com/tdvorak/primora/apps/backend/internal/services"
|
|
)
|
|
|
|
func (h *HTTPHandler) secretError(c *gin.Context, err error) {
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
apperrors.Abort(c, http.StatusNotFound, "not_found", "secret not found")
|
|
return
|
|
}
|
|
msg := err.Error()
|
|
if strings.Contains(msg, "invalid secret name") {
|
|
apperrors.Abort(c, http.StatusBadRequest, "bad_request", msg)
|
|
return
|
|
}
|
|
if strings.Contains(msg, "encryption is not configured") {
|
|
apperrors.Abort(c, http.StatusServiceUnavailable, "unavailable", msg)
|
|
return
|
|
}
|
|
h.handleError(c, err)
|
|
}
|
|
|
|
func (h *HTTPHandler) listProjectSecrets(c *gin.Context) {
|
|
actor, projectID, ok := h.actorAndProject(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
result, err := h.Platform.ListProjectSecrets(c.Request.Context(), actor, projectID)
|
|
if err != nil {
|
|
h.secretError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"items": result})
|
|
}
|
|
|
|
func (h *HTTPHandler) setProjectSecret(c *gin.Context) {
|
|
actor, projectID, ok := h.actorAndProject(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
var body services.SetProjectSecretInput
|
|
if !h.bindAndValidate(c, &body) {
|
|
return
|
|
}
|
|
body.Name = c.Param("name")
|
|
result, err := h.Platform.SetProjectSecret(c.Request.Context(), actor, projectID, body, middleware.RequestIDFromContext(c))
|
|
if err != nil {
|
|
h.secretError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, result)
|
|
}
|
|
|
|
func (h *HTTPHandler) deleteProjectSecret(c *gin.Context) {
|
|
actor, projectID, ok := h.actorAndProject(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
if err := h.Platform.DeleteProjectSecret(c.Request.Context(), actor, projectID, c.Param("name"), middleware.RequestIDFromContext(c)); err != nil {
|
|
h.secretError(c, err)
|
|
return
|
|
}
|
|
c.Status(http.StatusNoContent)
|
|
}
|
|
|
|
func (h *HTTPHandler) revealProjectSecret(c *gin.Context) {
|
|
actor, projectID, ok := h.actorAndProject(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
result, err := h.Platform.RevealProjectSecret(c.Request.Context(), actor, projectID, c.Param("name"), middleware.RequestIDFromContext(c))
|
|
if err != nil {
|
|
h.secretError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, result)
|
|
}
|