Skip to content

feat(webui): Add Workload Identities list#58971

Merged
nicholasmarais1158 merged 36 commits intomasterfrom
nicholasmarais1158/mwi-workload-id-list-ui
Sep 19, 2025
Merged

feat(webui): Add Workload Identities list#58971
nicholasmarais1158 merged 36 commits intomasterfrom
nicholasmarais1158/mwi-workload-id-list-ui

Conversation

@nicholasmarais1158
Copy link
Copy Markdown
Contributor

@nicholasmarais1158 nicholasmarais1158 commented Sep 10, 2025

Summary

This change adds a Workload Identities list to the web UI. It's a loose clone of the existing Bot Instances list. It is paginated, can be server-side sorted by name or SPIFFE ID, and supports filtering by search term contained in the name, SPIFFE ID or SPIFFE hint fields.

There are a few differences compared to the Bot Instances implementation;

  • There are separate sorting params for field and direction, instead of name:asc. This is mimicked by the server-side implementation (see feat(webapi): Add GET /webapi/sites/:site/workload-identity #58917).
  • There's an empty/welcome component that's shown when there are zero records, not including when a search/filter return zero hits.

Updates: #53582
Changelog: Added a Workload Identities page to the web UI to list workload identities

Changes

  • Add workload identities list
  • Add stories

Demo

Screen.Recording.2025-09-11.at.17.34.29.mov

Reviewer notes

Create a few Workload Identities using tctl create (instructions here).

Alternatively, I've used a sql script to insert many; workload-identities.sql.

$ sqlite3 <your cluster dir>/data/backend/sqlite.db

sqlite> .read workload-identities.sql

Check storybook to see the edge-cases, such as no permissions, errors and welcome empty state.

@nicholasmarais1158 nicholasmarais1158 force-pushed the nicholasmarais1158/mwi-workload-id-list-ui branch from cebc88f to f1502fa Compare September 11, 2025 16:06
@nicholasmarais1158 nicholasmarais1158 force-pushed the nicholasmarais1158/mwi-workload-id-list-ui branch from f1502fa to 4156c27 Compare September 11, 2025 17:07
serversideSearchPanel: (
<SearchPanel
updateSearch={onSearchChange}
hideAdvancedSearch={true}
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.

Why do we hide advanced search?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It is not implemented for this endpoint. Only a text-contains search is supported.

);
}

function valueOrEmpty(value: string | null | undefined, empty = '-') {
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.

Is the empty parameter actually used anywhere?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

No, but I felt the function was more complete this way and self-documenting.

sortField: 'name',
sortDir: 'ASC',
});
});
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.

Am I right that most of these tests don't actually verify what's been rendered? It may be prudent to verify that apart from calling API endpoints, the test verifies that the response has been used to rerender the UI.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Many don't, that's correct.

Given some of the tests verify the list is rendered correctly (such as "Shows a list") and the list just shows exactly what the API returns, I didn't think it was necessary to validate rendering the list for every case. Can you see an edge-case that I'm not covering?


export function WorkloadIdentities() {
const history = useHistory();
const location = useLocation<{ prevPageTokens?: readonly string[] }>();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Where does the need for maintaining prevPageTokens come from? Isn't it enough to update the page query param?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Relying on the query param and paging backwards using the browser back, presents it own set of problems. Namely, you can't know if there is a previous page to go back to.

Do you know an alternate approach?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Oh I forgot that this must not only work with browser navigation buttons but also when you manually click on the button within the table that gets you to the previous page. In which case you need to maintain some kind of list of the page query params somewhere.

I think I was also confused as this might've been the first time I saw the use of a location state! Connect doesn't use React Router so I didn't have to pay attention to that. I also don't intuitively understand why we need to store prevPageTokens on the location rather than in a separate piece of state. Is this because React Router hooks into browser's history so that if I hold the back button in the browser and jump three pages back, React Router will know the exact prevPageTokens at that point in the navigation history?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Location state is part of the History API, if I'm not mistaken. Navigating using the browser back/forward will recall the location state at that point including the prevPageTokens supplied when originally navigating to that location.


export function WorkloadIdentities() {
const history = useHistory();
const location = useLocation<{ prevPageTokens?: readonly string[] }>();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Oh I forgot that this must not only work with browser navigation buttons but also when you manually click on the button within the table that gets you to the previous page. In which case you need to maintain some kind of list of the page query params somewhere.

I think I was also confused as this might've been the first time I saw the use of a location state! Connect doesn't use React Router so I didn't have to pay attention to that. I also don't intuitively understand why we need to store prevPageTokens on the location rather than in a separate piece of state. Is this because React Router hooks into browser's history so that if I hold the back button in the browser and jump three pages back, React Router will know the exact prevPageTokens at that point in the navigation history?

Base automatically changed from nicholasmarais1158/mwi-workload-id-list to master September 19, 2025 16:43
# Conflicts:
#	lib/cache/workload_identity.go
#	lib/cache/workload_identity_test.go
#	web/packages/teleport/src/services/workloadIdentity/workloadIdentity.ts
#	web/packages/teleport/src/test/helpers/workloadIdentities.ts
@nicholasmarais1158 nicholasmarais1158 added this pull request to the merge queue Sep 19, 2025
Merged via the queue into master with commit c943e0e Sep 19, 2025
42 checks passed
@nicholasmarais1158 nicholasmarais1158 deleted the nicholasmarais1158/mwi-workload-id-list-ui branch September 19, 2025 17:25
@backport-bot-workflows
Copy link
Copy Markdown
Contributor

@nicholasmarais1158 See the table below for backport results.

Branch Result
branch/v17 Failed
branch/v18 Failed

nicholasmarais1158 added a commit that referenced this pull request Sep 23, 2025
* Add ACLs for workload identity

* Add list workload identities to webapi

* Add `newWebPackWithOptions`

* Add sorting by name and spiffe id

* Add filter by search term

* Use `require.ErrorContains`

* Refactor `newWebPackWithOptions`

* Use `t.Context()`

* Add context to uses of `require.NoError` in loops

* Tidy-up

* Un-deprecate `newWebPack`

* Rename `KindWorkloadIdentity`

* Add client-side API support for sort and filter

* Handle endpoint not supported scenario

* Fix cache keys for spiffe id index

* Add and use `ListWorkloadIdentitiesV2` RPC

* Return `CompareFailedError` for sorting unavailable

* Split sort into two fields (field and direction)

* Update unsupported sort tests

* Make `updateQuery` callback optional on `SearchPanel`

* Add workload identities list

* Add tests

* Add stories

* Rename nav item to "Workload Identities"

Co-authored-by: Zac Bergquist <zac.bergquist@goteleport.com>

* Revert change to conditional render (SearchPanel)

* Remove mono-spaced text

* Suggested code refinements

* Join spiffe_id page keys with a pipe

* Join spiffe_id page keys with a pipe

* Revert "Join spiffe_id page keys with a pipe"

This reverts commit 842b9cb.

* Base32 hex encode id for cache key

* Add missing useCallback dep

* Fix word break opportunities on Firefox

---------

Co-authored-by: Zac Bergquist <zac.bergquist@goteleport.com>
nicholasmarais1158 added a commit that referenced this pull request Sep 23, 2025
* Add ACLs for workload identity

* Add list workload identities to webapi

* Add `newWebPackWithOptions`

* Add sorting by name and spiffe id

* Add filter by search term

* Use `require.ErrorContains`

* Refactor `newWebPackWithOptions`

* Use `t.Context()`

* Add context to uses of `require.NoError` in loops

* Tidy-up

* Un-deprecate `newWebPack`

* Rename `KindWorkloadIdentity`

* Add client-side API support for sort and filter

* Handle endpoint not supported scenario

* Fix cache keys for spiffe id index

* Add and use `ListWorkloadIdentitiesV2` RPC

* Return `CompareFailedError` for sorting unavailable

* Split sort into two fields (field and direction)

* Update unsupported sort tests

* Make `updateQuery` callback optional on `SearchPanel`

* Add workload identities list

* Add tests

* Add stories

* Rename nav item to "Workload Identities"

Co-authored-by: Zac Bergquist <zac.bergquist@goteleport.com>

* Revert change to conditional render (SearchPanel)

* Remove mono-spaced text

* Suggested code refinements

* Join spiffe_id page keys with a pipe

* Join spiffe_id page keys with a pipe

* Revert "Join spiffe_id page keys with a pipe"

This reverts commit 842b9cb.

* Base32 hex encode id for cache key

* Add missing useCallback dep

* Fix word break opportunities on Firefox

---------

Co-authored-by: Zac Bergquist <zac.bergquist@goteleport.com>
nicholasmarais1158 added a commit that referenced this pull request Sep 23, 2025
* Add ACLs for workload identity

* Add list workload identities to webapi

* Add `newWebPackWithOptions`

* Add sorting by name and spiffe id

* Add filter by search term

* Use `require.ErrorContains`

* Refactor `newWebPackWithOptions`

* Use `t.Context()`

* Add context to uses of `require.NoError` in loops

* Tidy-up

* Un-deprecate `newWebPack`

* Rename `KindWorkloadIdentity`

* Add client-side API support for sort and filter

* Handle endpoint not supported scenario

* Fix cache keys for spiffe id index

* Add and use `ListWorkloadIdentitiesV2` RPC

* Return `CompareFailedError` for sorting unavailable

* Split sort into two fields (field and direction)

* Update unsupported sort tests

* Make `updateQuery` callback optional on `SearchPanel`

* Add workload identities list

* Add tests

* Add stories

* Rename nav item to "Workload Identities"

Co-authored-by: Zac Bergquist <zac.bergquist@goteleport.com>

* Revert change to conditional render (SearchPanel)

* Remove mono-spaced text

* Suggested code refinements

* Join spiffe_id page keys with a pipe

* Join spiffe_id page keys with a pipe

* Revert "Join spiffe_id page keys with a pipe"

This reverts commit 842b9cb.

* Base32 hex encode id for cache key

* Add missing useCallback dep

* Fix word break opportunities on Firefox

---------

Co-authored-by: Zac Bergquist <zac.bergquist@goteleport.com>
github-merge-queue bot pushed a commit that referenced this pull request Oct 29, 2025
* feat(webui): Add Workload Identities list (#58971)

* Add ACLs for workload identity

* Add list workload identities to webapi

* Add `newWebPackWithOptions`

* Add sorting by name and spiffe id

* Add filter by search term

* Use `require.ErrorContains`

* Refactor `newWebPackWithOptions`

* Use `t.Context()`

* Add context to uses of `require.NoError` in loops

* Tidy-up

* Un-deprecate `newWebPack`

* Rename `KindWorkloadIdentity`

* Add client-side API support for sort and filter

* Handle endpoint not supported scenario

* Fix cache keys for spiffe id index

* Add and use `ListWorkloadIdentitiesV2` RPC

* Return `CompareFailedError` for sorting unavailable

* Split sort into two fields (field and direction)

* Update unsupported sort tests

* Make `updateQuery` callback optional on `SearchPanel`

* Add workload identities list

* Add tests

* Add stories

* Rename nav item to "Workload Identities"

Co-authored-by: Zac Bergquist <zac.bergquist@goteleport.com>

* Revert change to conditional render (SearchPanel)

* Remove mono-spaced text

* Suggested code refinements

* Join spiffe_id page keys with a pipe

* Join spiffe_id page keys with a pipe

* Revert "Join spiffe_id page keys with a pipe"

This reverts commit 842b9cb.

* Base32 hex encode id for cache key

* Add missing useCallback dep

* Fix word break opportunities on Firefox

---------

Co-authored-by: Zac Bergquist <zac.bergquist@goteleport.com>

* Fix `CopyButton` conflict

---------

Co-authored-by: Zac Bergquist <zac.bergquist@goteleport.com>
dboslee pushed a commit that referenced this pull request Nov 21, 2025
* Add ACLs for workload identity

* Add list workload identities to webapi

* Add `newWebPackWithOptions`

* Add sorting by name and spiffe id

* Add filter by search term

* Use `require.ErrorContains`

* Refactor `newWebPackWithOptions`

* Use `t.Context()`

* Add context to uses of `require.NoError` in loops

* Tidy-up

* Un-deprecate `newWebPack`

* Rename `KindWorkloadIdentity`

* Add client-side API support for sort and filter

* Handle endpoint not supported scenario

* Fix cache keys for spiffe id index

* Add and use `ListWorkloadIdentitiesV2` RPC

* Return `CompareFailedError` for sorting unavailable

* Split sort into two fields (field and direction)

* Update unsupported sort tests

* Make `updateQuery` callback optional on `SearchPanel`

* Add workload identities list

* Add tests

* Add stories

* Rename nav item to "Workload Identities"



* Revert change to conditional render (SearchPanel)

* Remove mono-spaced text

* Suggested code refinements

* Join spiffe_id page keys with a pipe

* Join spiffe_id page keys with a pipe

* Revert "Join spiffe_id page keys with a pipe"

This reverts commit 842b9cb.

* Base32 hex encode id for cache key

* Add missing useCallback dep

* Fix word break opportunities on Firefox

---------

Co-authored-by: Zac Bergquist <zac.bergquist@goteleport.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants