Skip to content

fix(security-events): scope events by org#1292

Open
Nav-Prak wants to merge 3 commits into
BlazeUp-AI:mainfrom
Nav-Prak:pr/audit2-security-events-org-scope
Open

fix(security-events): scope events by org#1292
Nav-Prak wants to merge 3 commits into
BlazeUp-AI:mainfrom
Nav-Prak:pr/audit2-security-events-org-scope

Conversation

@Nav-Prak
Copy link
Copy Markdown
Contributor

Purpose / Description

Scope security-event reads to the caller's organization so a tenant admin cannot list another organization's security events.

This is part of the AUDIT2 tenant-scoping work and is split out from the closed broader tenant-scope PR #1184 to keep the security-events change independently reviewable.

Fixes

Approach

This change stamps security events with an org_id and filters the admin security-events query by the current admin's org.

  • Resolves the actor's organization when emitting security events.
  • Adds org filtering to the admin get_security_events query.
  • Adds an existing-deployment ClickHouse migration for security_events.org_id and its bloom-filter index.
  • Preserves org-less/global behavior when an event cannot be attributed to a known user.
  • Attributes wrong-password failures for existing users to that user so tenant admins can see failed login attempts for their own users.
  • Keeps unknown-identifier login failures orgless/global.

How Has This Been Tested?

Targeted security-events tenant-scope tests:

cd observal-server && uv run --with pytest --with pytest-asyncio --with pyyaml \
  --with typer --with rich --with hypothesis --with pyarrow \
  pytest ../tests/test_tenant_scope_security_events.py -q

Result: security-events tenant-scope tests passed, including schema migration coverage and login-failure attribution for both /login and /token.

Lint/format:

uv run --with ruff==0.15.10 ruff check \
  observal-server/services/security_events.py \
  observal-server/api/routes/admin/org.py \
  observal-server/api/routes/auth.py \
  observal-server/services/clickhouse/schema.py \
  tests/test_tenant_scope_security_events.py

uv run --with ruff==0.15.10 ruff format --check \
  observal-server/services/security_events.py \
  observal-server/api/routes/admin/org.py \
  observal-server/api/routes/auth.py \
  observal-server/services/clickhouse/schema.py \
  tests/test_tenant_scope_security_events.py

Also re-ran adjacent auth/security and security-events suites as part of split verification; no regressions were found.

Checklist

Please, go through these checks before submitting the PR.

  • You have a descriptive commit message with a short title (first line, max 50 chars).
  • You have commented your code, particularly in hard-to-understand areas
  • You have performed a self-review of your own code
  • UI changes: include screenshots of all affected screens (in particular showing any new or changed strings)

Nav-Prak added 2 commits May 30, 2026 12:54
Stamp each security event with the actor's organization on emit (resolved
from the database and cached per actor) and constrain the admin security-
event query to the caller's org, so a tenant admin cannot read another
organization's security events.

- Add _resolve_actor_org_id and apply it in emit_security_event.
- Add an org_id filter to the admin get_security_events query.
- Backfill security_events.org_id on existing deployments via ALTER TABLE
  ADD COLUMN/INDEX IF NOT EXISTS. CREATE TABLE IF NOT EXISTS is a no-op
  once the table exists, so without this an upgraded deployment would lack
  the column and fail on org-scoped insert/query (mirrors audit_log).
- Add tenant-scope regression tests for emit, resolver caching, the
  org-scoped admin query, and the schema migration.

Part of AUDIT2 tenant-scoping, split out of BlazeUp-AI#1184.
LOGIN_FAILURE was emitted with actor_email only, so emit_security_event
could not resolve an org for it. With the security-events query now scoped
by org, a real tenant user's wrong-password attempts would be stored with
org_id="" and stay invisible to their own tenant admins.

The /login and /token handlers now attribute a wrong-password failure for
an existing user to that user (actor_id/email/role) so it resolves to their
org; unknown-identifier attempts have no org and remain global.

Adds parametrized regression tests for both endpoints covering the
existing-user (attributed) and unknown-user (orgless) cases.

Part of AUDIT2 tenant-scoping, split out of BlazeUp-AI#1184.
@github-actions github-actions Bot added new contributor Pull request from a first-time contributor server Pull request touches server code tests Pull request adds or modifies tests labels May 30, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 1, 2026

Codecov Report

❌ Patch coverage is 89.28571% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
observal-server/services/security_events.py 88.00% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

new contributor Pull request from a first-time contributor server Pull request touches server code tests Pull request adds or modifies tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant