Skip to content

Commit

Permalink
Display permission banner on Enroll Resource page
Browse files Browse the repository at this point in the history
This adds an info banner to the Enroll Resource page if the user has no
permissions to add any resource kind.
  • Loading branch information
avatus committed Jan 3, 2025
1 parent 7a12f63 commit e5b346b
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,32 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import { MemoryRouter } from 'react-router';

import { Platform, UserAgent } from 'design/platform';
import { render, screen, waitFor } from 'design/utils/testing';
import {
OnboardUserPreferences,
Resource,
} from 'gen-proto-ts/teleport/userpreferences/v1/onboard_pb';

import { ContextProvider } from 'teleport/index';
import {
allAccessAcl,
createTeleportContext,
noAccess,
} from 'teleport/mocks/contexts';
import { OnboardDiscover } from 'teleport/services/user';
import { makeDefaultUserPreferences } from 'teleport/services/userPreferences/userPreferences';
import * as userUserContext from 'teleport/User/UserContext';

import { ResourceKind } from '../Shared';
import { resourceKindToPreferredResource } from '../Shared/ResourceKind';
import { filterResources, sortResources } from './SelectResource';
import {
filterResources,
SelectResource,
sortResources,
} from './SelectResource';
import { ResourceSpec } from './types';

const setUp = () => {
Expand Down Expand Up @@ -1112,6 +1126,56 @@ describe('sorting Connect My Computer', () => {
});
});

test('displays an info banner if lacking permissions to add resources', async () => {
jest.spyOn(userUserContext, 'useUser').mockReturnValue({
preferences: makeDefaultUserPreferences(),
updatePreferences: () => null,
updateClusterPinnedResources: () => null,
getClusterPinnedResources: () => null,
});

const ctx = createTeleportContext();
ctx.storeUser.setState({ acl: { ...allAccessAcl, tokens: noAccess } });

render(
<MemoryRouter>
<ContextProvider ctx={ctx}>
<SelectResource onSelect={() => {}} />
</ContextProvider>
</MemoryRouter>
);

await waitFor(() => {
expect(
screen.getByText(/You cannot add new resources./i)
).toBeInTheDocument();
});
});

test('does not display erorr banner if user has permissions to add', async () => {
jest.spyOn(userUserContext, 'useUser').mockReturnValue({
preferences: makeDefaultUserPreferences(),
updatePreferences: () => null,
updateClusterPinnedResources: () => null,
getClusterPinnedResources: () => null,
});

const ctx = createTeleportContext();
ctx.storeUser.setState({ acl: { ...allAccessAcl } });

render(
<MemoryRouter>
<ContextProvider ctx={ctx}>
<SelectResource onSelect={() => {}} />
</ContextProvider>
</MemoryRouter>
);

expect(
screen.queryByText(/You cannot add new resources./i)
).not.toBeInTheDocument();
});

describe('filterResources', () => {
it('filters out resources based on supportedPlatforms', () => {
const winAndLinux = makeResourceSpec({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { useEffect, useState, type ComponentPropsWithoutRef } from 'react';
import { useHistory, useLocation } from 'react-router';
import styled from 'styled-components';

import { Box, Flex, Link, P3, Text } from 'design';
import { Alert, Box, Flex, Link, P3, Text } from 'design';
import * as Icons from 'design/Icon';
import { NewTab } from 'design/Icon';
import { getPlatform, Platform } from 'design/platform';
Expand Down Expand Up @@ -71,12 +71,21 @@ export function SelectResource({ onSelect }: SelectResourceProps) {
const { preferences } = useUser();

const [search, setSearch] = useState('');
const [resources, setResources] = useState<ResourceSpec[]>([]);
const [defaultResources, setDefaultResources] = useState<ResourceSpec[]>([]);
const [showApp, setShowApp] = useState(false);
const RESOURCES = !cfg.isEnterprise
? BASE_RESOURCES
: [...BASE_RESOURCES, ...SAML_APPLICATIONS];
const { acl, authType } = ctx.storeUser.state;
const platform = getPlatform();
const [resources, setResources] = useState<ResourceSpec[]>(
addHasAccessField(acl, filterResources(platform, authType, RESOURCES))
);

// 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 [defaultResources, setDefaultResources] = useState<ResourceSpec[]>([]);
const [showApp, setShowApp] = useState(false);

function onSearch(s: string, customList?: ResourceSpec[]) {
const list = customList || defaultResources;
Expand All @@ -96,14 +105,6 @@ export function SelectResource({ onSelect }: SelectResourceProps) {

useEffect(() => {
// Apply access check to each resource.
const userContext = ctx.storeUser.state;
const { acl, authType } = userContext;
const platform = getPlatform();

const resources = addHasAccessField(
acl,
filterResources(platform, authType, RESOURCES)
);
const onboardDiscover = storageService.getOnboardDiscover();
const sortedResources = sortResources(
resources,
Expand Down Expand Up @@ -147,6 +148,12 @@ export function SelectResource({ onSelect }: SelectResourceProps) {

return (
<Box>
{!canAddResources && (
<Alert kind="info" mt={5}>
You cannot add new resources. Reach out to your Teleport administrator
for additional permissions.
</Alert>
)}
<FeatureHeader>
<FeatureHeaderTitle>Select Resource To Add</FeatureHeaderTitle>
</FeatureHeader>
Expand Down
5 changes: 4 additions & 1 deletion web/packages/teleport/src/features.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,10 @@ export class FeatureIntegrationEnroll implements TeleportFeature {
};

hasAccess(flags: FeatureFlags) {
return flags.enrollIntegrations;
if (cfg.hideInaccessibleFeatures) {
return flags.enrollIntegrations;
}
return true;
}

navigationItem = {
Expand Down

0 comments on commit e5b346b

Please sign in to comment.