Skip to content

Auto-creation groups: test plan + missing env variables in Docker dev file (IFC-2522)#9355

Open
polmichel wants to merge 4 commits into
developfrom
pmi-ifc-2522-docker-compose-test-plan
Open

Auto-creation groups: test plan + missing env variables in Docker dev file (IFC-2522)#9355
polmichel wants to merge 4 commits into
developfrom
pmi-ifc-2522-docker-compose-test-plan

Conversation

@polmichel
Copy link
Copy Markdown
Contributor

@polmichel polmichel commented May 26, 2026

Summary

Test plan related to the Auto-creation user groups feature IFC-2521.
Expose missing environment variables in development-only Docker file.

PRs summary

PR 1: All the feature except front-end and events management

#9302

PR 2: Event management

#9325

PR 3: Internal and external documentation

#9340
Contains external documentation to validate.

PR 4: Front-end

#9365
Contains screenshot of the front-end UI after the development.

Test Plan

Also included as a markdown file in the changes.

Manual Test Plan — Auto-create Account Groups (IFC-2521INFP-556)

Manual / live-instance walkthrough for the full feature as merged into develop:

  • Core MVP (filter, name capture, idempotency, default-group fallback, per-login cap, origin attribute) — squashed under PR #9302 → develop
  • Audit events layer (GroupAutoCreatedEvent, GroupAutoCreateRejectedEvent, GroupAutoCreateCappedEvent + typed GraphQL Events query + group_auto_create filter) — squashed under PR #9325 → develop
  • User-facing docs polish — squashed under PR #9340 → develop

Scenarios A–D restate the PR #9302 walkthrough; new event/origin/docs verifications are appended to each scenario and as scenarios E–H.


Prerequisites

  • Any OIDC IdP that emits a groups claim — Keycloak in Docker is used below.
  • Local Infrahub built from origin/develop.
  • A second browser (or incognito session) for the local admin to query the API/UI while the test user is signed in via SSO.

0 — Bring up Keycloak and configure the backend

The deps, task-manager, and Keycloak run in Docker via dev.deps; the API server, task worker, and frontend run natively.

0a — Write the dev-override (Keycloak + dep ports for native reach)

cat > development/docker-compose.dev-override.yml <<'YAML'
---
services:
  database:
    ports: ["7687:7687", "7474:7474"]
  message-queue:
    ports: ["5672:5672", "15672:15672"]
  cache:
    ports: ["6379:6379"]
  task-manager:
    ports: ["4200:4200"]

  keycloak:
    image: quay.io/keycloak/keycloak:25.0.6
    command: ["start-dev", "--import-realm"]
    environment:
      KEYCLOAK_ADMIN: admin
      KEYCLOAK_ADMIN_PASSWORD: admin
      KC_HOSTNAME: localhost
      KC_HOSTNAME_PORT: 8180
      KC_HOSTNAME_STRICT: "false"
      KC_HTTP_ENABLED: "true"
    ports: ["127.0.0.1:8180:8080"]
    volumes:
      - "keycloak_data:/opt/keycloak/data"
      - "./keycloak/import:/opt/keycloak/data/import:ro"

volumes:
  keycloak_data:
YAML

0b — Drop the realm import

Write development/keycloak/import/realm.json. The realm needs:

  • Groups: ops-admins, ops-readers, data-engineers, noise-group
  • User alice (firstName=Alice, lastName=Admin) in ops-admins, data-engineers, noise-group
  • Client my-oauth2 with a groups mapper (oidc-group-membership-mapper, full.path=false) and redirectUris containing both http://localhost:8000/* and http://localhost:8080/*
