Skip to content

fix(identity): login-bootstrap resolves by IdP source_type + external id, not email - #1989

Merged
mozhaev-dev merged 2 commits into
mainfrom
feat/identity-resolve-by-external-id
Aug 4, 2026
Merged

fix(identity): login-bootstrap resolves by IdP source_type + external id, not email#1989
mozhaev-dev merged 2 commits into
mainfrom
feat/identity-resolve-by-external-id

Conversation

@mozhaev-dev

@mozhaev-dev mozhaev-dev commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Replaces the internal login-bootstrap lookup with two SEPARATE, non-overlapping service-only contracts (no shared value_type dispatch, so a login that lacks its external id can never silently fall through to email):
    • GET /internal/persons/by-external-id?source_type=...&external_id=... — login bootstrap ONLY, scoped to the configured IdP's source_type (e.g. ms-entra) + its source-native external user id (Entra: the oid claim).
    • GET /internal/persons/by-email-override?email=... — the authenticator's admin __override (view-as, Admin 'view as another user' override (?__override=<email>) no longer works #1941) lookup ONLY.
  • authenticator's IdpIdentity now carries an explicit ResolveTarget::ExternalId(String) | Email(String) enum instead of inferring the resolution mode from field emptiness, and fails closed (login refused) if the configured idp.external_id_claim is absent from the id_token.
  • The authenticator is now unconditionally wired to identity-resolution (Rust) for this lookup — the .NET identity twin was intentionally NOT given an equivalent endpoint (out of scope by explicit direction; .NET identity service is untouched by this PR). Wired through docker-compose, the Helm chart (new insight.validate check + identityResolution.deploy default flipped to true), and the gitops compose-app-secrets.sh script (separate AUTHENTICATOR_IDENTITY_URL + required idp.source_type/idp.external_id_claim).
  • Fixes Keycloak-mode dev/demo seeding: the whole 25-person roster can log in now (not just the dev lead), since gen-realm.py pins every realm user's sub to their own roster uuid.
  • deploy/seed/identity.py's login-id seed is idempotent via an explicit existence check (not INSERT IGNORE — migration 004 put created_at in persons' unique key, so re-runs no longer dedupe on their own).

Part of #1960 (see the issue's "Update (post-implementation)" section for the final-scope writeup, including why the email lookup is intentionally kept for __override).

Test plan

  • cargo fmt --all -- --check (backend workspace) — clean
  • cargo test -p authenticator -p identity-resolution — 54 + 58 unit tests pass
  • cargo clippy -p authenticator -p identity-resolution --all-targets -- -D warnings — clean
  • Full src/backend/services/authenticator/tests/run-e2e.sh (real fakeidp + authenticator + Redis stack) — all scenarios pass, incl. login and __override
  • python3 -m unittest deploy/seed/test_identity.py -v — 2/2 pass (idempotency + Keycloak whole-roster seeding)
  • helm template of the umbrella chart (functional-ci values + local overlay values.yaml.template) — renders cleanly, identity_url/idp.source_type/idp.external_id_claim verified in output
  • docker compose config -q — valid
  • Python e2e contract tests (test_internal.py, test_persons_seed.py) — not run locally (needs the identity-resolution + MariaDB + ClickHouse compose stack)
  • Real cluster helm install/ArgoCD sync — only helm template was run, no live cluster available

Rollout notes (not code changes, just flagging for reviewers)

  • This is a breaking chart upgrade: without identityResolution.deploy: true + authenticator.oidc.sourceType set, the render/gitops-compose fails closed with a clear error; an authenticator upgraded to the new image but still on an old secret (missing idp.source_type) will crash-loop at boot validation.
  • Even with correct config, logins 403 until persons has value_type='id' rows under the right source_type (for Entra: a fresh persons-seed run after the ms-entra connector sync). Rollout order: seed → deploy.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added source-scoped external identity resolution for OIDC login bootstrap.
    • Added configurable OIDC source type and external ID claim, defaulting to sub.
    • Added identity seeding for roster users with safe, repeatable behavior.
    • Separated login identity lookup from administrator email-based overrides.
  • Bug Fixes

    • Improved authenticator identity resolution across supported identity providers.
    • Added validation to prevent deployments with incomplete identity-resolution configuration.
  • Documentation

    • Updated Helm deployment guidance, configuration examples, and identity-resolution endpoint documentation.

