fix(analytics): authorize person ids on metric-results - #2082
Conversation
Batch form of the existing visible-set predicate, so a caller can be authorized against many people in one query instead of one traversal each. Semantics are unchanged: the admin role confers no visibility, and an email is visible when any person it resolves to is. Emails resolve by input position because value_id matches case- and accent-insensitively, so the stored spelling cannot be mapped back to the request reliably. Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
Any authenticated caller could read any person's metrics by changing the requested id. Requested ids are now filtered through identity's visible-persons check, and the whole request is refused when any one of them is not visible. Failing to reach identity is a server error, never a denial, so an authorization backend that is down or unconfigured cannot read as "you may not see this". Closes #2035 Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
ADR-0015 relaxes ADR-0012's admin-only rule for one read: answering a caller's own visible set needs no admin, since the caller comes from the gateway JWT and the response is a subset of the ids they supplied. Corrects the analytics design, which described org-unit AccessScope validation as implemented. It is not — the OData filter paths still accept client-supplied person and org-unit ids unchecked, and that gap is now marked as planned rather than current. Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
|
Warning Review limit reached
Next review available in: 34 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (24)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
|
The queries is heavy, can we add caching for the requests? |
I dont think we need it here. At least not in this PR. We can measure and see how it behaves and then decide. |
An explicit COLLATE alone fails when the connection negotiates a charset
other than utf8mb4 ("COLLATION 'utf8mb4_unicode_ci' is not valid for
CHARACTER SET 'utf8mb3'"). CONVERT first, so matching follows the
column's own charset and collation whatever the session carries.
Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
…ts-authz-gate Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar> # Conflicts: # src/backend/services/analytics/src/api/metric_results.rs # src/backend/services/analytics/src/domain/metric_results/validation.rs
Problem
POST /v1/metric-resultsreturned any person's metrics to any authenticated caller in the tenant. The handler read theSecurityContextfor its tenant id only; nothing checked that the requestedentity.idswere the caller or within their reporting scope.Fix
Identity answers the authorization question, analytics enforces it.
POST /v1/visible-persons(identity-resolution): takes emails, returns the subset the caller may see. The existing visible-set predicate — caller, active grants, whole tenant on a wildcard grant,org_chartdescendants — materialised once and joined against the resolved candidates, instead of one traversal per person. A wildcard grant short-circuits the traversal entirely.403. No partial responses — a filtered200is indistinguishable from absent data.No semantic change to who may see whom. The
adminrole still confers no visibility, and an email is visible when any person it resolves to is, matchingPOST /v1/profiles.Failing to reach identity is a server error, never a denial, so an authorization backend that is down or unconfigured cannot read as "you may not see this".
Emails resolve by input position:
persons.value_idcompares case- and accent-insensitively, so the stored spelling cannot be mapped back to the request reliably. A candidate whose stored email merely compares equal — a different person under a loose collation — is dropped, since consumers key metric rows by the email bytes.Verification
Live-MariaDB cases assert the predicate: a caller with no reports still sees themselves, a manager sees a transitive descendant and not an unrelated person, an explicit grant reaches outside the reporting line, a wildcard grant covers the tenant, and an
adminrole holder sees no one extra. Gate cases cover admit, refuse, whole-request refusal, service-subject bypass, unconfigured identity, and an identity that predates the endpoint (404/405→ 5xx, not 403). Route-table cases drive the real operation table.Known gaps, not addressed here
POST /v1/metrics/{id}/queryandPOST /v1/metrics/queriesinterpolate client-suppliedperson_id/org_unit_idfilter values with no visibility check — the same exposure class, reachable without touching this endpoint. Now documented as planned rather than implemented.identityResolution.deploydefaults tofalsewhileidentity_urlis set unconditionally, so a default chart install refuses person metric requests. Needs a chart change and a release note.migrations_and_bootstrap_are_idempotent_against_live_mariadbfails on a pristine migrated database, independent of this branch — no migration or SQL file is touched here.ADR-0015 records the non-admin read, relaxing ADR-0012 for one self-scoped query.
Closes #2035