mkdir -p development/keycloak/import
cat > development/keycloak/import/realm.json <<'JSON'
{
  "realm": "infrahub-test",
  "enabled": true,
  "sslRequired": "none",
  "groups": [
    {"name": "ops-admins", "path": "/ops-admins"},
    {"name": "ops-readers", "path": "/ops-readers"},
    {"name": "data-engineers", "path": "/data-engineers"},
    {"name": "noise-group", "path": "/noise-group"}
  ],
  "users": [{
    "username": "alice",
    "enabled": true,
    "emailVerified": true,
    "firstName": "Alice",
    "lastName": "Admin",
    "email": "alice@example.com",
    "credentials": [{"type": "password", "value": "alice", "temporary": false}],
    "groups": ["/ops-admins", "/data-engineers", "/noise-group"]
  }],
  "clients": [{
    "clientId": "my-oauth2",
    "enabled": true,
    "protocol": "openid-connect",
    "publicClient": false,
    "secret": "infrahub-dev-secret-not-for-prod",
    "standardFlowEnabled": true,
    "redirectUris": ["http://localhost:8000/*", "http://localhost:8080/*"],
    "webOrigins": ["+"],
    "protocolMappers": [{
      "name": "groups",
      "protocol": "openid-connect",
      "protocolMapper": "oidc-group-membership-mapper",
      "config": {
        "full.path": "false",
        "id.token.claim": "true",
        "access.token.claim": "true",
        "userinfo.token.claim": "true",
        "claim.name": "groups"
      }
    }]
  }]
}
JSON

0c — Export the full backend config in the shell

# Addresses (deps reachable from host because 0a exposes their ports)
export INFRAHUB_DB_ADDRESS=localhost
export INFRAHUB_BROKER_ADDRESS=localhost
export INFRAHUB_CACHE_ADDRESS=localhost
export INFRAHUB_WORKFLOW_ADDRESS=localhost
export INFRAHUB_PUBLIC_URL=http://localhost:8080
export INFRAHUB_API_CORS_ALLOW_ORIGINS='["http://localhost:8080"]'

# Native-run paths (defaults point at /opt/infrahub which needs root on macOS)
mkdir -p /tmp/infrahub-test/storage /tmp/infrahub-test/git
export INFRAHUB_STORAGE_LOCAL_PATH=/tmp/infrahub-test/storage
export INFRAHUB_INTERNAL_ADDRESS=http://localhost:8000
export INFRAHUB_METRICS_PORT=8001
export PREFECT_API_URL=http://localhost:4200/api
export GIT_CONFIG_GLOBAL=/tmp/infrahub-test/.gitconfig

# OIDC provider config
export INFRAHUB_SECURITY_OIDC_PROVIDERS='["provider1"]'
export INFRAHUB_OIDC_PROVIDER1_CLIENT_ID=my-oauth2
export INFRAHUB_OIDC_PROVIDER1_CLIENT_SECRET=infrahub-dev-secret-not-for-prod
export INFRAHUB_OIDC_PROVIDER1_DISCOVERY_URL=http://localhost:8180/realms/infrahub-test/.well-known/openid-configuration
export INFRAHUB_OIDC_PROVIDER1_DISPLAY_LABEL=Keycloak
export INFRAHUB_OIDC_PROVIDER1_USERINFO_METHOD=post

# Per-scenario auto-create config
export INFRAHUB_SECURITY_AUTO_CREATE_GROUPS_FILTER='^(?P<name>(ops|data)-.*)$'
export INFRAHUB_SECURITY_AUTO_CREATE_GROUPS_MAX_PER_LOGIN=5

0d — Bring the deps up

uv run invoke dev.deps        # Neo4j + message bus + cache + task-manager + Keycloak

0e — Start the API server, task worker, and frontend

Run each in its own shell so logs stay separate. Shells 1 and 2 must inherit the exports from 0c.

# Shell 1: API server (auto-reloads on code change)
uv run infrahub server start --listen 0.0.0.0 --port 8000

# Shell 2: task worker
uv run prefect worker start --type infrahubasync --pool infrahub-worker --with-healthcheck

# Shell 3: frontend (Vite dev server on :8080 — the URL you use in the browser)
cd frontend/app && pnpm setup        # first time only — initializes submodules + installs deps
cd frontend/app && pnpm start

0f — Sanity-check the stack

docker ps --format '{{.Names}} {{.Status}}'                                                # every container Up / healthy
curl -sf http://localhost:8180/realms/infrahub-test/.well-known/openid-configuration >/dev/null && echo "keycloak OK"
curl -sf http://localhost:8000/api/config -o /tmp/cfg.json && \
  python3 -c 'import json;c=json.load(open("/tmp/cfg.json",encoding="utf-8",errors="replace"));assert c["sso"]["enabled"],"sso DISABLED";print("infrahub sso OK, providers=",[p["name"] for p in c["sso"]["providers"]])'
curl -sf -o /dev/null -w '%{http_code}\n' http://localhost:8080/ | grep -q 200 && echo "frontend OK"

Open http://localhost:8080 in a browser — the login page must show a "Continue with Keycloak" button.

To re-apply env changes mid-test, edit the export(s) and Ctrl-C → restart Shell 1 (and Shell 2 if you changed worker-side envs). Shell 3 doesn't need restarts.

Reusable GraphQL probes

Open the GraphQL sandbox as admin at http://localhost:8000/graphql and keep these queries handy.

Probe 1 — list every auto-create event in chronological order

query AutoCreateEvents {
  InfrahubEvent(
    event_type: [
      "infrahub.group.auto_created"
      "infrahub.group.auto_create_rejected"
      "infrahub.group.auto_create_capped"
    ]
    order: ASC
  ) {
    count
    edges {
      node {
        id
        event
        occurred_at
        ... on GroupAutoCreatedEventType {
          idp
          protocol
          triggering_user_name
          group_id
          group_name
          source_pattern
          origin_value
        }
        ... on GroupAutoCreateRejectedEventType {
          idp
          protocol
          triggering_user_name
          rejected_claim_value
        }
        ... on GroupAutoCreateCappedEventType {
          idp
          protocol
          triggering_user_name
          cap_value
          dropped_count
          dropped_claims
        }
      }
    }
  }
}

Probe 2 — inspect origin on every account group

query AccountGroupsOrigin {
  CoreAccountGroup {
    edges {
      node {
        id
        name { value }
        origin { value }
      }
    }
  }
}

Scenario A — Happy path, filter exclusion, origin, and the created-event

  1. Sign in as alice / alice via the Keycloak button.
  2. As admin, open /role-management/groups.
  3. Expect two new rows: ops-admins and data-engineers, with alice as a member of each.
  4. On each row, open the detail page → toggle Extra (eye icon) → origin is provider1. The field is rendered read-only (no edit affordance).
  5. noise-group is not present — it matched neither the captured name nor the filter.
  6. Run Probe 1: exactly two GroupAutoCreatedEventType events. For each one:
    • idp = "provider1", protocol = "oidc"
    • triggering_user_name = "Alice Admin"
    • source_pattern = "^(?P<name>(ops|data)-.*)$"
    • origin_value = "provider1" (matches the origin attribute on the group)
    • group_id matches the group's UUID from the UI
    • group_name is ops-admins or data-engineers
  7. Run Probe 2: origin.value is "provider1" on the two new rows and is null on every pre-existing group (Infrahub Users, admin-seeded rows, etc.).

Scenario B — Filter excludes unrelated claims (no event emitted)

Continuing from Scenario A:

  1. Run Probe 1 again.
  2. Expect no event whose group_name is noise-group and no rejected event for it — filter exclusion is silent.
  3. Expect no group named noise-group in the UI.

Scenario C — Idempotency on the second login (no duplicate event)

  1. Sign alice out and back in.
  2. Re-run Probe 1.
  3. Expect the event count is unchanged (still 2). Reusing existing groups does not emit GroupAutoCreatedEvent.
  4. Expect the group list is unchanged (same two rows, same UUIDs, same origin).

Scenario D — Per-login cap and GroupAutoCreateCappedEvent

  1. Edit the Keycloak realm to give a fresh user carol membership in ~12 new ops-* groups (any names that match the filter and don't yet exist locally).

  2. Recreate Keycloak's data so the new realm import lands:

    docker rm -fv keycloak && docker volume rm <stack>_keycloak_data
    uv run invoke dev.deps
  3. Keep INFRAHUB_SECURITY_AUTO_CREATE_GROUPS_MAX_PER_LOGIN=5 exported — no restart needed unless you changed the value.

  4. Sign in as carol. Login must succeed.

  5. Server logs show auth_groups.skip_claim_over_per_login_cap lines, one per dropped claim, each carrying effective_name=... and max_per_login=5.

  6. UI: exactly 5 new ops-* groups exist, each with origin=provider1.

  7. Run Probe 1:

    • 5 new GroupAutoCreatedEventType events (one per created group).
    • Exactly one GroupAutoCreateCappedEventType event with:
      • cap_value = 5
      • dropped_count equals the number of matching claims beyond 5 (7 if you added 12)
      • dropped_claims is the verbatim list of those claim values (each entry length-truncated)
      • idp = "provider1", protocol = "oidc", triggering_user_name equals carol's display name (the OIDC name claim — firstName + lastName as set in the realm)
  8. Sign carol in a second time. Re-run Probe 1 — neither the created nor the capped event count changes (existing-group reuse is uncapped and not audited as creation).

Scenario E — Rejected claim emits GroupAutoCreateRejectedEvent

The rejection path fires when a claim matches the filter but the captured name is not a usable Infrahub group identifier (empty, whitespace-only, etc.).

  1. Add a new Keycloak group whose path triggers an empty named capture under a permissive filter — easiest setup:
    • Add group pad- (or any name that the filter captures into an empty/whitespace string) and put a user bob in it.

    • Switch the backend's filter so the capture group can produce an empty string:

      export INFRAHUB_SECURITY_AUTO_CREATE_GROUPS_FILTER='^pad-(?P<name>.*)$'
    • In Shell 1, Ctrl-C and re-run the API-server command from 0e (uv run infrahub server start --listen 0.0.0.0 --port 8000) so the new INFRAHUB_SECURITY_AUTO_CREATE_GROUPS_FILTER is picked up.

  2. Sign in as bob. Login must succeed.
  3. Expect no new CoreAccountGroup named "" or whitespace; the offending claim is dropped silently from the user's perspective.
  4. Run Probe 1:
    • One new GroupAutoCreateRejectedEventType event.
    • rejected_claim_value holds the original verbatim claim (pad-), length-truncated.
    • idp = "provider1", protocol = "oidc", triggering_user_name equals bob's display name (the OIDC name claim — firstName + lastName as set in the realm).
  5. Run Probe 2 — no new row with a null/whitespace name.value exists.

Restore the original filter ('^(?P<name>(ops|data)-.*)$') and Ctrl-C → re-run the API-server command from 0e in Shell 1 before moving on.

Scenario F — origin is read-only and unset on non-auto-created groups

origin is omitted from CoreAccountGroupUpdateInput and CoreAccountGroupCreateInput, so every external write attempt fails at GraphQL parse time.

For an auto-created group from Scenario A (e.g. ops-admins):

  1. UI: detail page → click the Extra button (eye icon) in the Details card header → origin row appears as read-only with the eye indicator and no edit affordance.

  2. GraphQL update attempt as admin (replace <id> with the group's UUID):

    mutation { CoreAccountGroupUpdate(data: { id: "<id>", origin: { value: "tampered" } }) { ok } }

    Expect a parse-time error: Field 'origin' is not defined by type 'CoreAccountGroupUpdateInput'. Re-read with Probe 2origin.value is still "provider1".

For a manually-created group:

  1. GraphQL create attempt supplying origin:

    mutation { CoreAccountGroupCreate(data: { name: { value: "manual-test" }, origin: { value: "manual-attempt" } }) { object { id } } }

    Expect a parse-time error: Field 'origin' is not defined by type 'CoreAccountGroupCreateInput'.

  2. Re-run without the origin field — the create succeeds; re-read via Probe 2origin.value is null.

For a pre-existing group (any row that existed before the upgrade — e.g. Infrahub Users, Super Administrators):

  1. Probe 2 confirms origin.value is null — no migration backfill ran.

Scenario G — Default-group fallback (no auto-create events)

  1. Set:

    export INFRAHUB_SECURITY_SSO_USER_DEFAULT_GROUP=<existing-group-with-a-role>   # e.g. "Infrahub Users"
    export INFRAHUB_SECURITY_AUTO_CREATE_GROUPS_FILTER='^(?P<name>nope-.*)$'
  2. Restart the native API server (Ctrl-C in Shell 1, re-run uv run infrahub server start --listen 0.0.0.0 --port 8000).

  3. Sign in as alice (whose claims match nothing under ^nope-.*$).

  4. Server logs show auth_groups.default_group_fallback_applied.

  5. alice is a member of the default group; no new groups are created on this login.

  6. Run Probe 1 — the event counts are unchanged from before this scenario. The fallback path emits no auto-create events.

Scenario H — Docs polish (PR #9340)

Build the docs site and confirm the new content renders.

cd docs
npm install              # first time only
npm run start            # dev server with hot-reload — or `npm run build && npm run serve` for the prod-style static build
  1. Visit /docs/deploy-manage/user-management/sso/advanced-sso → the "Auto-create groups from identity provider claims" section is present and ready for review.
  2. Follow the link → /docs/reference/infrahub-events/group → all three auto-create event types are present with full payload tables.

Scenario I — Activity-feed UI for the auto-create events

Frontend verification of how the three events render. Run after Scenarios A, D and E have produced created, capped and rejected events. Sign in as admin and open the Activities tab at http://localhost:8080/activities.

  1. Provider is the subject. Every auto-create row leads with the identity-provider name (provider1) in bold — not the triggering user, who appears only in the event detail view. Confirm the wording:
    • Created: provider1 auto-created group ops-admins
    • Rejected: provider1 claim pad- rejected (invalid group name)
    • Capped: provider1 auto-create cap of 5 reached, 7 claims dropped
  2. Event-type filter. Open the Event Type filter → the dropdown lists Group auto-created, Group auto-create rejected and Group auto-create capped. Selecting one narrows the feed to that type; there is no idp/protocol filter (out of scope).
  3. Detail view. Click View details on a created event → the Details card shows Identity Provider, Protocol, Triggering User, Group Name, Group ID, Source Pattern and Origin. The rejected detail shows Rejected Claim; the capped detail shows Cap Value, Dropped Count and Dropped Claims.
  4. Group's own activity window. This verifies the auto-create event is reachable from the group it created, via the related-node wiring. Use a group auto-created after the related-node change landed — e.g. re-run Scenario D with MAX_PER_LOGIN=20 so ops-team-06…12 are created fresh.
    1. Open the Groups panel at http://localhost:8080/role-management/groups.
    2. Click into one of those auto-created groups (e.g. ops-team-06) to open its detail window.
    3. Open the Activities tab inside that window.
    4. Expect the provider1 auto-created group ops-team-06 event listed there, and clicking it opens the same event detail from step 3.
    5. Groups created before the related-node change (alice's ops-admins/data-engineers, carol's ops-team-01…05) do not show the event in their Activities tab — the related node is only attached to newly emitted events.

Cleanup

Unset the per-scenario / SSO envs:

unset INFRAHUB_SECURITY_AUTO_CREATE_GROUPS_FILTER \
      INFRAHUB_SECURITY_AUTO_CREATE_GROUPS_MAX_PER_LOGIN \
      INFRAHUB_SECURITY_SSO_USER_DEFAULT_GROUP

Ctrl-C the infrahub server start and prefect worker start shells. The Vite dev server (Shell 3) holds no state and can be left running between sessions — Ctrl-C it only if you want the port back. Then uv run invoke dev.stop to take the deps + Keycloak down (or dev.destroy to also wipe Neo4j/message-bus volumes).

Existing auto-created groups remain on the next bring-up; lifecycle/cleanup is INFP-536 and out of scope here.


Summary by cubic

Expose INFRAHUB_SECURITY_AUTO_CREATE_GROUPS_FILTER, INFRAHUB_SECURITY_AUTO_CREATE_GROUPS_MAX_PER_LOGIN, and INFRAHUB_OIDC_PROVIDER1_USERINFO_METHOD in development/docker-compose.yml to enable end-to-end testing of auto-create groups in dev. Update the manual test plan at dev/specs/infp-556-auto-create-groups/test-plan.md with Keycloak setup, reusable GraphQL probes, scenarios (MVP, audit events, per-login cap, default-group fallback, read-only origin), add activity-feed UI tests with explicit group-window navigation steps, and remove the deprecated idp/protocol event-filter probe; supports IFC-2522.

Written for commit 9e69659. Summary will update on new commits. Review in cubic

…-2522)

Surface the auto-create groups security envs and the OIDC userinfo method
in the dev docker-compose, and add the manual test plan for the full
auto-create groups feature under dev/specs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added the type/spec A specification for an upcoming change to the project label May 26, 2026
@polmichel polmichel changed the title chore(dev): expose auto-create groups env + add manual test plan (IFC-2522) Auto-creation groups: test plan + missing env variables in Docker dev file (IFC-2522) May 26, 2026
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented May 26, 2026

Merging this PR will not alter performance

✅ 12 untouched benchmarks


Comparing pmi-ifc-2522-docker-compose-test-plan (9e69659) with pmi-20260522-polish-autocreation-feature (06e6a27)

Open in CodSpeed

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 2 files

Confidence score: 5/5

  • This looks low risk and safe to merge: the only finding is a documentation metadata mismatch, not a functional or behavioral code issue.
  • The issue in dev/specs/infp-556-auto-create-groups/test-plan.md is a title Jira key update (IFC-2522), so user-facing runtime impact is unlikely.
  • Pay close attention to dev/specs/infp-556-auto-create-groups/test-plan.md - correct the Jira key in the title to keep tracking and traceability accurate.

Shadow auto-approve: would not auto-approve because issues were found.

Re-trigger cubic

Comment thread dev/specs/infp-556-auto-create-groups/test-plan.md
@polmichel polmichel changed the base branch from develop to pmi-20260522-polish-autocreation-feature May 26, 2026 13:44
@polmichel polmichel marked this pull request as ready for review May 26, 2026 14:38
@polmichel polmichel requested review from a team as code owners May 26, 2026 14:38
@github-actions github-actions Bot added type/documentation Improvements or additions to documentation group/frontend Issue related to the frontend (React) labels May 26, 2026
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

0 issues found across 1 file (changes from recent commits).

Shadow auto-approve: would require human review. This PR changes over 1700 lines across many frontend source files, including renaming types, adding new API functions, and refactoring Apollo Client usage to TanStack Query—changes that affect business logic and data fetching, with risk of introducing bugs in diff, IPAM, and task display...

Re-trigger cubic

polmichel and others added 2 commits May 27, 2026 18:20
Remove the group_auto_create idp/protocol filter probe (the frontend filter
was dropped) and renumber the remaining probes. Add Scenario I covering the
activity-feed UI: provider-as-subject titles, the event-type dropdown entries,
the per-type detail view, and the auto-created group's own activity timeline.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Expand Scenario I step 4 into explicit steps: open the Groups panel, click
into an auto-created group, and confirm the auto-create event is reachable
from that group's own Activities tab.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

0 issues found across 1 file (changes from recent commits).

Shadow auto-approve: would require human review. The PR includes a large-scale frontend refactor migrating Apollo Client hooks to TanStack Query across many files (diff refresh, conflict resolution, IPAM namespaces, validator details), along with interface renames and new domain layers, which touches core business logic and requires human review.

Re-trigger cubic

Base automatically changed from pmi-20260522-polish-autocreation-feature to develop May 27, 2026 19:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

group/frontend Issue related to the frontend (React) type/documentation Improvements or additions to documentation type/spec A specification for an upcoming change to the project

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant