Skip to content

feat(FR-3020): filter user folder keypair resource policies by user with assigned keypairs column#7672

Merged
graphite-app[bot] merged 1 commit into
mainfrom
fr-3020-keypair-resource-policy-user-filter
Jun 4, 2026
Merged

feat(FR-3020): filter user folder keypair resource policies by user with assigned keypairs column#7672
graphite-app[bot] merged 1 commit into
mainfrom
fr-3020-keypair-resource-policy-user-filter

Conversation

@ironAiken2

@ironAiken2 ironAiken2 commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

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.tsxBAIUserSelect 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.

ironAiken2 commented Jun 1, 2026

Copy link
Copy Markdown
Contributor Author

How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • flow:merge-queue - adds this PR to the back of the merge queue
  • flow:hotfix - for urgent changes, fast-track this PR to the front of 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.

@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Coverage Report for backend-ai-ui-coverage (./packages/backend.ai-ui)

Status Category Percentage Covered / Total
🔵 Lines 10.76% 528 / 4905
🔵 Statements 9.21% 589 / 6395
🔵 Functions 12.34% 135 / 1094
🔵 Branches 7.75% 486 / 6269
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/backend.ai-ui/src/components/fragments/BAIUserSelect.tsx 0% 0% 0% 0% 49-306
Generated in workflow #1444 for commit d2a0211 by the Vitest Coverage Report Action

@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Coverage Report for react-coverage (./react)

Status Category Percentage Covered / Total
🔵 Lines 6.46% 1799 / 27838
🔵 Statements 5.24% 1995 / 38002
🔵 Functions 5.34% 300 / 5609
🔵 Branches 3.67% 1305 / 35545
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
react/src/components/AdminUserCredentialList.tsx 0% 0% 0% 0% 56-602
react/src/components/KeypairInfoModal.tsx 0% 0% 0% 0% 18-75
react/src/components/KeypairResourcePolicyStoragePermissionTable.tsx 0% 0% 0% 0% 58-173
react/src/components/KeypairResourcePolicyStoragePermissionTableV2.tsx 0% 0% 0% 0% 69-329
react/src/components/StorageHostDetailDrawerContent.tsx 0% 0% 0% 0% 27-66
react/src/components/UserFolderPermissionPanelV2.tsx 0% 0% 0% 0% 30-65
react/src/helper/storageHostPermission.ts 0% 0% 0% 0% 19-161
Generated in workflow #1444 for commit d2a0211 by the Vitest Coverage Report Action

@ironAiken2 ironAiken2 force-pushed the fr-3020-keypair-resource-policy-user-filter branch from c5cb147 to 1aa70fc Compare June 1, 2026 09:49
@ironAiken2 ironAiken2 marked this pull request as ready for review June 1, 2026 10:08
Copilot AI review requested due to automatic review settings June 1, 2026 10:08

Copilot AI left a comment

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.

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 BAIUserSelect to 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.userId filter when a user is selected, and render an Assigned Keypairs column.
  • Synced GraphQL schema to include the new nested filter input and keypairs connection; 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

Comment thread react/src/components/KeypairResourcePolicyStoragePermissionTable.tsx Outdated
Comment thread resources/i18n/en.json
@ironAiken2 ironAiken2 force-pushed the fr-3020-keypair-resource-policy-user-filter branch 2 times, most recently from c6f02dd to fffca5f Compare June 2, 2026 00:09
@ironAiken2 ironAiken2 force-pushed the fr-3010-storage-permission-folder-tabs branch from bcaadc8 to 25f12f8 Compare June 2, 2026 00:09
@ironAiken2 ironAiken2 force-pushed the fr-3020-keypair-resource-policy-user-filter branch from fffca5f to 9ad54e3 Compare June 2, 2026 00:35
@ironAiken2 ironAiken2 force-pushed the fr-3010-storage-permission-folder-tabs branch from 25f12f8 to 50c608e Compare June 2, 2026 00:35

@agatha197 agatha197 left a comment

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.

image.png

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
    }
}

@ironAiken2 ironAiken2 force-pushed the fr-3020-keypair-resource-policy-user-filter branch 2 times, most recently from d9e23cf to fc7ef06 Compare June 2, 2026 04:56
@ironAiken2 ironAiken2 marked this pull request as draft June 2, 2026 04:59
@ironAiken2 ironAiken2 changed the base branch from fr-3010-storage-permission-folder-tabs to graphite-base/7672 June 4, 2026 02:06
@ironAiken2 ironAiken2 force-pushed the fr-3020-keypair-resource-policy-user-filter branch from fc7ef06 to 2ced069 Compare June 4, 2026 02:55
@ironAiken2 ironAiken2 force-pushed the graphite-base/7672 branch from 50c608e to 10b3dfd Compare June 4, 2026 02:55
@ironAiken2 ironAiken2 changed the base branch from graphite-base/7672 to fr-3010-storage-permission-folder-tabs June 4, 2026 02:55
@graphite-app graphite-app Bot changed the base branch from fr-3010-storage-permission-folder-tabs to graphite-base/7672 June 4, 2026 04:46
@graphite-app graphite-app Bot force-pushed the fr-3020-keypair-resource-policy-user-filter branch from 2ced069 to f4e4801 Compare June 4, 2026 04:49
@graphite-app graphite-app Bot force-pushed the graphite-base/7672 branch from 10b3dfd to ec8d961 Compare June 4, 2026 04:49
@graphite-app graphite-app Bot changed the base branch from graphite-base/7672 to main June 4, 2026 04:49
@graphite-app graphite-app Bot force-pushed the fr-3020-keypair-resource-policy-user-filter branch from f4e4801 to 9197761 Compare June 4, 2026 04:50
@ironAiken2 ironAiken2 force-pushed the fr-3020-keypair-resource-policy-user-filter branch from 9197761 to f64aad0 Compare June 4, 2026 05:47
@ironAiken2 ironAiken2 marked this pull request as ready for review June 4, 2026 05:49
@ironAiken2 ironAiken2 marked this pull request as draft June 4, 2026 06:27
@ironAiken2 ironAiken2 marked this pull request as ready for review June 4, 2026 07:50
@ironAiken2 ironAiken2 force-pushed the fr-3020-keypair-resource-policy-user-filter branch 2 times, most recently from 434d95f to f784034 Compare June 4, 2026 08:04
graphite-app Bot pushed a commit that referenced this pull request Jun 4, 2026
…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.
@ironAiken2 ironAiken2 force-pushed the fr-3020-keypair-resource-policy-user-filter branch 2 times, most recently from 98caeb5 to 67f721b Compare June 4, 2026 08:45
Comment thread react/src/components/StorageHostDetailDrawerContent.tsx Outdated
@ironAiken2 ironAiken2 force-pushed the fr-3020-keypair-resource-policy-user-filter branch from 67f721b to 40c87d6 Compare June 4, 2026 09:02
@ironAiken2

Copy link
Copy Markdown
Contributor Author

@agatha197 Re: the user_nodes is_active parsing error — this is already fixed on this branch. BAIUserSelect's default active-user filter now sends status == "active" (the backend UserRow has no is_active attribute; UserManagement uses status too), not the legacy is_active == true. The error in your screenshot was from a pre-fix build. Could you re-test on the latest? (If it still shows is_active, the workspace backend.ai-ui build cache may be stale — the dev server dev-aliases it to source, so a fresh load picks up the fix.)

@ironAiken2 ironAiken2 requested a review from agatha197 June 4, 2026 09:02
Comment thread react/src/components/UserFolderPermissionPanelV2.tsx

@agatha197 agatha197 left a comment

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.

LGTM

@graphite-app

graphite-app Bot commented Jun 4, 2026

Copy link
Copy Markdown

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._
@graphite-app graphite-app Bot force-pushed the fr-3020-keypair-resource-policy-user-filter branch from 40c87d6 to d2a0211 Compare June 4, 2026 11:19
@graphite-app graphite-app Bot merged commit d2a0211 into main Jun 4, 2026
14 checks passed
@graphite-app graphite-app Bot deleted the fr-3020-keypair-resource-policy-user-filter branch June 4, 2026 11:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:i18n Localization area:ux UI / UX issue. size:XL 500~ LoC

Projects

None yet

Development

Successfully merging this pull request may close these issues.

User Folder Permissions: filter keypair resource policies by user and add assigned keypairs column

3 participants