Remove legacy client-manager platform path
This commit is contained in:
@@ -18,6 +18,7 @@ import (
|
||||
"browser.local/platform/dto"
|
||||
"browser.local/platform/repo"
|
||||
"browser.local/platform/service"
|
||||
"browser.local/platform/validator"
|
||||
)
|
||||
|
||||
func TestAuthorizedRouterEnforcesAdminAndCrossOwnerBoundaries(t *testing.T) {
|
||||
@@ -165,6 +166,71 @@ func TestRunHTTPEnvelopeRequiresValidSignatureAndRejectsReplay(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignedRunArtifactChunkUploadUsesHeaderEnvelope(t *testing.T) {
|
||||
store := repo.NewMemoryStore()
|
||||
core := service.NewCoreService(store)
|
||||
if _, err := core.CreateGamePlugin(validGamePluginRequest().ToDomain()); err != nil {
|
||||
t.Fatalf("create plugin: %v", err)
|
||||
}
|
||||
if _, err := core.CreateRunEndpoint(validRunEndpointRequest().ToDomain()); err != nil {
|
||||
t.Fatalf("create endpoint: %v", err)
|
||||
}
|
||||
if _, err := core.CreateServerInstance(domain.ServerInstance{ID: "server-signed-artifact", PluginID: "server.scum", RunEndpointID: "run-local", Name: "Signed Artifact", State: domain.ServerInstanceStateReady, ConfigVersion: 1}); err != nil {
|
||||
t.Fatalf("create server: %v", err)
|
||||
}
|
||||
if _, err := core.CreateJob(domain.Job{ID: "job-signed-artifact", ServerInstanceID: "server-signed-artifact", RunEndpointID: "run-local", Capability: "process.start", IdempotencyKey: "job-signed-artifact"}); err != nil {
|
||||
t.Fatalf("create job: %v", err)
|
||||
}
|
||||
token := "run-artifact-session-secret"
|
||||
stamp := time.Now().UTC()
|
||||
hash := sha256.Sum256([]byte(token))
|
||||
if err := store.RunControlSessions().Create(domain.RunControlSession{RunEndpointID: "run-local", SessionTokenHash: hex.EncodeToString(hash[:]), Status: domain.AuthSessionStatusActive, Generation: 1, CapabilityFingerprint: "cap-v1", HeartbeatIntervalSeconds: 15, CreatedAt: stamp, UpdatedAt: stamp, ExpiresAt: stamp.Add(time.Hour), RequireSignedRequests: true}); err != nil {
|
||||
t.Fatalf("create Run session: %v", err)
|
||||
}
|
||||
payload := []byte("signed raw artifact chunk")
|
||||
opened, err := core.OpenArtifactTransfer(domain.ArtifactTransferOpen{RunEndpointID: "run-local", SessionToken: token, ArtifactID: "artifact-signed-raw", Direction: domain.ArtifactTransferDirectionUpload, OwnerKind: domain.ArtifactOwnerKindJob, OwnerID: "job-signed-artifact", SizeBytes: int64(len(payload)), ChunkSizeBytes: len(payload), Checksum: validator.BytesChecksum(payload), IdempotencyKey: "signed-raw-artifact"})
|
||||
if err != nil {
|
||||
t.Fatalf("open transfer: %v", err)
|
||||
}
|
||||
router := NewTestRouterWithCore(core)
|
||||
unsigned := rawArtifactChunkRequest(t, router, token, opened.TransferID, payload, "nonce-unsigned-raw", stamp, false)
|
||||
assertErrorResponse(t, unsigned, http.StatusUnauthorized, errorCodeUnauthorized)
|
||||
signed := rawArtifactChunkRequest(t, router, token, opened.TransferID, payload, "nonce-signed-raw", stamp, true)
|
||||
assertStatus(t, signed, http.StatusOK)
|
||||
response := decodeBody[dto.ArtifactChunkUploadResponse](t, signed)
|
||||
if !response.Accepted || response.TransferID != opened.TransferID || response.NextMissingChunkIndex != 1 {
|
||||
t.Fatalf("unexpected signed chunk response: %+v", response)
|
||||
}
|
||||
}
|
||||
|
||||
func rawArtifactChunkRequest(t *testing.T, router http.Handler, token string, transferID string, payload []byte, nonce string, stamp time.Time, signed bool) *httptest.ResponseRecorder {
|
||||
t.Helper()
|
||||
path := "/api/v1/run/artifacts/chunks"
|
||||
timestamp := strconv.FormatInt(stamp.Unix(), 10)
|
||||
req := httptest.NewRequest(http.MethodPost, path, bytes.NewReader(payload))
|
||||
req.Header.Set("Content-Type", "application/octet-stream")
|
||||
req.Header.Set("X-Run-Endpoint", "run-local")
|
||||
req.Header.Set("X-Run-Session-Token", token)
|
||||
req.Header.Set("X-Run-Timestamp", timestamp)
|
||||
req.Header.Set("X-Run-Nonce", nonce)
|
||||
req.Header.Set("X-Artifact-Transfer-Id", transferID)
|
||||
req.Header.Set("X-Artifact-Id", "artifact-signed-raw")
|
||||
req.Header.Set("X-Artifact-Chunk-Index", "0")
|
||||
req.Header.Set("X-Artifact-Offset", "0")
|
||||
req.Header.Set("X-Artifact-Size", strconv.Itoa(len(payload)))
|
||||
req.Header.Set("X-Artifact-Checksum", validator.BytesChecksum(payload))
|
||||
if signed {
|
||||
bodyHash := sha256.Sum256(payload)
|
||||
canonical := strings.Join([]string{http.MethodPost, path, timestamp, nonce, hex.EncodeToString(bodyHash[:])}, "\n")
|
||||
mac := hmac.New(sha256.New, []byte(token))
|
||||
_, _ = mac.Write([]byte(canonical))
|
||||
req.Header.Set("X-Run-Signature", hex.EncodeToString(mac.Sum(nil)))
|
||||
}
|
||||
recorder := httptest.NewRecorder()
|
||||
router.ServeHTTP(recorder, req)
|
||||
return recorder
|
||||
}
|
||||
|
||||
func TestAuthorizedRouterAllowsRunLifecycleReportWithoutBearer(t *testing.T) {
|
||||
store := repo.NewMemoryStore()
|
||||
core := service.NewCoreService(store)
|
||||
|
||||
Reference in New Issue
Block a user