You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(homepage): add unless exclusion and tags for RBAC conditional filtering (#3546)
* feat(homepage): add unless exclusion and tags for RBAC conditional filtering
Signed-off-by: Karthik <karthik.jk11@gmail.com>
* refactor(homepage): simplify permission checks by moving logic into buildUserContext
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Christoph Jerolimov <jerolimov+git@redhat.com>
---------
Signed-off-by: Karthik <karthik.jk11@gmail.com>
Signed-off-by: Christoph Jerolimov <jerolimov+git@redhat.com>
Co-authored-by: Christoph Jerolimov <jerolimov+git@redhat.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
'@red-hat-developer-hub/backstage-plugin-homepage-backend': minor
4
+
'@red-hat-developer-hub/backstage-plugin-homepage-common': minor
5
+
---
6
+
7
+
Add `unless` exclusion block and `tags` for RBAC conditional policy filtering to homepage default widgets.
8
+
9
+
`unless` is the denylist counterpart to `if` — it uses the same shape (`users`, `groups`, `permissions`) and hides a widget when any condition matches. Deny wins over `if`, and on group nodes it prunes the entire subtree.
10
+
11
+
`tags` is an optional string array on leaf nodes (e.g. `['admin', 'developer']`) used with the new `HAS_TAG` permission rule for RBAC conditional filtering. Widgets without tags bypass tag-based filtering.
Copy file name to clipboardExpand all lines: workspaces/homepage/plugins/homepage-backend/README.md
+65-29Lines changed: 65 additions & 29 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -43,7 +43,20 @@ For each request to `GET /api/homepage/default-widgets`, the backend looks at th
43
43
44
44
Rules inside `if` use **OR** logic. If the parent fails its `if`, nothing under it is shown.
45
45
46
-
At startup the backend finds every permission name used in the tree. Each request checks only those names in one batch.
46
+
**Who cannot see a card (`unless`).** A node can have an optional `unless` block. It uses the same shape as `if` (`users`, `groups`, `permissions`) but acts as a **denylist**—if any condition matches, the widget is hidden.
47
+
48
+
-`unless` is checked **before**`if`. Deny wins: if both match, the widget is hidden.
49
+
- Rules inside `unless` also use **OR** logic—matching any user, group, or permission triggers the exclusion.
50
+
- On a group node, `unless` prunes the entire subtree without evaluating children.
51
+
- If `unless` is missing or empty, it never excludes.
52
+
53
+
**Tags (`tags`).** A leaf node can have an optional `tags` array of strings (for example `['admin', 'developer']`). Tags are used for RBAC conditional policy filtering with the `HAS_TAG` permission rule.
54
+
55
+
- Tags are passed through to the API response so the RBAC layer can filter on them.
56
+
- Widgets **without** tags bypass tag-based RBAC filtering entirely—they are always included when the RBAC decision is `CONDITIONAL`.
57
+
- Tags have no effect on config-time `if`/`unless` checks. They only matter at the RBAC layer.
58
+
59
+
At startup the backend finds every permission name used in `if` and `unless` blocks across the tree. Each request checks only those names in one batch.
47
60
48
61
**Leaves vs groups.** A **leaf** needs `id` and `ref`. A **group** needs `children` and must not use `id` or `ref`. The full rules are in `src/defaultWidgets/loadDefaultWidgets.ts`.
49
62
@@ -52,51 +65,74 @@ At startup the backend finds every permission name used in the tree. Each reques
52
65
```yaml
53
66
homepage:
54
67
defaultWidgets:
55
-
# --- Simple cards (leaves) ---
56
-
# id = mountpoint id for the card; ref = which widget to render.
68
+
# --- Simple card with tags ---
57
69
- id: onboarding
58
70
ref: 'rhdh-onboarding-section'
71
+
tags: [public]
59
72
layout:
60
73
xl: { w: 12, h: 6 }
61
74
lg: { w: 12, h: 6 }
75
+
76
+
# --- Card visible to developers, tagged for RBAC filtering ---
77
+
- id: template-list
78
+
ref: 'rhdh-template-section'
79
+
tags: [developer]
80
+
if:
81
+
groups: [group:default/developers]
82
+
layout:
83
+
xl: { w: 12, h: 5 }
84
+
85
+
# --- Card visible to developers but hidden from interns (unless) ---
62
86
- id: quickaccess-card
63
87
ref: quickaccess-card
88
+
tags: [developer]
89
+
if:
90
+
groups: [group:default/developers]
91
+
unless:
92
+
groups: [group:default/interns]
64
93
layout:
65
94
xl: { w: 6, h: 8, x: 6 }
66
95
67
96
# --- Group with children (shared visibility) ---
68
-
# The group row has `if` and `children` only. All listed cards are hidden
69
-
# unless the user passes the group's `if` (here: member of admins).
70
97
- if:
71
98
groups: [group:default/admins]
72
99
children:
73
100
- id: rbac
74
101
ref: RBAC
102
+
tags: [admin]
75
103
layout:
76
104
xl: { w: 12, h: 6 }
77
105
78
-
# --- group with several children ---
79
-
# Use one parent to apply the same visibility to multiple cards.
80
-
# - if:
81
-
# groups: [group:default/platform-team]
82
-
# children:
83
-
# - id: metrics-card
84
-
# ref: platform-metrics
85
-
# layout:
86
-
# xl: { w: 6, h: 8 }
87
-
# - id: logs-card
88
-
# ref: platform-logs
89
-
# layout:
90
-
# xl: { w: 6, h: 8 }
91
-
# # Each child can still have its own `if` for finer rules.
92
-
# - id: audit-card
93
-
# ref: platform-audit
94
-
# if:
95
-
# users: [user:default/auditor]
96
-
97
-
# --- Commented: single card gated by permission ---
98
-
# - id: admin-insights
99
-
# ref: admin-insights-card
100
-
# if:
101
-
# permissions: ['homepage.default-widgets.read']
106
+
# --- Group hidden from a specific user via unless ---
107
+
- if:
108
+
groups: [group:default/admins]
109
+
unless:
110
+
users: [user:default/alice]
111
+
children:
112
+
- id: audit-log
113
+
ref: platform-audit
114
+
tags: [admin]
115
+
layout:
116
+
xl: { w: 12, h: 6 }
117
+
118
+
# --- Entire subtree hidden from viewers ---
119
+
- unless:
120
+
groups: [group:default/viewers]
121
+
children:
122
+
- id: dev-tools
123
+
ref: dev-tools-card
124
+
tags: [developer]
125
+
layout:
126
+
xl: { w: 12, h: 4 }
102
127
```
128
+
129
+
### Permission rules
130
+
131
+
The plugin registers two permission rules for the `homepage-default-widget` resource type:
| `HAS_WIDGET_ID` | `widgetIds: string[]` | Matches widgets whose `id` is in the list |
136
+
| `HAS_TAG` | `tags: string[]` | Matches widgets that have at least one overlapping tag |
137
+
138
+
These rules can be used in RBAC conditional policies (via file or the RBAC UI) to control which widgets a role can see. Widgets without tags bypass `HAS_TAG` filtering entirely.
0 commit comments