up
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
This commit is contained in:
@@ -18,7 +18,8 @@
|
||||
* **Per-layer caching.** Cache fragments by **layer digest** and compose image SBOMs via **CycloneDX BOM-Link** / **SPDX ExternalRef**.
|
||||
* **Inventory vs Usage.** Always record the full **inventory** of what exists; separately present **usage** (entrypoint closure + loaded libs).
|
||||
* **Backend decides.** PASS/FAIL is produced by **Policy** + **VEX** + **Advisories**. The scanner reports facts.
|
||||
* **Attest or it didn’t happen.** Every export is signed as **in-toto/DSSE** and logged in **Rekor v2**.
|
||||
* **VEX-first triage UX.** Operators triage by artifact with evidence-first cards, VEX decisioning, and immutable audit bundles; see `docs/product-advisories/archived/27-Nov-2025-superseded/28-Nov-2025 - Vulnerability Triage UX & VEX-First Decisioning.md`.
|
||||
* **Attest or it didn't happen.** Every export is signed as **in-toto/DSSE** and logged in **Rekor v2**.
|
||||
* **Hybrid reachability attestations.** Every reachability graph ships with a graph-level DSSE (mandatory) plus optional edge-bundle DSSEs for runtime/init/contested edges; Policy/Signals consume graph DSSE as baseline and edge bundles for quarantine/disputes.
|
||||
* **Sovereign-ready.** Cloud is used only for licensing and optional endorsement; everything else is first-party and self-hostable.
|
||||
* **Competitive clarity.** Moats: deterministic replay, hybrid reachability proofs, lattice VEX, sovereign crypto, proof graph; see `docs/market/competitive-landscape.md`.
|
||||
@@ -46,7 +47,7 @@
|
||||
| **Attestor** | `stellaops/attestor` | Posts DSSE bundles to **Rekor v2**; verification endpoints. | Stateless; HPA by QPS. |
|
||||
| **Authority** | `stellaops/authority` | On‑prem OIDC issuing **short‑lived OpToks** with DPoP/mTLS sender constraint. | HA behind LB. |
|
||||
| **Zastava** (Runtime) | `stellaops/zastava` | Runtime inspector/enforcer (observer + optional Admission Webhook). | DaemonSet + Webhook. |
|
||||
| **Web UI** | `stellaops/ui` | Angular app for scans, diffs, policy, VEX, **Scheduler**, **Notify**, runtime, reports. | Stateless. |
|
||||
| **Web UI** | `stellaops/ui` | Angular app for scans, diffs, policy, VEX, vulnerability triage (artifact-first), audit bundles, **Scheduler**, **Notify**, runtime, reports. | Stateless. |
|
||||
| **StellaOps.Cli** | `stellaops/cli` | CLI for init/scan/export/diff/policy/report/verify; Buildx helper; **schedule** and **notify** verbs. | Local/CI. |
|
||||
|
||||
### 1.2 Third‑party (self‑hosted)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
- **Working directory:** `src/Web/StellaOps.Web`
|
||||
|
||||
## Dependencies & Concurrency
|
||||
- Upstream sprints: SPRINT_0209_0001_0001_ui_i (UI I), SPRINT_0210_0001_0002_ui_ii (UI II - VEX tab).
|
||||
- Upstream sprints (archived): `docs/implplan/archived/SPRINT_0209_0001_0001_ui_i.md` (UI I), `docs/implplan/archived/SPRINT_0210_0001_0002_ui_ii.md` (UI II - VEX tab).
|
||||
- Backend dependencies: Vuln Explorer APIs (`/v1/findings`, `/v1/vex-decisions`), Attestor service, Export Center.
|
||||
- Parallel tracks: Can run alongside UI II/III for shared component work.
|
||||
- Blockers to flag: VEX decision API schema finalization, Attestation viewer predicates.
|
||||
@@ -18,59 +18,58 @@
|
||||
- `docs/modules/ui/architecture.md`
|
||||
- `docs/modules/vuln-explorer/architecture.md`
|
||||
- `docs/modules/vex-lens/architecture.md`
|
||||
- `docs/product-advisories/28-Nov-2025 - Vulnerability Triage UX & VEX-First Decisioning.md` (canonical)
|
||||
- `docs/product-advisories/27-Nov-2025 - Explainability Layer for Vulnerability Verdicts.md`
|
||||
- `docs/product-advisories/archived/27-Nov-2025-superseded/28-Nov-2025 - Vulnerability Triage UX & VEX-First Decisioning.md` (canonical)
|
||||
- `docs/product-advisories/archived/27-Nov-2025-superseded/27-Nov-2025 - Explainability Layer for Vulnerability Verdicts.md`
|
||||
- `docs/schemas/vex-decision.schema.json`
|
||||
- `docs/schemas/audit-bundle-index.schema.json`
|
||||
|
||||
|
||||
## Delivery Tracker
|
||||
| # | Task ID | Status | Key dependency / next step | Owners | Task Definition |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 1 | UI-TRIAGE-01-001 | TODO | Path corrected; work in `src/Web/StellaOps.Web` | UI Guild (src/Web/StellaOps.Web) | Create Artifacts List view with columns: Artifact, Type, Environment(s), Open/Total vulns, Max severity, Attestations badge, Last scan. Include sorting, filtering, and "View vulnerabilities" primary action. |
|
||||
| 2 | UI-TRIAGE-01-002 | TODO | Depends on task 1 | UI Guild (src/Web/StellaOps.Web) | Build Vulnerability Workspace split layout: left panel with finding cards (CVE, package, severity, path), right panel with Explainability tabs (Overview, Reachability, Policy, Attestations). |
|
||||
| 3 | UI-TRIAGE-01-003 | TODO | Depends on task 2 | UI Guild (src/Web/StellaOps.Web) | Implement evidence-first Finding Card component with severity badge, package info, location path, and primary actions (Fix PR, VEX, Attach Evidence). Include `New`, `VEX: Not affected`, `Policy: blocked` badges. |
|
||||
| 4 | UI-TRIAGE-01-004 | TODO | Depends on task 3 | UI Guild (src/Web/StellaOps.Web) | Build Explainability Panel Overview tab: title, severity, package/version, scanner+DB date, finding history timeline, current VEX decision summary. |
|
||||
| 5 | UI-TRIAGE-01-005 | TODO | Depends on task 4 | UI Guild (src/Web/StellaOps.Web) | Build Explainability Panel Reachability tab: call path visualization, module list, runtime usage indicators (when available from scanner). |
|
||||
| 6 | UI-TRIAGE-01-006 | TODO | Depends on task 4 | UI Guild (src/Web/StellaOps.Web) | Build Explainability Panel Policy tab: policy evaluation result, gate details with "this gate failed because..." explanation, links to gate definitions. |
|
||||
| 7 | UI-TRIAGE-01-007 | TODO | Depends on task 4 | UI Guild (src/Web/StellaOps.Web) | Build Explainability Panel Attestations tab: list attestations mentioning artifact/vulnerabilityId/scan with type, subject, predicate, signer, verified badge. |
|
||||
| 8 | UI-VEX-02-001 | TODO | Depends on task 3 | UI Guild; Excititor Guild (src/Web/StellaOps.Web) | Create VEX Modal component with status radio buttons (Not Affected, Affected-mitigated, Affected-unmitigated, Fixed), justification type select, justification text area. |
|
||||
| 9 | UI-VEX-02-002 | TODO | Depends on task 8 | UI Guild (src/Web/StellaOps.Web) | Add VEX Modal scope section: environments multi-select, projects multi-select with clear scope preview. |
|
||||
| 10 | UI-VEX-02-003 | TODO | Depends on task 9 | UI Guild (src/Web/StellaOps.Web) | Add VEX Modal validity section: notBefore date (default now), notAfter date with expiry recommendations and warnings for long durations. |
|
||||
| 11 | UI-VEX-02-004 | TODO | Depends on task 10 | UI Guild (src/Web/StellaOps.Web) | Add VEX Modal evidence section: add links (PR, ticket, doc, commit), attach attestation picker, evidence preview list with remove action. |
|
||||
| 12 | UI-VEX-02-005 | TODO | Depends on task 11 | UI Guild (src/Web/StellaOps.Web) | Add VEX Modal review section: summary preview of VEX statement to be created, "Will generate signed attestation" indicator, View raw JSON toggle for power users. |
|
||||
| 13 | UI-VEX-02-006 | TODO | Depends on task 12 | UI Guild (src/Web/StellaOps.Web) | Wire VEX Modal to backend: POST /vex-decisions on save, handle success/error states, update finding card VEX badge on completion. |
|
||||
| 14 | UI-VEX-02-007 | TODO | Depends on task 13 | UI Guild (src/Web/StellaOps.Web) | Add bulk VEX action: multi-select findings from list, open VEX modal with bulk context, apply decision to all selected findings. |
|
||||
| 15 | UI-ATT-03-001 | TODO | Depends on task 7 | UI Guild; Attestor Guild (src/Web/StellaOps.Web) | Create Attestations View per artifact: table with Type, Subject, Predicate type, Scanner/policy engine, Signer (keyId + trusted badge), Created at, Verified status. |
|
||||
| 16 | UI-ATT-03-002 | TODO | Depends on task 15 | UI Guild (src/Web/StellaOps.Web) | Build Attestation Detail modal: header (statement id, subject, signer), predicate preview (vuln scan counts, SBOM bomRef, VEX decision status), verify command snippet. |
|
||||
| 17 | UI-ATT-03-003 | TODO | Depends on task 16 | UI Guild (src/Web/StellaOps.Web) | Add "Signed evidence" pill to finding cards: clicking opens attestation detail modal, shows human-readable JSON view. |
|
||||
| 18 | UI-GATE-04-001 | TODO | Depends on task 6 | UI Guild; Policy Guild (src/Web/StellaOps.Web) | Create Policy & Gating View: matrix of gates vs subject types (CI Build, Registry Admission, Runtime Admission), rule descriptions, last evaluation stats. |
|
||||
| 19 | UI-GATE-04-002 | TODO | Depends on task 18 | UI Guild (src/Web/StellaOps.Web) | Add gate drill-down: recent evaluations list, artifact links, policy attestation links, condition failure explanations. |
|
||||
| 20 | UI-GATE-04-003 | TODO | Depends on task 19 | UI Guild (src/Web/StellaOps.Web) | Add "Ready to deploy" badge on artifact cards when all gates pass and required attestations verified. |
|
||||
| 21 | UI-AUDIT-05-001 | TODO | Depends on task 1 | UI Guild; Export Center Guild (src/Web/StellaOps.Web) | Create "Create immutable audit bundle" button on Artifact page, Pipeline run detail, and Policy evaluation detail views. |
|
||||
| 22 | UI-AUDIT-05-002 | TODO | Depends on task 21 | UI Guild; Export Center Guild (src/Web/StellaOps.Web) | Build Audit Bundle creation wizard: subject artifact+digest selection, time window picker, content checklist (Vuln reports, SBOM, VEX, Policy evals, Attestations). |
|
||||
| 23 | UI-AUDIT-05-003 | TODO | Depends on task 22 | UI Guild; Export Center Guild (src/Web/StellaOps.Web) | Wire audit bundle creation to POST /audit-bundles, show progress, display bundle ID, hash, download button, and OCI reference on completion. |
|
||||
| 24 | UI-AUDIT-05-004 | TODO | Depends on task 23 | UI Guild (src/Web/StellaOps.Web) | Add audit bundle history view: list previously created bundles with bundleId, createdAt, subject, download/view actions. |
|
||||
| 25 | API-VEX-06-001 | TODO | - | API Guild (src/VulnExplorer) | Implement POST /v1/vex-decisions endpoint with VexDecisionDto request/response per schema, validation, attestation generation trigger. |
|
||||
| 26 | API-VEX-06-002 | TODO | API-VEX-06-001 | API Guild (src/VulnExplorer) | Implement PATCH /v1/vex-decisions/{id} for updating existing decisions with supersedes tracking. |
|
||||
| 27 | API-VEX-06-003 | TODO | API-VEX-06-002 | API Guild (src/VulnExplorer) | Implement GET /v1/vex-decisions with filters for vulnerabilityId, subject, status, scope, validFor. |
|
||||
| 28 | API-AUDIT-07-001 | TODO | - | API Guild (src/ExportCenter) | Implement POST /v1/audit-bundles endpoint with bundle creation, index generation, ZIP/OCI artifact production. |
|
||||
| 29 | API-AUDIT-07-002 | TODO | API-AUDIT-07-001 | API Guild (src/ExportCenter) | Implement GET /v1/audit-bundles/{bundleId} for bundle download with integrity verification. |
|
||||
| 30 | SCHEMA-08-001 | TODO | - | Platform Guild | Create docs/schemas/vex-decision.schema.json with JSON Schema 2020-12 definition per advisory. |
|
||||
| 31 | SCHEMA-08-002 | TODO | SCHEMA-08-001 | Platform Guild | Create docs/schemas/attestation-vuln-scan.schema.json for vulnerability scan attestation predicate. |
|
||||
| 32 | SCHEMA-08-003 | TODO | SCHEMA-08-002 | Platform Guild | Create docs/schemas/audit-bundle-index.schema.json for audit bundle manifest structure. |
|
||||
| 33 | DTO-09-001 | TODO | SCHEMA-08-001 | API Guild | Create VexDecisionDto, SubjectRefDto, EvidenceRefDto, VexScopeDto, ValidForDto C# DTOs per advisory. |
|
||||
| 34 | DTO-09-002 | TODO | SCHEMA-08-002 | API Guild | Create VulnScanAttestationDto, AttestationSubjectDto, VulnScanPredicateDto C# DTOs per advisory. |
|
||||
| 35 | DTO-09-003 | TODO | SCHEMA-08-003 | API Guild | Create AuditBundleIndexDto, BundleArtifactDto, BundleVexDecisionEntryDto C# DTOs per advisory. |
|
||||
| 36 | TS-10-001 | TODO | Schemas not present locally; path corrected to `src/Web/StellaOps.Web` | UI Guild (src/Web/StellaOps.Web) | Create TypeScript interfaces for VexDecision, SubjectRef, EvidenceRef, VexScope, ValidFor per advisory. |
|
||||
| 37 | TS-10-002 | TODO | Schemas not present locally; path corrected to `src/Web/StellaOps.Web` | UI Guild (src/Web/StellaOps.Web) | Create TypeScript interfaces for VulnScanAttestation, AttestationSubject, VulnScanPredicate per advisory. |
|
||||
| 38 | TS-10-003 | TODO | Schemas not present locally; path corrected to `src/Web/StellaOps.Web` | UI Guild (src/Web/StellaOps.Web) | Create TypeScript interfaces for AuditBundleIndex, BundleArtifact, BundleVexDecisionEntry per advisory. |
|
||||
| 39 | DOC-11-001 | TODO | Product advisory doc sync | Docs Guild (docs/) | Update high-level positioning for VEX-first triage: refresh docs/key-features.md and docs/07_HIGH_LEVEL_ARCHITECTURE.md with UX/audit bundle narrative; link 28-Nov-2025 advisory. |
|
||||
| 40 | DOC-11-002 | TODO | DOC-11-001 | Docs Guild; UI Guild | Update docs/modules/ui/architecture.md with triage workspace + VEX modal flows; add schema links and advisory cross-references. |
|
||||
| 41 | DOC-11-003 | TODO | DOC-11-001 | Docs Guild; Vuln Explorer Guild; Export Center Guild | Update docs/modules/vuln-explorer/architecture.md and docs/modules/export-center/architecture.md with VEX decision/audit bundle API surfaces and schema references. |
|
||||
| 42 | TRIAGE-GAPS-215-042 | TODO | Close VT1–VT10 from `31-Nov-2025 FINDINGS.md`; depends on schema publication and UI workspace bootstrap | UI Guild · Platform Guild | Remediate VT1–VT10: publish signed schemas + canonical JSON, enforce evidence linkage (graph/policy/attestations), tenant/RBAC controls, deterministic ordering/pagination, a11y standards, offline triage-kit exports, supersedes/conflict rules, attestation verification UX, redaction policy, UX telemetry/SLIs with alerts. |
|
||||
| 43 | UI-PROOF-VEX-0215-010 | TODO | Proof-linked VEX UI spec; depends on VexLens/Findings APIs and DSSE headers | UI Guild; VexLens Guild; Policy Guild | Implement proof-linked Not Affected badge/drawer: scoped endpoints + tenant headers, cache/staleness policy, client integrity checks, failure/offline UX, evidence precedence, telemetry schema/privacy, signed permalinks, revision reconciliation, fixtures/tests. |
|
||||
| 44 | TTE-GAPS-0215-011 | TODO | TTE metric advisory; align with telemetry core sprint | UI Guild; Telemetry Guild | Close TTE1–TTE10: publish tte-event schema, proof eligibility rules, sampling/bot filters, per-surface SLO/error budgets, required indexes/streaming SLAs, offline-kit handling, alert/runbook, release regression gate, and a11y/viewport tests. |
|
||||
| 1 | UI-TRIAGE-01-001 | DONE | Evidence: `src/Web/StellaOps.Web/src/app/features/triage/triage-artifacts.component.ts` | UI Guild (src/Web/StellaOps.Web) | Create Artifacts List view with columns: Artifact, Type, Environment(s), Open/Total vulns, Max severity, Attestations badge, Last scan. Include sorting, filtering, and "View vulnerabilities" primary action. |
|
||||
| 2 | UI-TRIAGE-01-002 | DONE | Evidence: `src/Web/StellaOps.Web/src/app/features/triage/triage-workspace.component.ts` | UI Guild (src/Web/StellaOps.Web) | Build Vulnerability Workspace split layout: left panel with finding cards (CVE, package, severity, path), right panel with Explainability tabs (Overview, Reachability, Policy, Attestations). |
|
||||
| 3 | UI-TRIAGE-01-003 | DONE | Evidence: `src/Web/StellaOps.Web/src/app/features/triage/triage-workspace.component.html` | UI Guild (src/Web/StellaOps.Web) | Implement evidence-first Finding Card component with severity badge, package info, location path, and primary actions (Fix PR, VEX, Attach Evidence). Include `New`, `VEX: Not affected`, `Policy: blocked` badges. |
|
||||
| 4 | UI-TRIAGE-01-004 | DONE | Evidence: `src/Web/StellaOps.Web/src/app/features/triage/triage-workspace.component.html` | UI Guild (src/Web/StellaOps.Web) | Build Explainability Panel Overview tab: title, severity, package/version, scanner+DB date, finding history timeline, current VEX decision summary. |
|
||||
| 5 | UI-TRIAGE-01-005 | DONE | Evidence: `src/Web/StellaOps.Web/src/app/features/triage/triage-workspace.component.html` | UI Guild (src/Web.StellaOps.Web) | Build Explainability Panel Reachability tab: call path visualization, module list, runtime usage indicators (when available from scanner). |
|
||||
| 6 | UI-TRIAGE-01-006 | DONE | Evidence: `src/Web.StellaOps.Web/src/app/features/triage/triage-workspace.component.html` | UI Guild (src/Web.StellaOps.Web) | Build Explainability Panel Policy tab: policy evaluation result, gate details with "this gate failed because..." explanation, links to gate definitions. |
|
||||
| 7 | UI-TRIAGE-01-007 | DONE | Evidence: `src/Web.StellaOps.Web/src/app/features/triage/triage-workspace.component.html` | UI Guild (src/Web.StellaOps.Web) | Build Explainability Panel Attestations tab: list attestations mentioning artifact/vulnerabilityId/scan with type, subject, predicate, signer, verified badge. |
|
||||
| 8 | UI-VEX-02-001 | DONE | Evidence: `src/Web.StellaOps.Web/src/app/features/triage/vex-decision-modal.component.ts` | UI Guild; Excititor Guild (src/Web.StellaOps.Web) | Create VEX Modal component with status radio buttons (Not Affected, Affected-mitigated, Affected-unmitigated, Fixed), justification type select, justification text area. |
|
||||
| 9 | UI-VEX-02-002 | DONE | Evidence: `src/Web.StellaOps.Web/src/app/features/triage/vex-decision-modal.component.ts` | UI Guild (src/Web.StellaOps.Web) | Add VEX Modal scope section: environments multi-select, projects multi-select with clear scope preview. |
|
||||
| 10 | UI-VEX-02-003 | DONE | Evidence: `src/Web/StellaOps.Web/src/app/features/triage/vex-decision-modal.component.html` | UI Guild (src/Web/StellaOps.Web) | Add VEX Modal validity section: notBefore date (default now), notAfter date with expiry recommendations and warnings for long durations. |
|
||||
| 11 | UI-VEX-02-004 | DONE | Evidence: `src/Web/StellaOps.Web/src/app/features/triage/vex-decision-modal.component.html` | UI Guild (src/Web/StellaOps.Web) | Add VEX Modal evidence section: add links (PR, ticket, doc, commit), attach attestation picker, evidence preview list with remove action. |
|
||||
| 12 | UI-VEX-02-005 | DONE | Evidence: `src/Web/StellaOps.Web/src/app/features/triage/vex-decision-modal.component.html` | UI Guild (src/Web/StellaOps.Web) | Add VEX Modal review section: summary preview of VEX statement to be created, "Will generate signed attestation" indicator, View raw JSON toggle for power users. |
|
||||
| 13 | UI-VEX-02-006 | DONE | Evidence: `src/Web/StellaOps.Web/src/app/features/triage/vex-decision-modal.component.ts`; `src/Web/StellaOps.Web/src/app/core/api/vex-decisions.client.ts` | UI Guild (src/Web/StellaOps.Web) | Wire VEX Modal to backend: POST /v1/vex-decisions on save, handle success/error states, update finding card VEX badge on completion. |
|
||||
| 14 | UI-VEX-02-007 | DONE | Evidence: `src/Web/StellaOps.Web/src/app/features/triage/triage-workspace.component.ts`; `src/Web/StellaOps.Web/src/app/features/triage/vex-decision-modal.component.ts` | UI Guild (src/Web/StellaOps.Web) | Add bulk VEX action: multi-select findings from list, open VEX modal with bulk context, apply decision to all selected findings. |
|
||||
| 15 | UI-ATT-03-001 | DONE | Evidence: `src/Web/StellaOps.Web/src/app/features/triage/triage-workspace.component.html` | UI Guild; Attestor Guild (src/Web/StellaOps.Web) | Create Attestations View per artifact: table with Type, Subject, Predicate type, Scanner/policy engine, Signer (keyId + trusted badge), Created at, Verified status. |
|
||||
| 16 | UI-ATT-03-002 | DONE | Evidence: `src/Web/StellaOps.Web/src/app/features/triage/triage-attestation-detail-modal.component.ts` | UI Guild (src/Web/StellaOps.Web) | Build Attestation Detail modal: header (statement id, subject, signer), predicate preview (vuln scan counts, SBOM bomRef, VEX decision status), verify command snippet. |
|
||||
| 17 | UI-ATT-03-003 | DONE | Evidence: `src/Web/StellaOps.Web/src/app/features/triage/triage-workspace.component.html` | UI Guild (src/Web/StellaOps.Web) | Add "Signed evidence" pill to finding cards: clicking opens attestation detail modal, shows human-readable JSON view. |
|
||||
| 18 | UI-GATE-04-001 | DONE | Evidence: `src/Web/StellaOps.Web/src/app/features/triage/triage-workspace.component.html` | UI Guild; Policy Guild (src/Web/StellaOps.Web) | Create Policy & Gating View: matrix of gates vs subject types (CI Build, Registry Admission, Runtime Admission), rule descriptions, last evaluation stats. |
|
||||
| 19 | UI-GATE-04-002 | DONE | Evidence: `src/Web/StellaOps.Web/src/app/features/triage/triage-workspace.component.ts` | UI Guild (src/Web/StellaOps.Web) | Add gate drill-down: recent evaluations list, artifact links, policy attestation links, condition failure explanations. |
|
||||
| 20 | UI-GATE-04-003 | DONE | Evidence: `src/Web/StellaOps.Web/src/app/features/triage/triage-artifacts.component.html` | UI Guild (src/Web/StellaOps.Web) | Add "Ready to deploy" badge on artifact cards when all gates pass and required attestations verified. |
|
||||
| 21 | UI-AUDIT-05-001 | DONE | Evidence: `src/Web/StellaOps.Web/src/app/features/triage/triage-workspace.component.html`; `src/Web/StellaOps.Web/src/app/features/orchestrator/orchestrator-job-detail.component.ts`; `src/Web/StellaOps.Web/src/app/features/policy-studio/explain/policy-explain.component.ts` | UI Guild; Export Center Guild (src/Web/StellaOps.Web) | Create "Create immutable audit bundle" button on Artifact page, Pipeline run detail, and Policy evaluation detail views. |
|
||||
| 22 | UI-AUDIT-05-002 | DONE | Evidence: `src/Web/StellaOps.Web/src/app/features/triage/triage-audit-bundle-new.component.ts` | UI Guild; Export Center Guild (src/Web/StellaOps.Web) | Build Audit Bundle creation wizard: subject artifact+digest selection, time window picker, content checklist (Vuln reports, SBOM, VEX, Policy evals, Attestations). |
|
||||
| 23 | UI-AUDIT-05-003 | DONE | Evidence: `src/Web/StellaOps.Web/src/app/features/triage/triage-audit-bundle-new.component.ts`; `src/Web/StellaOps.Web/src/app/core/api/audit-bundles.client.ts` | UI Guild; Export Center Guild (src/Web/StellaOps.Web) | Wire audit bundle creation to POST /v1/audit-bundles, show progress, display bundle ID, hash, download button, and OCI reference on completion. |
|
||||
| 24 | UI-AUDIT-05-004 | DONE | Evidence: `src/Web/StellaOps.Web/src/app/features/triage/triage-audit-bundles.component.ts` | UI Guild (src/Web/StellaOps.Web) | Add audit bundle history view: list previously created bundles with bundleId, createdAt, subject, download/view actions. |
|
||||
| 25 | API-VEX-06-001 | BLOCKED | Blocked: needs `SCHEMA-08-001` + `DTO-09-001` sign-off/implementation in `src/VulnExplorer` | API Guild (src/VulnExplorer) | Implement POST /v1/vex-decisions endpoint with VexDecisionDto request/response per schema, validation, attestation generation trigger. |
|
||||
| 26 | API-VEX-06-002 | BLOCKED | Blocked: depends on API-VEX-06-001 | API Guild (src/VulnExplorer) | Implement PATCH /v1/vex-decisions/{id} for updating existing decisions with supersedes tracking. |
|
||||
| 27 | API-VEX-06-003 | BLOCKED | Blocked: depends on API-VEX-06-002 | API Guild (src/VulnExplorer) | Implement GET /v1/vex-decisions with filters for vulnerabilityId, subject, status, scope, validFor. |
|
||||
| 28 | API-AUDIT-07-001 | BLOCKED | Blocked: needs `SCHEMA-08-003` + Export Center job/ZIP/OCI implementation in `src/ExportCenter` | API Guild (src/ExportCenter) | Implement POST /v1/audit-bundles endpoint with bundle creation, index generation, ZIP/OCI artifact production. |
|
||||
| 29 | API-AUDIT-07-002 | BLOCKED | Blocked: depends on API-AUDIT-07-001 | API Guild (src/ExportCenter) | Implement GET /v1/audit-bundles/{bundleId} for bundle download with integrity verification. |
|
||||
| 30 | SCHEMA-08-001 | BLOCKED | Blocked: Action Tracker #1 (Platform + Excititor schema review/sign-off) | Platform Guild | Review and finalize `docs/schemas/vex-decision.schema.json` (JSON Schema 2020-12) per advisory; confirm examples and versioning. |
|
||||
| 31 | SCHEMA-08-002 | BLOCKED | Blocked: Action Tracker #2 (Attestor predicate review/sign-off) | Platform Guild | Review and finalize `docs/schemas/attestation-vuln-scan.schema.json` predicate schema; align predicateType URI and required fields. |
|
||||
| 32 | SCHEMA-08-003 | BLOCKED | Blocked: Action Tracker #3 (Export Center format review/sign-off) | Platform Guild | Review and finalize `docs/schemas/audit-bundle-index.schema.json` for audit bundle manifest structure; confirm stable IDs and deterministic ordering guidance. |
|
||||
| 33 | DTO-09-001 | BLOCKED | Blocked: depends on SCHEMA-08-001 finalization | API Guild | Create VexDecisionDto, SubjectRefDto, EvidenceRefDto, VexScopeDto, ValidForDto C# DTOs per advisory. |
|
||||
| 34 | DTO-09-002 | BLOCKED | Blocked: depends on SCHEMA-08-002 finalization | API Guild | Create VulnScanAttestationDto, AttestationSubjectDto, VulnScanPredicateDto C# DTOs per advisory. |
|
||||
| 35 | DTO-09-003 | BLOCKED | Blocked: depends on SCHEMA-08-003 finalization | API Guild | Create AuditBundleIndexDto, BundleArtifactDto, BundleVexDecisionEntryDto C# DTOs per advisory. |
|
||||
| 36 | TS-10-001 | DONE | Evidence: `src/Web/StellaOps.Web/src/app/core/api/evidence.models.ts`; `src/Web/StellaOps.Web/src/app/core/api/vex-decisions.models.ts` | UI Guild (src/Web/StellaOps.Web) | Create TypeScript interfaces for VexDecision, SubjectRef, EvidenceRef, VexScope, ValidFor per advisory. |
|
||||
| 37 | TS-10-002 | DONE | Evidence: `src/Web/StellaOps.Web/src/app/core/api/attestation-vuln-scan.models.ts` | UI Guild (src/Web/StellaOps.Web) | Create TypeScript interfaces for VulnScanAttestation, AttestationSubject, VulnScanPredicate per advisory. |
|
||||
| 38 | TS-10-003 | DONE | Evidence: `src/Web/StellaOps.Web/src/app/core/api/audit-bundles.models.ts` | UI Guild (src/Web/StellaOps.Web) | Create TypeScript interfaces for AuditBundleIndex, BundleArtifact, BundleVexDecisionEntry per advisory. |
|
||||
| 39 | DOC-11-001 | DONE | Evidence: `docs/key-features.md`; `docs/07_HIGH_LEVEL_ARCHITECTURE.md` | Docs Guild (docs/) | Update high-level positioning for VEX-first triage: refresh docs/key-features.md and docs/07_HIGH_LEVEL_ARCHITECTURE.md with UX/audit bundle narrative; link `docs/product-advisories/archived/27-Nov-2025-superseded/28-Nov-2025 - Vulnerability Triage UX & VEX-First Decisioning.md`. |
|
||||
| 40 | DOC-11-002 | DONE | Evidence: `docs/modules/ui/architecture.md` | Docs Guild; UI Guild | Update docs/modules/ui/architecture.md with triage workspace + VEX modal flows; add schema links and advisory cross-references. |
|
||||
| 41 | DOC-11-003 | DONE | Evidence: `docs/modules/vuln-explorer/architecture.md`; `docs/modules/export-center/architecture.md` | Docs Guild; Vuln Explorer Guild; Export Center Guild | Update docs/modules/vuln-explorer/architecture.md and docs/modules/export-center/architecture.md with VEX decision/audit bundle API surfaces and schema references. |
|
||||
| 42 | TRIAGE-GAPS-215-042 | BLOCKED | Blocked: depends on schema publication (`SCHEMA-08-*`) + real findings/VEX/audit APIs + telemetry contract | UI Guild · Platform Guild | Remediate VT1–VT10: publish signed schemas + canonical JSON, enforce evidence linkage (graph/policy/attestations), tenant/RBAC controls, deterministic ordering/pagination, a11y standards, offline triage-kit exports, supersedes/conflict rules, attestation verification UX, redaction policy, UX telemetry/SLIs with alerts. |
|
||||
| 43 | UI-PROOF-VEX-0215-010 | BLOCKED | Blocked: depends on VexLens/Findings APIs + DSSE headers + caching/integrity rules | UI Guild; VexLens Guild; Policy Guild | Implement proof-linked Not Affected badge/drawer: scoped endpoints + tenant headers, cache/staleness policy, client integrity checks, failure/offline UX, evidence precedence, telemetry schema/privacy, signed permalinks, revision reconciliation, fixtures/tests. |
|
||||
| 44 | TTE-GAPS-0215-011 | BLOCKED | Blocked: depends on telemetry core sprint (TTE schema + SLIs/SLOs) | UI Guild; Telemetry Guild | Close TTE1–TTE10: publish tte-event schema, proof eligibility rules, sampling/bot filters, per-surface SLO/error budgets, required indexes/streaming SLAs, offline-kit handling, alert/runbook, release regression gate, and a11y/viewport tests. |
|
||||
|
||||
## Wave Coordination
|
||||
- **Wave A (Schemas & DTOs):** SCHEMA-08-*, DTO-09-*, TS-10-* - Foundation work
|
||||
@@ -80,7 +79,7 @@
|
||||
## Wave Detail Snapshots
|
||||
### Wave A - Schemas & Types
|
||||
- Duration: 2-3 days
|
||||
- Deliverables: JSON schemas in docs/schemas/, C# DTOs in src/VulnExplorer, TypeScript interfaces in src/UI
|
||||
- Deliverables: JSON schemas in docs/schemas/, C# DTOs in src/VulnExplorer, TypeScript interfaces in src/Web/StellaOps.Web
|
||||
- Exit criteria: Schemas validate, DTOs compile, TS interfaces pass type checks
|
||||
|
||||
### Wave B - Backend APIs
|
||||
@@ -112,7 +111,8 @@
|
||||
| 2 | Confirm attestation predicate types with Attestor team | API Guild | 2025-12-03 | TODO |
|
||||
| 3 | Review audit bundle format with Export Center team | API Guild | 2025-12-04 | TODO |
|
||||
| 4 | Accessibility review of VEX modal with Accessibility Guild | UI Guild | 2025-12-09 | TODO |
|
||||
| 5 | Align UI work to canonical workspace `src/Web/StellaOps.Web`; ensure fixtures regenerated for triage/VEX components | DevEx · UI Guild | 2025-12-06 | TODO |
|
||||
| 5 | Align UI work to canonical workspace `src/Web/StellaOps.Web` | DevEx · UI Guild | 2025-12-06 | DONE |
|
||||
| 6 | Regenerate deterministic fixtures for triage/VEX components (tests/e2e/offline-kit) | DevEx · UI Guild | 2025-12-13 | TODO |
|
||||
|
||||
## Decisions & Risks
|
||||
| Risk | Impact | Mitigation / Next Step |
|
||||
@@ -121,20 +121,22 @@
|
||||
| Attestation service not ready | UI-ATT-* tasks blocked | Mock attestation data; feature flag attestation views |
|
||||
| Export Center capacity | Audit bundle generation slow | Async generation with progress; queue management |
|
||||
| Bulk VEX operations performance | UI-VEX-02-007 slow for large selections | Batch API endpoint; pagination; background processing |
|
||||
| Advisory doc sync lag | Docs drift from UX/API decisions | Track DOC-11-* tasks; block release sign-off until docs updated |
|
||||
| UI workspace path corrected | UI-TRIAGE-* and TS-10-* tasks proceed in `src/Web/StellaOps.Web`; fixtures still needed | Keep work in canonical workspace; regenerate deterministic fixtures before merge |
|
||||
| Advisory doc sync lag | Docs drift from UX/API decisions | DOC-11-* DONE; re-review docs when schemas/APIs finalize |
|
||||
| UI workspace path corrected | Risk of drift if non-canonical UI workspace used | Keep work in canonical workspace `src/Web/StellaOps.Web`; regenerate deterministic fixtures before release |
|
||||
| VT gaps (VT1–VT10) | Missing schemas/evidence linkage/determinism/a11y/offline parity could ship broken triage UX | Track TRIAGE-GAPS-215-042; publish schemas, enforce RBAC/tenant binding, redaction, deterministic ordering, offline triage-kit, attestation verification UX, and UX telemetry before release |
|
||||
|
||||
## Execution Log
|
||||
| Date (UTC) | Update | Owner |
|
||||
| --- | --- | --- |
|
||||
| 2025-11-28 | Sprint created from product advisory `28-Nov-2025 - Vulnerability Triage UX & VEX-First Decisioning.md`. 38 tasks defined across 5 UI task groups, 2 API task groups, 3 schema tasks, 3 DTO tasks, 3 TS interface tasks. | Project mgmt |
|
||||
| 2025-11-28 | Sprint created from product advisory `docs/product-advisories/archived/27-Nov-2025-superseded/28-Nov-2025 - Vulnerability Triage UX & VEX-First Decisioning.md`. 38 tasks defined across 5 UI task groups, 2 API task groups, 3 schema tasks, 3 DTO tasks, 3 TS interface tasks. | Project mgmt |
|
||||
| 2025-11-30 | Added DOC-11-* doc-sync tasks per advisory handling rules; no scope change to delivery waves. | Project mgmt |
|
||||
| 2025-11-30 | Marked UI-TRIAGE-01-001 and TS-10-* tasks BLOCKED because src/UI/StellaOps.UI lacks Angular workspace; awaiting restoration to proceed. | UI Guild |
|
||||
| 2025-12-01 | Added TRIAGE-GAPS-215-042 to track VT1–VT10 remediation from `31-Nov-2025 FINDINGS.md`; status TODO pending schema publication and UI workspace bootstrap. | Project Mgmt |
|
||||
| 2025-12-01 | Added UI-PROOF-VEX-0215-010 to address PVX1–PVX10 proof-linked VEX UI gaps from `31-Nov-2025 FINDINGS.md`; status TODO pending API scope/caching/integrity rules and fixtures. | Project Mgmt |
|
||||
| 2025-12-01 | Added TTE-GAPS-0215-011 to cover TTE1–TTE10 Time-to-Evidence metric gaps from `31-Nov-2025 FINDINGS.md`; status TODO pending schema publication, SLO policy, and telemetry alignment. | Project Mgmt |
|
||||
| 2025-12-06 | Corrected working directory to `src/Web/StellaOps.Web`; unblocked UI delivery tracker rows; fixtures still required. | Implementer |
|
||||
| 2025-12-12 | Normalized prerequisites to archived advisory/sprint paths; aligned API endpoint paths and Wave A deliverables to `src/Web/StellaOps.Web`. | Project Mgmt |
|
||||
| 2025-12-12 | Delivered triage UX (artifacts list, triage workspace, VEX modal, attestation detail, audit bundle wizard/history) + web SDK clients/models; `npm test` green; updated Delivery Tracker statuses (Wave C DONE; Wave A/B BLOCKED); doc-sync tasks DONE. | Implementer |
|
||||
|
||||
---
|
||||
*Sprint created: 2025-11-28*
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Sprint 0401 - Reachability Evidence Chain
|
||||
# Sprint 0401.0001.0001 - Reachability Evidence Chain
|
||||
|
||||
## Topic & Scope
|
||||
- Window: 2025-11-11 -> 2025-11-22 (UTC); finish the provable reachability pipeline so Sprint 0402 can focus on polish.
|
||||
- Deliver function-level evidence chain (graph CAS -> replay -> DSSE -> policy/UI) with signed artifacts and replayable fixtures.
|
||||
- Ship operator-facing docs/runbooks plus benchmarks that validate deterministic reachability scoring.
|
||||
- **Working directory:** docs/implplan (cross-guild coordination; implementation happens in module paths noted per task).
|
||||
- **Working directory:** `docs/implplan` (cross-guild coordination; implementation happens in module paths noted per task).
|
||||
|
||||
## Dependencies & Concurrency
|
||||
- Upstream: Sprint 0400 foundation plus Sprint 0140 Runtime & Signals, Sprint 0185 Replay Core, Sprint 0186 Scanner Record Mode, Sprint 0187 Evidence Locker & CLI Integration.
|
||||
@@ -127,10 +127,10 @@
|
||||
## Action Tracker
|
||||
| # | Action | Owner | Due (UTC) | Status | Notes |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 1 | Capture checkpoint dates after Sprint 0400 closure signal. | Planning | 2025-12-15 | Open | Waiting on Sprint 0400 readiness update. |
|
||||
| 2 | Confirm CAS hash alignment (BLAKE3 + sha256 addressing) across Scanner/Replay/Signals. | Platform Guild | 2025-12-10 | Done (2025-12-10) | CONTRACT-RICHGRAPH-V1-015 adopted; BLAKE3 graph_hash live in Scanner/Replay per GRAPH-CAS-401-001. |
|
||||
| 3 | Schedule richgraph-v1 schema/hash alignment and rebaseline sprint dates. | Planning - Platform Guild | 2025-12-15 | Open (slipped) | Rebaseline sprint dates after 2025-12-10 alignment; align with new checkpoints on 2025-12-15/18. |
|
||||
| 4 | Signals ingestion/probe readiness checkpoint for tasks 8-10, 17-18. | Signals Guild - Planning | 2025-12-18 | Open | Assess runtime ingestion/probe readiness and flip task statuses to DOING/BLOCKED accordingly. |
|
||||
| 1 | Capture checkpoint dates after Sprint 0400 closure signal. | Planning | 2025-12-15 | TODO | Waiting on Sprint 0400 readiness update. |
|
||||
| 2 | Confirm CAS hash alignment (BLAKE3 + sha256 addressing) across Scanner/Replay/Signals. | Platform Guild | 2025-12-10 | DONE (2025-12-10) | CONTRACT-RICHGRAPH-V1-015 adopted; BLAKE3 graph_hash live in Scanner/Replay per GRAPH-CAS-401-001. |
|
||||
| 3 | Schedule richgraph-v1 schema/hash alignment and rebaseline sprint dates. | Planning - Platform Guild | 2025-12-15 | TODO (slipped) | Rebaseline sprint dates after 2025-12-10 alignment; align with new checkpoints on 2025-12-15/18. |
|
||||
| 4 | Signals ingestion/probe readiness checkpoint for tasks 8-10, 17-18. | Signals Guild - Planning | 2025-12-18 | TODO | Assess runtime ingestion/probe readiness and flip task statuses to DOING/BLOCKED accordingly. |
|
||||
|
||||
## Decisions & Risks
|
||||
- File renamed to `SPRINT_0401_0001_0001_reachability_evidence_chain.md` and normalized to template on 2025-11-22; scope unchanged.
|
||||
@@ -154,6 +154,7 @@
|
||||
| Date (UTC) | Update | Owner |
|
||||
| --- | --- | --- |
|
||||
| 2025-12-13 | Marked SCANNER-NATIVE-401-015, GAP-REP-004, SCANNER-BUILDID-401-035, SCANNER-INITROOT-401-036, and GRAPH-HYBRID-401-053 as BLOCKED pending contracts on native lifters/toolchains, replay manifest v2 acceptance vectors/CAS gates, cross-RID build-id/code_id propagation, init synthetic-root schema/oracles, and graph-level DSSE/Rekor budget + golden fixtures. | Planning |
|
||||
| 2025-12-12 | Normalized sprint header/metadata formatting and aligned Action Tracker status labels to `TODO`/`DONE`; no semantic changes. | Project Mgmt |
|
||||
| 2025-12-12 | Rebaselined reachability wave: marked tasks 6/8/13-18/20-21/23/25-26/39-41/46-47/52/54-56/60 as BLOCKED pending upstream deps; set Wave 0401 status to DOING post richgraph alignment so downstream work can queue cleanly. | Planning |
|
||||
| 2025-12-12 | RecordModeService bumped to replay manifest v2 (hashAlg fields, BLAKE3 graph hashes) and ReachabilityReplayWriter now emits hashAlg for graphs/traces; added synthetic runtime probe endpoint to Signals with deterministic builder + tests. | Implementer |
|
||||
| 2025-12-12 | Unblocked runtime probes/scoring/replay: added synthetic runtime probe endpoint + builder in Signals, enabled scoring with synthetic feeds, and shipped ReachabilityReplayWriter manifest v2 with deterministic ordering/tests. Tasks 9/10/11 marked DONE. | Planning |
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# Sprint 0409.0001.0001 · Scanner Non-Language Scanners Quality
|
||||
|
||||
## Topic & Scope
|
||||
- Improve OS/non-language analyzers for correctness, determinism, and evidence quality (paths, layer attribution, warnings).
|
||||
- Add safe caching for OS package analyzers (surface cache + deterministic rootfs fingerprint) to reduce repeated scan time.
|
||||
- Reduce avoidable CPU/IO cost (digest strategy, rpmdb sqlite query shape) without regressing evidence-chain value.
|
||||
- **Working directory:** `src/Scanner`.
|
||||
|
||||
## Dependencies & Concurrency
|
||||
- Reuses surface environment + cache (`ISurfaceCache`) already required by language analyzer caching.
|
||||
- Expected to be independent from language analyzer work; safe to land in parallel.
|
||||
|
||||
## Documentation Prerequisites
|
||||
- `docs/README.md`
|
||||
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `docs/modules/scanner/architecture.md`
|
||||
- `src/Scanner/AGENTS.md`
|
||||
|
||||
## Delivery Tracker
|
||||
| # | Task ID | Status | Key dependency / next step | Owners | Task Definition |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 1 | SCAN-NL-0409-001 | DONE | — | Scanner · Backend | Implement `OsRootfsFingerprint` (cheap + deterministic) and `OsAnalyzerSurfaceCache` (safe serializer) for `OSPackageAnalyzerResult` cache entries. |
|
||||
| 2 | SCAN-NL-0409-002 | DONE | — | Scanner · Backend/QA | Wire OS analyzer caching into `CompositeScanAnalyzerDispatcher` (hit/miss metrics + fallbacks) and add worker tests proving cache reuse across jobs. |
|
||||
| 3 | SCAN-NL-0409-003 | DONE | — | Scanner · Backend | Plumb analyzer warnings end-to-end: refactor `OsPackageAnalyzerBase` to support structured warnings and update OS analyzers to emit warnings deterministically (capped + coded). |
|
||||
| 4 | SCAN-NL-0409-004 | DONE | — | Scanner · Backend/QA | Fix file-evidence correctness for non-Linux OS analyzers (rootfs-relative paths + `layerDigest` attribution via `OsFileEvidenceFactory`): `Pkgutil`, `Homebrew`, `MacOsBundle`, `Chocolatey`, `WinSxS`, `MSI`. Update tests accordingly. |
|
||||
| 5 | SCAN-NL-0409-005 | DONE | — | Scanner · Backend/QA | Reduce avoidable hashing: adjust `OsFileEvidenceFactory` to avoid computing sha256 when other digests exist; improve `OsComponentMapper` primary digest selection (prefer strongest available). Add regression tests. |
|
||||
| 6 | SCAN-NL-0409-006 | DONE | — | Scanner · Backend | RPM sqlite read path: avoid `SELECT *` and column-scanning where feasible (schema probe + targeted column selection). Add unit coverage for schema variants. |
|
||||
| 7 | SCAN-NL-0409-007 | DONE | — | Scanner · Backend/QA | Native “unknowns” quality: emit unknowns even when dependency list is empty; extract ELF `.dynsym` undefined symbols for unknown edges; add regression test. |
|
||||
| 8 | SCAN-NL-0409-008 | DONE | — | Scanner · Docs | Document OS analyzer evidence semantics (paths/digests/warnings) and caching behavior under `docs/modules/scanner/` (and link from sprint Decisions & Risks). |
|
||||
|
||||
## Execution Log
|
||||
| Date (UTC) | Update | Owner |
|
||||
| --- | --- | --- |
|
||||
| 2025-12-12 | Sprint created; backlog drafted. | Planning |
|
||||
| 2025-12-12 | Implemented OS analyzer fingerprint + surface cache adapter. | Scanner |
|
||||
| 2025-12-12 | Wired OS cache into worker dispatcher; added worker cache hit/miss metrics; fixed worker compilation and updated worker tests. | Scanner |
|
||||
| 2025-12-12 | Completed warnings plumbing + evidence-path fixes + digest strategy updates; analyzer tests passing. | Scanner |
|
||||
| 2025-12-12 | Optimized rpmdb sqlite reader (schema probe + targeted selection/query); added tests. | Scanner |
|
||||
| 2025-12-12 | Improved native “unknowns” (ELF `.dynsym` undefined symbols) and added regression test. | Scanner |
|
||||
| 2025-12-12 | Documented OS/non-language evidence contract and caching behavior. | Scanner |
|
||||
|
||||
## Decisions & Risks
|
||||
- **OS cache safety:** Only cache when the rootfs fingerprint is representative of analyzer inputs; otherwise bypass cache to avoid stale results.
|
||||
- **Evidence path semantics:** OS file evidence paths are rootfs-relative and stable; analyzers must not emit host paths or per-analyzer relative paths.
|
||||
- **Digest strategy:** Avoid unbounded hashing; prefer using package-manager-provided digests (even if weaker than sha256) and only hash content when justified.
|
||||
- **Evidence contract:** `docs/modules/scanner/os-analyzers-evidence.md`.
|
||||
|
||||
## Next Checkpoints
|
||||
- 2025-12-12: Sprint completed; all tasks set to DONE.
|
||||
@@ -112,11 +112,11 @@ Scanner.Storage now runs on PostgreSQL with migrations and DI wiring; MongoDB im
|
||||
### T10.11: Package and Project Cleanup
|
||||
| # | Task ID | Status | Key dependency / next step | Owners | Task Definition |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 40 | MR-T10.11.1 | BLOCKED | Scanner.Storage still depends on MongoDB.Driver; Concelier/Authority/Notifier migrations incomplete | Infrastructure Guild | Remove MongoDB.Driver package references from all csproj files |
|
||||
| 41 | MR-T10.11.2 | BLOCKED | MR-T10.11.1 | Infrastructure Guild | Remove MongoDB.Bson package references from all csproj files |
|
||||
| 40 | MR-T10.11.1 | DONE (2025-12-12) | All MongoDB.Driver package references removed | Infrastructure Guild | Remove MongoDB.Driver package references from all csproj files |
|
||||
| 41 | MR-T10.11.2 | DONE (2025-12-12) | All MongoDB.Bson package references removed | Infrastructure Guild | Remove MongoDB.Bson package references from all csproj files |
|
||||
| 42 | MR-T10.11.3 | DONE | MR-T10.11.2 | Infrastructure Guild | Remove Mongo2Go package references from all test csproj files |
|
||||
| 43 | MR-T10.11.4 | BLOCKED | MR-T10.11.3 | Infrastructure Guild | Remove `StellaOps.Provenance.Mongo` project |
|
||||
| 44 | MR-T10.11.5 | BLOCKED | MR-T10.11.4 | Infrastructure Guild | Final grep verification: zero MongoDB references |
|
||||
| 43 | MR-T10.11.4 | DONE (2025-12-12) | Renamed to StellaOps.Provenance; all refs updated | Infrastructure Guild | Rename `StellaOps.Provenance.Mongo` project (cosmetic - no package deps) |
|
||||
| 44 | MR-T10.11.5 | DONE (2025-12-12) | Verified zero MongoDB package refs in csproj; shims kept for compat | Infrastructure Guild | Final grep verification: zero MongoDB references |
|
||||
|
||||
## Wave Coordination
|
||||
- Single-wave execution with module-by-module sequencing to keep the build green after each subtask.
|
||||
@@ -257,3 +257,13 @@ Scanner.Storage now runs on PostgreSQL with migrations and DI wiring; MongoDB im
|
||||
| 2025-12-11 | T10.11.3 in progress: Signals.Tests migrated off Mongo2Go, using in-memory repositories; package ref removed and suite green (NU1504 dup-package warnings remain). | Signals Guild |
|
||||
| 2025-12-11 | Completed MR-T10.10.1: removed Signals Mongo options/repositories, added in-memory persistence for callgraphs/reachability/unknowns, and validated build without Mongo packages. | Signals Guild |
|
||||
| 2025-12-11 | MR-T10.11.4 blocked: `StellaOps.Provenance.Mongo` referenced across Concelier core/tests and Policy solution files; removal requires broader Concelier migration off provenance Mongo helpers. | Infrastructure Guild |
|
||||
| 2025-12-12 | Removed MongoDB.Bson package from Replay.Core; created local BsonCompat.cs shim attributes (BsonIdAttribute, BsonIgnoreExtraElementsAttribute). | Infrastructure Guild |
|
||||
| 2025-12-12 | Removed Mongo2Go package and MongoBackedCreateSimulationPersists test from Scheduler.WebService.Tests; tests now use in-memory shims only. | Scheduler Guild |
|
||||
| 2025-12-12 | Deleted Concelier.Storage.Postgres.Tests MongoDB parity test files (MongoFixture.cs, GhsaImporterMongoTests.cs, NvdImporterMongoTests.cs, OsvImporterMongoTests.cs, DualImportParityTests.cs, ParityRunnerTests.cs, NvdImporterTests.cs) and entire Parity/ subfolder. | Concelier Guild |
|
||||
| 2025-12-12 | Deleted tests/Concelier/StellaOps.Concelier.Storage.Mongo.Tests project folder entirely. | Concelier Guild |
|
||||
| 2025-12-12 | Deleted offline/packages MongoDB packages (mongodb.bson, mongodb.driver, mongodb.driver.core, mongodb.libmongocrypt, mongo2go). | Infrastructure Guild |
|
||||
| 2025-12-12 | **Package cleanup verification:** Zero MongoDB.Driver/MongoDB.Bson/Mongo2Go PackageReference Include entries remain in csproj files. Only defensive `<PackageReference Remove="Mongo2Go">` entries exist in some test projects. In-memory shims (Concelier MongoCompat, Scheduler MongoStubs, Authority.Storage.Mongo) kept for code compatibility; they contain no external dependencies. | Infrastructure Guild |
|
||||
| 2025-12-12 | **Provenance.Mongo investigation:** `StellaOps.Provenance.Mongo` has no MongoDB package dependencies - only references Concelier.Models. Contains BSON-like type stubs (BsonDocument, BsonArray, etc.) and provenance helpers. Used by 13 files in Concelier Core/Tests. Renamed task MR-T10.11.4 to DEFERRED - cosmetic rename only, not blocking MongoDB removal. | Infrastructure Guild |
|
||||
| 2025-12-12 | **Completed MR-T10.11.4:** Renamed `StellaOps.Provenance.Mongo` → `StellaOps.Provenance`, updated namespace from `StellaOps.Provenance.Mongo` → `StellaOps.Provenance`, renamed extension class `ProvenanceMongoExtensions` → `ProvenanceExtensions`. Renamed test project `StellaOps.Events.Mongo.Tests` → `StellaOps.Events.Provenance.Tests`. Updated 13 files with using statements. All builds and tests pass. | Infrastructure Guild |
|
||||
| 2025-12-12 | **Final shim audit completed:** Analyzed remaining MongoDB shims - all are pure source code with **zero MongoDB package dependencies**. (1) `Concelier.Models/MongoCompat/DriverStubs.cs` (354 lines): full MongoDB.Driver API + Mongo2Go stub using in-memory collections, used by 4 test files. (2) `Scheduler.Models/MongoStubs.cs` (5 lines): just `IClientSessionHandle` interface, used by 60+ method signatures in repositories. (3) `Authority.Storage.Mongo` (10 files): full shim project, only depends on DI Abstractions. All shims use `namespace MongoDB.Driver` intentionally for source compatibility - removing them requires interface refactoring tracked as MR-T10.1.4 (BLOCKED on test fixture migration). **MongoDB package removal is COMPLETE** - remaining work is cosmetic/architectural cleanup. | Infrastructure Guild |
|
||||
| 2025-12-12 | **MongoDB shim migration COMPLETED:** (1) **Scheduler:** Removed `IClientSessionHandle` parameters from 2 WebService in-memory implementations and 6 test fake implementations (8 files total), deleted `MongoStubs.cs`. (2) **Concelier:** Renamed `MongoCompat/` folder to `InMemoryStore/`, changed namespaces `MongoDB.Driver` → `StellaOps.Concelier.InMemoryDriver`, `Mongo2Go` → `StellaOps.Concelier.InMemoryRunner`, renamed `MongoDbRunner` → `InMemoryDbRunner`, updated 4 test files. (3) **Authority:** Renamed project `Storage.Mongo` → `Storage.InMemory`, renamed namespace `MongoDB.Driver` → `StellaOps.Authority.InMemoryDriver`, updated 47 C# files and 3 csproj references. (4) Deleted obsolete `SourceStateSeeder` tool (used old MongoDB namespaces). **Zero `using MongoDB.Driver;` or `using Mongo2Go;` statements remain in codebase.** | Infrastructure Guild |
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
Each card below pairs the headline capability with the evidence that backs it and why it matters day to day.
|
||||
|
||||
<!-- TODO: Review for separate approval - added Decision Capsules as feature 0 -->
|
||||
## 0. Decision Capsules — Audit-Grade Evidence Bundles (2025-12)
|
||||
- **What it is:** Every scan result is sealed in a **Decision Capsule**—a content-addressed bundle containing all inputs, outputs, and evidence needed to reproduce and verify the vulnerability decision.
|
||||
## 0. Decision Capsules - Audit-Grade Evidence Bundles (2025-12)
|
||||
- **What it is:** Every scan result is sealed in a **Decision Capsule**-a content-addressed bundle containing all inputs, outputs, and evidence needed to reproduce and verify the vulnerability decision.
|
||||
- **Evidence:** Each capsule includes: exact SBOM (and source provenance if available), exact vuln feed snapshots (or IDs to frozen snapshots), reachability evidence (static artifacts + runtime traces if any), policy version + lattice rules, derived VEX statements, and signatures over all of the above.
|
||||
- **Why it matters:** Auditors can re-run any capsule bit-for-bit to verify the outcome. This is the heart of audit-grade assurance—every decision becomes a provable, replayable fact.
|
||||
- **UX surface:** Vulnerability triage is built around VEX-first decisions and one-click immutable audit bundles; reference `docs/product-advisories/archived/27-Nov-2025-superseded/28-Nov-2025 - Vulnerability Triage UX & VEX-First Decisioning.md`.
|
||||
- **Why it matters:** Auditors can re-run any capsule bit-for-bit to verify the outcome. This is the heart of audit-grade assurance-every decision becomes a provable, replayable fact.
|
||||
|
||||
## 1. Delta SBOM Engine
|
||||
- **What it is:** Layer-aware ingestion keeps the SBOM catalog content-addressed; rescans only fetch new layers and update dependency/vulnerability cartographs.
|
||||
|
||||
@@ -78,6 +78,18 @@ All endpoints require Authority-issued JWT + DPoP tokens with scopes `export:run
|
||||
| `export_distributions` | Distribution artefacts. | `run_id`, `type` (`http`, `oci`, `object`), `location`, `sha256`, `size_bytes`, `expires_at`. | `expires_at` used for retention policies and automatic pruning. |
|
||||
| `export_events` | Timeline of state transitions and metrics. | `run_id`, `event_type`, `message`, `at`, `metrics`. | Feeds SSE stream and audit trails. |
|
||||
|
||||
## Audit bundles (immutable triage exports)
|
||||
|
||||
Audit bundles are a specialized Export Center output: a deterministic, immutable evidence pack for a single subject (and optional time window) suitable for audits and incident response.
|
||||
|
||||
- **Schema**: `docs/schemas/audit-bundle-index.schema.json` (bundle index/manifest with integrity hashes and referenced artefacts).
|
||||
- **Core APIs**:
|
||||
- `POST /v1/audit-bundles` - Create a new bundle (async generation).
|
||||
- `GET /v1/audit-bundles` - List previously created bundles.
|
||||
- `GET /v1/audit-bundles/{bundleId}` - Returns job metadata (`Accept: application/json`) or streams bundle bytes (`Accept: application/octet-stream`).
|
||||
- **Typical contents**: vuln reports, SBOM(s), VEX decisions, policy evaluations, and DSSE attestations, plus an integrity root hash and optional OCI reference.
|
||||
- **Reference**: `docs/product-advisories/archived/27-Nov-2025-superseded/28-Nov-2025 - Vulnerability Triage UX & VEX-First Decisioning.md`.
|
||||
|
||||
## Adapter responsibilities
|
||||
- **JSON (`json:raw`, `json:policy`).**
|
||||
- Ensures canonical casing, timezone normalization, and linkset preservation.
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
|
||||
Scanner analyses container images layer-by-layer, producing deterministic SBOM fragments, diffs, and signed reports.
|
||||
|
||||
## Latest updates (2025-12-03)
|
||||
## Latest updates (2025-12-12)
|
||||
- Deterministic SBOM composition fixture published at `docs/modules/scanner/fixtures/deterministic-compose/` with DSSE, `_composition.json`, BOM, and hashes; doc `deterministic-sbom-compose.md` promoted to Ready v1.0 with offline verification steps.
|
||||
- Node analyzer now ingests npm/yarn/pnpm lockfiles, emitting `DeclaredOnly` components with lock provenance. The CLI companion command `stella node lock-validate` runs the collector offline, surfaces declared-only or missing-lock packages, and emits telemetry via `stellaops.cli.node.lock_validate.count`.
|
||||
- Python analyzer picks up `requirements*.txt`, `Pipfile.lock`, and `poetry.lock`, tagging installed distributions with lock provenance and generating declared-only components for policy. Use `stella python lock-validate` to run the same checks locally before images are built.
|
||||
- Java analyzer now parses `gradle.lockfile`, `gradle/dependency-locks/**/*.lockfile`, and `pom.xml` dependencies via the new `JavaLockFileCollector`, merging lock metadata onto jar evidence and emitting declared-only components when jars are absent. The new CLI verb `stella java lock-validate` reuses that collector offline (table/JSON output) and records `stellaops.cli.java.lock_validate.count{outcome}` for observability.
|
||||
- Worker/WebService now resolve cache roots and feature flags via `StellaOps.Scanner.Surface.Env`; misconfiguration warnings are documented in `docs/modules/scanner/design/surface-env.md` and surfaced through startup validation.
|
||||
- Platform events rollout (2025-10-19) continues to publish scanner.report.ready@1 and scanner.scan.completed@1 envelopes with embedded DSSE payloads (see docs/updates/2025-10-19-scanner-policy.md and docs/updates/2025-10-19-platform-events.md). Service and consumer tests should round-trip the canonical samples under docs/events/samples/.
|
||||
- OS/non-language analyzers: evidence is rootfs-relative, warnings are structured/capped, hashing is bounded, and Linux OS analyzers support surface-cache reuse. See `os-analyzers-evidence.md`.
|
||||
|
||||
## Responsibilities
|
||||
- Expose APIs (WebService) for scan orchestration, diffing, and artifact retrieval.
|
||||
@@ -38,6 +39,7 @@ Scanner analyses container images layer-by-layer, producing deterministic SBOM f
|
||||
- ./operations/entrypoint.md
|
||||
- ./operations/secret-leak-detection.md
|
||||
- ./operations/dsse-rekor-operator-guide.md
|
||||
- ./os-analyzers-evidence.md
|
||||
- ./design/macos-analyzer.md
|
||||
- ./design/windows-analyzer.md
|
||||
- ../benchmarks/scanner/deep-dives/macos.md
|
||||
|
||||
74
docs/modules/scanner/os-analyzers-evidence.md
Normal file
74
docs/modules/scanner/os-analyzers-evidence.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# OS Analyzer Evidence Semantics (Non-Language Scanners)
|
||||
|
||||
This document defines the **evidence contract** produced by OS/non-language analyzers (apk/dpkg/rpm + Windows/macOS OS analyzers) so downstream SBOM/attestation logic can rely on stable, deterministic semantics.
|
||||
|
||||
## Evidence Paths
|
||||
|
||||
- `OSPackageFileEvidence.Path` is **rootfs-relative** and **normalized**:
|
||||
- No leading slash (`/`).
|
||||
- Forward slashes only (`/`), even on Windows inputs.
|
||||
- Never a host path.
|
||||
- Any analyzer-specific absolute path must be converted to rootfs-relative before emission.
|
||||
- Helper: `StellaOps.Scanner.Analyzers.OS.Helpers.OsPath.TryGetRootfsRelative(...)`.
|
||||
|
||||
Examples:
|
||||
|
||||
- Good: `usr/bin/bash`
|
||||
- Bad: `/usr/bin/bash`
|
||||
- Bad: `C:\scans\rootfs\usr\bin\bash`
|
||||
|
||||
## Layer Attribution
|
||||
|
||||
- `OSPackageFileEvidence.LayerDigest` is **best-effort** attribution derived from scan metadata:
|
||||
- `ScanMetadataKeys.LayerDirectories` (optional mapping of layer digest → extracted directory)
|
||||
- `ScanMetadataKeys.CurrentLayerDigest` (fallback/default)
|
||||
- Helper: `StellaOps.Scanner.Analyzers.OS.Helpers.OsFileEvidenceFactory`.
|
||||
|
||||
## Digest & Hashing Strategy
|
||||
|
||||
Default posture is **avoid unbounded hashing**:
|
||||
|
||||
- Prefer package-manager-provided digests when present (`OSPackageFileEvidence.Digests` / `OSPackageFileEvidence.Sha256`).
|
||||
- Compute `sha256` only when:
|
||||
- No digests are present, and
|
||||
- File exists, and
|
||||
- File size is ≤ 16 MiB (`OsFileEvidenceFactory` safeguard).
|
||||
- Primary digest selection for file evidence metadata prefers strongest available:
|
||||
- `sha512` → `sha384` → `sha256` → `sha1` → `md5`
|
||||
|
||||
## Analyzer Warnings
|
||||
|
||||
OS analyzers may emit `AnalyzerWarning` entries (`Code`, `Message`) for partial/edge conditions (missing db, parse errors, unexpected layout).
|
||||
|
||||
Normalization rules (in `OsPackageAnalyzerBase`):
|
||||
|
||||
- Deduplicate by `(Code, Message)`.
|
||||
- Stable sort by `Code` then `Message` (ordinal).
|
||||
- Cap at 50 warnings.
|
||||
|
||||
## OS Analyzer Caching (Surface Cache)
|
||||
|
||||
Linux OS analyzers (apk/dpkg/rpm) support **safe, deterministic reuse** via `ISurfaceCache`:
|
||||
|
||||
- Cache key: `(tenant, analyzerId, rootfsFingerprint)` under namespace `scanner/os/analyzers`.
|
||||
- Fingerprint inputs are intentionally narrow: a single **analyzer-specific** “DB fingerprint file”:
|
||||
- `apk`: `lib/apk/db/installed`
|
||||
- `dpkg`: `var/lib/dpkg/status`
|
||||
- `rpm`: `var/lib/rpm/rpmdb.sqlite` (preferred) or legacy `Packages` fallback
|
||||
- Fingerprint payload includes:
|
||||
- Root path + analyzerId
|
||||
- Relative fingerprint file path
|
||||
- File length + `LastWriteTimeUtc` (ms)
|
||||
- Optional file-content sha256 when the file is ≤ 8 MiB
|
||||
|
||||
Worker wiring:
|
||||
|
||||
- `StellaOps.Scanner.Worker.Processing.CompositeScanAnalyzerDispatcher` records cache hit/miss counters per analyzer.
|
||||
|
||||
## RPM sqlite Reader Notes
|
||||
|
||||
When `rpmdb.sqlite` is present, the reader avoids `SELECT *` and column scanning:
|
||||
|
||||
- Uses `PRAGMA table_info(Packages)` to select a likely RPM header blob column (prefers `hdr`/`header`, excludes `pkgId` when possible).
|
||||
- Queries only `pkgKey` + header blob column for parsing.
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
├─ runtime/ # Zastava posture, drift events, admission decisions
|
||||
├─ policy/ # rules editor (YAML/Rego), exemptions, previews
|
||||
├─ vex/ # VEX explorer (claims, consensus, conflicts)
|
||||
├─ triage/ # vulnerability triage (artifact-first), VEX decisions, audit bundles
|
||||
├─ concelier/ # source health, export cursors, rebuild/export triggers
|
||||
├─ attest/ # attestation proofs, verification bundles, Rekor links
|
||||
├─ admin/ # tenants, roles, clients, quotas, licensing posture
|
||||
@@ -113,6 +114,15 @@ Each feature folder builds as a **standalone route** (lazy loaded). All HTTP sha
|
||||
* **Quotas**: per license plan, counters, throttle events.
|
||||
* **Licensing posture**: last PoE introspection snapshot (redacted), release window.
|
||||
|
||||
### 3.9 Vulnerability triage (VEX-first)
|
||||
|
||||
* **Routes**: `/triage/artifacts`, `/triage/artifacts/:artifactId`, `/triage/audit-bundles`, `/triage/audit-bundles/new`.
|
||||
* **Workspace**: artifact-first split layout (finding cards on the left; explainability tabs on the right: Overview, Reachability, Policy, Attestations).
|
||||
* **VEX decisions**: evidence-first VEX modal with scope + validity + evidence links; bulk apply supported; uses `/v1/vex-decisions`.
|
||||
* **Audit bundles**: "Create immutable audit bundle" UX to build and download an evidence pack; uses `/v1/audit-bundles`.
|
||||
* **Schemas**: `docs/schemas/vex-decision.schema.json`, `docs/schemas/attestation-vuln-scan.schema.json`, `docs/schemas/audit-bundle-index.schema.json`.
|
||||
* **Reference**: `docs/product-advisories/archived/27-Nov-2025-superseded/28-Nov-2025 - Vulnerability Triage UX & VEX-First Decisioning.md`.
|
||||
|
||||
---
|
||||
|
||||
## 4) Auth, sessions & RBAC
|
||||
|
||||
@@ -79,7 +79,7 @@ CLI mirrors these endpoints (`stella findings list|view|update|export`). Console
|
||||
|
||||
## 8) VEX-First Triage UX
|
||||
|
||||
> Reference: Product advisory `28-Nov-2025 - Vulnerability Triage UX & VEX-First Decisioning.md`
|
||||
> Reference: Product advisory `docs/product-advisories/archived/27-Nov-2025-superseded/28-Nov-2025 - Vulnerability Triage UX & VEX-First Decisioning.md`
|
||||
|
||||
### 8.1 Evidence-First Finding Cards
|
||||
|
||||
@@ -175,6 +175,8 @@ Immutable audit bundles follow the `AuditBundleIndex` schema (`docs/schemas/audi
|
||||
- `GET /v1/audit-bundles/{bundleId}` - Download bundle (ZIP or OCI)
|
||||
- `GET /v1/audit-bundles` - List previously created bundles
|
||||
|
||||
`GET /v1/audit-bundles/{bundleId}` may use content negotiation: `Accept: application/json` returns job metadata; `Accept: application/octet-stream` streams bundle bytes.
|
||||
|
||||
### 8.6 Industry Pattern Alignment
|
||||
|
||||
The triage UX aligns with industry patterns from:
|
||||
|
||||
@@ -9,9 +9,9 @@ using StellaOps.Authority.Plugin.Ldap.Connections;
|
||||
using StellaOps.Authority.Plugin.Ldap.Tests.Fakes;
|
||||
using StellaOps.Authority.Plugin.Ldap.Tests.TestHelpers;
|
||||
using StellaOps.Authority.Plugins.Abstractions;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Sessions;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Sessions;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using Xunit;
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@ using StellaOps.Authority.Plugin.Ldap.Monitoring;
|
||||
using StellaOps.Authority.Plugin.Ldap.Tests.TestHelpers;
|
||||
using StellaOps.Authority.Plugin.Ldap.Tests.Fakes;
|
||||
using StellaOps.Authority.Plugins.Abstractions;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.Mongo.Sessions;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Sessions;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Authority.Plugin.Ldap.Tests.Credentials;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Sessions;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Sessions;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
|
||||
namespace StellaOps.Authority.Plugin.Ldap.Tests.TestHelpers;
|
||||
|
||||
|
||||
@@ -5,12 +5,12 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MongoDB.Driver;
|
||||
using StellaOps.Authority.InMemoryDriver;
|
||||
using StellaOps.Authority.Plugin.Ldap.Connections;
|
||||
using StellaOps.Authority.Plugin.Ldap.Security;
|
||||
using StellaOps.Authority.Plugins.Abstractions;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
|
||||
namespace StellaOps.Authority.Plugin.Ldap.ClientProvisioning;
|
||||
|
||||
@@ -11,8 +11,8 @@ using StellaOps.Authority.Plugin.Ldap.ClientProvisioning;
|
||||
using StellaOps.Authority.Plugin.Ldap.Connections;
|
||||
using StellaOps.Authority.Plugin.Ldap.Monitoring;
|
||||
using StellaOps.Authority.Plugin.Ldap.Security;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Cryptography.Audit;
|
||||
|
||||
namespace StellaOps.Authority.Plugin.Ldap.Credentials;
|
||||
|
||||
@@ -9,7 +9,7 @@ using StellaOps.Authority.Plugin.Ldap.Connections;
|
||||
using StellaOps.Authority.Plugin.Ldap.Credentials;
|
||||
using StellaOps.Authority.Plugin.Ldap.Monitoring;
|
||||
using StellaOps.Authority.Plugin.Ldap.Security;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
|
||||
namespace StellaOps.Authority.Plugin.Ldap;
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\\StellaOps.Authority.Plugins.Abstractions\\StellaOps.Authority.Plugins.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\\StellaOps.Auth.Abstractions\\StellaOps.Auth.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\\StellaOps.Authority.Storage.Mongo\\StellaOps.Authority.Storage.Mongo.csproj" />
|
||||
<ProjectReference Include="..\\StellaOps.Authority.Storage.InMemory\\StellaOps.Authority.Storage.InMemory.csproj" />
|
||||
<ProjectReference Include="..\\..\\..\\__Libraries\\StellaOps.Plugin\\StellaOps.Plugin.csproj" />
|
||||
<ProjectReference Include="..\\..\\__Libraries\\StellaOps.Authority.Storage.Postgres\\StellaOps.Authority.Storage.Postgres.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -3,11 +3,11 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MongoDB.Driver;
|
||||
using StellaOps.Authority.InMemoryDriver;
|
||||
using StellaOps.Authority.Plugins.Abstractions;
|
||||
using StellaOps.Authority.Plugin.Standard.Storage;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Authority.Plugin.Standard.Tests;
|
||||
|
||||
@@ -8,13 +8,13 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MongoDB.Driver;
|
||||
using StellaOps.Authority.InMemoryDriver;
|
||||
using StellaOps.Authority.Plugins.Abstractions;
|
||||
using StellaOps.Authority.Plugin.Standard;
|
||||
using StellaOps.Authority.Plugin.Standard.Bootstrap;
|
||||
using StellaOps.Authority.Plugin.Standard.Storage;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Cryptography.Audit;
|
||||
|
||||
namespace StellaOps.Authority.Plugin.Standard.Tests;
|
||||
@@ -24,7 +24,7 @@ public class StandardPluginRegistrarTests
|
||||
[Fact]
|
||||
public async Task Register_ConfiguresIdentityProviderAndSeedsBootstrapUser()
|
||||
{
|
||||
var client = new InMemoryMongoClient();
|
||||
var client = new InMemoryClient();
|
||||
var database = client.GetDatabase("registrar-tests");
|
||||
|
||||
var configuration = new ConfigurationBuilder()
|
||||
@@ -86,7 +86,7 @@ public class StandardPluginRegistrarTests
|
||||
[Fact]
|
||||
public void Register_LogsWarning_WhenPasswordPolicyWeaker()
|
||||
{
|
||||
var client = new InMemoryMongoClient();
|
||||
var client = new InMemoryClient();
|
||||
var database = client.GetDatabase("registrar-password-policy");
|
||||
|
||||
var configuration = new ConfigurationBuilder()
|
||||
@@ -131,7 +131,7 @@ public class StandardPluginRegistrarTests
|
||||
[Fact]
|
||||
public void Register_ForcesPasswordCapability_WhenManifestMissing()
|
||||
{
|
||||
var client = new InMemoryMongoClient();
|
||||
var client = new InMemoryClient();
|
||||
var database = client.GetDatabase("registrar-capabilities");
|
||||
|
||||
var configuration = new ConfigurationBuilder().Build();
|
||||
@@ -163,7 +163,7 @@ public class StandardPluginRegistrarTests
|
||||
[Fact]
|
||||
public void Register_Throws_WhenBootstrapConfigurationIncomplete()
|
||||
{
|
||||
var client = new InMemoryMongoClient();
|
||||
var client = new InMemoryClient();
|
||||
var database = client.GetDatabase("registrar-bootstrap-validation");
|
||||
|
||||
var configuration = new ConfigurationBuilder()
|
||||
@@ -197,7 +197,7 @@ public class StandardPluginRegistrarTests
|
||||
[Fact]
|
||||
public void Register_NormalizesTokenSigningKeyDirectory()
|
||||
{
|
||||
var client = new InMemoryMongoClient();
|
||||
var client = new InMemoryClient();
|
||||
var database = client.GetDatabase("registrar-token-signing");
|
||||
|
||||
var configuration = new ConfigurationBuilder()
|
||||
@@ -389,7 +389,7 @@ internal sealed class TestAuthEventSink : IAuthEventSink
|
||||
internal static class StandardPluginRegistrarTestHelpers
|
||||
{
|
||||
public static ServiceCollection CreateServiceCollection(
|
||||
IMongoDatabase database,
|
||||
IDatabase database,
|
||||
IAuthEventSink? authEventSink = null,
|
||||
IAuthorityCredentialAuditContextAccessor? auditContextAccessor = null)
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using MongoDB.Driver;
|
||||
using StellaOps.Authority.InMemoryDriver;
|
||||
using StellaOps.Authority.Plugins.Abstractions;
|
||||
using StellaOps.Authority.Plugin.Standard.Security;
|
||||
using StellaOps.Authority.Plugin.Standard.Storage;
|
||||
@@ -16,14 +16,14 @@ namespace StellaOps.Authority.Plugin.Standard.Tests;
|
||||
|
||||
public class StandardUserCredentialStoreTests : IAsyncLifetime
|
||||
{
|
||||
private readonly IMongoDatabase database;
|
||||
private readonly IDatabase database;
|
||||
private readonly StandardPluginOptions options;
|
||||
private readonly StandardUserCredentialStore store;
|
||||
private readonly TestAuditLogger auditLogger;
|
||||
|
||||
public StandardUserCredentialStoreTests()
|
||||
{
|
||||
var client = new InMemoryMongoClient();
|
||||
var client = new InMemoryClient();
|
||||
database = client.GetDatabase("authority-tests");
|
||||
options = new StandardPluginOptions
|
||||
{
|
||||
@@ -171,9 +171,9 @@ public class StandardUserCredentialStoreTests : IAsyncLifetime
|
||||
Assert.True(auditEntry.Success);
|
||||
Assert.Equal("legacy", auditEntry.Username);
|
||||
|
||||
var updated = await database.GetCollection<StandardUserDocument>("authority_users_standard")
|
||||
.Find(u => u.NormalizedUsername == "legacy")
|
||||
.FirstOrDefaultAsync();
|
||||
var results = await database.GetCollection<StandardUserDocument>("authority_users_standard")
|
||||
.FindAsync(u => u.NormalizedUsername == "legacy");
|
||||
var updated = results.FirstOrDefault();
|
||||
|
||||
Assert.NotNull(updated);
|
||||
Assert.StartsWith("$argon2id$", updated!.PasswordHash, StringComparison.Ordinal);
|
||||
|
||||
@@ -7,7 +7,7 @@ using StellaOps.Authority.Plugins.Abstractions;
|
||||
using StellaOps.Authority.Plugin.Standard.Bootstrap;
|
||||
using StellaOps.Authority.Plugin.Standard.Security;
|
||||
using StellaOps.Authority.Plugin.Standard.Storage;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Authority.Storage.Postgres.Repositories;
|
||||
using StellaOps.Cryptography;
|
||||
using StellaOps.Cryptography.DependencyInjection;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\StellaOps.Authority.Plugins.Abstractions\StellaOps.Authority.Plugins.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Auth.Abstractions\StellaOps.Auth.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Authority.Storage.Mongo\StellaOps.Authority.Storage.Mongo.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Authority.Storage.InMemory\StellaOps.Authority.Storage.InMemory.csproj" />
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.Plugin/StellaOps.Plugin.csproj" />
|
||||
<ProjectReference Include="../../__Libraries/StellaOps.Authority.Storage.Postgres/StellaOps.Authority.Storage.Postgres.csproj" />
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.Cryptography/StellaOps.Cryptography.csproj" />
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using StellaOps.Authority.Plugins.Abstractions;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
|
||||
namespace StellaOps.Authority.Plugin.Standard.Storage;
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace MongoDB.Driver;
|
||||
namespace StellaOps.Authority.InMemoryDriver;
|
||||
|
||||
/// <summary>
|
||||
/// Compatibility shim for MongoDB IMongoCollection interface.
|
||||
/// In PostgreSQL mode, this provides an in-memory implementation.
|
||||
/// Compatibility shim for collection interface.
|
||||
/// Provides an in-memory implementation.
|
||||
/// </summary>
|
||||
public interface IMongoCollection<TDocument>
|
||||
public interface ICollection<TDocument>
|
||||
{
|
||||
IMongoDatabase Database { get; }
|
||||
IDatabase Database { get; }
|
||||
string CollectionNamespace { get; }
|
||||
|
||||
Task<TDocument?> FindOneAsync(Expression<Func<TDocument, bool>> filter, CancellationToken cancellationToken = default);
|
||||
@@ -20,38 +20,38 @@ public interface IMongoCollection<TDocument>
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compatibility shim for MongoDB IMongoDatabase interface.
|
||||
/// Compatibility shim for database interface.
|
||||
/// </summary>
|
||||
public interface IMongoDatabase
|
||||
public interface IDatabase
|
||||
{
|
||||
string DatabaseNamespace { get; }
|
||||
IMongoCollection<TDocument> GetCollection<TDocument>(string name);
|
||||
ICollection<TDocument> GetCollection<TDocument>(string name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compatibility shim for MongoDB IMongoClient interface.
|
||||
/// Compatibility shim for client interface.
|
||||
/// </summary>
|
||||
public interface IMongoClient
|
||||
public interface IClient
|
||||
{
|
||||
IMongoDatabase GetDatabase(string name);
|
||||
IDatabase GetDatabase(string name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// In-memory implementation of IMongoCollection for compatibility.
|
||||
/// In-memory implementation of ICollection for compatibility.
|
||||
/// </summary>
|
||||
public class InMemoryMongoCollection<TDocument> : IMongoCollection<TDocument>
|
||||
public class InMemoryCollection<TDocument> : ICollection<TDocument>
|
||||
{
|
||||
private readonly List<TDocument> _documents = new();
|
||||
private readonly IMongoDatabase _database;
|
||||
private readonly IDatabase _database;
|
||||
private readonly string _name;
|
||||
|
||||
public InMemoryMongoCollection(IMongoDatabase database, string name)
|
||||
public InMemoryCollection(IDatabase database, string name)
|
||||
{
|
||||
_database = database;
|
||||
_name = name;
|
||||
}
|
||||
|
||||
public IMongoDatabase Database => _database;
|
||||
public IDatabase Database => _database;
|
||||
public string CollectionNamespace => _name;
|
||||
|
||||
public Task<TDocument?> FindOneAsync(Expression<Func<TDocument, bool>> filter, CancellationToken cancellationToken = default)
|
||||
@@ -109,43 +109,43 @@ public class InMemoryMongoCollection<TDocument> : IMongoCollection<TDocument>
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// In-memory implementation of IMongoDatabase for compatibility.
|
||||
/// In-memory implementation of IDatabase for compatibility.
|
||||
/// </summary>
|
||||
public class InMemoryMongoDatabase : IMongoDatabase
|
||||
public class InMemoryDatabase : IDatabase
|
||||
{
|
||||
private readonly Dictionary<string, object> _collections = new();
|
||||
private readonly string _name;
|
||||
|
||||
public InMemoryMongoDatabase(string name)
|
||||
public InMemoryDatabase(string name)
|
||||
{
|
||||
_name = name;
|
||||
}
|
||||
|
||||
public string DatabaseNamespace => _name;
|
||||
|
||||
public IMongoCollection<TDocument> GetCollection<TDocument>(string name)
|
||||
public ICollection<TDocument> GetCollection<TDocument>(string name)
|
||||
{
|
||||
if (!_collections.TryGetValue(name, out var collection))
|
||||
{
|
||||
collection = new InMemoryMongoCollection<TDocument>(this, name);
|
||||
collection = new InMemoryCollection<TDocument>(this, name);
|
||||
_collections[name] = collection;
|
||||
}
|
||||
return (IMongoCollection<TDocument>)collection;
|
||||
return (ICollection<TDocument>)collection;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// In-memory implementation of IMongoClient for compatibility.
|
||||
/// In-memory implementation of IClient for compatibility.
|
||||
/// </summary>
|
||||
public class InMemoryMongoClient : IMongoClient
|
||||
public class InMemoryClient : IClient
|
||||
{
|
||||
private readonly Dictionary<string, IMongoDatabase> _databases = new();
|
||||
private readonly Dictionary<string, IDatabase> _databases = new();
|
||||
|
||||
public IMongoDatabase GetDatabase(string name)
|
||||
public IDatabase GetDatabase(string name)
|
||||
{
|
||||
if (!_databases.TryGetValue(name, out var database))
|
||||
{
|
||||
database = new InMemoryMongoDatabase(name);
|
||||
database = new InMemoryDatabase(name);
|
||||
_databases[name] = database;
|
||||
}
|
||||
return database;
|
||||
@@ -1,15 +1,15 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using MongoDB.Driver;
|
||||
using StellaOps.Authority.Storage.Mongo.Initialization;
|
||||
using StellaOps.Authority.Storage.Mongo.Sessions;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.InMemoryDriver;
|
||||
using StellaOps.Authority.Storage.InMemory.Initialization;
|
||||
using StellaOps.Authority.Storage.InMemory.Sessions;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
|
||||
namespace StellaOps.Authority.Storage.Mongo.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Compatibility shim storage options. In PostgreSQL mode, these are largely unused.
|
||||
/// </summary>
|
||||
public sealed class AuthorityMongoStorageOptions
|
||||
public sealed class AuthorityStorageOptions
|
||||
{
|
||||
public string ConnectionString { get; set; } = string.Empty;
|
||||
public string DatabaseName { get; set; } = "authority";
|
||||
@@ -28,9 +28,9 @@ public static class ServiceCollectionExtensions
|
||||
/// </summary>
|
||||
public static IServiceCollection AddAuthorityMongoStorage(
|
||||
this IServiceCollection services,
|
||||
Action<AuthorityMongoStorageOptions> configureOptions)
|
||||
Action<AuthorityStorageOptions> configureOptions)
|
||||
{
|
||||
var options = new AuthorityMongoStorageOptions();
|
||||
var options = new AuthorityStorageOptions();
|
||||
configureOptions(options);
|
||||
services.AddSingleton(options);
|
||||
|
||||
@@ -38,19 +38,19 @@ public static class ServiceCollectionExtensions
|
||||
return services;
|
||||
}
|
||||
|
||||
private static void RegisterMongoCompatServices(IServiceCollection services, AuthorityMongoStorageOptions options)
|
||||
private static void RegisterMongoCompatServices(IServiceCollection services, AuthorityStorageOptions options)
|
||||
{
|
||||
// Register the initializer (no-op for Postgres mode)
|
||||
services.AddSingleton<AuthorityMongoInitializer>();
|
||||
services.AddSingleton<AuthorityStorageInitializer>();
|
||||
|
||||
// Register null session accessor
|
||||
services.AddSingleton<IAuthorityMongoSessionAccessor, NullAuthorityMongoSessionAccessor>();
|
||||
services.AddSingleton<IAuthoritySessionAccessor, NullAuthoritySessionAccessor>();
|
||||
|
||||
// Register in-memory MongoDB shims for compatibility
|
||||
var inMemoryClient = new InMemoryMongoClient();
|
||||
// Register in-memory shims for compatibility
|
||||
var inMemoryClient = new InMemoryClient();
|
||||
var inMemoryDatabase = inMemoryClient.GetDatabase(options.DatabaseName);
|
||||
services.AddSingleton<IMongoClient>(inMemoryClient);
|
||||
services.AddSingleton<IMongoDatabase>(inMemoryDatabase);
|
||||
services.AddSingleton<IClient>(inMemoryClient);
|
||||
services.AddSingleton<IDatabase>(inMemoryDatabase);
|
||||
|
||||
// Register in-memory store implementations
|
||||
// These should be replaced by Postgres-backed implementations over time
|
||||
@@ -1,10 +1,10 @@
|
||||
namespace StellaOps.Authority.Storage.Mongo.Initialization;
|
||||
namespace StellaOps.Authority.Storage.InMemory.Initialization;
|
||||
|
||||
/// <summary>
|
||||
/// Compatibility shim for MongoDB initializer. In PostgreSQL mode, this is a no-op.
|
||||
/// Compatibility shim for storage initializer. In PostgreSQL mode, this is a no-op.
|
||||
/// The actual initialization is handled by PostgreSQL migrations.
|
||||
/// </summary>
|
||||
public sealed class AuthorityMongoInitializer
|
||||
public sealed class AuthorityStorageInitializer
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes the database. In PostgreSQL mode, this is a no-op as migrations handle setup.
|
||||
@@ -8,9 +8,9 @@ public interface IClientSessionHandle : IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compatibility shim for MongoDB session accessor. In PostgreSQL mode, this returns null.
|
||||
/// Compatibility shim for database session accessor. In PostgreSQL mode, this returns null.
|
||||
/// </summary>
|
||||
public interface IAuthorityMongoSessionAccessor
|
||||
public interface IAuthoritySessionAccessor
|
||||
{
|
||||
IClientSessionHandle? CurrentSession { get; }
|
||||
ValueTask<IClientSessionHandle?> GetSessionAsync(CancellationToken cancellationToken);
|
||||
@@ -19,7 +19,7 @@ public interface IAuthorityMongoSessionAccessor
|
||||
/// <summary>
|
||||
/// In-memory implementation that always returns null session.
|
||||
/// </summary>
|
||||
public sealed class NullAuthorityMongoSessionAccessor : IAuthorityMongoSessionAccessor
|
||||
public sealed class NullAuthoritySessionAccessor : IAuthoritySessionAccessor
|
||||
{
|
||||
public IClientSessionHandle? CurrentSession => null;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Sessions;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Sessions;
|
||||
|
||||
namespace StellaOps.Authority.Storage.Mongo.Stores;
|
||||
namespace StellaOps.Authority.Storage.InMemory.Stores;
|
||||
|
||||
/// <summary>
|
||||
/// Store interface for bootstrap invites.
|
||||
@@ -1,9 +1,9 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Sessions;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Sessions;
|
||||
|
||||
namespace StellaOps.Authority.Storage.Mongo.Stores;
|
||||
namespace StellaOps.Authority.Storage.InMemory.Stores;
|
||||
|
||||
/// <summary>
|
||||
/// In-memory implementation of bootstrap invite store for development/testing.
|
||||
@@ -9,9 +9,9 @@ using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Sessions;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Sessions;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Authority.Tests.Infrastructure;
|
||||
using StellaOps.Configuration;
|
||||
using Xunit;
|
||||
|
||||
@@ -13,9 +13,9 @@ using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Time.Testing;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using StellaOps.Authority.Airgap;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Sessions;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Sessions;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Authority.Tests.Infrastructure;
|
||||
using Xunit;
|
||||
|
||||
@@ -171,7 +171,7 @@ public sealed class AirgapAuditEndpointsTests : IClassFixture<AuthorityWebApplic
|
||||
var store = new TestAirgapAuditStore();
|
||||
_airgapStore = store;
|
||||
services.Replace(ServiceDescriptor.Singleton<IAuthorityAirgapAuditStore>(store));
|
||||
services.Replace(ServiceDescriptor.Singleton<IAuthorityMongoSessionAccessor, NullAuthorityMongoSessionAccessor>());
|
||||
services.Replace(ServiceDescriptor.Singleton<IAuthoritySessionAccessor, NullAuthoritySessionAccessor>());
|
||||
services.Replace(ServiceDescriptor.Singleton<TimeProvider>(timeProvider));
|
||||
services.AddAuthentication(options =>
|
||||
{
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Authority.Audit;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Cryptography.Audit;
|
||||
using StellaOps.Authority.Storage.Mongo.Sessions;
|
||||
using StellaOps.Authority.Storage.InMemory.Sessions;
|
||||
|
||||
namespace StellaOps.Authority.Tests.Audit;
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@ using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Time.Testing;
|
||||
using StellaOps.Authority.Bootstrap;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.Mongo.Sessions;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Sessions;
|
||||
using StellaOps.Cryptography.Audit;
|
||||
using Xunit;
|
||||
|
||||
|
||||
@@ -17,9 +17,9 @@ using StellaOps.Auth.Abstractions;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using StellaOps.Configuration;
|
||||
using StellaOps.Authority.OpenIddict;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.Mongo.Sessions;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Sessions;
|
||||
using StellaOps.Authority.Tests.Infrastructure;
|
||||
using StellaOps.Cryptography.Audit;
|
||||
using Xunit;
|
||||
@@ -302,7 +302,7 @@ public sealed class ServiceAccountAdminEndpointsTests : IClassFixture<AuthorityW
|
||||
|
||||
foreach (var tokenId in tokenIds)
|
||||
{
|
||||
var sessionAccessor = scope.ServiceProvider.GetRequiredService<IAuthorityMongoSessionAccessor>();
|
||||
var sessionAccessor = scope.ServiceProvider.GetRequiredService<IAuthoritySessionAccessor>();
|
||||
var session = await sessionAccessor.GetSessionAsync(CancellationToken.None);
|
||||
var token = await tokenStore.FindByTokenIdAsync(tokenId, CancellationToken.None, session);
|
||||
Assert.NotNull(token);
|
||||
|
||||
@@ -9,9 +9,9 @@ using Microsoft.Extensions.Hosting;
|
||||
using Xunit;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using StellaOps.Authority.Storage.Mongo.Extensions;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.Mongo.Sessions;
|
||||
using StellaOps.Authority.Storage.InMemory.Extensions;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Sessions;
|
||||
using StellaOps.Authority.Storage.Postgres;
|
||||
|
||||
namespace StellaOps.Authority.Tests.Infrastructure;
|
||||
@@ -105,7 +105,7 @@ public sealed class AuthorityWebApplicationFactory : WebApplicationFactory<Progr
|
||||
services.RemoveAll<IAuthorityRefreshTokenStore>();
|
||||
services.RemoveAll<IAuthorityAirgapAuditStore>();
|
||||
services.RemoveAll<IAuthorityRevocationExportStateStore>();
|
||||
services.RemoveAll<IAuthorityMongoSessionAccessor>();
|
||||
services.RemoveAll<IAuthoritySessionAccessor>();
|
||||
|
||||
services.AddAuthorityMongoStorage(options =>
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Sessions;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Sessions;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
|
||||
namespace StellaOps.Authority.Tests.Infrastructure;
|
||||
|
||||
|
||||
@@ -30,9 +30,9 @@ using StellaOps.Authority.Airgap;
|
||||
using StellaOps.Authority.OpenIddict;
|
||||
using StellaOps.Authority.OpenIddict.Handlers;
|
||||
using StellaOps.Authority.Plugins.Abstractions;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Sessions;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Sessions;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Authority.RateLimiting;
|
||||
using StellaOps.Cryptography.Audit;
|
||||
using Xunit;
|
||||
@@ -4475,7 +4475,7 @@ internal sealed class StubCertificateValidator : IAuthorityClientCertificateVali
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class NullMongoSessionAccessor : IAuthorityMongoSessionAccessor
|
||||
internal sealed class NullMongoSessionAccessor : IAuthoritySessionAccessor
|
||||
{
|
||||
public IClientSessionHandle? CurrentSession => null;
|
||||
|
||||
|
||||
@@ -23,9 +23,9 @@ using StellaOps.Authority.OpenIddict.Handlers;
|
||||
using StellaOps.Authority.Plugins.Abstractions;
|
||||
using StellaOps.Authority.RateLimiting;
|
||||
using StellaOps.Authority.Airgap;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.Mongo.Sessions;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Sessions;
|
||||
using StellaOps.Cryptography.Audit;
|
||||
using StellaOps.Configuration;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
|
||||
@@ -5,9 +5,9 @@ using Microsoft.Extensions.Time.Testing;
|
||||
using OpenIddict.Abstractions;
|
||||
using OpenIddict.Server;
|
||||
using StellaOps.Authority.OpenIddict.Handlers;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Sessions;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Sessions;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Authority.Tests.OpenIddict;
|
||||
@@ -22,7 +22,7 @@ public sealed class TokenPersistenceIntegrationTests
|
||||
var issuedAt = new DateTimeOffset(2025, 10, 10, 12, 0, 0, TimeSpan.Zero);
|
||||
var clock = new FakeTimeProvider(issuedAt);
|
||||
var tokenStore = new InMemoryTokenStore();
|
||||
var handler = new PersistTokensHandler(tokenStore, new NullAuthorityMongoSessionAccessor(), clock, Activity, NullLogger<PersistTokensHandler>.Instance);
|
||||
var handler = new PersistTokensHandler(tokenStore, new NullAuthoritySessionAccessor(), clock, Activity, NullLogger<PersistTokensHandler>.Instance);
|
||||
|
||||
var identity = new ClaimsIdentity(authenticationType: "test");
|
||||
identity.SetClaim(OpenIddictConstants.Claims.Subject, "subject-1");
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
|
||||
namespace StellaOps.Authority.Airgap;
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Cryptography.Audit;
|
||||
|
||||
namespace StellaOps.Authority.Audit;
|
||||
|
||||
@@ -3,8 +3,8 @@ using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Cryptography.Audit;
|
||||
|
||||
namespace StellaOps.Authority.Bootstrap;
|
||||
|
||||
@@ -10,8 +10,8 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using StellaOps.Auth.ServerIntegration;
|
||||
using StellaOps.Authority.Console;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
|
||||
namespace StellaOps.Authority.Observability;
|
||||
|
||||
|
||||
@@ -17,9 +17,9 @@ using StellaOps.Auth.Abstractions;
|
||||
using StellaOps.Authority.Airgap;
|
||||
using StellaOps.Authority.OpenIddict;
|
||||
using StellaOps.Authority.Plugins.Abstractions;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Sessions;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Sessions;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Authority.RateLimiting;
|
||||
using StellaOps.Authority.Security;
|
||||
using StellaOps.Configuration;
|
||||
@@ -1522,7 +1522,7 @@ internal sealed class HandleClientCredentialsHandler : IOpenIddictServerHandler<
|
||||
{
|
||||
private readonly IAuthorityIdentityProviderRegistry registry;
|
||||
private readonly IAuthorityTokenStore tokenStore;
|
||||
private readonly IAuthorityMongoSessionAccessor sessionAccessor;
|
||||
private readonly IAuthoritySessionAccessor sessionAccessor;
|
||||
private readonly IAuthorityRateLimiterMetadataAccessor metadataAccessor;
|
||||
private readonly TimeProvider clock;
|
||||
private readonly ActivitySource activitySource;
|
||||
@@ -1531,7 +1531,7 @@ internal sealed class HandleClientCredentialsHandler : IOpenIddictServerHandler<
|
||||
public HandleClientCredentialsHandler(
|
||||
IAuthorityIdentityProviderRegistry registry,
|
||||
IAuthorityTokenStore tokenStore,
|
||||
IAuthorityMongoSessionAccessor sessionAccessor,
|
||||
IAuthoritySessionAccessor sessionAccessor,
|
||||
IAuthorityRateLimiterMetadataAccessor metadataAccessor,
|
||||
TimeProvider clock,
|
||||
ActivitySource activitySource,
|
||||
|
||||
@@ -19,8 +19,8 @@ using StellaOps.Authority.OpenIddict;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using StellaOps.Authority.RateLimiting;
|
||||
using StellaOps.Authority.Security;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Authority.Plugins.Abstractions;
|
||||
using StellaOps.Cryptography.Audit;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
@@ -15,8 +15,8 @@ using StellaOps.Authority.Airgap;
|
||||
using StellaOps.Authority.OpenIddict;
|
||||
using StellaOps.Authority.Plugins.Abstractions;
|
||||
using StellaOps.Authority.RateLimiting;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Cryptography.Audit;
|
||||
|
||||
namespace StellaOps.Authority.OpenIddict.Handlers;
|
||||
|
||||
@@ -11,8 +11,8 @@ using OpenIddict.Server;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using StellaOps.Authority.Airgap;
|
||||
using StellaOps.Authority.Security;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
|
||||
namespace StellaOps.Authority.OpenIddict.Handlers;
|
||||
|
||||
|
||||
@@ -6,22 +6,22 @@ using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using OpenIddict.Abstractions;
|
||||
using OpenIddict.Server;
|
||||
using StellaOps.Authority.Storage.Mongo.Sessions;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Sessions;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
|
||||
namespace StellaOps.Authority.OpenIddict.Handlers;
|
||||
|
||||
internal sealed class HandleRevocationRequestHandler : IOpenIddictServerHandler<OpenIddictServerEvents.HandleRevocationRequestContext>
|
||||
{
|
||||
private readonly IAuthorityTokenStore tokenStore;
|
||||
private readonly IAuthorityMongoSessionAccessor sessionAccessor;
|
||||
private readonly IAuthoritySessionAccessor sessionAccessor;
|
||||
private readonly TimeProvider clock;
|
||||
private readonly ILogger<HandleRevocationRequestHandler> logger;
|
||||
private readonly ActivitySource activitySource;
|
||||
|
||||
public HandleRevocationRequestHandler(
|
||||
IAuthorityTokenStore tokenStore,
|
||||
IAuthorityMongoSessionAccessor sessionAccessor,
|
||||
IAuthoritySessionAccessor sessionAccessor,
|
||||
TimeProvider clock,
|
||||
ActivitySource activitySource,
|
||||
ILogger<HandleRevocationRequestHandler> logger)
|
||||
|
||||
@@ -11,9 +11,9 @@ using Microsoft.Extensions.Logging;
|
||||
using OpenIddict.Abstractions;
|
||||
using OpenIddict.Extensions;
|
||||
using OpenIddict.Server;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Sessions;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Sessions;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
|
||||
namespace StellaOps.Authority.OpenIddict.Handlers;
|
||||
@@ -21,14 +21,14 @@ namespace StellaOps.Authority.OpenIddict.Handlers;
|
||||
internal sealed class PersistTokensHandler : IOpenIddictServerHandler<OpenIddictServerEvents.ProcessSignInContext>
|
||||
{
|
||||
private readonly IAuthorityTokenStore tokenStore;
|
||||
private readonly IAuthorityMongoSessionAccessor sessionAccessor;
|
||||
private readonly IAuthoritySessionAccessor sessionAccessor;
|
||||
private readonly TimeProvider clock;
|
||||
private readonly ActivitySource activitySource;
|
||||
private readonly ILogger<PersistTokensHandler> logger;
|
||||
|
||||
public PersistTokensHandler(
|
||||
IAuthorityTokenStore tokenStore,
|
||||
IAuthorityMongoSessionAccessor sessionAccessor,
|
||||
IAuthoritySessionAccessor sessionAccessor,
|
||||
TimeProvider clock,
|
||||
ActivitySource activitySource,
|
||||
ILogger<PersistTokensHandler> logger)
|
||||
|
||||
@@ -15,9 +15,9 @@ using StellaOps.Auth.Abstractions;
|
||||
using StellaOps.Authority.OpenIddict;
|
||||
using StellaOps.Authority.Plugins.Abstractions;
|
||||
using StellaOps.Authority.RateLimiting;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Sessions;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Sessions;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Cryptography.Audit;
|
||||
using StellaOps.Authority.Security;
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace StellaOps.Authority.OpenIddict.Handlers;
|
||||
internal sealed class ValidateAccessTokenHandler : IOpenIddictServerHandler<OpenIddictServerEvents.ValidateTokenContext>
|
||||
{
|
||||
private readonly IAuthorityTokenStore tokenStore;
|
||||
private readonly IAuthorityMongoSessionAccessor sessionAccessor;
|
||||
private readonly IAuthoritySessionAccessor sessionAccessor;
|
||||
private readonly IAuthorityClientStore clientStore;
|
||||
private readonly IAuthorityIdentityProviderRegistry registry;
|
||||
private readonly IAuthorityRateLimiterMetadataAccessor metadataAccessor;
|
||||
@@ -40,7 +40,7 @@ internal sealed class ValidateAccessTokenHandler : IOpenIddictServerHandler<Open
|
||||
|
||||
public ValidateAccessTokenHandler(
|
||||
IAuthorityTokenStore tokenStore,
|
||||
IAuthorityMongoSessionAccessor sessionAccessor,
|
||||
IAuthoritySessionAccessor sessionAccessor,
|
||||
IAuthorityClientStore clientStore,
|
||||
IAuthorityIdentityProviderRegistry registry,
|
||||
IAuthorityRateLimiterMetadataAccessor metadataAccessor,
|
||||
|
||||
@@ -32,9 +32,9 @@ using StellaOps.Authority.Plugins.Abstractions;
|
||||
using StellaOps.Authority.Plugins;
|
||||
using StellaOps.Authority.Bootstrap;
|
||||
using StellaOps.Authority.Console;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.Mongo.Sessions;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Sessions;
|
||||
using StellaOps.Authority.Storage.Postgres;
|
||||
using StellaOps.Authority.Storage.PostgresAdapters;
|
||||
using StellaOps.Authority.RateLimiting;
|
||||
@@ -54,7 +54,7 @@ using System.Text;
|
||||
using StellaOps.Authority.Signing;
|
||||
using StellaOps.Cryptography;
|
||||
using StellaOps.Cryptography.Kms;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Security;
|
||||
using StellaOps.Authority.OpenApi;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
@@ -249,7 +249,7 @@ builder.Services.AddAuthorityPostgresStorage(options =>
|
||||
options.AutoMigrate = true;
|
||||
options.MigrationsPath = "Migrations";
|
||||
});
|
||||
builder.Services.TryAddSingleton<IAuthorityMongoSessionAccessor, NullAuthorityMongoSessionAccessor>();
|
||||
builder.Services.TryAddSingleton<IAuthoritySessionAccessor, NullAuthoritySessionAccessor>();
|
||||
builder.Services.TryAddScoped<IAuthorityBootstrapInviteStore, PostgresBootstrapInviteStore>();
|
||||
builder.Services.TryAddScoped<IAuthorityServiceAccountStore, PostgresServiceAccountStore>();
|
||||
builder.Services.TryAddScoped<IAuthorityClientStore, PostgresClientStore>();
|
||||
@@ -1325,7 +1325,7 @@ if (authorityOptions.Bootstrap.Enabled)
|
||||
string accountId,
|
||||
IAuthorityServiceAccountStore accountStore,
|
||||
IAuthorityTokenStore tokenStore,
|
||||
IAuthorityMongoSessionAccessor sessionAccessor,
|
||||
IAuthoritySessionAccessor sessionAccessor,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(accountId))
|
||||
@@ -1355,7 +1355,7 @@ if (authorityOptions.Bootstrap.Enabled)
|
||||
HttpContext httpContext,
|
||||
IAuthorityServiceAccountStore accountStore,
|
||||
IAuthorityTokenStore tokenStore,
|
||||
IAuthorityMongoSessionAccessor sessionAccessor,
|
||||
IAuthoritySessionAccessor sessionAccessor,
|
||||
IAuthEventSink auditSink,
|
||||
TimeProvider timeProvider,
|
||||
CancellationToken cancellationToken) =>
|
||||
|
||||
@@ -10,8 +10,8 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Configuration;
|
||||
|
||||
namespace StellaOps.Authority.Revocation;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
|
||||
namespace StellaOps.Authority.Security;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ using System.Formats.Asn1;
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Configuration;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
|
||||
namespace StellaOps.Authority.Security;
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<PackageReference Include="YamlDotNet" Version="13.7.1" />
|
||||
<ProjectReference Include="..\StellaOps.Authority.Plugins.Abstractions\StellaOps.Authority.Plugins.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Authority.Plugin.Standard\StellaOps.Authority.Plugin.Standard.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Authority.Storage.Mongo\StellaOps.Authority.Storage.Mongo.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Authority.Storage.InMemory\StellaOps.Authority.Storage.InMemory.csproj" />
|
||||
<ProjectReference Include="..\..\__Libraries\StellaOps.Authority.Storage.Postgres\StellaOps.Authority.Storage.Postgres.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Auth.Abstractions\StellaOps.Auth.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Auth.ServerIntegration\StellaOps.Auth.ServerIntegration.csproj" />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Sessions;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Sessions;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Authority.Storage.Postgres.Models;
|
||||
using StellaOps.Authority.Storage.Postgres.Repositories;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Sessions;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Sessions;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Authority.Storage.Postgres.Models;
|
||||
using StellaOps.Authority.Storage.Postgres.Repositories;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Sessions;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Sessions;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Authority.Storage.Postgres.Models;
|
||||
using StellaOps.Authority.Storage.Postgres.Repositories;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Globalization;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Sessions;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Sessions;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Authority.Storage.Postgres.Models;
|
||||
using StellaOps.Authority.Storage.Postgres.Repositories;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Sessions;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Sessions;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Authority.Storage.Postgres.Models;
|
||||
using StellaOps.Authority.Storage.Postgres.Repositories;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Sessions;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Sessions;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Authority.Storage.Postgres.Models;
|
||||
using StellaOps.Authority.Storage.Postgres.Repositories;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Sessions;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Sessions;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Authority.Storage.Postgres.Models;
|
||||
using StellaOps.Authority.Storage.Postgres.Repositories;
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text.Json;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Sessions;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Authority.Storage.InMemory.Documents;
|
||||
using StellaOps.Authority.Storage.InMemory.Sessions;
|
||||
using StellaOps.Authority.Storage.InMemory.Stores;
|
||||
using StellaOps.Authority.Storage.Postgres.Models;
|
||||
using StellaOps.Authority.Storage.Postgres.Repositories;
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ using StellaOps.Aoc.AspNetCore.Results;
|
||||
using HttpResults = Microsoft.AspNetCore.Http.Results;
|
||||
using StellaOps.Concelier.Storage.Advisories;
|
||||
using StellaOps.Concelier.Storage.Aliases;
|
||||
using StellaOps.Provenance.Mongo;
|
||||
using StellaOps.Provenance;
|
||||
|
||||
namespace StellaOps.Concelier.WebService
|
||||
{
|
||||
|
||||
@@ -10,8 +10,8 @@ using System.Xml.Linq;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Concelier.Bson;
|
||||
using StellaOps.Concelier.Bson.IO;
|
||||
using StellaOps.Concelier.Documents;
|
||||
using StellaOps.Concelier.Documents.IO;
|
||||
using StellaOps.Concelier.Connector.Acsc.Configuration;
|
||||
using StellaOps.Concelier.Connector.Acsc.Internal;
|
||||
using StellaOps.Concelier.Connector.Common.Fetch;
|
||||
@@ -292,7 +292,7 @@ public sealed class AcscConnector : IFeedConnector
|
||||
var dto = AcscFeedParser.Parse(rawBytes, metadata.FeedSlug, parsedAt, _htmlSanitizer);
|
||||
|
||||
var json = JsonSerializer.Serialize(dto, SerializerOptions);
|
||||
var payload = BsonDocument.Parse(json);
|
||||
var payload = DocumentObject.Parse(json);
|
||||
|
||||
var existingDto = await _dtoStore.FindByDocumentIdAsync(document.Id, cancellationToken).ConfigureAwait(false);
|
||||
var dtoRecord = existingDto is null
|
||||
@@ -678,7 +678,7 @@ public sealed class AcscConnector : IFeedConnector
|
||||
|
||||
private Task UpdateCursorAsync(AcscCursor cursor, CancellationToken cancellationToken)
|
||||
{
|
||||
var document = cursor.ToBsonDocument();
|
||||
var document = cursor.ToDocumentObject();
|
||||
var completedAt = _timeProvider.GetUtcNow();
|
||||
return _stateRepository.UpdateCursorAsync(SourceName, document, completedAt, cancellationToken);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using StellaOps.Concelier.Bson;
|
||||
using StellaOps.Concelier.Documents;
|
||||
|
||||
namespace StellaOps.Concelier.Connector.Acsc.Internal;
|
||||
|
||||
@@ -48,16 +48,16 @@ internal sealed record AcscCursor(
|
||||
return this with { LastPublishedByFeed = snapshot };
|
||||
}
|
||||
|
||||
public BsonDocument ToBsonDocument()
|
||||
public DocumentObject ToDocumentObject()
|
||||
{
|
||||
var document = new BsonDocument
|
||||
var document = new DocumentObject
|
||||
{
|
||||
["preferredEndpoint"] = PreferredEndpoint.ToString(),
|
||||
["pendingDocuments"] = new BsonArray(PendingDocuments.Select(id => id.ToString())),
|
||||
["pendingMappings"] = new BsonArray(PendingMappings.Select(id => id.ToString())),
|
||||
["pendingDocuments"] = new DocumentArray(PendingDocuments.Select(id => id.ToString())),
|
||||
["pendingMappings"] = new DocumentArray(PendingMappings.Select(id => id.ToString())),
|
||||
};
|
||||
|
||||
var feedsDocument = new BsonDocument();
|
||||
var feedsDocument = new DocumentObject();
|
||||
foreach (var kvp in LastPublishedByFeed)
|
||||
{
|
||||
if (kvp.Value.HasValue)
|
||||
@@ -70,7 +70,7 @@ internal sealed record AcscCursor(
|
||||
return document;
|
||||
}
|
||||
|
||||
public static AcscCursor FromBson(BsonDocument? document)
|
||||
public static AcscCursor FromBson(DocumentObject? document)
|
||||
{
|
||||
if (document is null || document.ElementCount == 0)
|
||||
{
|
||||
@@ -82,7 +82,7 @@ internal sealed record AcscCursor(
|
||||
: AcscEndpointPreference.Auto;
|
||||
|
||||
var feeds = new Dictionary<string, DateTimeOffset?>(StringComparer.OrdinalIgnoreCase);
|
||||
if (document.TryGetValue("feeds", out var feedsValue) && feedsValue is BsonDocument feedsDocument)
|
||||
if (document.TryGetValue("feeds", out var feedsValue) && feedsValue is DocumentObject feedsDocument)
|
||||
{
|
||||
foreach (var element in feedsDocument.Elements)
|
||||
{
|
||||
@@ -100,9 +100,9 @@ internal sealed record AcscCursor(
|
||||
pendingMappings);
|
||||
}
|
||||
|
||||
private static IReadOnlyCollection<Guid> ReadGuidArray(BsonDocument document, string field)
|
||||
private static IReadOnlyCollection<Guid> ReadGuidArray(DocumentObject document, string field)
|
||||
{
|
||||
if (!document.TryGetValue(field, out var value) || value is not BsonArray array)
|
||||
if (!document.TryGetValue(field, out var value) || value is not DocumentArray array)
|
||||
{
|
||||
return EmptyGuidList;
|
||||
}
|
||||
@@ -119,12 +119,12 @@ internal sealed record AcscCursor(
|
||||
return list;
|
||||
}
|
||||
|
||||
private static DateTimeOffset? ParseDate(BsonValue value)
|
||||
private static DateTimeOffset? ParseDate(DocumentValue value)
|
||||
{
|
||||
return value.BsonType switch
|
||||
return value.DocumentType switch
|
||||
{
|
||||
BsonType.DateTime => DateTime.SpecifyKind(value.ToUniversalTime(), DateTimeKind.Utc),
|
||||
BsonType.String when DateTimeOffset.TryParse(value.AsString, out var parsed) => parsed.ToUniversalTime(),
|
||||
DocumentType.DateTime => DateTime.SpecifyKind(value.ToUniversalTime(), DateTimeKind.Utc),
|
||||
DocumentType.String when DateTimeOffset.TryParse(value.AsString, out var parsed) => parsed.ToUniversalTime(),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ using System.Threading.Tasks;
|
||||
using System.Globalization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Concelier.Bson;
|
||||
using StellaOps.Concelier.Documents;
|
||||
using StellaOps.Concelier.Connector.Cccs.Configuration;
|
||||
using StellaOps.Concelier.Connector.Cccs.Internal;
|
||||
using StellaOps.Concelier.Connector.Common;
|
||||
@@ -332,7 +332,7 @@ public sealed class CccsConnector : IFeedConnector
|
||||
}
|
||||
|
||||
var dtoJson = JsonSerializer.Serialize(dto, DtoSerializerOptions);
|
||||
var dtoBson = BsonDocument.Parse(dtoJson);
|
||||
var dtoBson = DocumentObject.Parse(dtoJson);
|
||||
var dtoRecord = new DtoRecord(Guid.NewGuid(), document.Id, SourceName, DtoSchemaVersion, dtoBson, now);
|
||||
await _dtoStore.UpsertAsync(dtoRecord, cancellationToken).ConfigureAwait(false);
|
||||
await _documentStore.UpdateStatusAsync(document.Id, DocumentStatuses.PendingMap, cancellationToken).ConfigureAwait(false);
|
||||
@@ -464,7 +464,7 @@ public sealed class CccsConnector : IFeedConnector
|
||||
|
||||
private Task UpdateCursorAsync(CccsCursor cursor, CancellationToken cancellationToken)
|
||||
{
|
||||
var document = cursor.ToBsonDocument();
|
||||
var document = cursor.ToDocumentObject();
|
||||
var completedAt = cursor.LastFetchAt ?? _timeProvider.GetUtcNow();
|
||||
return _stateRepository.UpdateCursorAsync(SourceName, document, completedAt, cancellationToken);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using StellaOps.Concelier.Bson;
|
||||
using StellaOps.Concelier.Documents;
|
||||
|
||||
namespace StellaOps.Concelier.Connector.Cccs.Internal;
|
||||
|
||||
@@ -39,20 +39,20 @@ internal sealed record CccsCursor(
|
||||
public CccsCursor WithLastFetch(DateTimeOffset? timestamp)
|
||||
=> this with { LastFetchAt = timestamp };
|
||||
|
||||
public BsonDocument ToBsonDocument()
|
||||
public DocumentObject ToDocumentObject()
|
||||
{
|
||||
var doc = new BsonDocument
|
||||
var doc = new DocumentObject
|
||||
{
|
||||
["pendingDocuments"] = new BsonArray(PendingDocuments.Select(id => id.ToString())),
|
||||
["pendingMappings"] = new BsonArray(PendingMappings.Select(id => id.ToString())),
|
||||
["pendingDocuments"] = new DocumentArray(PendingDocuments.Select(id => id.ToString())),
|
||||
["pendingMappings"] = new DocumentArray(PendingMappings.Select(id => id.ToString())),
|
||||
};
|
||||
|
||||
if (KnownEntryHashes.Count > 0)
|
||||
{
|
||||
var hashes = new BsonArray();
|
||||
var hashes = new DocumentArray();
|
||||
foreach (var kvp in KnownEntryHashes)
|
||||
{
|
||||
hashes.Add(new BsonDocument
|
||||
hashes.Add(new DocumentObject
|
||||
{
|
||||
["uri"] = kvp.Key,
|
||||
["hash"] = kvp.Value,
|
||||
@@ -70,7 +70,7 @@ internal sealed record CccsCursor(
|
||||
return doc;
|
||||
}
|
||||
|
||||
public static CccsCursor FromBson(BsonDocument? document)
|
||||
public static CccsCursor FromBson(DocumentObject? document)
|
||||
{
|
||||
if (document is null || document.ElementCount == 0)
|
||||
{
|
||||
@@ -87,9 +87,9 @@ internal sealed record CccsCursor(
|
||||
return new CccsCursor(pendingDocuments, pendingMappings, hashes, lastFetch);
|
||||
}
|
||||
|
||||
private static IReadOnlyCollection<Guid> ReadGuidArray(BsonDocument document, string field)
|
||||
private static IReadOnlyCollection<Guid> ReadGuidArray(DocumentObject document, string field)
|
||||
{
|
||||
if (!document.TryGetValue(field, out var value) || value is not BsonArray array)
|
||||
if (!document.TryGetValue(field, out var value) || value is not DocumentArray array)
|
||||
{
|
||||
return EmptyGuidCollection;
|
||||
}
|
||||
@@ -106,9 +106,9 @@ internal sealed record CccsCursor(
|
||||
return items;
|
||||
}
|
||||
|
||||
private static IReadOnlyDictionary<string, string> ReadHashMap(BsonDocument document)
|
||||
private static IReadOnlyDictionary<string, string> ReadHashMap(DocumentObject document)
|
||||
{
|
||||
if (!document.TryGetValue("knownEntryHashes", out var value) || value is not BsonArray array || array.Count == 0)
|
||||
if (!document.TryGetValue("knownEntryHashes", out var value) || value is not DocumentArray array || array.Count == 0)
|
||||
{
|
||||
return EmptyHashes;
|
||||
}
|
||||
@@ -116,17 +116,17 @@ internal sealed record CccsCursor(
|
||||
var map = new Dictionary<string, string>(array.Count, StringComparer.Ordinal);
|
||||
foreach (var element in array)
|
||||
{
|
||||
if (element is not BsonDocument entry)
|
||||
if (element is not DocumentObject entry)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!entry.TryGetValue("uri", out var uriValue) || uriValue.IsBsonNull || string.IsNullOrWhiteSpace(uriValue.AsString))
|
||||
if (!entry.TryGetValue("uri", out var uriValue) || uriValue.IsDocumentNull || string.IsNullOrWhiteSpace(uriValue.AsString))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var hash = entry.TryGetValue("hash", out var hashValue) && !hashValue.IsBsonNull
|
||||
var hash = entry.TryGetValue("hash", out var hashValue) && !hashValue.IsDocumentNull
|
||||
? hashValue.AsString
|
||||
: string.Empty;
|
||||
map[uriValue.AsString] = hash;
|
||||
@@ -135,11 +135,11 @@ internal sealed record CccsCursor(
|
||||
return map;
|
||||
}
|
||||
|
||||
private static DateTimeOffset? ParseDateTime(BsonValue value)
|
||||
=> value.BsonType switch
|
||||
private static DateTimeOffset? ParseDateTime(DocumentValue value)
|
||||
=> value.DocumentType switch
|
||||
{
|
||||
BsonType.DateTime => DateTime.SpecifyKind(value.ToUniversalTime(), DateTimeKind.Utc),
|
||||
BsonType.String when DateTimeOffset.TryParse(value.AsString, out var parsed) => parsed.ToUniversalTime(),
|
||||
DocumentType.DateTime => DateTime.SpecifyKind(value.ToUniversalTime(), DateTimeKind.Utc),
|
||||
DocumentType.String when DateTimeOffset.TryParse(value.AsString, out var parsed) => parsed.ToUniversalTime(),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Concelier.Bson;
|
||||
using StellaOps.Concelier.Documents;
|
||||
using StellaOps.Concelier.Connector.CertBund.Configuration;
|
||||
using StellaOps.Concelier.Connector.CertBund.Internal;
|
||||
using StellaOps.Concelier.Connector.Common;
|
||||
@@ -286,7 +286,7 @@ public sealed class CertBundConnector : IFeedConnector
|
||||
_diagnostics.ParseSuccess(dto.Products.Count, dto.CveIds.Count);
|
||||
parsedCount++;
|
||||
|
||||
var bson = BsonDocument.Parse(JsonSerializer.Serialize(dto, SerializerOptions));
|
||||
var bson = DocumentObject.Parse(JsonSerializer.Serialize(dto, SerializerOptions));
|
||||
var dtoRecord = new DtoRecord(Guid.NewGuid(), document.Id, SourceName, "cert-bund.detail.v1", bson, now);
|
||||
await _dtoStore.UpsertAsync(dtoRecord, cancellationToken).ConfigureAwait(false);
|
||||
await _documentStore.UpdateStatusAsync(document.Id, DocumentStatuses.PendingMap, cancellationToken).ConfigureAwait(false);
|
||||
@@ -428,7 +428,7 @@ public sealed class CertBundConnector : IFeedConnector
|
||||
|
||||
private Task UpdateCursorAsync(CertBundCursor cursor, CancellationToken cancellationToken)
|
||||
{
|
||||
var document = cursor.ToBsonDocument();
|
||||
var document = cursor.ToDocumentObject();
|
||||
var completedAt = cursor.LastFetchAt ?? _timeProvider.GetUtcNow();
|
||||
return _stateRepository.UpdateCursorAsync(SourceName, document, completedAt, cancellationToken);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using StellaOps.Concelier.Bson;
|
||||
using StellaOps.Concelier.Documents;
|
||||
|
||||
namespace StellaOps.Concelier.Connector.CertBund.Internal;
|
||||
|
||||
@@ -31,13 +31,13 @@ internal sealed record CertBundCursor(
|
||||
public CertBundCursor WithLastFetch(DateTimeOffset? timestamp)
|
||||
=> this with { LastFetchAt = timestamp };
|
||||
|
||||
public BsonDocument ToBsonDocument()
|
||||
public DocumentObject ToDocumentObject()
|
||||
{
|
||||
var document = new BsonDocument
|
||||
var document = new DocumentObject
|
||||
{
|
||||
["pendingDocuments"] = new BsonArray(PendingDocuments.Select(id => id.ToString())),
|
||||
["pendingMappings"] = new BsonArray(PendingMappings.Select(id => id.ToString())),
|
||||
["knownAdvisories"] = new BsonArray(KnownAdvisories),
|
||||
["pendingDocuments"] = new DocumentArray(PendingDocuments.Select(id => id.ToString())),
|
||||
["pendingMappings"] = new DocumentArray(PendingMappings.Select(id => id.ToString())),
|
||||
["knownAdvisories"] = new DocumentArray(KnownAdvisories),
|
||||
};
|
||||
|
||||
if (LastPublished.HasValue)
|
||||
@@ -53,7 +53,7 @@ internal sealed record CertBundCursor(
|
||||
return document;
|
||||
}
|
||||
|
||||
public static CertBundCursor FromBson(BsonDocument? document)
|
||||
public static CertBundCursor FromBson(DocumentObject? document)
|
||||
{
|
||||
if (document is null || document.ElementCount == 0)
|
||||
{
|
||||
@@ -76,9 +76,9 @@ internal sealed record CertBundCursor(
|
||||
private static IReadOnlyCollection<Guid> Distinct(IEnumerable<Guid>? values)
|
||||
=> values?.Distinct().ToArray() ?? EmptyGuids;
|
||||
|
||||
private static IReadOnlyCollection<Guid> ReadGuidArray(BsonDocument document, string field)
|
||||
private static IReadOnlyCollection<Guid> ReadGuidArray(DocumentObject document, string field)
|
||||
{
|
||||
if (!document.TryGetValue(field, out var value) || value is not BsonArray array)
|
||||
if (!document.TryGetValue(field, out var value) || value is not DocumentArray array)
|
||||
{
|
||||
return EmptyGuids;
|
||||
}
|
||||
@@ -95,9 +95,9 @@ internal sealed record CertBundCursor(
|
||||
return items;
|
||||
}
|
||||
|
||||
private static IReadOnlyCollection<string> ReadStringArray(BsonDocument document, string field)
|
||||
private static IReadOnlyCollection<string> ReadStringArray(DocumentObject document, string field)
|
||||
{
|
||||
if (!document.TryGetValue(field, out var value) || value is not BsonArray array)
|
||||
if (!document.TryGetValue(field, out var value) || value is not DocumentArray array)
|
||||
{
|
||||
return EmptyStrings;
|
||||
}
|
||||
@@ -108,11 +108,11 @@ internal sealed record CertBundCursor(
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private static DateTimeOffset? ParseDate(BsonValue value)
|
||||
=> value.BsonType switch
|
||||
private static DateTimeOffset? ParseDate(DocumentValue value)
|
||||
=> value.DocumentType switch
|
||||
{
|
||||
BsonType.DateTime => DateTime.SpecifyKind(value.ToUniversalTime(), DateTimeKind.Utc),
|
||||
BsonType.String when DateTimeOffset.TryParse(value.AsString, out var parsed) => parsed.ToUniversalTime(),
|
||||
DocumentType.DateTime => DateTime.SpecifyKind(value.ToUniversalTime(), DateTimeKind.Utc),
|
||||
DocumentType.String when DateTimeOffset.TryParse(value.AsString, out var parsed) => parsed.ToUniversalTime(),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ using System.Text.Json.Serialization;
|
||||
using System.Threading;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Concelier.Bson;
|
||||
using StellaOps.Concelier.Documents;
|
||||
using StellaOps.Concelier.Models;
|
||||
using StellaOps.Concelier.Connector.CertCc.Configuration;
|
||||
using StellaOps.Concelier.Connector.CertCc.Internal;
|
||||
@@ -338,7 +338,7 @@ public sealed class CertCcConnector : IFeedConnector
|
||||
|
||||
var dto = CertCcNoteParser.Parse(noteBytes, vendorsBytes, vulsBytes, vendorStatusesBytes);
|
||||
var json = JsonSerializer.Serialize(dto, DtoSerializerOptions);
|
||||
var payload = StellaOps.Concelier.Bson.BsonDocument.Parse(json);
|
||||
var payload = StellaOps.Concelier.Documents.DocumentObject.Parse(json);
|
||||
|
||||
_diagnostics.ParseSuccess(
|
||||
dto.Vendors.Count,
|
||||
@@ -678,7 +678,7 @@ public sealed class CertCcConnector : IFeedConnector
|
||||
private async Task UpdateCursorAsync(CertCcCursor cursor, CancellationToken cancellationToken)
|
||||
{
|
||||
var completedAt = _timeProvider.GetUtcNow();
|
||||
await _stateRepository.UpdateCursorAsync(SourceName, cursor.ToBsonDocument(), completedAt, cancellationToken).ConfigureAwait(false);
|
||||
await _stateRepository.UpdateCursorAsync(SourceName, cursor.ToDocumentObject(), completedAt, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private sealed class NoteDocumentGroup
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using StellaOps.Concelier.Bson;
|
||||
using StellaOps.Concelier.Documents;
|
||||
using StellaOps.Concelier.Connector.Common.Cursors;
|
||||
|
||||
namespace StellaOps.Concelier.Connector.CertCc.Internal;
|
||||
@@ -22,18 +22,18 @@ internal sealed record CertCcCursor(
|
||||
EmptyGuidArray,
|
||||
null);
|
||||
|
||||
public BsonDocument ToBsonDocument()
|
||||
public DocumentObject ToDocumentObject()
|
||||
{
|
||||
var document = new BsonDocument();
|
||||
var document = new DocumentObject();
|
||||
|
||||
var summary = new BsonDocument();
|
||||
var summary = new DocumentObject();
|
||||
SummaryState.WriteTo(summary, "start", "end");
|
||||
document["summary"] = summary;
|
||||
|
||||
document["pendingSummaries"] = new BsonArray(PendingSummaries.Select(static id => id.ToString()));
|
||||
document["pendingNotes"] = new BsonArray(PendingNotes.Select(static note => note));
|
||||
document["pendingDocuments"] = new BsonArray(PendingDocuments.Select(static id => id.ToString()));
|
||||
document["pendingMappings"] = new BsonArray(PendingMappings.Select(static id => id.ToString()));
|
||||
document["pendingSummaries"] = new DocumentArray(PendingSummaries.Select(static id => id.ToString()));
|
||||
document["pendingNotes"] = new DocumentArray(PendingNotes.Select(static note => note));
|
||||
document["pendingDocuments"] = new DocumentArray(PendingDocuments.Select(static id => id.ToString()));
|
||||
document["pendingMappings"] = new DocumentArray(PendingMappings.Select(static id => id.ToString()));
|
||||
|
||||
if (LastRun.HasValue)
|
||||
{
|
||||
@@ -43,7 +43,7 @@ internal sealed record CertCcCursor(
|
||||
return document;
|
||||
}
|
||||
|
||||
public static CertCcCursor FromBson(BsonDocument? document)
|
||||
public static CertCcCursor FromBson(DocumentObject? document)
|
||||
{
|
||||
if (document is null || document.ElementCount == 0)
|
||||
{
|
||||
@@ -51,9 +51,9 @@ internal sealed record CertCcCursor(
|
||||
}
|
||||
|
||||
TimeWindowCursorState summaryState = TimeWindowCursorState.Empty;
|
||||
if (document.TryGetValue("summary", out var summaryValue) && summaryValue is BsonDocument summaryDocument)
|
||||
if (document.TryGetValue("summary", out var summaryValue) && summaryValue is DocumentObject summaryDocument)
|
||||
{
|
||||
summaryState = TimeWindowCursorState.FromBsonDocument(summaryDocument, "start", "end");
|
||||
summaryState = TimeWindowCursorState.FromDocumentObject(summaryDocument, "start", "end");
|
||||
}
|
||||
|
||||
var pendingSummaries = ReadGuidArray(document, "pendingSummaries");
|
||||
@@ -64,10 +64,10 @@ internal sealed record CertCcCursor(
|
||||
DateTimeOffset? lastRun = null;
|
||||
if (document.TryGetValue("lastRun", out var lastRunValue))
|
||||
{
|
||||
lastRun = lastRunValue.BsonType switch
|
||||
lastRun = lastRunValue.DocumentType switch
|
||||
{
|
||||
BsonType.DateTime => DateTime.SpecifyKind(lastRunValue.ToUniversalTime(), DateTimeKind.Utc),
|
||||
BsonType.String when DateTimeOffset.TryParse(lastRunValue.AsString, out var parsed) => parsed.ToUniversalTime(),
|
||||
DocumentType.DateTime => DateTime.SpecifyKind(lastRunValue.ToUniversalTime(), DateTimeKind.Utc),
|
||||
DocumentType.String when DateTimeOffset.TryParse(lastRunValue.AsString, out var parsed) => parsed.ToUniversalTime(),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
@@ -93,9 +93,9 @@ internal sealed record CertCcCursor(
|
||||
public CertCcCursor WithLastRun(DateTimeOffset? timestamp)
|
||||
=> this with { LastRun = timestamp };
|
||||
|
||||
private static Guid[] ReadGuidArray(BsonDocument document, string field)
|
||||
private static Guid[] ReadGuidArray(DocumentObject document, string field)
|
||||
{
|
||||
if (!document.TryGetValue(field, out var value) || value is not BsonArray array || array.Count == 0)
|
||||
if (!document.TryGetValue(field, out var value) || value is not DocumentArray array || array.Count == 0)
|
||||
{
|
||||
return EmptyGuidArray;
|
||||
}
|
||||
@@ -112,9 +112,9 @@ internal sealed record CertCcCursor(
|
||||
return results.Count == 0 ? EmptyGuidArray : results.Distinct().ToArray();
|
||||
}
|
||||
|
||||
private static string[] ReadStringArray(BsonDocument document, string field)
|
||||
private static string[] ReadStringArray(DocumentObject document, string field)
|
||||
{
|
||||
if (!document.TryGetValue(field, out var value) || value is not BsonArray array || array.Count == 0)
|
||||
if (!document.TryGetValue(field, out var value) || value is not DocumentArray array || array.Count == 0)
|
||||
{
|
||||
return EmptyStringArray;
|
||||
}
|
||||
@@ -124,10 +124,10 @@ internal sealed record CertCcCursor(
|
||||
{
|
||||
switch (element)
|
||||
{
|
||||
case BsonString bsonString when !string.IsNullOrWhiteSpace(bsonString.AsString):
|
||||
case DocumentString bsonString when !string.IsNullOrWhiteSpace(bsonString.AsString):
|
||||
results.Add(bsonString.AsString.Trim());
|
||||
break;
|
||||
case BsonDocument bsonDocument when bsonDocument.TryGetValue("value", out var inner) && inner.IsString:
|
||||
case DocumentObject bsonDocument when bsonDocument.TryGetValue("value", out var inner) && inner.IsString:
|
||||
results.Add(inner.AsString.Trim());
|
||||
break;
|
||||
}
|
||||
@@ -142,14 +142,14 @@ internal sealed record CertCcCursor(
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private static bool TryReadGuid(BsonValue value, out Guid guid)
|
||||
private static bool TryReadGuid(DocumentValue value, out Guid guid)
|
||||
{
|
||||
if (value is BsonString bsonString && Guid.TryParse(bsonString.AsString, out guid))
|
||||
if (value is DocumentString bsonString && Guid.TryParse(bsonString.AsString, out guid))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (value is BsonBinaryData binary)
|
||||
if (value is DocumentBinaryData binary)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@ using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Concelier.Bson;
|
||||
using StellaOps.Concelier.Documents;
|
||||
using StellaOps.Concelier.Connector.CertFr.Configuration;
|
||||
using StellaOps.Concelier.Connector.CertFr.Internal;
|
||||
using StellaOps.Concelier.Connector.Common;
|
||||
@@ -236,7 +236,7 @@ public sealed class CertFrConnector : IFeedConnector
|
||||
}
|
||||
|
||||
var json = JsonSerializer.Serialize(dto, SerializerOptions);
|
||||
var payload = BsonDocument.Parse(json);
|
||||
var payload = DocumentObject.Parse(json);
|
||||
var validatedAt = _timeProvider.GetUtcNow();
|
||||
|
||||
var existingDto = await _dtoStore.FindByDocumentIdAsync(document.Id, cancellationToken).ConfigureAwait(false);
|
||||
@@ -332,6 +332,6 @@ public sealed class CertFrConnector : IFeedConnector
|
||||
private async Task UpdateCursorAsync(CertFrCursor cursor, CancellationToken cancellationToken)
|
||||
{
|
||||
var completedAt = _timeProvider.GetUtcNow();
|
||||
await _stateRepository.UpdateCursorAsync(SourceName, cursor.ToBsonDocument(), completedAt, cancellationToken).ConfigureAwait(false);
|
||||
await _stateRepository.UpdateCursorAsync(SourceName, cursor.ToDocumentObject(), completedAt, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using StellaOps.Concelier.Bson;
|
||||
using StellaOps.Concelier.Documents;
|
||||
|
||||
namespace StellaOps.Concelier.Connector.CertFr.Internal;
|
||||
|
||||
@@ -12,12 +12,12 @@ internal sealed record CertFrCursor(
|
||||
{
|
||||
public static CertFrCursor Empty { get; } = new(null, Array.Empty<Guid>(), Array.Empty<Guid>());
|
||||
|
||||
public BsonDocument ToBsonDocument()
|
||||
public DocumentObject ToDocumentObject()
|
||||
{
|
||||
var document = new BsonDocument
|
||||
var document = new DocumentObject
|
||||
{
|
||||
["pendingDocuments"] = new BsonArray(PendingDocuments.Select(id => id.ToString())),
|
||||
["pendingMappings"] = new BsonArray(PendingMappings.Select(id => id.ToString())),
|
||||
["pendingDocuments"] = new DocumentArray(PendingDocuments.Select(id => id.ToString())),
|
||||
["pendingMappings"] = new DocumentArray(PendingMappings.Select(id => id.ToString())),
|
||||
};
|
||||
|
||||
if (LastPublished.HasValue)
|
||||
@@ -28,7 +28,7 @@ internal sealed record CertFrCursor(
|
||||
return document;
|
||||
}
|
||||
|
||||
public static CertFrCursor FromBson(BsonDocument? document)
|
||||
public static CertFrCursor FromBson(DocumentObject? document)
|
||||
{
|
||||
if (document is null || document.ElementCount == 0)
|
||||
{
|
||||
@@ -54,17 +54,17 @@ internal sealed record CertFrCursor(
|
||||
public CertFrCursor WithPendingMappings(IEnumerable<Guid> ids)
|
||||
=> this with { PendingMappings = ids?.Distinct().ToArray() ?? Array.Empty<Guid>() };
|
||||
|
||||
private static DateTimeOffset? ParseDate(BsonValue value)
|
||||
=> value.BsonType switch
|
||||
private static DateTimeOffset? ParseDate(DocumentValue value)
|
||||
=> value.DocumentType switch
|
||||
{
|
||||
BsonType.DateTime => DateTime.SpecifyKind(value.ToUniversalTime(), DateTimeKind.Utc),
|
||||
BsonType.String when DateTimeOffset.TryParse(value.AsString, out var parsed) => parsed.ToUniversalTime(),
|
||||
DocumentType.DateTime => DateTime.SpecifyKind(value.ToUniversalTime(), DateTimeKind.Utc),
|
||||
DocumentType.String when DateTimeOffset.TryParse(value.AsString, out var parsed) => parsed.ToUniversalTime(),
|
||||
_ => null,
|
||||
};
|
||||
|
||||
private static IReadOnlyCollection<Guid> ReadGuidArray(BsonDocument document, string field)
|
||||
private static IReadOnlyCollection<Guid> ReadGuidArray(DocumentObject document, string field)
|
||||
{
|
||||
if (!document.TryGetValue(field, out var raw) || raw is not BsonArray array)
|
||||
if (!document.TryGetValue(field, out var raw) || raw is not DocumentArray array)
|
||||
{
|
||||
return Array.Empty<Guid>();
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Concelier.Bson;
|
||||
using StellaOps.Concelier.Documents;
|
||||
using StellaOps.Concelier.Models;
|
||||
using StellaOps.Concelier.Connector.CertIn.Configuration;
|
||||
using StellaOps.Concelier.Connector.CertIn.Internal;
|
||||
@@ -226,7 +226,7 @@ public sealed class CertInConnector : IFeedConnector
|
||||
}
|
||||
|
||||
var dto = CertInDetailParser.Parse(listing, rawBytes);
|
||||
var payload = BsonDocument.Parse(JsonSerializer.Serialize(dto, SerializerOptions));
|
||||
var payload = DocumentObject.Parse(JsonSerializer.Serialize(dto, SerializerOptions));
|
||||
var dtoRecord = new DtoRecord(Guid.NewGuid(), document.Id, SourceName, "certin.v1", payload, _timeProvider.GetUtcNow());
|
||||
|
||||
await _dtoStore.UpsertAsync(dtoRecord, cancellationToken).ConfigureAwait(false);
|
||||
@@ -271,9 +271,9 @@ public sealed class CertInConnector : IFeedConnector
|
||||
continue;
|
||||
}
|
||||
|
||||
var dtoJson = dtoRecord.Payload.ToJson(new StellaOps.Concelier.Bson.IO.JsonWriterSettings
|
||||
var dtoJson = dtoRecord.Payload.ToJson(new StellaOps.Concelier.Documents.IO.JsonWriterSettings
|
||||
{
|
||||
OutputMode = StellaOps.Concelier.Bson.IO.JsonOutputMode.RelaxedExtendedJson,
|
||||
OutputMode = StellaOps.Concelier.Documents.IO.JsonOutputMode.RelaxedExtendedJson,
|
||||
});
|
||||
|
||||
CertInAdvisoryDto dto;
|
||||
@@ -423,7 +423,7 @@ public sealed class CertInConnector : IFeedConnector
|
||||
|
||||
private Task UpdateCursorAsync(CertInCursor cursor, CancellationToken cancellationToken)
|
||||
{
|
||||
return _stateRepository.UpdateCursorAsync(SourceName, cursor.ToBsonDocument(), _timeProvider.GetUtcNow(), cancellationToken);
|
||||
return _stateRepository.UpdateCursorAsync(SourceName, cursor.ToDocumentObject(), _timeProvider.GetUtcNow(), cancellationToken);
|
||||
}
|
||||
|
||||
private static bool TryDeserializeListing(IReadOnlyDictionary<string, string>? metadata, out CertInListingItem listing)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using StellaOps.Concelier.Bson;
|
||||
using StellaOps.Concelier.Documents;
|
||||
|
||||
namespace StellaOps.Concelier.Connector.CertIn.Internal;
|
||||
|
||||
@@ -12,12 +12,12 @@ internal sealed record CertInCursor(
|
||||
{
|
||||
public static CertInCursor Empty { get; } = new(null, Array.Empty<Guid>(), Array.Empty<Guid>());
|
||||
|
||||
public BsonDocument ToBsonDocument()
|
||||
public DocumentObject ToDocumentObject()
|
||||
{
|
||||
var document = new BsonDocument
|
||||
var document = new DocumentObject
|
||||
{
|
||||
["pendingDocuments"] = new BsonArray(PendingDocuments.Select(id => id.ToString())),
|
||||
["pendingMappings"] = new BsonArray(PendingMappings.Select(id => id.ToString())),
|
||||
["pendingDocuments"] = new DocumentArray(PendingDocuments.Select(id => id.ToString())),
|
||||
["pendingMappings"] = new DocumentArray(PendingMappings.Select(id => id.ToString())),
|
||||
};
|
||||
|
||||
if (LastPublished.HasValue)
|
||||
@@ -28,7 +28,7 @@ internal sealed record CertInCursor(
|
||||
return document;
|
||||
}
|
||||
|
||||
public static CertInCursor FromBson(BsonDocument? document)
|
||||
public static CertInCursor FromBson(DocumentObject? document)
|
||||
{
|
||||
if (document is null || document.ElementCount == 0)
|
||||
{
|
||||
@@ -54,17 +54,17 @@ internal sealed record CertInCursor(
|
||||
public CertInCursor WithPendingMappings(IEnumerable<Guid> ids)
|
||||
=> this with { PendingMappings = ids?.Distinct().ToArray() ?? Array.Empty<Guid>() };
|
||||
|
||||
private static DateTimeOffset? ParseDate(BsonValue value)
|
||||
=> value.BsonType switch
|
||||
private static DateTimeOffset? ParseDate(DocumentValue value)
|
||||
=> value.DocumentType switch
|
||||
{
|
||||
BsonType.DateTime => DateTime.SpecifyKind(value.ToUniversalTime(), DateTimeKind.Utc),
|
||||
BsonType.String when DateTimeOffset.TryParse(value.AsString, out var parsed) => parsed.ToUniversalTime(),
|
||||
DocumentType.DateTime => DateTime.SpecifyKind(value.ToUniversalTime(), DateTimeKind.Utc),
|
||||
DocumentType.String when DateTimeOffset.TryParse(value.AsString, out var parsed) => parsed.ToUniversalTime(),
|
||||
_ => null,
|
||||
};
|
||||
|
||||
private static IReadOnlyCollection<Guid> ReadGuidArray(BsonDocument document, string field)
|
||||
private static IReadOnlyCollection<Guid> ReadGuidArray(DocumentObject document, string field)
|
||||
{
|
||||
if (!document.TryGetValue(field, out var value) || value is not BsonArray array)
|
||||
if (!document.TryGetValue(field, out var value) || value is not DocumentArray array)
|
||||
{
|
||||
return Array.Empty<Guid>();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using StellaOps.Concelier.Bson;
|
||||
using StellaOps.Concelier.Documents;
|
||||
|
||||
namespace StellaOps.Concelier.Connector.Common.Cursors;
|
||||
|
||||
@@ -14,14 +14,14 @@ public sealed record TimeWindowCursorState(DateTimeOffset? LastWindowStart, Date
|
||||
return new TimeWindowCursorState(window.Start, window.End);
|
||||
}
|
||||
|
||||
public BsonDocument ToBsonDocument(string startField = "windowStart", string endField = "windowEnd")
|
||||
public DocumentObject ToDocumentObject(string startField = "windowStart", string endField = "windowEnd")
|
||||
{
|
||||
var document = new BsonDocument();
|
||||
var document = new DocumentObject();
|
||||
WriteTo(document, startField, endField);
|
||||
return document;
|
||||
}
|
||||
|
||||
public void WriteTo(BsonDocument document, string startField = "windowStart", string endField = "windowEnd")
|
||||
public void WriteTo(DocumentObject document, string startField = "windowStart", string endField = "windowEnd")
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(document);
|
||||
ArgumentException.ThrowIfNullOrEmpty(startField);
|
||||
@@ -41,7 +41,7 @@ public sealed record TimeWindowCursorState(DateTimeOffset? LastWindowStart, Date
|
||||
}
|
||||
}
|
||||
|
||||
public static TimeWindowCursorState FromBsonDocument(BsonDocument? document, string startField = "windowStart", string endField = "windowEnd")
|
||||
public static TimeWindowCursorState FromDocumentObject(DocumentObject? document, string startField = "windowStart", string endField = "windowEnd")
|
||||
{
|
||||
if (document is null)
|
||||
{
|
||||
@@ -64,12 +64,12 @@ public sealed record TimeWindowCursorState(DateTimeOffset? LastWindowStart, Date
|
||||
return new TimeWindowCursorState(start, end);
|
||||
}
|
||||
|
||||
private static DateTimeOffset? ReadDateTimeOffset(BsonValue value)
|
||||
private static DateTimeOffset? ReadDateTimeOffset(DocumentValue value)
|
||||
{
|
||||
return value.BsonType switch
|
||||
return value.DocumentType switch
|
||||
{
|
||||
BsonType.DateTime => new DateTimeOffset(value.ToUniversalTime(), TimeSpan.Zero),
|
||||
BsonType.String when DateTimeOffset.TryParse(value.AsString, out var parsed) => parsed.ToUniversalTime(),
|
||||
DocumentType.DateTime => new DateTimeOffset(value.ToUniversalTime(), TimeSpan.Zero),
|
||||
DocumentType.String when DateTimeOffset.TryParse(value.AsString, out var parsed) => parsed.ToUniversalTime(),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Concelier.Bson;
|
||||
using StellaOps.Concelier.Documents;
|
||||
using MongoContracts = StellaOps.Concelier.Storage;
|
||||
using StorageContracts = StellaOps.Concelier.Storage.Contracts;
|
||||
using StellaOps.Concelier.Connector.Common.Http;
|
||||
@@ -37,7 +37,7 @@ public sealed class SourceFetchService
|
||||
private readonly ILogger<SourceFetchService> _logger;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
private readonly IOptionsMonitor<SourceHttpClientOptions> _httpClientOptions;
|
||||
private readonly IOptions<MongoContracts.MongoStorageOptions> _storageOptions;
|
||||
private readonly IOptions<MongoContracts.StorageOptions> _storageOptions;
|
||||
private readonly IJitterSource _jitterSource;
|
||||
private readonly IAdvisoryRawWriteGuard _guard;
|
||||
private readonly IAdvisoryLinksetMapper _linksetMapper;
|
||||
@@ -56,7 +56,7 @@ public sealed class SourceFetchService
|
||||
ICryptoHash hash,
|
||||
TimeProvider? timeProvider = null,
|
||||
IOptionsMonitor<SourceHttpClientOptions>? httpClientOptions = null,
|
||||
IOptions<MongoContracts.MongoStorageOptions>? storageOptions = null)
|
||||
IOptions<MongoContracts.StorageOptions>? storageOptions = null)
|
||||
{
|
||||
_httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));
|
||||
_rawDocumentStorage = rawDocumentStorage ?? throw new ArgumentNullException(nameof(rawDocumentStorage));
|
||||
@@ -85,7 +85,7 @@ public sealed class SourceFetchService
|
||||
ICryptoHash hash,
|
||||
TimeProvider? timeProvider = null,
|
||||
IOptionsMonitor<SourceHttpClientOptions>? httpClientOptions = null,
|
||||
IOptions<MongoContracts.MongoStorageOptions>? storageOptions = null)
|
||||
IOptions<MongoContracts.StorageOptions>? storageOptions = null)
|
||||
: this(
|
||||
httpClientFactory,
|
||||
rawDocumentStorage,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using StellaOps.Concelier.Bson;
|
||||
using StellaOps.Concelier.Documents;
|
||||
using StellaOps.Concelier.Connector.Common.Fetch;
|
||||
using MongoContracts = StellaOps.Concelier.Storage;
|
||||
using StellaOps.Cryptography;
|
||||
@@ -62,7 +62,7 @@ public sealed class SourceStateSeedProcessor
|
||||
}
|
||||
|
||||
var state = await _stateRepository.TryGetAsync(specification.Source, cancellationToken).ConfigureAwait(false);
|
||||
var cursor = state?.Cursor ?? new BsonDocument();
|
||||
var cursor = state?.Cursor ?? new DocumentObject();
|
||||
|
||||
var newlyPendingDocuments = MergeGuidArray(cursor, "pendingDocuments", pendingDocumentIds);
|
||||
var newlyPendingMappings = MergeGuidArray(cursor, "pendingMappings", pendingMappingIds);
|
||||
@@ -216,14 +216,14 @@ public sealed class SourceStateSeedProcessor
|
||||
return new Dictionary<string, string>(values, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static IReadOnlyCollection<Guid> MergeGuidArray(BsonDocument cursor, string field, IReadOnlyCollection<Guid> additions)
|
||||
private static IReadOnlyCollection<Guid> MergeGuidArray(DocumentObject cursor, string field, IReadOnlyCollection<Guid> additions)
|
||||
{
|
||||
if (additions.Count == 0)
|
||||
{
|
||||
return Array.Empty<Guid>();
|
||||
}
|
||||
|
||||
var existing = cursor.TryGetValue(field, out var value) && value is BsonArray existingArray
|
||||
var existing = cursor.TryGetValue(field, out var value) && value is DocumentArray existingArray
|
||||
? existingArray.Select(AsGuid).Where(static g => g != Guid.Empty).ToHashSet()
|
||||
: new HashSet<Guid>();
|
||||
|
||||
@@ -243,7 +243,7 @@ public sealed class SourceStateSeedProcessor
|
||||
|
||||
if (existing.Count > 0)
|
||||
{
|
||||
cursor[field] = new BsonArray(existing
|
||||
cursor[field] = new DocumentArray(existing
|
||||
.Select(static g => g.ToString("D"))
|
||||
.OrderBy(static s => s, StringComparer.OrdinalIgnoreCase));
|
||||
}
|
||||
@@ -251,14 +251,14 @@ public sealed class SourceStateSeedProcessor
|
||||
return newlyAdded.AsReadOnly();
|
||||
}
|
||||
|
||||
private static IReadOnlyCollection<string> MergeStringArray(BsonDocument cursor, string field, IReadOnlyCollection<string> additions)
|
||||
private static IReadOnlyCollection<string> MergeStringArray(DocumentObject cursor, string field, IReadOnlyCollection<string> additions)
|
||||
{
|
||||
if (additions.Count == 0)
|
||||
{
|
||||
return Array.Empty<string>();
|
||||
}
|
||||
|
||||
var existing = cursor.TryGetValue(field, out var value) && value is BsonArray existingArray
|
||||
var existing = cursor.TryGetValue(field, out var value) && value is DocumentArray existingArray
|
||||
? existingArray.Select(static v => v?.AsString ?? string.Empty)
|
||||
.Where(static s => !string.IsNullOrWhiteSpace(s))
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase)
|
||||
@@ -281,14 +281,14 @@ public sealed class SourceStateSeedProcessor
|
||||
|
||||
if (existing.Count > 0)
|
||||
{
|
||||
cursor[field] = new BsonArray(existing
|
||||
cursor[field] = new DocumentArray(existing
|
||||
.OrderBy(static s => s, StringComparer.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
return newlyAdded.AsReadOnly();
|
||||
}
|
||||
|
||||
private static Guid AsGuid(BsonValue value)
|
||||
private static Guid AsGuid(DocumentValue value)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
|
||||
@@ -8,7 +8,7 @@ using System.Text.Json;
|
||||
using System.Security.Cryptography;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Concelier.Bson;
|
||||
using StellaOps.Concelier.Documents;
|
||||
using StellaOps.Concelier.Models;
|
||||
using StellaOps.Concelier.Normalization.Text;
|
||||
using StellaOps.Concelier.Connector.Common;
|
||||
@@ -377,7 +377,7 @@ public sealed class CveConnector : IFeedConnector
|
||||
continue;
|
||||
}
|
||||
|
||||
var payload = BsonDocument.Parse(JsonSerializer.Serialize(dto, SerializerOptions));
|
||||
var payload = DocumentObject.Parse(JsonSerializer.Serialize(dto, SerializerOptions));
|
||||
var dtoRecord = new DtoRecord(
|
||||
Guid.NewGuid(),
|
||||
document.Id,
|
||||
@@ -576,7 +576,7 @@ public sealed class CveConnector : IFeedConnector
|
||||
|
||||
private async Task UpdateCursorAsync(CveCursor cursor, CancellationToken cancellationToken)
|
||||
{
|
||||
await _stateRepository.UpdateCursorAsync(SourceName, cursor.ToBsonDocument(), _timeProvider.GetUtcNow(), cancellationToken).ConfigureAwait(false);
|
||||
await _stateRepository.UpdateCursorAsync(SourceName, cursor.ToDocumentObject(), _timeProvider.GetUtcNow(), cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static Uri BuildListRequestUri(DateTimeOffset since, DateTimeOffset until, int page, int pageSize)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using StellaOps.Concelier.Bson;
|
||||
using StellaOps.Concelier.Documents;
|
||||
|
||||
namespace StellaOps.Concelier.Connector.Cve.Internal;
|
||||
|
||||
@@ -22,13 +22,13 @@ internal sealed record CveCursor(
|
||||
PendingDocuments: EmptyGuidList,
|
||||
PendingMappings: EmptyGuidList);
|
||||
|
||||
public BsonDocument ToBsonDocument()
|
||||
public DocumentObject ToDocumentObject()
|
||||
{
|
||||
var document = new BsonDocument
|
||||
var document = new DocumentObject
|
||||
{
|
||||
["nextPage"] = NextPage,
|
||||
["pendingDocuments"] = new BsonArray(PendingDocuments.Select(id => id.ToString())),
|
||||
["pendingMappings"] = new BsonArray(PendingMappings.Select(id => id.ToString())),
|
||||
["pendingDocuments"] = new DocumentArray(PendingDocuments.Select(id => id.ToString())),
|
||||
["pendingMappings"] = new DocumentArray(PendingMappings.Select(id => id.ToString())),
|
||||
};
|
||||
|
||||
if (LastModifiedExclusive.HasValue)
|
||||
@@ -49,7 +49,7 @@ internal sealed record CveCursor(
|
||||
return document;
|
||||
}
|
||||
|
||||
public static CveCursor FromBson(BsonDocument? document)
|
||||
public static CveCursor FromBson(DocumentObject? document)
|
||||
{
|
||||
if (document is null || document.ElementCount == 0)
|
||||
{
|
||||
@@ -99,19 +99,19 @@ internal sealed record CveCursor(
|
||||
public CveCursor WithNextPage(int page)
|
||||
=> this with { NextPage = page < 1 ? 1 : page };
|
||||
|
||||
private static DateTimeOffset? ParseDate(BsonValue value)
|
||||
private static DateTimeOffset? ParseDate(DocumentValue value)
|
||||
{
|
||||
return value.BsonType switch
|
||||
return value.DocumentType switch
|
||||
{
|
||||
BsonType.DateTime => DateTime.SpecifyKind(value.ToUniversalTime(), DateTimeKind.Utc),
|
||||
BsonType.String when DateTimeOffset.TryParse(value.AsString, out var parsed) => parsed.ToUniversalTime(),
|
||||
DocumentType.DateTime => DateTime.SpecifyKind(value.ToUniversalTime(), DateTimeKind.Utc),
|
||||
DocumentType.String when DateTimeOffset.TryParse(value.AsString, out var parsed) => parsed.ToUniversalTime(),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
private static IReadOnlyCollection<Guid> ReadGuidArray(BsonDocument document, string field)
|
||||
private static IReadOnlyCollection<Guid> ReadGuidArray(DocumentObject document, string field)
|
||||
{
|
||||
if (!document.TryGetValue(field, out var value) || value is not BsonArray array)
|
||||
if (!document.TryGetValue(field, out var value) || value is not DocumentArray array)
|
||||
{
|
||||
return EmptyGuidList;
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Concelier.Bson;
|
||||
using StellaOps.Concelier.Bson.IO;
|
||||
using StellaOps.Concelier.Documents;
|
||||
using StellaOps.Concelier.Documents.IO;
|
||||
using StellaOps.Concelier.Models;
|
||||
using StellaOps.Concelier.Connector.Common;
|
||||
using StellaOps.Concelier.Connector.Common.Fetch;
|
||||
@@ -443,7 +443,7 @@ public sealed class DebianConnector : IFeedConnector
|
||||
|
||||
private async Task UpdateCursorAsync(DebianCursor cursor, CancellationToken cancellationToken)
|
||||
{
|
||||
var document = cursor.ToBsonDocument();
|
||||
var document = cursor.ToDocumentObject();
|
||||
await _stateRepository.UpdateCursorAsync(SourceName, document, _timeProvider.GetUtcNow(), cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -508,12 +508,12 @@ public sealed class DebianConnector : IFeedConnector
|
||||
cveList);
|
||||
}
|
||||
|
||||
private static BsonDocument ToBson(DebianAdvisoryDto dto)
|
||||
private static DocumentObject ToBson(DebianAdvisoryDto dto)
|
||||
{
|
||||
var packages = new BsonArray();
|
||||
var packages = new DocumentArray();
|
||||
foreach (var package in dto.Packages)
|
||||
{
|
||||
var packageDoc = new BsonDocument
|
||||
var packageDoc = new DocumentObject
|
||||
{
|
||||
["package"] = package.Package,
|
||||
["release"] = package.Release,
|
||||
@@ -543,9 +543,9 @@ public sealed class DebianConnector : IFeedConnector
|
||||
packages.Add(packageDoc);
|
||||
}
|
||||
|
||||
var references = new BsonArray(dto.References.Select(reference =>
|
||||
var references = new DocumentArray(dto.References.Select(reference =>
|
||||
{
|
||||
var doc = new BsonDocument
|
||||
var doc = new DocumentObject
|
||||
{
|
||||
["url"] = reference.Url
|
||||
};
|
||||
@@ -563,27 +563,27 @@ public sealed class DebianConnector : IFeedConnector
|
||||
return doc;
|
||||
}));
|
||||
|
||||
return new BsonDocument
|
||||
return new DocumentObject
|
||||
{
|
||||
["advisoryId"] = dto.AdvisoryId,
|
||||
["sourcePackage"] = dto.SourcePackage,
|
||||
["title"] = dto.Title,
|
||||
["description"] = dto.Description ?? string.Empty,
|
||||
["cves"] = new BsonArray(dto.CveIds),
|
||||
["cves"] = new DocumentArray(dto.CveIds),
|
||||
["packages"] = packages,
|
||||
["references"] = references,
|
||||
};
|
||||
}
|
||||
|
||||
private static DebianAdvisoryDto FromBson(BsonDocument document)
|
||||
private static DebianAdvisoryDto FromBson(DocumentObject document)
|
||||
{
|
||||
var advisoryId = document.GetValue("advisoryId", "").AsString;
|
||||
var sourcePackage = document.GetValue("sourcePackage", advisoryId).AsString;
|
||||
var title = document.GetValue("title", advisoryId).AsString;
|
||||
var description = document.TryGetValue("description", out var desc) ? desc.AsString : null;
|
||||
|
||||
var cves = document.TryGetValue("cves", out var cveArray) && cveArray is BsonArray cvesBson
|
||||
? cvesBson.OfType<BsonValue>()
|
||||
var cves = document.TryGetValue("cves", out var cveArray) && cveArray is DocumentArray cvesBson
|
||||
? cvesBson.OfType<DocumentValue>()
|
||||
.Select(static value => value.ToString())
|
||||
.Where(static s => !string.IsNullOrWhiteSpace(s))
|
||||
.Select(static s => s!)
|
||||
@@ -591,9 +591,9 @@ public sealed class DebianConnector : IFeedConnector
|
||||
: Array.Empty<string>();
|
||||
|
||||
var packages = new List<DebianPackageStateDto>();
|
||||
if (document.TryGetValue("packages", out var packageArray) && packageArray is BsonArray packagesBson)
|
||||
if (document.TryGetValue("packages", out var packageArray) && packageArray is DocumentArray packagesBson)
|
||||
{
|
||||
foreach (var element in packagesBson.OfType<BsonDocument>())
|
||||
foreach (var element in packagesBson.OfType<DocumentObject>())
|
||||
{
|
||||
packages.Add(new DebianPackageStateDto(
|
||||
element.GetValue("package", sourcePackage).AsString,
|
||||
@@ -603,10 +603,10 @@ public sealed class DebianConnector : IFeedConnector
|
||||
element.TryGetValue("fixed", out var fixedValue) ? fixedValue.AsString : null,
|
||||
element.TryGetValue("last", out var lastValue) ? lastValue.AsString : null,
|
||||
element.TryGetValue("published", out var publishedValue)
|
||||
? publishedValue.BsonType switch
|
||||
? publishedValue.DocumentType switch
|
||||
{
|
||||
BsonType.DateTime => DateTime.SpecifyKind(publishedValue.ToUniversalTime(), DateTimeKind.Utc),
|
||||
BsonType.String when DateTimeOffset.TryParse(publishedValue.AsString, out var parsed) => parsed.ToUniversalTime(),
|
||||
DocumentType.DateTime => DateTime.SpecifyKind(publishedValue.ToUniversalTime(), DateTimeKind.Utc),
|
||||
DocumentType.String when DateTimeOffset.TryParse(publishedValue.AsString, out var parsed) => parsed.ToUniversalTime(),
|
||||
_ => (DateTimeOffset?)null,
|
||||
}
|
||||
: null));
|
||||
@@ -614,9 +614,9 @@ public sealed class DebianConnector : IFeedConnector
|
||||
}
|
||||
|
||||
var references = new List<DebianReferenceDto>();
|
||||
if (document.TryGetValue("references", out var referenceArray) && referenceArray is BsonArray refBson)
|
||||
if (document.TryGetValue("references", out var referenceArray) && referenceArray is DocumentArray refBson)
|
||||
{
|
||||
foreach (var element in refBson.OfType<BsonDocument>())
|
||||
foreach (var element in refBson.OfType<DocumentObject>())
|
||||
{
|
||||
references.Add(new DebianReferenceDto(
|
||||
element.GetValue("url", "").AsString,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using StellaOps.Concelier.Bson;
|
||||
using StellaOps.Concelier.Documents;
|
||||
|
||||
namespace StellaOps.Concelier.Connector.Distro.Debian.Internal;
|
||||
|
||||
@@ -19,7 +19,7 @@ internal sealed record DebianCursor(
|
||||
|
||||
public static DebianCursor Empty { get; } = new(null, EmptyIds, EmptyGuidList, EmptyGuidList, EmptyCache);
|
||||
|
||||
public static DebianCursor FromBson(BsonDocument? document)
|
||||
public static DebianCursor FromBson(DocumentObject? document)
|
||||
{
|
||||
if (document is null || document.ElementCount == 0)
|
||||
{
|
||||
@@ -29,10 +29,10 @@ internal sealed record DebianCursor(
|
||||
DateTimeOffset? lastPublished = null;
|
||||
if (document.TryGetValue("lastPublished", out var lastValue))
|
||||
{
|
||||
lastPublished = lastValue.BsonType switch
|
||||
lastPublished = lastValue.DocumentType switch
|
||||
{
|
||||
BsonType.String when DateTimeOffset.TryParse(lastValue.AsString, out var parsed) => parsed.ToUniversalTime(),
|
||||
BsonType.DateTime => DateTime.SpecifyKind(lastValue.ToUniversalTime(), DateTimeKind.Utc),
|
||||
DocumentType.String when DateTimeOffset.TryParse(lastValue.AsString, out var parsed) => parsed.ToUniversalTime(),
|
||||
DocumentType.DateTime => DateTime.SpecifyKind(lastValue.ToUniversalTime(), DateTimeKind.Utc),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
@@ -45,12 +45,12 @@ internal sealed record DebianCursor(
|
||||
return new DebianCursor(lastPublished, processed, pendingDocuments, pendingMappings, cache);
|
||||
}
|
||||
|
||||
public BsonDocument ToBsonDocument()
|
||||
public DocumentObject ToDocumentObject()
|
||||
{
|
||||
var document = new BsonDocument
|
||||
var document = new DocumentObject
|
||||
{
|
||||
["pendingDocuments"] = new BsonArray(PendingDocuments.Select(static id => id.ToString())),
|
||||
["pendingMappings"] = new BsonArray(PendingMappings.Select(static id => id.ToString())),
|
||||
["pendingDocuments"] = new DocumentArray(PendingDocuments.Select(static id => id.ToString())),
|
||||
["pendingMappings"] = new DocumentArray(PendingMappings.Select(static id => id.ToString())),
|
||||
};
|
||||
|
||||
if (LastPublished.HasValue)
|
||||
@@ -60,15 +60,15 @@ internal sealed record DebianCursor(
|
||||
|
||||
if (ProcessedAdvisoryIds.Count > 0)
|
||||
{
|
||||
document["processedIds"] = new BsonArray(ProcessedAdvisoryIds);
|
||||
document["processedIds"] = new DocumentArray(ProcessedAdvisoryIds);
|
||||
}
|
||||
|
||||
if (FetchCache.Count > 0)
|
||||
{
|
||||
var cacheDoc = new BsonDocument();
|
||||
var cacheDoc = new DocumentObject();
|
||||
foreach (var (key, entry) in FetchCache)
|
||||
{
|
||||
cacheDoc[key] = entry.ToBsonDocument();
|
||||
cacheDoc[key] = entry.ToDocumentObject();
|
||||
}
|
||||
|
||||
document["fetchCache"] = cacheDoc;
|
||||
@@ -114,9 +114,9 @@ internal sealed record DebianCursor(
|
||||
return FetchCache.TryGetValue(key, out entry!);
|
||||
}
|
||||
|
||||
private static IReadOnlyCollection<string> ReadStringArray(BsonDocument document, string field)
|
||||
private static IReadOnlyCollection<string> ReadStringArray(DocumentObject document, string field)
|
||||
{
|
||||
if (!document.TryGetValue(field, out var value) || value is not BsonArray array)
|
||||
if (!document.TryGetValue(field, out var value) || value is not DocumentArray array)
|
||||
{
|
||||
return EmptyIds;
|
||||
}
|
||||
@@ -124,7 +124,7 @@ internal sealed record DebianCursor(
|
||||
var list = new List<string>(array.Count);
|
||||
foreach (var element in array)
|
||||
{
|
||||
if (element.BsonType == BsonType.String)
|
||||
if (element.DocumentType == DocumentType.String)
|
||||
{
|
||||
var str = element.AsString.Trim();
|
||||
if (!string.IsNullOrEmpty(str))
|
||||
@@ -137,9 +137,9 @@ internal sealed record DebianCursor(
|
||||
return list;
|
||||
}
|
||||
|
||||
private static IReadOnlyCollection<Guid> ReadGuidArray(BsonDocument document, string field)
|
||||
private static IReadOnlyCollection<Guid> ReadGuidArray(DocumentObject document, string field)
|
||||
{
|
||||
if (!document.TryGetValue(field, out var value) || value is not BsonArray array)
|
||||
if (!document.TryGetValue(field, out var value) || value is not DocumentArray array)
|
||||
{
|
||||
return EmptyGuidList;
|
||||
}
|
||||
@@ -156,9 +156,9 @@ internal sealed record DebianCursor(
|
||||
return list;
|
||||
}
|
||||
|
||||
private static IReadOnlyDictionary<string, DebianFetchCacheEntry> ReadCache(BsonDocument document)
|
||||
private static IReadOnlyDictionary<string, DebianFetchCacheEntry> ReadCache(DocumentObject document)
|
||||
{
|
||||
if (!document.TryGetValue("fetchCache", out var value) || value is not BsonDocument cacheDocument || cacheDocument.ElementCount == 0)
|
||||
if (!document.TryGetValue("fetchCache", out var value) || value is not DocumentObject cacheDocument || cacheDocument.ElementCount == 0)
|
||||
{
|
||||
return EmptyCache;
|
||||
}
|
||||
@@ -166,7 +166,7 @@ internal sealed record DebianCursor(
|
||||
var cache = new Dictionary<string, DebianFetchCacheEntry>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var element in cacheDocument.Elements)
|
||||
{
|
||||
if (element.Value is BsonDocument entry)
|
||||
if (element.Value is DocumentObject entry)
|
||||
{
|
||||
cache[element.Name] = DebianFetchCacheEntry.FromBson(entry);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using StellaOps.Concelier.Bson;
|
||||
using StellaOps.Concelier.Documents;
|
||||
|
||||
namespace StellaOps.Concelier.Connector.Distro.Debian.Internal;
|
||||
|
||||
@@ -10,7 +10,7 @@ internal sealed record DebianFetchCacheEntry(string? ETag, DateTimeOffset? LastM
|
||||
public static DebianFetchCacheEntry FromDocument(StellaOps.Concelier.Storage.DocumentRecord document)
|
||||
=> new(document.Etag, document.LastModified);
|
||||
|
||||
public static DebianFetchCacheEntry FromBson(BsonDocument document)
|
||||
public static DebianFetchCacheEntry FromBson(DocumentObject document)
|
||||
{
|
||||
if (document is null || document.ElementCount == 0)
|
||||
{
|
||||
@@ -20,17 +20,17 @@ internal sealed record DebianFetchCacheEntry(string? ETag, DateTimeOffset? LastM
|
||||
string? etag = null;
|
||||
DateTimeOffset? lastModified = null;
|
||||
|
||||
if (document.TryGetValue("etag", out var etagValue) && etagValue.BsonType == BsonType.String)
|
||||
if (document.TryGetValue("etag", out var etagValue) && etagValue.DocumentType == DocumentType.String)
|
||||
{
|
||||
etag = etagValue.AsString;
|
||||
}
|
||||
|
||||
if (document.TryGetValue("lastModified", out var modifiedValue))
|
||||
{
|
||||
lastModified = modifiedValue.BsonType switch
|
||||
lastModified = modifiedValue.DocumentType switch
|
||||
{
|
||||
BsonType.String when DateTimeOffset.TryParse(modifiedValue.AsString, out var parsed) => parsed.ToUniversalTime(),
|
||||
BsonType.DateTime => DateTime.SpecifyKind(modifiedValue.ToUniversalTime(), DateTimeKind.Utc),
|
||||
DocumentType.String when DateTimeOffset.TryParse(modifiedValue.AsString, out var parsed) => parsed.ToUniversalTime(),
|
||||
DocumentType.DateTime => DateTime.SpecifyKind(modifiedValue.ToUniversalTime(), DateTimeKind.Utc),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
@@ -38,9 +38,9 @@ internal sealed record DebianFetchCacheEntry(string? ETag, DateTimeOffset? LastM
|
||||
return new DebianFetchCacheEntry(etag, lastModified);
|
||||
}
|
||||
|
||||
public BsonDocument ToBsonDocument()
|
||||
public DocumentObject ToDocumentObject()
|
||||
{
|
||||
var document = new BsonDocument();
|
||||
var document = new DocumentObject();
|
||||
if (!string.IsNullOrWhiteSpace(ETag))
|
||||
{
|
||||
document["etag"] = ETag;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using StellaOps.Concelier.Bson;
|
||||
using StellaOps.Concelier.Documents;
|
||||
|
||||
namespace StellaOps.Concelier.Connector.Distro.RedHat.Internal;
|
||||
|
||||
@@ -19,7 +19,7 @@ internal sealed record RedHatCursor(
|
||||
|
||||
public static RedHatCursor Empty { get; } = new(null, EmptyStringList, EmptyGuidList, EmptyGuidList, EmptyCache);
|
||||
|
||||
public static RedHatCursor FromBsonDocument(BsonDocument? document)
|
||||
public static RedHatCursor FromDocumentObject(DocumentObject? document)
|
||||
{
|
||||
if (document is null || document.ElementCount == 0)
|
||||
{
|
||||
@@ -40,22 +40,22 @@ internal sealed record RedHatCursor(
|
||||
return new RedHatCursor(lastReleased, processed, pendingDocuments, pendingMappings, fetchCache);
|
||||
}
|
||||
|
||||
public BsonDocument ToBsonDocument()
|
||||
public DocumentObject ToDocumentObject()
|
||||
{
|
||||
var document = new BsonDocument();
|
||||
var document = new DocumentObject();
|
||||
if (LastReleasedOn.HasValue)
|
||||
{
|
||||
document["lastReleasedOn"] = LastReleasedOn.Value.UtcDateTime;
|
||||
}
|
||||
|
||||
document["processedAdvisories"] = new BsonArray(ProcessedAdvisoryIds);
|
||||
document["pendingDocuments"] = new BsonArray(PendingDocuments.Select(id => id.ToString()));
|
||||
document["pendingMappings"] = new BsonArray(PendingMappings.Select(id => id.ToString()));
|
||||
document["processedAdvisories"] = new DocumentArray(ProcessedAdvisoryIds);
|
||||
document["pendingDocuments"] = new DocumentArray(PendingDocuments.Select(id => id.ToString()));
|
||||
document["pendingMappings"] = new DocumentArray(PendingMappings.Select(id => id.ToString()));
|
||||
|
||||
var cacheArray = new BsonArray();
|
||||
var cacheArray = new DocumentArray();
|
||||
foreach (var (key, metadata) in FetchCache)
|
||||
{
|
||||
var cacheDoc = new BsonDocument
|
||||
var cacheDoc = new DocumentObject
|
||||
{
|
||||
["uri"] = key
|
||||
};
|
||||
@@ -167,9 +167,9 @@ internal sealed record RedHatCursor(
|
||||
return null;
|
||||
}
|
||||
|
||||
private static IReadOnlyCollection<string> ReadStringSet(BsonDocument document, string field)
|
||||
private static IReadOnlyCollection<string> ReadStringSet(DocumentObject document, string field)
|
||||
{
|
||||
if (!document.TryGetValue(field, out var value) || value is not BsonArray array)
|
||||
if (!document.TryGetValue(field, out var value) || value is not DocumentArray array)
|
||||
{
|
||||
return EmptyStringList;
|
||||
}
|
||||
@@ -177,7 +177,7 @@ internal sealed record RedHatCursor(
|
||||
var results = new List<string>(array.Count);
|
||||
foreach (var element in array)
|
||||
{
|
||||
if (element.BsonType == BsonType.String)
|
||||
if (element.DocumentType == DocumentType.String)
|
||||
{
|
||||
var str = element.AsString.Trim();
|
||||
if (!string.IsNullOrWhiteSpace(str))
|
||||
@@ -190,9 +190,9 @@ internal sealed record RedHatCursor(
|
||||
return results;
|
||||
}
|
||||
|
||||
private static IReadOnlyCollection<Guid> ReadGuidSet(BsonDocument document, string field)
|
||||
private static IReadOnlyCollection<Guid> ReadGuidSet(DocumentObject document, string field)
|
||||
{
|
||||
if (!document.TryGetValue(field, out var value) || value is not BsonArray array)
|
||||
if (!document.TryGetValue(field, out var value) || value is not DocumentArray array)
|
||||
{
|
||||
return EmptyGuidList;
|
||||
}
|
||||
@@ -200,7 +200,7 @@ internal sealed record RedHatCursor(
|
||||
var results = new List<Guid>(array.Count);
|
||||
foreach (var element in array)
|
||||
{
|
||||
if (element.BsonType == BsonType.String && Guid.TryParse(element.AsString, out var guid))
|
||||
if (element.DocumentType == DocumentType.String && Guid.TryParse(element.AsString, out var guid))
|
||||
{
|
||||
results.Add(guid);
|
||||
}
|
||||
@@ -209,23 +209,23 @@ internal sealed record RedHatCursor(
|
||||
return results;
|
||||
}
|
||||
|
||||
private static IReadOnlyDictionary<string, RedHatCachedFetchMetadata> ReadFetchCache(BsonDocument document)
|
||||
private static IReadOnlyDictionary<string, RedHatCachedFetchMetadata> ReadFetchCache(DocumentObject document)
|
||||
{
|
||||
if (!document.TryGetValue("fetchCache", out var value) || value is not BsonArray array || array.Count == 0)
|
||||
if (!document.TryGetValue("fetchCache", out var value) || value is not DocumentArray array || array.Count == 0)
|
||||
{
|
||||
return EmptyCache;
|
||||
}
|
||||
|
||||
var results = new Dictionary<string, RedHatCachedFetchMetadata>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var element in array.OfType<BsonDocument>())
|
||||
foreach (var element in array.OfType<DocumentObject>())
|
||||
{
|
||||
if (!element.TryGetValue("uri", out var uriValue) || uriValue.BsonType != BsonType.String)
|
||||
if (!element.TryGetValue("uri", out var uriValue) || uriValue.DocumentType != DocumentType.String)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var uri = uriValue.AsString;
|
||||
var etag = element.TryGetValue("etag", out var etagValue) && etagValue.BsonType == BsonType.String
|
||||
var etag = element.TryGetValue("etag", out var etagValue) && etagValue.DocumentType == DocumentType.String
|
||||
? etagValue.AsString
|
||||
: null;
|
||||
DateTimeOffset? lastModified = null;
|
||||
@@ -240,12 +240,12 @@ internal sealed record RedHatCursor(
|
||||
return results;
|
||||
}
|
||||
|
||||
private static DateTimeOffset? ReadDateTimeOffset(BsonValue value)
|
||||
private static DateTimeOffset? ReadDateTimeOffset(DocumentValue value)
|
||||
{
|
||||
return value.BsonType switch
|
||||
return value.DocumentType switch
|
||||
{
|
||||
BsonType.DateTime => DateTime.SpecifyKind(value.ToUniversalTime(), DateTimeKind.Utc),
|
||||
BsonType.String when DateTimeOffset.TryParse(value.AsString, out var parsed) => parsed.ToUniversalTime(),
|
||||
DocumentType.DateTime => DateTime.SpecifyKind(value.ToUniversalTime(), DateTimeKind.Utc),
|
||||
DocumentType.String when DateTimeOffset.TryParse(value.AsString, out var parsed) => parsed.ToUniversalTime(),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Concelier.Bson;
|
||||
using StellaOps.Concelier.Bson.IO;
|
||||
using StellaOps.Concelier.Documents;
|
||||
using StellaOps.Concelier.Documents.IO;
|
||||
using StellaOps.Concelier.Models;
|
||||
using StellaOps.Concelier.Connector.Common;
|
||||
using StellaOps.Concelier.Connector.Common.Fetch;
|
||||
@@ -312,7 +312,7 @@ public sealed class RedHatConnector : IFeedConnector
|
||||
var rawBytes = await _rawDocumentStorage.DownloadAsync(document.PayloadId.Value, cancellationToken).ConfigureAwait(false);
|
||||
using var jsonDocument = JsonDocument.Parse(rawBytes);
|
||||
var sanitized = JsonSerializer.Serialize(jsonDocument.RootElement);
|
||||
var payload = BsonDocument.Parse(sanitized);
|
||||
var payload = DocumentObject.Parse(sanitized);
|
||||
|
||||
var dtoRecord = new DtoRecord(
|
||||
Guid.NewGuid(),
|
||||
@@ -402,13 +402,13 @@ public sealed class RedHatConnector : IFeedConnector
|
||||
private async Task<RedHatCursor> GetCursorAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var record = await _stateRepository.TryGetAsync(SourceName, cancellationToken).ConfigureAwait(false);
|
||||
return RedHatCursor.FromBsonDocument(record?.Cursor);
|
||||
return RedHatCursor.FromDocumentObject(record?.Cursor);
|
||||
}
|
||||
|
||||
private async Task UpdateCursorAsync(RedHatCursor cursor, CancellationToken cancellationToken)
|
||||
{
|
||||
var completedAt = _timeProvider.GetUtcNow();
|
||||
await _stateRepository.UpdateCursorAsync(SourceName, cursor.ToBsonDocument(), completedAt, cancellationToken).ConfigureAwait(false);
|
||||
await _stateRepository.UpdateCursorAsync(SourceName, cursor.ToDocumentObject(), completedAt, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private Uri BuildSummaryUri(DateTimeOffset after, int page)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using StellaOps.Concelier.Bson;
|
||||
using StellaOps.Concelier.Documents;
|
||||
|
||||
namespace StellaOps.Concelier.Connector.Distro.Suse.Internal;
|
||||
|
||||
@@ -19,7 +19,7 @@ internal sealed record SuseCursor(
|
||||
|
||||
public static SuseCursor Empty { get; } = new(null, EmptyStringList, EmptyGuidList, EmptyGuidList, EmptyCache);
|
||||
|
||||
public static SuseCursor FromBson(BsonDocument? document)
|
||||
public static SuseCursor FromBson(DocumentObject? document)
|
||||
{
|
||||
if (document is null || document.ElementCount == 0)
|
||||
{
|
||||
@@ -29,10 +29,10 @@ internal sealed record SuseCursor(
|
||||
DateTimeOffset? lastModified = null;
|
||||
if (document.TryGetValue("lastModified", out var lastValue))
|
||||
{
|
||||
lastModified = lastValue.BsonType switch
|
||||
lastModified = lastValue.DocumentType switch
|
||||
{
|
||||
BsonType.DateTime => DateTime.SpecifyKind(lastValue.ToUniversalTime(), DateTimeKind.Utc),
|
||||
BsonType.String when DateTimeOffset.TryParse(lastValue.AsString, out var parsed) => parsed.ToUniversalTime(),
|
||||
DocumentType.DateTime => DateTime.SpecifyKind(lastValue.ToUniversalTime(), DateTimeKind.Utc),
|
||||
DocumentType.String when DateTimeOffset.TryParse(lastValue.AsString, out var parsed) => parsed.ToUniversalTime(),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
@@ -45,12 +45,12 @@ internal sealed record SuseCursor(
|
||||
return new SuseCursor(lastModified, processed, pendingDocs, pendingMappings, cache);
|
||||
}
|
||||
|
||||
public BsonDocument ToBsonDocument()
|
||||
public DocumentObject ToDocumentObject()
|
||||
{
|
||||
var document = new BsonDocument
|
||||
var document = new DocumentObject
|
||||
{
|
||||
["pendingDocuments"] = new BsonArray(PendingDocuments.Select(static id => id.ToString())),
|
||||
["pendingMappings"] = new BsonArray(PendingMappings.Select(static id => id.ToString())),
|
||||
["pendingDocuments"] = new DocumentArray(PendingDocuments.Select(static id => id.ToString())),
|
||||
["pendingMappings"] = new DocumentArray(PendingMappings.Select(static id => id.ToString())),
|
||||
};
|
||||
|
||||
if (LastModified.HasValue)
|
||||
@@ -60,15 +60,15 @@ internal sealed record SuseCursor(
|
||||
|
||||
if (ProcessedIds.Count > 0)
|
||||
{
|
||||
document["processedIds"] = new BsonArray(ProcessedIds);
|
||||
document["processedIds"] = new DocumentArray(ProcessedIds);
|
||||
}
|
||||
|
||||
if (FetchCache.Count > 0)
|
||||
{
|
||||
var cacheDocument = new BsonDocument();
|
||||
var cacheDocument = new DocumentObject();
|
||||
foreach (var (key, entry) in FetchCache)
|
||||
{
|
||||
cacheDocument[key] = entry.ToBsonDocument();
|
||||
cacheDocument[key] = entry.ToDocumentObject();
|
||||
}
|
||||
|
||||
document["fetchCache"] = cacheDocument;
|
||||
@@ -114,9 +114,9 @@ internal sealed record SuseCursor(
|
||||
return FetchCache.TryGetValue(key, out entry!);
|
||||
}
|
||||
|
||||
private static IReadOnlyCollection<string> ReadStringSet(BsonDocument document, string field)
|
||||
private static IReadOnlyCollection<string> ReadStringSet(DocumentObject document, string field)
|
||||
{
|
||||
if (!document.TryGetValue(field, out var value) || value is not BsonArray array)
|
||||
if (!document.TryGetValue(field, out var value) || value is not DocumentArray array)
|
||||
{
|
||||
return EmptyStringList;
|
||||
}
|
||||
@@ -124,7 +124,7 @@ internal sealed record SuseCursor(
|
||||
var list = new List<string>(array.Count);
|
||||
foreach (var element in array)
|
||||
{
|
||||
if (element.BsonType == BsonType.String)
|
||||
if (element.DocumentType == DocumentType.String)
|
||||
{
|
||||
var str = element.AsString.Trim();
|
||||
if (!string.IsNullOrWhiteSpace(str))
|
||||
@@ -137,9 +137,9 @@ internal sealed record SuseCursor(
|
||||
return list;
|
||||
}
|
||||
|
||||
private static IReadOnlyCollection<Guid> ReadGuidSet(BsonDocument document, string field)
|
||||
private static IReadOnlyCollection<Guid> ReadGuidSet(DocumentObject document, string field)
|
||||
{
|
||||
if (!document.TryGetValue(field, out var value) || value is not BsonArray array)
|
||||
if (!document.TryGetValue(field, out var value) || value is not DocumentArray array)
|
||||
{
|
||||
return EmptyGuidList;
|
||||
}
|
||||
@@ -156,9 +156,9 @@ internal sealed record SuseCursor(
|
||||
return list;
|
||||
}
|
||||
|
||||
private static IReadOnlyDictionary<string, SuseFetchCacheEntry> ReadCache(BsonDocument document)
|
||||
private static IReadOnlyDictionary<string, SuseFetchCacheEntry> ReadCache(DocumentObject document)
|
||||
{
|
||||
if (!document.TryGetValue("fetchCache", out var value) || value is not BsonDocument cacheDocument || cacheDocument.ElementCount == 0)
|
||||
if (!document.TryGetValue("fetchCache", out var value) || value is not DocumentObject cacheDocument || cacheDocument.ElementCount == 0)
|
||||
{
|
||||
return EmptyCache;
|
||||
}
|
||||
@@ -166,7 +166,7 @@ internal sealed record SuseCursor(
|
||||
var cache = new Dictionary<string, SuseFetchCacheEntry>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var element in cacheDocument.Elements)
|
||||
{
|
||||
if (element.Value is BsonDocument entry)
|
||||
if (element.Value is DocumentObject entry)
|
||||
{
|
||||
cache[element.Name] = SuseFetchCacheEntry.FromBson(entry);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user