feat(FR-3020): filter user folder keypair resource policies by user with assigned keypairs column#7672
Conversation
How to use the Graphite Merge QueueAdd either label to this PR to merge it via the merge queue:
You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has required the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
Coverage Report for backend-ai-ui-coverage (./packages/backend.ai-ui)
File Coverage
|
||||||||||||||||||||||||||||||||||||||
Coverage Report for react-coverage (./react)
File Coverage
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
c5cb147 to
1aa70fc
Compare
There was a problem hiding this comment.
Pull request overview
This PR updates the User Folder Permissions tab to let operators inspect storage host permissions scoped by user, by filtering keypair resource policies via the new backend keypair.userId nested filter and adding an Assigned Keypairs column (resolved from the new keypairs connection).
Changes:
- Replaced the policy-name multi-select with a
BAIUserSelectto scope the table by selected user (or show all policies when unset). - Reworked the keypair resource policy table to use offset pagination, apply the
keypair.userIdfilter when a user is selected, and render an Assigned Keypairs column. - Synced GraphQL schema to include the new nested filter input and
keypairsconnection; updated i18n keys used by the new UI.
Reviewed changes
Copilot reviewed 24 out of 26 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| react/src/components/UserFolderPermissionPanel.tsx | Switches from policy selection to user selection and passes selected user id into the table. |
| react/src/components/KeypairResourcePolicyStoragePermissionTable.tsx | Implements offset pagination + user-scoped filtering and adds the Assigned Keypairs column UI. |
| data/schema.graphql | Adds KeypairResourcePolicyV2.keypairs connection and KeypairResourcePolicyV2Filter.keypair nested filter input. |
| react/src/generated/KeypairResourcePolicyStoragePermissionTableQuery.graphql.ts | Regenerated Relay artifacts for the updated query variables and selections (count, pagination, keypairs connection). |
| packages/backend.ai-ui/src/generated/BAIAdminKeypairResourcePolicySelectPaginatedQuery.graphql.ts | Regenerated artifacts reflecting schema filter additions. |
| resources/i18n/en.json | Adds new strings for Assigned Keypairs UI and updates empty-state text key. |
| resources/i18n/ko.json | Adds Korean translations for new Assigned Keypairs UI strings and updates empty-state text key. |
| resources/i18n/de.json | Removes now-unused storageHost.permission keys (from prior UI). |
| resources/i18n/el.json | Removes now-unused storageHost.permission keys (from prior UI). |
| resources/i18n/es.json | Removes now-unused storageHost.permission keys (from prior UI). |
| resources/i18n/fi.json | Removes now-unused storageHost.permission keys (from prior UI). |
| resources/i18n/fr.json | Removes now-unused storageHost.permission keys (from prior UI). |
| resources/i18n/id.json | Removes now-unused storageHost.permission keys (from prior UI). |
| resources/i18n/it.json | Removes now-unused storageHost.permission keys (from prior UI). |
| resources/i18n/ja.json | Removes now-unused storageHost.permission keys (from prior UI). |
| resources/i18n/mn.json | Removes now-unused storageHost.permission keys (from prior UI). |
| resources/i18n/ms.json | Removes now-unused storageHost.permission keys (from prior UI). |
| resources/i18n/pl.json | Removes now-unused storageHost.permission keys (from prior UI). |
| resources/i18n/pt-BR.json | Removes now-unused storageHost.permission keys (from prior UI). |
| resources/i18n/pt.json | Removes now-unused storageHost.permission keys (from prior UI). |
| resources/i18n/ru.json | Removes now-unused storageHost.permission keys (from prior UI). |
| resources/i18n/th.json | Removes now-unused storageHost.permission keys (from prior UI). |
| resources/i18n/tr.json | Removes now-unused storageHost.permission keys (from prior UI). |
| resources/i18n/vi.json | Removes now-unused storageHost.permission keys (from prior UI). |
| resources/i18n/zh-CN.json | Removes now-unused storageHost.permission keys (from prior UI). |
| resources/i18n/zh-TW.json | Removes now-unused storageHost.permission keys (from prior UI). |
Files not reviewed (2)
- packages/backend.ai-ui/src/generated/BAIAdminKeypairResourcePolicySelectPaginatedQuery.graphql.ts: Language not supported
- react/src/generated/KeypairResourcePolicyStoragePermissionTableQuery.graphql.ts: Language not supported
c6f02dd to
fffca5f
Compare
bcaadc8 to
25f12f8
Compare
fffca5f to
9ad54e3
Compare
25f12f8 to
50c608e
Compare
agatha197
left a comment
There was a problem hiding this comment.
I cannot select user due to below error. But I tested it in latest main branch. (core)
{
"errors": [
{
"message": "Query filter parsing error: Error trying to process rule \"binary_expr\":\n\ntype object 'UserRow' has no attribute 'is_active'",
"path": [
"user_nodes"
],
"extensions": {
"code": "DOWNSTREAM_SERVICE_ERROR",
"serviceName": "graphene"
}
}
],
"data": {
"user_nodes": null
}
}
d9e23cf to
fc7ef06
Compare
fc7ef06 to
2ced069
Compare
50c608e to
10b3dfd
Compare
2ced069 to
f4e4801
Compare
10b3dfd to
ec8d961
Compare
f4e4801 to
9197761
Compare
9197761 to
f64aad0
Compare
434d95f to
f784034
Compare
…st (#7685) Resolves #7683 (FR-3024) ## Summary Expose the newly added `adminUsersV2` (`UserV2Filter`) filters in the super-admin user list's `BAIGraphQLPropertyFilter`. The list migrated to `adminUsersV2` in #7666 (merged) but only exposed 5 filters (email / ID / username / project / role). Backend BA-6247 (lablup/backend.ai#11878) and BA-6249 (lablup/backend.ai#11879) added 10 more filter fields to the v2 schema. These v2 filters are gated behind a capability flag — older managers keep showing just the 5 base filters. ### Added filters (gated, shown only when supported) | Type | Fields | | --- | --- | | String | `fullName`, `description`, `statusInfo`, `resourcePolicy` | | Boolean | `needPasswordChange`, `totpActivated`, `sudoSessionEnabled` | | Integer | `containerUid`, `containerMainGid` | | Integer array | `containerGids` (single-GID membership via `contains`) | ### Version gating - Registers a new capability flag `user-v2-extended-filter` in `packages/backend.ai-client/src/client.ts`, mirroring the existing `model-deployment-extended-filter` / `rbac-element-type-filter` (rc-pinned) pattern. - **Gated at `26.4.4rc6`.** BA-6247 / BA-6249 merged *after* the `26.4.4rc6` tag, so they ship in **26.4.4 (final)**. The flag is pinned to the `rc6` tag so it also activates against the rc6 staging manager (a post-rc6 snapshot reporting `26.4.4rc6`). A `TODO(FR-3024)` notes simplifying to `26.4.4` once rc builds are out of use. - The 10 extended filters live in a single `extendedFilterProperties` array and are conditionally spread into `filterProperties` via `bailClient.supports('user-v2-extended-filter')` — the 5 base filters are always present. ### Schema sync - Syncs `data/schema.graphql` to backend.ai `main`'s composed supergraph (`docs/manager/graphql-reference/supergraph.graphql`) so `UserV2Filter` now defines the new fields. This is a **full schema bump** (+596 lines) and also pulls in other already-merged backend types (RBAC role presets, keypair filters, etc.). - `pnpm relay` regenerated the affected artifacts (`AdminUserManagementQuery` now models the new filter fields; 7 other `__generated__` queries/mutations updated incidentally by the bump). - Note: #7672 (FR-3020) also edits `data/schema.graphql` (surgical, IntArrayFilter + keypair nested filter). Whichever merges second will need a trivial `data/schema.graphql` restack against the other. ### Notes / out of scope - `containerGids` is an `IntArrayFilter` (`contains` / `containsAny` / `containsAll`). `BAIGraphQLPropertyFilter` has no array-operator input, so only the single-GID `contains` operator is exposed via `fixedOperator: 'contains'`. - The three integer filters (`containerUid` / `containerMainGid` / `containerGids`) carry an `integerRule` so non-integer input is rejected before `Number(...)` coercion (avoids `NaN` / invalid `Int` variables). - All labels reuse existing `credential.*` i18n keys (same keys the legacy v1 user list uses) — no new i18n keys, no hardcoded strings. ## Testing - Test against the **`10.82.0.130`** test server (reports `26.4.4rc6`). - With the gate pinned at `26.4.4rc6`, the `user-v2-extended-filter` flag activates directly on this server — **no manual version edit needed**. All 15 filters appear in the super-admin **User Management** list; on a manager older than `26.4.4rc6`, only the 5 base filters. ## Verification `bash scripts/verify.sh` → `=== ALL PASS ===` (Relay, Lint, Format, TypeScript). ## Test plan - [ ] On `10.82.0.130` (26.4.4rc6), all 15 filters are offered and each produces a valid `UserV2Filter` query. - [ ] On a manager older than 26.4.4rc6, only the 5 base filters are offered. - [ ] Boolean filters emit scalar `true` / `false`; `containerUid` / `containerMainGid` emit `{ equals: N }`; `containerGids` emits `{ contains: N }`. - [ ] Non-integer input in `containerUid` / `containerMainGid` / `containerGids` is rejected with an inline error.
98caeb5 to
67f721b
Compare
67f721b to
40c87d6
Compare
|
@agatha197 Re: the |
Merge activity
|
…ith assigned keypairs column (#7672) Resolves #7674 (FR-3020) ## Summary Reworks the keypair resource policy permission table on the **User Folder Permissions** tab so an operator can inspect *which storage host permissions apply to a specific user*, scoped to that user's keypairs. - **User selector** — replaces the policy-name multi-select with a `BAIUserSelect` (`valuePropName="id"`). - **No user selected** → the table pages through **all** keypair resource policies (`adminKeypairResourcePoliciesV2`, offset pagination) with their permission matrix; the Assigned Keypairs column shows a⚠️ icon + tooltip prompting the operator to pick a user. - **User selected** → - policies are filtered to those governing that user's keypairs via the `keypair.userId` nested filter, **and** - each policy's `keypairs` connection is **also** server-scoped to the selected user via `KeypairFilter.userId`, so the Assigned Keypairs column lists only that user's keypairs under the policy. - **Assigned Keypairs column** — every assigned keypair is rendered as its own tag, stacked vertically. The user's **main access key** is sorted to the top (client-side) and highlighted in `colorError` with a leading info-circle icon and a "Main access key" tooltip (reuses the existing `credential.MainAccessKey` term). ## Permission save (V2 enum vs kebab) Saving goes through `adminUpdateKeypairResourcePolicyV2`. Its `VFolderHostPermissionEntryInput.permissions` is `[String!]`, validated server-side against the V1 `VFolderHostPermission` enum — i.e. **kebab** values like `mount-in-session`, **not** the `VFolderHostPermissionV2` enum NAMES (`MOUNT_IN_SESSION`) the read returns. The payload now normalizes every host's permissions to kebab keys: the edited host's keys are already kebab, and other hosts' read values are converted back via `v2PermissionToKey`. (Fixes a `'CREATE_VFOLDER' is not a valid VFolderHostPermission` save error.) ## Schema `data/schema.graphql` synced to the latest backend `main` supergraph, which includes `KeypairFilter.userId` (enabling the per-row user scoping above) and the `keypairs` connection on `KeypairResourcePolicyV2` (backend.ai#11866 / #11867). ## Test environment A test server is set up at **http://10.82.0.130:8090** — log in and, on the **User Folder Permissions** tab, select the **admin@lablup.com** account in the user selector to compare multiple keypairs (the account's main access key is highlighted at the top of the Assigned Keypairs column). ## Changes - `react/src/components/UserFolderPermissionPanel.tsx` — `BAIUserSelect` instead of the policy multi-select. - `react/src/components/KeypairResourcePolicyStoragePermissionTable.tsx` — offset pagination, `keypair.userId` policy filter + `KeypairFilter.userId` connection filter, kebab save payload, vertically-stacked Assigned Keypairs tags with main-access-key highlight sorted to the top. - `react/src/helper/storageHostPermission.ts` — dropped the unused `keyToV2Enum` inverse helper (the V2 save takes kebab directly). - `data/schema.graphql` — synced to the latest `main` supergraph. - `resources/i18n/*` — `AssignedKeypairs`, `SelectUserToSeeKeypairs`, `NoKeypairResourcePolicies` (tooltip reuses `credential.MainAccessKey`). ## Verification `bash scripts/verify.sh`: **Relay / Lint / Format / TypeScript — ALL PASS**. ## Screenshots _TODO — User Folder Permissions tab: no-user paginated view + user-selected with vertically-stacked Assigned Keypairs and highlighted main access key._
40c87d6 to
d2a0211
Compare


Resolves #7674 (FR-3020)
Summary
Reworks the keypair resource policy permission table on the User Folder Permissions tab so an operator can inspect which storage host permissions apply to a specific user, scoped to that user's keypairs.
BAIUserSelect(valuePropName="id").adminKeypairResourcePoliciesV2, offset pagination) with their permission matrix; the Assigned Keypairs column shows akeypair.userIdnested filter, andkeypairsconnection is also server-scoped to the selected user viaKeypairFilter.userId, so the Assigned Keypairs column lists only that user's keypairs under the policy.colorErrorwith a leading info-circle icon and a "Main access key" tooltip (reuses the existingcredential.MainAccessKeyterm).Permission save (V2 enum vs kebab)
Saving goes through
adminUpdateKeypairResourcePolicyV2. ItsVFolderHostPermissionEntryInput.permissionsis[String!], validated server-side against the V1VFolderHostPermissionenum — i.e. kebab values likemount-in-session, not theVFolderHostPermissionV2enum NAMES (MOUNT_IN_SESSION) the read returns. The payload now normalizes every host's permissions to kebab keys: the edited host's keys are already kebab, and other hosts' read values are converted back viav2PermissionToKey. (Fixes a'CREATE_VFOLDER' is not a valid VFolderHostPermissionsave error.)Schema
data/schema.graphqlsynced to the latest backendmainsupergraph, which includesKeypairFilter.userId(enabling the per-row user scoping above) and thekeypairsconnection onKeypairResourcePolicyV2(backend.ai#11866 / #11867).Test environment
A test server is set up at http://10.82.0.130:8090 — log in and, on the User Folder Permissions tab, select the admin@lablup.com account in the user selector to compare multiple keypairs (the account's main access key is highlighted at the top of the Assigned Keypairs column).
Changes
react/src/components/UserFolderPermissionPanel.tsx—BAIUserSelectinstead of the policy multi-select.react/src/components/KeypairResourcePolicyStoragePermissionTable.tsx— offset pagination,keypair.userIdpolicy filter +KeypairFilter.userIdconnection filter, kebab save payload, vertically-stacked Assigned Keypairs tags with main-access-key highlight sorted to the top.react/src/helper/storageHostPermission.ts— dropped the unusedkeyToV2Enuminverse helper (the V2 save takes kebab directly).data/schema.graphql— synced to the latestmainsupergraph.resources/i18n/*—AssignedKeypairs,SelectUserToSeeKeypairs,NoKeypairResourcePolicies(tooltip reusescredential.MainAccessKey).Verification
bash scripts/verify.sh: Relay / Lint / Format / TypeScript — ALL PASS.Screenshots
TODO — User Folder Permissions tab: no-user paginated view + user-selected with vertically-stacked Assigned Keypairs and highlighted main access key.