Skip to content

Commit

Permalink
Add remaining role fields to support the built-in roles in the editor
Browse files Browse the repository at this point in the history
  • Loading branch information
bl-nero committed Jan 24, 2025
1 parent 72bc782 commit e946468
Show file tree
Hide file tree
Showing 9 changed files with 191 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ describe('AccessRules', () => {
'list',
'read',
]);
await user.type(screen.getByLabelText('Condition'), 'some-condition');
expect(modelRef).toHaveBeenLastCalledWith([
{
id: expect.any(String),
Expand All @@ -69,6 +70,7 @@ describe('AccessRules', () => {
{ label: 'list', value: 'list' },
{ label: 'read', value: 'read' },
],
where: 'some-condition',
},
] as RuleModel[]);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@

import { memo } from 'react';
import { components, MultiValueProps } from 'react-select';
import styled from 'styled-components';
import styled, { useTheme } from 'styled-components';

import { ButtonSecondary } from 'design/Button';
import Flex from 'design/Flex';
import { Plus } from 'design/Icon';
import Text from 'design/Text';
import { HoverTooltip } from 'design/Tooltip';
import FieldInput from 'shared/components/FieldInput';
import {
FieldSelect,
FieldSelectCreatable,
Expand Down Expand Up @@ -78,7 +80,8 @@ const AccessRule = memo(function AccessRule({
validation,
dispatch,
}: SectionPropsWithDispatch<RuleModel, AccessRuleValidationResult>) {
const { id, resources, verbs } = value;
const { id, resources, verbs, where } = value;
const theme = useTheme();
function setRule(rule: RuleModel) {
dispatch({ type: 'set-access-rule', payload: rule });
}
Expand Down Expand Up @@ -112,6 +115,26 @@ const AccessRule = memo(function AccessRule({
value={verbs}
onChange={v => setRule({ ...value, verbs: v })}
rule={precomputed(validation.fields.verbs)}
/>
<FieldInput
label="Condition"
toolTipContent={
<>
Additional condition, expressed using the{' '}
<Text
as="a"
href="https://goteleport.com/docs/reference/predicate-language/"
target="_blank"
color={theme.colors.interactive.solid.accent.default}
>
Teleport predicate language
</Text>
</>
}
tooltipSticky
disabled={isProcessing}
value={where}
onChange={e => setRule({ ...value, where: e.target.value })}
mb={0}
/>
</SectionBox>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,13 @@ describe('KubernetesAccessSection', () => {
await user.type(screen.getByPlaceholderText('label key'), 'some-key');
await user.type(screen.getByPlaceholderText('label value'), 'some-value');

await selectEvent.create(screen.getByLabelText('Users'), 'joe', {
createOptionText: 'User: joe',
});
await selectEvent.create(screen.getByLabelText('Users'), 'mary', {
createOptionText: 'User: mary',
});

await user.click(screen.getByRole('button', { name: 'Add a Resource' }));
expect(
reactSelectValueContainer(screen.getByLabelText('Kind'))
Expand Down Expand Up @@ -178,6 +185,10 @@ describe('KubernetesAccessSection', () => {
roleVersion: 'v7',
},
],
users: [
expect.objectContaining({ value: 'joe' }),
expect.objectContaining({ value: 'mary' }),
],
roleVersion: 'v7',
} as KubernetesAccess);
});
Expand Down Expand Up @@ -391,9 +402,12 @@ describe('DatabaseAccessSection', () => {

test('editing', async () => {
const { user, onChange } = setup();
await user.click(screen.getByRole('button', { name: 'Add a Label' }));
await user.type(screen.getByPlaceholderText('label key'), 'env');
await user.type(screen.getByPlaceholderText('label value'), 'prod');

const labels = within(screen.getByRole('group', { name: 'Labels' }));
await user.click(labels.getByRole('button', { name: 'Add a Label' }));
await user.type(labels.getByPlaceholderText('label key'), 'env');
await user.type(labels.getByPlaceholderText('label value'), 'prod');

await selectEvent.create(screen.getByLabelText('Database Names'), 'stuff', {
createOptionText: 'Database Name: stuff',
});
Expand All @@ -403,6 +417,16 @@ describe('DatabaseAccessSection', () => {
await selectEvent.create(screen.getByLabelText('Database Roles'), 'admin', {
createOptionText: 'Database Role: admin',
});

const dbServiceLabels = within(
screen.getByRole('group', { name: 'Database Service Labels' })
);
await user.click(
dbServiceLabels.getByRole('button', { name: 'Add a Label' })
);
await user.type(dbServiceLabels.getByPlaceholderText('label key'), 'foo');
await user.type(dbServiceLabels.getByPlaceholderText('label value'), 'bar');

expect(onChange).toHaveBeenLastCalledWith({
kind: 'db',
labels: [{ name: 'env', value: 'prod' }],
Expand All @@ -418,18 +442,29 @@ describe('DatabaseAccessSection', () => {
expect.objectContaining({ value: '{{internal.db_users}}' }),
expect.objectContaining({ label: 'mary', value: 'mary' }),
],
dbServiceLabels: [{ name: 'foo', value: 'bar' }],
} as DatabaseAccess);
});

test('validation', async () => {
const { user, validator } = setup();
await user.click(screen.getByRole('button', { name: 'Add a Label' }));
const labels = within(screen.getByRole('group', { name: 'Labels' }));
await user.click(labels.getByRole('button', { name: 'Add a Label' }));
const dbServiceLabelsGroup = within(
screen.getByRole('group', { name: 'Database Service Labels' })
);
await user.click(
dbServiceLabelsGroup.getByRole('button', { name: 'Add a Label' })
);
await selectEvent.create(screen.getByLabelText('Database Roles'), '*', {
createOptionText: 'Database Role: *',
});
act(() => validator.validate());
expect(
screen.getByPlaceholderText('label key')
labels.getByPlaceholderText('label key')
).toHaveAccessibleDescription('required');
expect(
dbServiceLabelsGroup.getByPlaceholderText('label key')
).toHaveAccessibleDescription('required');
expect(
screen.getByText('Wildcard is not allowed in database roles')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import ButtonIcon from 'design/ButtonIcon';
import Flex from 'design/Flex';
import { Add, Plus, Trash } from 'design/Icon';
import { Mark } from 'design/Mark';
import Text, { H4 } from 'design/Text';
import { H4 } from 'design/Text';
import FieldInput from 'shared/components/FieldInput';
import { FieldMultiInput } from 'shared/components/FieldMultiInput/FieldMultiInput';
import {
Expand Down Expand Up @@ -232,10 +232,8 @@ export function ServerAccessSection({
}: SectionProps<ServerAccess, ServerAccessValidationResult>) {
return (
<>
<Text typography="body3" mb={1}>
Labels
</Text>
<LabelsInput
legend="Labels"
disableBtns={isProcessing}
labels={value.labels}
setLabels={labels => onChange?.({ ...value, labels })}
Expand Down Expand Up @@ -281,10 +279,21 @@ export function KubernetesAccessSection({
onChange={groups => onChange?.({ ...value, groups })}
/>

<Text typography="body3" mb={1}>
Labels
</Text>
<FieldSelectCreatable
isMulti
label="Users"
isDisabled={isProcessing}
formatCreateLabel={label => `User: ${label}`}
components={{
DropdownIndicator: null,
}}
openMenuOnClick={false}
value={value.users}
onChange={users => onChange?.({ ...value, users })}
/>

<LabelsInput
legend="Labels"
disableBtns={isProcessing}
labels={value.labels}
rule={precomputed(validation.fields.labels)}
Expand Down Expand Up @@ -433,17 +442,13 @@ export function AppAccessSection({
}: SectionProps<AppAccess, AppAccessValidationResult>) {
return (
<Flex flexDirection="column" gap={3}>
<Box>
<Text typography="body3" mb={1}>
Labels
</Text>
<LabelsInput
disableBtns={isProcessing}
labels={value.labels}
setLabels={labels => onChange?.({ ...value, labels })}
rule={precomputed(validation.fields.labels)}
/>
</Box>
<LabelsInput
legend="Labels"
disableBtns={isProcessing}
labels={value.labels}
setLabels={labels => onChange?.({ ...value, labels })}
rule={precomputed(validation.fields.labels)}
/>
<FieldMultiInput
label="AWS Role ARNs"
disabled={isProcessing}
Expand Down Expand Up @@ -478,10 +483,8 @@ export function DatabaseAccessSection({
return (
<>
<Box mb={3}>
<Text typography="body3" mb={1}>
Labels
</Text>
<LabelsInput
legend="Labels"
disableBtns={isProcessing}
labels={value.labels}
setLabels={labels => onChange?.({ ...value, labels })}
Expand Down Expand Up @@ -537,7 +540,14 @@ export function DatabaseAccessSection({
value={value.roles}
onChange={roles => onChange?.({ ...value, roles })}
rule={precomputed(validation.fields.roles)}
mb={0}
/>
<LabelsInput
legend="Database Service Labels"
tooltipContent="The database service labels have no influence on which databases' access is controlled by this role. Instead, they control which database services are discoverable while enrolling a new database."
disableBtns={isProcessing}
labels={value.dbServiceLabels}
setLabels={dbServiceLabels => onChange?.({ ...value, dbServiceLabels })}
rule={precomputed(validation.fields.dbServiceLabels)}
/>
</>
);
Expand All @@ -552,10 +562,8 @@ export function WindowsDesktopAccessSection({
return (
<>
<Box mb={3}>
<Text typography="body3" mb={1}>
Labels
</Text>
<LabelsInput
legend="Labels"
disableBtns={isProcessing}
labels={value.labels}
setLabels={labels => onChange?.({ ...value, labels })}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ describe.each<{ name: string; role: Role; model: RoleEditorModel }>([
db_names: ['stuff', 'knickknacks'],
db_users: ['joe', 'mary'],
db_roles: ['admin', 'auditor'],
db_service_labels: { foo: 'bar' },
},
},
},
Expand All @@ -218,6 +219,7 @@ describe.each<{ name: string; role: Role; model: RoleEditorModel }>([
{ label: 'admin', value: 'admin' },
{ label: 'auditor', value: 'auditor' },
],
dbServiceLabels: [{ name: 'foo', value: 'bar' }],
},
],
},
Expand Down Expand Up @@ -440,6 +442,7 @@ describe('roleToRoleEditorModel', () => {
groups: [],
labels: [],
resources: [],
users: [],
roleVersion: defaultRoleVersion,
});

Expand Down Expand Up @@ -868,6 +871,7 @@ describe('roleToRoleEditorModel', () => {
name: 'some-node',
},
],
kubernetes_users: ['alice', 'bob'],
},
},
})
Expand Down Expand Up @@ -902,6 +906,10 @@ describe('roleToRoleEditorModel', () => {
roleVersion: defaultRoleVersion,
},
],
users: [
{ label: 'alice', value: 'alice' },
{ label: 'bob', value: 'bob' },
],
roleVersion: defaultRoleVersion,
},
],
Expand Down Expand Up @@ -948,6 +956,11 @@ describe('roleToRoleEditorModel', () => {
verbs: ['read', 'list'],
},
{ resources: [ResourceKind.Lock], verbs: ['create'] },
{
resources: [ResourceKind.Session],
verbs: ['read', 'list'],
where: 'contains(session.participants, user.metadata.name)',
},
],
},
},
Expand All @@ -962,11 +975,19 @@ describe('roleToRoleEditorModel', () => {
resourceKindOptionsMap.get(ResourceKind.DatabaseService),
],
verbs: [verbOptionsMap.get('read'), verbOptionsMap.get('list')],
where: '',
},
{
id: expect.any(String),
resources: [resourceKindOptionsMap.get(ResourceKind.Lock)],
verbs: [verbOptionsMap.get('create')],
where: '',
},
{
id: expect.any(String),
resources: [resourceKindOptionsMap.get(ResourceKind.Session)],
verbs: [verbOptionsMap.get('read'), verbOptionsMap.get('list')],
where: 'contains(session.participants, user.metadata.name)',
},
],
} as RoleEditorModel);
Expand Down Expand Up @@ -1042,6 +1063,10 @@ describe('roleEditorModelToRole', () => {
roleVersion: defaultRoleVersion,
},
],
users: [
{ label: 'alice', value: 'alice' },
{ label: 'bob', value: 'bob' },
],
roleVersion: defaultRoleVersion,
},
],
Expand All @@ -1067,6 +1092,7 @@ describe('roleEditorModelToRole', () => {
verbs: [],
},
],
kubernetes_users: ['alice', 'bob'],
},
},
} as Role);
Expand All @@ -1084,11 +1110,19 @@ describe('roleEditorModelToRole', () => {
resourceKindOptionsMap.get(ResourceKind.DatabaseService),
],
verbs: [verbOptionsMap.get('read'), verbOptionsMap.get('list')],
where: '',
},
{
id: 'dummy-id-2',
resources: [resourceKindOptionsMap.get(ResourceKind.Lock)],
verbs: [verbOptionsMap.get('create')],
where: '',
},
{
id: expect.any(String),
resources: [resourceKindOptionsMap.get(ResourceKind.Session)],
verbs: [verbOptionsMap.get('read'), verbOptionsMap.get('list')],
where: 'contains(session.participants, user.metadata.name)',
},
],
})
Expand All @@ -1100,6 +1134,11 @@ describe('roleEditorModelToRole', () => {
rules: [
{ resources: ['user', 'db_service'], verbs: ['read', 'list'] },
{ resources: ['lock'], verbs: ['create'] },
{
resources: ['session'],
verbs: ['read', 'list'],
where: 'contains(session.participants, user.metadata.name)',
},
],
},
},
Expand Down
Loading

0 comments on commit e946468

Please sign in to comment.