feat: reveal saved server deployment inputs
This commit is contained in:
@@ -68,6 +68,7 @@ func (h *coreHandlers) register(mux *http.ServeMux) {
|
||||
mux.HandleFunc("/api/v1/server-instances/{id}/process/status", h.serverInstanceProcessStatus)
|
||||
mux.HandleFunc("/api/v1/server-instances/{id}/runtime/actions", h.serverRuntimeActions)
|
||||
mux.HandleFunc("/api/v1/server-instances/{id}/runtime-binding", h.serverRuntimeBinding)
|
||||
mux.HandleFunc("/api/v1/server-instances/{id}/deployment/reveal", h.serverDeploymentReveal)
|
||||
mux.HandleFunc("/api/v1/server-instances/{id}/deployment", h.serverDeployment)
|
||||
mux.HandleFunc("/api/v1/server-instances/{id}/deploy", h.serverInstanceDeploy)
|
||||
mux.HandleFunc("/api/v1/server-instances/{id}/remote-adapters", h.remoteAdapters)
|
||||
|
||||
@@ -719,6 +719,36 @@ func TestServerLifecycleWorkflowAPI(t *testing.T) {
|
||||
assertErrorResponse(t, invalidStop, http.StatusBadRequest, errorCodeValidation)
|
||||
}
|
||||
|
||||
func TestServerDeploymentRevealAPIIsExplicitAndOwnerScoped(t *testing.T) {
|
||||
router := newTestRouter()
|
||||
adminSession := createAdminSession(t, router)
|
||||
postJSON[dto.GamePluginResponse](t, router, "/api/v1/game-plugins", validGamePluginRequest())
|
||||
endpoint := validRunEndpointRequest()
|
||||
endpoint.Capabilities = append(endpoint.Capabilities, domain.JobCapabilityDeploymentPlan)
|
||||
postJSON[dto.RunEndpointResponse](t, router, "/api/v1/run/endpoints", endpoint)
|
||||
created := postOKJSONWithAuth[dto.ServerLifecycleResponse](t, router, "/api/v1/server-instances/workflows/create", dto.ServerLifecycleCreateRequest{ID: "deployment-reveal", PluginID: "server.scum", RunEndpointID: "run-local", Name: "Reveal", IdempotencyKey: "deployment-reveal", ProfileKey: "local", Deployment: dto.ServerDeploymentRequest{Mode: domain.ServerDeploymentModeCustom, ServerRoot: "/srv/reveal", WorkingDirectory: "/srv/reveal", StartCommand: "./start-server"}}, adminSession)
|
||||
|
||||
redactedRecorder := requestWithAuth(t, router, http.MethodGet, "/api/v1/server-instances/deployment-reveal/deployment", "", adminSession)
|
||||
assertStatus(t, redactedRecorder, http.StatusOK)
|
||||
if body := redactedRecorder.Body.String(); strings.Contains(body, "/srv/reveal") || strings.Contains(body, "./start-server") {
|
||||
t.Fatalf("normal deployment view leaked protected inputs: %s", body)
|
||||
}
|
||||
redacted := decodeBody[dto.ServerDeploymentResponse](t, redactedRecorder)
|
||||
if redacted.LatestDispatch == nil || !redacted.LatestDispatch.DeploymentDefinitionIncluded || redacted.LatestDispatch.JobID != created.Job.ID || redacted.LatestDispatch.DeploymentRevision != 1 {
|
||||
t.Fatalf("expected safe deployment dispatch evidence, got %+v", redacted.LatestDispatch)
|
||||
}
|
||||
|
||||
revealed := getJSONWithAuth[dto.ServerDeploymentRevealResponse](t, router, "/api/v1/server-instances/deployment-reveal/deployment/reveal", adminSession)
|
||||
if revealed.ServerRoot != "/srv/reveal" || revealed.WorkingDirectory != "/srv/reveal" || revealed.StartCommand != "./start-server" || revealed.InstallCommand != "" {
|
||||
t.Fatalf("unexpected explicitly revealed deployment: %+v", revealed)
|
||||
}
|
||||
|
||||
postJSONWithAuth[dto.UserResponse](t, router, "/api/v1/users", dto.UserCreateRequest{ID: "deployment-other", DisplayName: "Other", Email: "deployment-other@example.test", Roles: []string{"server-owner"}, Password: "other-password"}, adminSession)
|
||||
other := postOKJSON[dto.AuthSessionResponse](t, router, "/api/v1/auth/login", dto.LoginRequest{Account: "deployment-other@example.test", Password: "other-password"})
|
||||
denied := requestWithAuth(t, router, http.MethodGet, "/api/v1/server-instances/deployment-reveal/deployment/reveal", "", other.SessionID)
|
||||
assertErrorResponse(t, denied, http.StatusForbidden, errorCodeForbidden)
|
||||
}
|
||||
|
||||
func TestServerInstanceManagementAPI(t *testing.T) {
|
||||
router := newTestRouter()
|
||||
adminSession := createAdminSession(t, router)
|
||||
|
||||
@@ -37,6 +37,21 @@ func (h *coreHandlers) serverInstanceCreateWorkflow(w http.ResponseWriter, r *ht
|
||||
writeJSON(w, http.StatusOK, dto.ServerLifecycleFromDomain(result))
|
||||
}
|
||||
|
||||
// serverDeploymentReveal explicitly returns saved deployment paths and commands
|
||||
// to an authorized server manager. Ordinary deployment views remain redacted.
|
||||
func (h *coreHandlers) serverDeploymentReveal(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
writeMethodNotAllowed(w, http.MethodGet)
|
||||
return
|
||||
}
|
||||
reveal, err := h.core.RevealServerDeploymentForSession(bearerToken(r), r.PathValue("id"))
|
||||
if err != nil {
|
||||
writeServiceError(w, err)
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, dto.ServerDeploymentRevealFromDomain(reveal))
|
||||
}
|
||||
|
||||
// serverDeployment reads safe deployment metadata or updates protected deployment input.
|
||||
func (h *coreHandlers) serverDeployment(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.Method {
|
||||
|
||||
Reference in New Issue
Block a user