diff --git a/web/packages/teleport/src/Discover/SelectResource/SelectResource.tsx b/web/packages/teleport/src/Discover/SelectResource/SelectResource.tsx index d8fa09e0645e3..0efc30f646a22 100644 --- a/web/packages/teleport/src/Discover/SelectResource/SelectResource.tsx +++ b/web/packages/teleport/src/Discover/SelectResource/SelectResource.tsx @@ -16,7 +16,12 @@ * along with this program. If not, see . */ -import { useEffect, useState, type ComponentPropsWithoutRef } from 'react'; +import { + useEffect, + useMemo, + useState, + type ComponentPropsWithoutRef, +} from 'react'; import { useHistory, useLocation } from 'react-router'; import styled from 'styled-components'; @@ -64,6 +69,15 @@ type UrlLocationState = { searchKeywords: string; }; +function getDefaultResources( + includeEnterpriseResources: boolean +): ResourceSpec[] { + const RESOURCES = includeEnterpriseResources + ? BASE_RESOURCES + : [...BASE_RESOURCES, ...SAML_APPLICATIONS]; + return RESOURCES; +} + export function SelectResource({ onSelect }: SelectResourceProps) { const ctx = useTeleport(); const location = useLocation(); @@ -71,20 +85,34 @@ export function SelectResource({ onSelect }: SelectResourceProps) { const { preferences } = useUser(); const [search, setSearch] = useState(''); - const RESOURCES = !cfg.isEnterprise - ? BASE_RESOURCES - : [...BASE_RESOURCES, ...SAML_APPLICATIONS]; const { acl, authType } = ctx.storeUser.state; const platform = getPlatform(); - const [resources, setResources] = useState( - addHasAccessField(acl, filterResources(platform, authType, RESOURCES)) + const defaultResources: ResourceSpec[] = useMemo( + () => + sortResources( + // Apply access check to each resource. + addHasAccessField( + acl, + filterResources( + platform, + authType, + getDefaultResources(cfg.isEnterprise) + ) + ), + preferences, + storageService.getOnboardDiscover() + ), + [acl, authType, platform, preferences] ); + const [resources, setResources] = useState(defaultResources); // a user must be able to create tokens AND have access to create at least one // type of resource in order to be considered eligible to "add resources" - const canAddResources = acl.tokens.create && resources.some(r => r.hasAccess); + const canAddResources = useMemo( + () => acl.tokens.create && defaultResources.some(r => r.hasAccess), + [acl, defaultResources] + ); - const [defaultResources, setDefaultResources] = useState([]); const [showApp, setShowApp] = useState(false); function onSearch(s: string, customList?: ResourceSpec[]) { @@ -104,15 +132,6 @@ export function SelectResource({ onSelect }: SelectResourceProps) { } useEffect(() => { - // Apply access check to each resource. - const onboardDiscover = storageService.getOnboardDiscover(); - const sortedResources = sortResources( - resources, - preferences, - onboardDiscover - ); - setDefaultResources(sortedResources); - // A user can come to this screen by clicking on // a `add ` button. // We sort the list by the specified resource type, @@ -128,7 +147,7 @@ export function SelectResource({ onSelect }: SelectResourceProps) { ) { const sortedResourcesByKind = sortResourcesByKind( resourceKindSpecifiedByUrlLoc, - sortedResources + defaultResources ); onSearch(resourceKindSpecifiedByUrlLoc, sortedResourcesByKind); return; @@ -136,11 +155,11 @@ export function SelectResource({ onSelect }: SelectResourceProps) { const searchKeywordSpecifiedByUrlLoc = location.state?.searchKeywords; if (searchKeywordSpecifiedByUrlLoc) { - onSearch(searchKeywordSpecifiedByUrlLoc, sortedResources); + onSearch(searchKeywordSpecifiedByUrlLoc, defaultResources); return; } - setResources(sortedResources); + setResources(defaultResources); // Processing of the lists should only happen once on init. // User perms remain static and URL loc state does not change. // eslint-disable-next-line react-hooks/exhaustive-deps