@mozhaev-dev
mozhaev-dev requested a review from a team as a code owner July 28, 2026 07:57
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Regenerate the connectors-ddl snapshot

This PR changes src/ingestion/**. If your change affects any
bronze / silver / gold schema, regenerate the committed DDL snapshot
and include it in this PR.

Prerequisites (details: src/ingestion/scripts/bootstrap-db/README.md):

  • docker + a fresh throwaway ClickHouse 25.7.5 (README "Local ClickHouse for testing")
  • .env from .env.bootstrap.example pointing at it; use the host LAN IP,
    reachable from both the host and connector containers
    (host.docker.internal does not resolve on the macOS host itself)
  • python3.12 or python3.11 on PATH (pinned dbt venv)
  • HubSpot + Salesforce credentials in .env — their discover calls the
    live APIs; without them, apply ../connectors-ddl/{hubspot,salesforce}.sql
    (relative to bootstrap-db/) to seed their bronze, then run the dbt step
cd src/ingestion/scripts/bootstrap-db
set -a; source pins.env; source .env; set +a
./bootstrap-db.sh connectors-config.yaml   # fresh ClickHouse 25.7.5
./dump-ddl.sh                              # writes scripts/connectors-ddl/*.sql

Commit the resulting scripts/connectors-ddl/*.sql diff. If nothing
changed, no snapshot update is needed. (Regeneration is manual for now.)

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds source-scoped external-ID resolution for authenticator login bootstrap. It separates login resolution from email-based admin overrides, requires identity-resolution deployment and OIDC mapping configuration, updates deployment wiring, and seeds matching login-ID observations.

Changes

Identity-resolution login bootstrap

Layer / File(s) Summary
Helm validation and configuration
charts/insight/*, deploy/HELM_DEPLOY.md, src/backend/services/identity-resolution/helm/tests/*
Helm validation now requires identity-resolution deployment and authenticator.oidc.sourceType. Values, Secrets, deployment guidance, and contract tests define sourceType and externalIdClaim.
Deployment configuration and Secret composition
deploy/gitops/environments/*, deploy/gitops/scripts/compose-app-secrets.sh
GitOps values configure IdP mappings. Secret composition requires identity-resolution, uses its URL for the authenticator, and emits source and claim settings.
Local compose and seed environment wiring
dev-compose.sh, docker-compose.yml
Development startup propagates AUTH_MODE and IdP source type to the authenticator and seed container.
Authenticator identity resolution logic
src/backend/services/authenticator/config/*, src/backend/services/authenticator/src/*
Authenticator configuration validates source and claim fields. OIDC tokens produce explicit external-ID targets, while admin overrides use email targets. Resolver requests use separate internal endpoints.
Authenticator stub and end-to-end harness
src/backend/services/authenticator/tests/*
The identity stub supports external-ID and email-override query routes. End-to-end probes and authenticator instances configure the test source type.
Identity-resolution routes and repository
src/backend/services/identity-resolution/src/*, src/backend/services/identity-resolution/README.md
Identity-resolution exposes separate service-only lookup routes. The repository resolves the latest source-scoped id observation across tenants.
Login-ID seed data and tests
deploy/seed/*, src/ingestion/tests/e2e/lib/identity_seed.py
Seed scripts create idempotent external-login observations for fakeidp and Keycloak modes. Unit tests cover roster scope and reruns. ALICE_ACCOUNT_ID names the fixture value.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant OIDCProvider
  participant Authenticator
  participant IdentityResolution
  participant PersonsRepository
  OIDCProvider->>Authenticator: validated token with external_id_claim
  Authenticator->>IdentityResolution: source_type and external_id lookup
  IdentityResolution->>PersonsRepository: query latest id observation
  PersonsRepository-->>IdentityResolution: person ID or no match
  IdentityResolution-->>Authenticator: resolved person response
Loading

Possibly related PRs

Suggested reviewers: mitasovr

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: login-bootstrap now resolves identities by IdP source type and external ID instead of email.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/identity-resolve-by-external-id

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread charts/insight/templates/secrets.yaml
Comment thread src/backend/services/identity-resolution/src/api/handlers.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
charts/insight/values.yaml (1)

491-510: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update the runbook’s remaining .NET Identity requirements.

This makes .NET identity optional and identityResolution mandatory, but deploy/HELM_DEPLOY.md still instructs users to set identity.deploy: true, expects an Identity pod/config Secret during verification, and repeats that override in its appendix. Following the runbook now deploys an unnecessary legacy service and omits verification of the required resolver.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@charts/insight/values.yaml` around lines 491 - 510, Update
deploy/HELM_DEPLOY.md to remove the remaining .NET Identity requirements: stop
instructing users to set identity.deploy: true, remove expectations for the
legacy Identity pod and config Secret during verification, and delete the
repeated override in the appendix. Replace those checks with verification that
identityResolution is deployed and available.
src/backend/services/authenticator/src/identity.rs (1)

160-190: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Avoid embedding __override parameters in the error message.

resolve_query interpolates {query:?} in its failure path, so overriding by email can put that raw query into the anyhow error. internal_problem() logs the full error chain (error = ?err), while resolve_override() returns that same chain via person_resolution, so the override target email can reach logs and the internal-error response.

🔒 Proposed fix: avoid raw query params in this error
         anyhow::ensure!(
             resp.status().is_success(),
-            "Identity returned {} for {path}?{query:?}",
+            "Identity returned {} for {path}",
             resp.status()
         );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/backend/services/authenticator/src/identity.rs` around lines 160 - 190,
Update the non-success error handling in resolve_query to stop interpolating the
raw query parameters into the anyhow error; retain the response status and path
context without exposing query values, including __override targets. Leave
successful responses and NOT_FOUND handling unchanged.
🧹 Nitpick comments (2)
src/backend/services/authenticator/src/oidc.rs (1)

449-461: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Minor: sub isn't filtered for emptiness like the configured-claim branch.

For consistency with the non-default branch (which filters empty values), consider filtering the sub case too, so a malformed IdP that omits/empties sub gets the same clear "claim missing" error instead of a generic downstream 400.

♻️ Proposed tweak
 fn extract_external_id(raw_id_token: &str, external_id_claim: &str, sub: &str) -> Option<String> {
     if external_id_claim == "sub" {
-        return Some(sub.to_owned());
+        return Some(sub.to_owned()).filter(|v| !v.is_empty());
     }
     payload_string(raw_id_token, external_id_claim).filter(|v| !v.is_empty())
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/backend/services/authenticator/src/oidc.rs` around lines 449 - 461, The
sub branch in extract_external_id currently accepts an empty value unlike the
configured-claim branch. Filter sub for emptiness as well, returning None when
it is empty while preserving the existing Some result for non-empty sub values.
src/backend/services/identity-resolution/src/api/handlers.rs (1)

149-153: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Missing vs. empty query params return inconsistent error shapes.

source_type/external_id/email are declared as required (non-Option) String fields. When a param is entirely absent, axum's Query extractor rejects the request itself with a plain-text 400 body ("Failed to deserialize query string: missing field ..."), bypassing the handler entirely — differing from the JSON CanonicalError body produced by the explicit empty-string checks below (Lines 182-191, 243-247). Both cases are "required field not usably present" from the caller's perspective but yield different response shapes.

Consider making the fields Option<String> and unifying both the missing- and empty-string cases through the same ProfileError::invalid_argument() path for a consistent internal API error contract.

Confirmation of axum's default Query rejection behavior

Per axum's docs and rejection source, a Query extractor failure uses FailedToDeserializeQueryString, which is #[status = BAD_REQUEST] with body "Failed to deserialize query string" (plain text, not the service's JSON error format).

Also applies to: 217-220

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/backend/services/identity-resolution/src/api/handlers.rs` around lines
149 - 153, Update InternalByExternalIdQuery and the corresponding email query
type to use Option<String> for required parameters, then validate missing and
empty values in the handlers through the existing
ProfileError::invalid_argument() path. Preserve the current validation semantics
while ensuring absent and blank source_type, external_id, and email parameters
return the same CanonicalError response.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@deploy/HELM_DEPLOY.md`:
- Around line 248-274: Update the later installation and verification
instructions to reflect identity.deploy being optional and
identityResolution.deploy being required. Remove mandatory .NET identity pod and
Secret expectations, add identity-resolution deployment resources to the
expected resources, and ensure all referenced commands and checks use the
identityResolution topology consistently.

In `@src/backend/services/identity-resolution/src/infra/db/persons_repo.rs`:
- Around line 120-175: Update resolve_person_id_by_source_any_tenant so login
resolution cannot arbitrarily select a person when the (source_type,
external_id) pair collides across tenants. Enforce that login-enabled
source_type labels use a globally unique namespace, or require a globally unique
external-id claim such as Entra oid; remove the unconditional cross-tenant LIMIT
1 behavior unless this invariant is validated for every deployed source_type.

---

Outside diff comments:
In `@charts/insight/values.yaml`:
- Around line 491-510: Update deploy/HELM_DEPLOY.md to remove the remaining .NET
Identity requirements: stop instructing users to set identity.deploy: true,
remove expectations for the legacy Identity pod and config Secret during
verification, and delete the repeated override in the appendix. Replace those
checks with verification that identityResolution is deployed and available.

In `@src/backend/services/authenticator/src/identity.rs`:
- Around line 160-190: Update the non-success error handling in resolve_query to
stop interpolating the raw query parameters into the anyhow error; retain the
response status and path context without exposing query values, including
__override targets. Leave successful responses and NOT_FOUND handling unchanged.

---

Nitpick comments:
In `@src/backend/services/authenticator/src/oidc.rs`:
- Around line 449-461: The sub branch in extract_external_id currently accepts
an empty value unlike the configured-claim branch. Filter sub for emptiness as
well, returning None when it is empty while preserving the existing Some result
for non-empty sub values.

In `@src/backend/services/identity-resolution/src/api/handlers.rs`:
- Around line 149-153: Update InternalByExternalIdQuery and the corresponding
email query type to use Option<String> for required parameters, then validate
missing and empty values in the handlers through the existing
ProfileError::invalid_argument() path. Preserve the current validation semantics
while ensuring absent and blank source_type, external_id, and email parameters
return the same CanonicalError response.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c45433ca-6321-4cd9-943b-e19cc3d2420c

📥 Commits

Reviewing files that changed from the base of the PR and between f322f40 and e8f254dbce634c178d164733bef96b33642ce4bb.

📒 Files selected for processing (29)
  • charts/insight/templates/_helpers.tpl
  • charts/insight/templates/secrets.yaml
  • charts/insight/values.yaml
  • deploy/HELM_DEPLOY.md
  • deploy/gitops/environments/functional-ci/values.yaml
  • deploy/gitops/environments/local/values.yaml.template
  • deploy/gitops/scripts/compose-app-secrets.sh
  • deploy/seed/identity.py
  • deploy/seed/profiles.py
  • deploy/seed/test_identity.py
  • dev-compose.sh
  • docker-compose.yml
  • src/backend/services/authenticator/config/insight.yaml
  • src/backend/services/authenticator/src/api/handlers.rs
  • src/backend/services/authenticator/src/config.rs
  • src/backend/services/authenticator/src/gear.rs
  • src/backend/services/authenticator/src/identity.rs
  • src/backend/services/authenticator/src/oidc.rs
  • src/backend/services/authenticator/tests/identity-stub.py
  • src/backend/services/authenticator/tests/run-e2e.sh
  • src/backend/services/identity-resolution/README.md
  • src/backend/services/identity-resolution/src/api/gate.rs
  • src/backend/services/identity-resolution/src/api/handlers.rs
  • src/backend/services/identity-resolution/src/api/mod.rs
  • src/backend/services/identity-resolution/src/infra/db/persons_repo.rs
  • src/backend/services/identity-resolution/src/main.rs
  • src/ingestion/tests/e2e/identity/test_internal.py
  • src/ingestion/tests/e2e/identity/test_persons_seed.py
  • src/ingestion/tests/e2e/lib/identity_seed.py

Comment thread deploy/HELM_DEPLOY.md
# The identity-resolution source_type this IdP is seeded under (e.g.
# "ms-entra") — required; drives the login-bootstrap person lookup
# (GET /internal/persons/by-external-id?source_type=...&external_id=...).
APP__gears__authenticator__config__idp__source_type: {{ required "authenticator.oidc.sourceType is required" .Values.authenticator.oidc.sourceType | quote }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not forget to put proper values to the gitlab gitops

The login bootstrap resolved the caller by email, which breaks the moment
an IdP account has no email observation and silently ties login to a
mutable attribute. Replace it with two SEPARATE service-only contracts,
so a login that lacks its external id can never fall through to email:

- `GET /internal/persons/by-external-id?source_type=&external_id=` —
  login bootstrap only, scoped to the configured IdP's source_type
  (e.g. `ms-entra`) and its source-native user id (Entra: the `oid`
  claim).
- `GET /internal/persons/by-email-override?email=` — the authenticator's
  admin `__override` (view-as) lookup only.

The authenticator's `IdpIdentity` now carries an explicit
`ResolveTarget::ExternalId | Email` instead of inferring the mode from
field emptiness, and fails closed when the configured
`idp.external_id_claim` is absent from the id_token. It is wired to
identity-resolution unconditionally for this lookup, through
docker-compose, the Helm chart (new `insight.validate` check,
`identityResolution.deploy` defaulted to true) and
`compose-app-secrets.sh`.

Keycloak dev/demo seeding is fixed too: `gen-realm.py` pins every realm
user's `sub` to their own roster uuid, so the whole roster can log in
rather than only the dev lead. `deploy/seed/identity.py` makes its
login-id seed idempotent with an explicit existence check — migration
004 put `created_at` in the `persons` unique key, so `INSERT IGNORE` no
longer dedupes on its own.

Rebased onto main after the metrics person_id cutover. Three things the
cutover changed under this branch:

- `resolve_person_ids_by_emails` is gone from main (visible-persons now
  filters by person id, not email), so it is dropped here; only the
  `__override` doc note on `resolve_person_id_by_email_any_tenant`
  survives.
- main's `return_to_prefix` config test builds its fixture from
  `valid_config()`, which this branch made stricter by requiring
  `idp.source_type` — the helper now sets it, or the fixture builds a
  config `validate()` refuses.
- two e2e helpers still resolved a seeded person through the removed
  `/internal/persons/by-email/{email}` and answered 404, which is what
  the `identity-rust` e2e job was failing on; they now use the override
  route.

Verified on the merged tree: `cargo test -p authenticator` 61/61 and
`-p identity-resolution` 88/88 against a live MariaDB, clippy and fmt
clean, `./e2e.sh test identity/` 149 passed / 3 skipped with the runner
image rebuilt from this branch, helm render-contract 14/14, identity
chart contract 17/17, `deploy/seed/test_identity.py` 2/2,
`docker compose config` valid.

Part of #1960.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Sergei Mozhaev <mozhaev.dev@gmail.com>
@mozhaev-dev
mozhaev-dev force-pushed the feat/identity-resolve-by-external-id branch from 68124d0 to 9c666a4 Compare August 4, 2026 03:47
@mozhaev-dev

Copy link
Copy Markdown
Contributor Author

Rebased onto current main (the branch was 144 commits behind) and force-pushed. CI is green; the branch now needs review only.

History was rewritten — the ten commits (six real ones plus three stale "merge main" commits) are collapsed into a single signed commit. Every commit on the branch was missing Signed-off-by, so DCO failed on all of them, and that is only fixable by rewriting. The previous tip was 68124d0e if anyone wants the old sequence.

Three things main changed under this branch, all of them fixed here:

  1. resolve_person_ids_by_emails no longer exists on main — the metrics person_id cutover replaced the email-keyed visible-persons path. It is dropped; only the __override doc note on resolve_person_id_by_email_any_tenant survives. This was the one textual conflict.
  2. main added a return_to_prefix config test that builds its fixture from valid_config(), while this branch made validate() require idp.source_type. The helper now sets it — otherwise the fixture builds a config validate() refuses. Git merged both sides cleanly; only the test run caught it.
  3. Two e2e helpers still resolved a seeded person through the removed /internal/persons/by-email/{email} and answered 404. This is what the red identity-rust job was failing on before the rebase — it was not a flake. They now use /internal/persons/by-email-override.

Verified on the rebased tree, which closes the two unchecked boxes in the test plan above:

  • cargo test -p authenticator 61/61, -p identity-resolution 88/88 against a live MariaDB; clippy --all-targets --all-features and cargo fmt --all --check clean.
  • ./e2e.sh test identity/149 passed, 3 skipped, with the runner image rebuilt from this branch so the new internal routes are actually the ones under test.
  • helm render-contract 14/14, identity chart contract 17/17, deploy/seed/test_identity.py 2/2, docker compose config valid.

The rollout notes in the description still stand unchanged: this is a breaking chart upgrade, and logins 403 until persons carries value_type='id' rows under the configured source_type, so the seed has to run before the deploy.

@mozhaev-dev
mozhaev-dev enabled auto-merge August 4, 2026 05:10
…ve-by-external-id

# Conflicts:
#	deploy/seed/identity.py
#	src/ingestion/tests/e2e/identity/test_internal.py
#	src/ingestion/tests/e2e/identity/test_persons_seed.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docker-compose.yml (1)

767-767: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove the duplicate AUTH_MODE key.

seed-sample.environment defines AUTH_MODE twice in docker-compose.yml: lines 735 and 767. Keep one assignment so the configuration has one unambiguous value.

Proposed fix
-      AUTH_MODE: "${AUTH_MODE:-fakeidp}"
       AUTHENTICATOR_OIDC_ISSUER: "${AUTHENTICATOR_OIDC_ISSUER:-}"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docker-compose.yml` at line 767, The AUTH_MODE environment variable is
defined twice in the seed-sample.environment configuration section of
docker-compose.yml. Remove the duplicate AUTH_MODE assignment at line 767 to
ensure a single, unambiguous configuration value. Retain the AUTH_MODE
definition that appears earlier in the file to preserve the intended default
behavior.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@deploy/gitops/environments/functional-ci/values.yaml`:
- Around line 63-74: Remove the duplicate identityResolution mapping in the
values configuration, keeping a single deploy: true declaration. Preserve the
explanatory comment by placing it directly above the remaining
identityResolution block.

---

Outside diff comments:
In `@docker-compose.yml`:
- Line 767: The AUTH_MODE environment variable is defined twice in the
seed-sample.environment configuration section of docker-compose.yml. Remove the
duplicate AUTH_MODE assignment at line 767 to ensure a single, unambiguous
configuration value. Retain the AUTH_MODE definition that appears earlier in the
file to preserve the intended default behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 52b59fc0-3abd-49c1-bf2b-077b2c73a72a

📥 Commits

Reviewing files that changed from the base of the PR and between e8f254dbce634c178d164733bef96b33642ce4bb and 6d24615.

📒 Files selected for processing (27)
  • charts/insight/templates/_helpers.tpl
  • charts/insight/templates/secrets.yaml
  • charts/insight/values.yaml
  • deploy/HELM_DEPLOY.md
  • deploy/gitops/environments/functional-ci/values.yaml
  • deploy/gitops/environments/local/values.yaml.template
  • deploy/gitops/scripts/compose-app-secrets.sh
  • deploy/seed/identity.py
  • deploy/seed/profiles.py
  • deploy/seed/test_identity.py
  • dev-compose.sh
  • docker-compose.yml
  • src/backend/services/authenticator/config/insight.yaml
  • src/backend/services/authenticator/src/api/handlers.rs
  • src/backend/services/authenticator/src/config.rs
  • src/backend/services/authenticator/src/gear.rs
  • src/backend/services/authenticator/src/identity.rs
  • src/backend/services/authenticator/src/oidc.rs
  • src/backend/services/authenticator/tests/identity-stub.py
  • src/backend/services/authenticator/tests/run-e2e.sh
  • src/backend/services/identity-resolution/README.md
  • src/backend/services/identity-resolution/helm/tests/test_seed_cronjob_contract.py
  • src/backend/services/identity-resolution/src/api/gate.rs
  • src/backend/services/identity-resolution/src/api/handlers.rs
  • src/backend/services/identity-resolution/src/api/mod.rs
  • src/backend/services/identity-resolution/src/infra/db/persons_repo.rs
  • src/backend/services/identity-resolution/src/main.rs
🚧 Files skipped from review as they are similar to previous changes (20)
  • src/backend/services/identity-resolution/src/api/gate.rs
  • src/backend/services/authenticator/src/gear.rs
  • src/backend/services/identity-resolution/src/api/mod.rs
  • charts/insight/values.yaml
  • src/backend/services/authenticator/src/api/handlers.rs
  • deploy/seed/profiles.py
  • src/backend/services/authenticator/src/config.rs
  • src/backend/services/identity-resolution/src/main.rs
  • deploy/seed/identity.py
  • deploy/HELM_DEPLOY.md
  • src/backend/services/identity-resolution/README.md
  • charts/insight/templates/secrets.yaml
  • src/backend/services/identity-resolution/src/infra/db/persons_repo.rs
  • deploy/seed/test_identity.py
  • src/backend/services/authenticator/src/oidc.rs
  • src/backend/services/authenticator/config/insight.yaml
  • src/backend/services/authenticator/src/identity.rs
  • deploy/gitops/environments/local/values.yaml.template
  • src/backend/services/identity-resolution/src/api/handlers.rs
  • charts/insight/templates/_helpers.tpl

Comment on lines 63 to +74
identityResolution:
deploy: true

# The authenticator's login-bootstrap resolve (GET /internal/persons/
# by-external-id / by-email-override) is Rust-only — the frozen .NET
# `identity` twin above never gained it (constructorfabric/insight#1960) — so
# identity-resolution must ALSO be deployed here, and the authenticator talks
# to it unconditionally (deploy/gitops/scripts/compose-app-secrets.sh enforces
# this at apply time regardless of what `.identityUrl` — analytics' own
# .NET<->Rust switch — is set to).
identityResolution:
deploy: true

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

Remove the duplicated identityResolution mapping.

identityResolution is declared twice: at Line 63 and again at Line 73. The new comment block was inserted before a second copy of the key instead of before the existing one. Strict YAML parsers reject duplicate mapping keys, and yamllint already fails on it. Keep one mapping with the comment above it.

🐛 Proposed fix
-identityResolution:
-  deploy: true
-
 # The authenticator's login-bootstrap resolve (GET /internal/persons/
 # by-external-id / by-email-override) is Rust-only — the frozen .NET
 # `identity` twin above never gained it (constructorfabric/insight#1960) — so
 # identity-resolution must ALSO be deployed here, and the authenticator talks
 # to it unconditionally (deploy/gitops/scripts/compose-app-secrets.sh enforces
 # this at apply time regardless of what `.identityUrl` — analytics' own
 # .NET<->Rust switch — is set to).
 identityResolution:
   deploy: true
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
identityResolution:
deploy: true
# The authenticator's login-bootstrap resolve (GET /internal/persons/
# by-external-id / by-email-override) is Rust-only — the frozen .NET
# `identity` twin above never gained it (constructorfabric/insight#1960) — so
# identity-resolution must ALSO be deployed here, and the authenticator talks
# to it unconditionally (deploy/gitops/scripts/compose-app-secrets.sh enforces
# this at apply time regardless of what `.identityUrl` — analytics' own
# .NET<->Rust switch — is set to).
identityResolution:
deploy: true
# The authenticator's login-bootstrap resolve (GET /internal/persons/
# by-external-id / by-email-override) is Rust-only — the frozen .NET
# `identity` twin above never gained it (constructorfabric/insight#1960) — so
# identity-resolution must ALSO be deployed here, and the authenticator talks
# to it unconditionally (deploy/gitops/scripts/compose-app-secrets.sh enforces
# this at apply time regardless of what `.identityUrl` — analytics' own
# .NET<->Rust switch — is set to).
identityResolution:
deploy: true
🧰 Tools
🪛 YAMLlint (1.37.1)

[error] 73-73: duplication of key "identityResolution" in mapping

(key-duplicates)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@deploy/gitops/environments/functional-ci/values.yaml` around lines 63 - 74,
Remove the duplicate identityResolution mapping in the values configuration,
keeping a single deploy: true declaration. Preserve the explanatory comment by
placing it directly above the remaining identityResolution block.

Source: Linters/SAST tools

@mozhaev-dev
mozhaev-dev added this pull request to the merge queue Aug 4, 2026
Merged via the queue into main with commit 12a959f Aug 4, 2026
46 of 56 checks passed
@mozhaev-dev
mozhaev-dev deleted the feat/identity-resolve-by-external-id branch August 4, 2026 13:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants