Skip to content

Commit

Permalink
Allow labels for generic add web app flow
Browse files Browse the repository at this point in the history
  • Loading branch information
kimlisa committed Jan 8, 2025
1 parent 3f04271 commit 7d16d25
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 46 deletions.
48 changes: 41 additions & 7 deletions web/packages/teleport/src/Apps/AddApp/AddApp.story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,50 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import { useState } from 'react';

import { JoinToken } from 'teleport/services/joinToken';

import { AddApp } from './AddApp';

export default {
title: 'Teleport/Apps/Add',
title: 'Teleport/Discover/Application/Web',
};

export const Created = () => (
<AddApp {...props} attempt={{ status: 'success' }} />
);
export const CreatedWithoutLabels = () => {
const [token, setToken] = useState<JoinToken>();

return (
<AddApp
{...props}
attempt={{ status: 'success' }}
token={token}
createToken={() => {
setToken(props.token);
return Promise.resolve(true);
}}
/>
);
};

export const CreatedWithLabels = () => {
const [token, setToken] = useState<JoinToken>();

export const Loaded = () => {
return <AddApp {...props} />;
return (
<AddApp
{...props}
attempt={{ status: 'success' }}
labels={[
{ name: 'env', value: 'staging' },
{ name: 'fruit', value: 'apple' },
]}
token={token}
createToken={() => {
setToken(props.token);
return Promise.resolve(true);
}}
/>
);
};

export const Processing = () => (
Expand Down Expand Up @@ -72,8 +104,10 @@ const props = {
createJoinToken: () => Promise.resolve(null),
version: '5.0.0-dev',
reset: () => null,
labels: [],
setLabels: () => null,
attempt: {
status: '',
status: 'success',
statusText: '',
} as any,
token: {
Expand Down
4 changes: 4 additions & 0 deletions web/packages/teleport/src/Apps/AddApp/AddApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ export function AddApp({
setAutomatic,
isAuthTypeLocal,
token,
labels,
setLabels,
}: State & Props) {
return (
<Dialog
Expand Down Expand Up @@ -82,6 +84,8 @@ export function AddApp({
onCreate={createToken}
attempt={attempt}
token={token}
labels={labels}
setLabels={setLabels}
/>
)}
{!automatic && (
Expand Down
2 changes: 2 additions & 0 deletions web/packages/teleport/src/Apps/AddApp/Automatically.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ test('render command only after form submit', async () => {
attempt={{ status: 'success' }}
onClose={() => {}}
onCreate={() => Promise.resolve(true)}
labels={[]}
setLabels={() => null}
/>
);

Expand Down
70 changes: 35 additions & 35 deletions web/packages/teleport/src/Apps/AddApp/Automatically.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { KeyboardEvent, useEffect, useState } from 'react';

import {
Alert,
Box,
ButtonPrimary,
ButtonSecondary,
Flex,
Expand All @@ -33,50 +34,40 @@ import { Attempt } from 'shared/hooks/useAttemptNext';

import TextSelectCopy from 'teleport/components/TextSelectCopy';
import cfg from 'teleport/config';
import { LabelsCreater } from 'teleport/Discover/Shared';
import { ResourceLabelTooltip } from 'teleport/Discover/Shared/ResourceLabelTooltip';
import { ResourceLabel } from 'teleport/services/agents';

import { State } from './useAddApp';

export function Automatically(props: Props) {
const { onClose, attempt, token } = props;
const { onClose, attempt, token, labels, setLabels } = props;

const [name, setName] = useState('');
const [uri, setUri] = useState('');
const [cmd, setCmd] = useState('');

useEffect(() => {
if (name && uri) {
if (name && uri && token) {
const cmd = createAppBashCommand(token.id, name, uri);
setCmd(cmd);
}
}, [token]);

function handleRegenerate(validator: Validator) {
function onGenerateScript(validator: Validator) {
if (!validator.validate()) {
return;
}

props.onCreate(name, uri);
}

function handleGenerate(validator: Validator) {
if (!validator.validate()) {
return;
}

const cmd = createAppBashCommand(token.id, name, uri);
setCmd(cmd);
}

function handleEnterPress(
e: KeyboardEvent<HTMLInputElement>,
validator: Validator
) {
if (e.key === 'Enter') {
if (cmd) {
handleRegenerate(validator);
} else {
handleGenerate(validator);
}
onGenerateScript(validator);
}
}

Expand All @@ -96,6 +87,7 @@ export function Automatically(props: Props) {
mr="3"
onKeyPress={e => handleEnterPress(e, validator)}
onChange={e => setName(e.target.value.toLowerCase())}
disabled={attempt.status === 'processing'}
/>
<FieldInput
rule={requiredAppUri}
Expand All @@ -105,8 +97,25 @@ export function Automatically(props: Props) {
placeholder="https://localhost:4000"
onKeyPress={e => handleEnterPress(e, validator)}
onChange={e => setUri(e.target.value)}
disabled={attempt.status === 'processing'}
/>
</Flex>
<Box mt={-3} mb={3}>
<Flex alignItems="center" gap={1} mb={2} mt={4}>
<Text bold>Add Labels (Optional)</Text>
<ResourceLabelTooltip
toolTipPosition="top"
resourceKind="app"
/>
</Flex>
<LabelsCreater
labels={labels}
setLabels={setLabels}
isLabelOptional={true}
disableBtns={attempt.status === 'processing'}
noDuplicateKey={true}
/>
</Box>
{!cmd && (
<Text mb="3">
Teleport can automatically set up application access. Provide
Expand Down Expand Up @@ -136,24 +145,13 @@ export function Automatically(props: Props) {
)}
</DialogContent>
<DialogFooter>
{!cmd && (
<ButtonPrimary
mr="3"
disabled={attempt.status === 'processing'}
onClick={() => handleGenerate(validator)}
>
Generate Script
</ButtonPrimary>
)}
{cmd && (
<ButtonPrimary
mr="3"
disabled={attempt.status === 'processing'}
onClick={() => handleRegenerate(validator)}
>
Regenerate
</ButtonPrimary>
)}
<ButtonPrimary
mr="3"
disabled={attempt.status === 'processing'}
onClick={() => onGenerateScript(validator)}
>
{cmd ? 'Regenerate Script' : 'Generate Script'}
</ButtonPrimary>
<ButtonSecondary
disabled={attempt.status === 'processing'}
onClick={onClose}
Expand Down Expand Up @@ -271,4 +269,6 @@ type Props = {
onCreate(name: string, uri: string): Promise<any>;
token: State['token'];
attempt: Attempt;
labels: ResourceLabel[];
setLabels(r: ResourceLabel[]): void;
};
22 changes: 19 additions & 3 deletions web/packages/teleport/src/Apps/AddApp/useAddApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { useEffect, useState } from 'react';

import useAttempt from 'shared/hooks/useAttemptNext';

import { ResourceLabel } from 'teleport/services/agents';
import type { JoinToken } from 'teleport/services/joinToken';
import TeleportContext from 'teleport/teleportContext';

Expand All @@ -31,14 +32,27 @@ export default function useAddApp(ctx: TeleportContext) {
const isEnterprise = ctx.isEnterprise;
const [automatic, setAutomatic] = useState(isEnterprise);
const [token, setToken] = useState<JoinToken>();
const [labels, setLabels] = useState<ResourceLabel[]>([]);

useEffect(() => {
createToken();
}, []);
// We don't want to create token on first render
// which defaults to the automatic tab because
// user may want to add labels.
if (!automatic) {
setLabels([]);
// When switching to manual tab, token can be re-used
// if token was already generated from automatic tab.
if (!token) {
createToken();
}
}
}, [automatic]);

function createToken() {
return run(() =>
ctx.joinTokenService.fetchJoinToken({ roles: ['App'] }).then(setToken)
ctx.joinTokenService
.fetchJoinToken({ roles: ['App'], suggestedLabels: labels })
.then(setToken)
);
}

Expand All @@ -52,6 +66,8 @@ export default function useAddApp(ctx: TeleportContext) {
isAuthTypeLocal,
isEnterprise,
token,
labels,
setLabels,
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,36 @@ export function ResourceLabelTooltip({
resourceKind,
toolTipPosition,
}: {
resourceKind: 'server' | 'eks' | 'rds' | 'kube' | 'db';
resourceKind: 'server' | 'eks' | 'rds' | 'kube' | 'db' | 'app';
toolTipPosition?: Position;
}) {
let tip;

switch (resourceKind) {
case 'app': {
tip = (
<>
Labels allow you to do the following:
<Ul>
<li>
Filter applications by labels when using tsh, tctl, or the web UI.
</li>
<li>
Restrict access to this application with{' '}
<Link
target="_blank"
href="https://goteleport.com/docs/enroll-resources/application-access/controls/"
>
Teleport RBAC
</Link>
. Only roles with <MarkInverse>app_labels</MarkInverse> that match
these labels will be allowed to access this application.
</li>
</Ul>
</>
);
break;
}
case 'server': {
tip = (
<>
Expand Down

0 comments on commit 7d16d25

Please sign in to comment.