Skip to content

Show propagated roles on the tag page with a Tag Source column - #602

Draft
barborico wants to merge 3 commits into
brynna/tag_constraint_propagationfrom
brynna/tag_page_source_column
Draft

Show propagated roles on the tag page with a Tag Source column#602
barborico wants to merge 3 commits into
brynna/tag_constraint_propagationfrom
brynna/tag_page_source_column

Conversation

@barborico

@barborico barborico commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Shows roles that a tag reaches by propagation on the tag page, and reworks the "Groups with Tag" table from row-per-tag-map to row-per-entity with a Tag Source column.

Stacked on #601 — review and merge that one first. This PR's diff against main will look larger than it is until #601 lands; read it against brynna/tag_constraint_propagation.

Urgency: TODO — set desired turnaround
Expected review effort: MEDIUM — 3 commits, one non-obvious SQL detail

Motivation

#601 makes a tag's constraints reach roles associated with a tagged group. But the tag page still lists only groups the tag is stored on, so an operator opening the SOX tag cannot see which roles it actually governs — the propagated ones are invisible.

The table also had a pre-existing shortcoming this fixes: a group tagged both directly and via its app produced two rows for the same group.

Description & Screenshots of Changes

Backend. TagDetail gains propagated_to_groups, populated by a query in get_tag that finds roles associated with any group carrying the tag. Each entry names the role, the source group, and whether the association was membership or ownership.

The query is gated on propagate_to_roles and enabled, and filters five active-record conditions: both the OktaGroupTagMap and the RoleGroupMap must be active, the source group must be non-deleted and managed, and the role must be non-deleted and managed. A disabled tag propagates nothing and an unmanaged role enforces nothing, so listing either would claim reach the enforcement path does not have.

Frontend. groupsWithTagRows.ts merges active_group_tags (direct and app-inherited) with propagated_to_groups into one row per entity, each carrying every source that applies. The column becomes Tag Source with one chip per source — Direct, App: {name}, Group: {name}. The remove button shows only on a row with a Direct chip and removes only the direct assignment; app-inherited and propagated rows are removed by removing the app tag or ending the association.

image

Validation of Changes

  • make test green: 924 backend / 63 frontend at head.
  • Each of the five active-record filters has a negative regression test, and each was proven load-bearing by removing its .where(...) clause and confirming only that test fails.
  • The enabled gate and the role is_managed filter were verified the same way.
  • Row-merge logic is extracted as pure functions and unit-tested directly, rather than mounting the table behind mocked MUI.

Guidance for Reviewers

Read commit-by-commit; three commits, each green.

One thing worth a careful look: the aliased() in get_tag's query. RoleGroup is joined-table inheritance on okta_group, so selecting both it and the plain source OktaGroup makes the two overlap on one physical table. Left unaliased, SQLAlchemy silently auto-aliases RoleGroup's implicit parent join, the later where clauses bind to that alias instead of the source-group row, and the query returns zero rows with no error. That bug was live during development and is exactly the kind a passing test suite would not have caught without the negative filters.

The Tag Source column also changes the table's "Total" from a tag-map count to a row count — correct for a row-per-entity table, but operators eyeball that number, so it belongs in release notes.

🤖 Generated with Claude Code

barborico and others added 3 commits August 24, 2026 21:52
Row-per-entity with a Tag Source column, so a role appearing via two
groups is one row with two sources.
Only the top-level propagate_to_roles gate had test coverage; none of the
five active-record filters in get_tag's propagation query (OktaGroupTagMap
active, RoleGroupMap active, source group not deleted, source group
managed, role not deleted) did, so a refactor could silently drop any one
of them. Each new test breaks exactly one filter's condition and asserts
the row is excluded; verified each is load-bearing by removing its clause
and confirming the corresponding test fails.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A disabled tag propagates nothing, and an unmanaged role enforces nothing,
so neither belongs in propagated_to_groups -- listing them would claim
reach the enforcement path does not have.

Also names the row-merge test for what it asserts and describes the module
by what it builds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@matthew-bass

Copy link
Copy Markdown
Contributor

seems nice!

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.

2 participants