mirror of
https://github.com/gravitational/teleport.git
synced 2026-10-31 03:27:49 +08:00
WebDiscover: Enroll New AWS Integration Flow (#24459)
* Enroll new AWS integration flow * Move IntegrationEnroll/* into Integration/Enroll/* * Rename AWS into AwsOidc, story expects pascal case but ran into non-story issues where casing was a problem even renaming AWS to Aws wasn't enough, i don't know why * Capitalize browser directory to Browser * Remove passing in clusterId for integration requests Also define IntegrationKind as enum string values to avoid hard coding it all over. * Add sub routing for lazy loading AwsOidc comp * Fix some styling, remove unused props, and add story * Add a success dialog after creating integration * Remove first in wording for now * Replace teleport.lol with root cluster public URI * revert renaming browser to Browser * Fix lint: add license headers --------- Co-authored-by: Ryan Clark <ryan.clark@goteleport.com>
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
Copyright 2023 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import type { SVGIconProps } from './common';
|
||||
|
||||
export function AWSIcon({ size = 24, fill = 'white' }: SVGIconProps) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 304 182"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={size}
|
||||
height={size}
|
||||
fill={fill}
|
||||
>
|
||||
<path
|
||||
fill="white"
|
||||
d="M86.4 66.4c0 3.7.4 6.7 1.1 8.9.8 2.2 1.8 4.6 3.2 7.2.5.8.7 1.6.7 2.3 0 1-.6 2-1.9 3L83.2 92c-.9.6-1.8.9-2.6.9-1 0-2-.5-3-1.4-1.4-1.5-2.6-3.1-3.6-4.7-1-1.7-2-3.6-3.1-5.9-7.8 9.2-17.6 13.8-29.4 13.8-8.4 0-15.1-2.4-20-7.2-4.9-4.8-7.4-11.2-7.4-19.2 0-8.5 3-15.4 9.1-20.6 6.1-5.2 14.2-7.8 24.5-7.8 3.4 0 6.9.3 10.6.8 3.7.5 7.5 1.3 11.5 2.2v-7.3c0-7.6-1.6-12.9-4.7-16-3.2-3.1-8.6-4.6-16.3-4.6-3.5 0-7.1.4-10.8 1.3-3.7.9-7.3 2-10.8 3.4-1.6.7-2.8 1.1-3.5 1.3-.7.2-1.2.3-1.6.3-1.4 0-2.1-1-2.1-3.1v-4.9c0-1.6.2-2.8.7-3.5.5-.7 1.4-1.4 2.8-2.1 3.5-1.8 7.7-3.3 12.6-4.5C41 1.9 46.2 1.3 51.7 1.3c11.9 0 20.6 2.7 26.2 8.1 5.5 5.4 8.3 13.6 8.3 24.6v32.4zM45.8 81.6c3.3 0 6.7-.6 10.3-1.8 3.6-1.2 6.8-3.4 9.5-6.4 1.6-1.9 2.8-4 3.4-6.4.6-2.4 1-5.3 1-8.7v-4.2c-2.9-.7-6-1.3-9.2-1.7-3.2-.4-6.3-.6-9.4-.6-6.7 0-11.6 1.3-14.9 4-3.3 2.7-4.9 6.5-4.9 11.5 0 4.7 1.2 8.2 3.7 10.6 2.4 2.5 5.9 3.7 10.5 3.7zm80.3 10.8c-1.8 0-3-.3-3.8-1-.8-.6-1.5-2-2.1-3.9L96.7 10.2c-.6-2-.9-3.3-.9-4 0-1.6.8-2.5 2.4-2.5h9.8c1.9 0 3.2.3 3.9 1 .8.6 1.4 2 2 3.9l16.8 66.2 15.6-66.2c.5-2 1.1-3.3 1.9-3.9.8-.6 2.2-1 4-1h8c1.9 0 3.2.3 4 1 .8.6 1.5 2 1.9 3.9l15.8 67 17.3-67c.6-2 1.3-3.3 2-3.9.8-.6 2.1-1 3.9-1h9.3c1.6 0 2.5.8 2.5 2.5 0 .5-.1 1-.2 1.6-.1.6-.3 1.4-.7 2.5l-24.1 77.3c-.6 2-1.3 3.3-2.1 3.9-.8.6-2.1 1-3.8 1h-8.6c-1.9 0-3.2-.3-4-1-.8-.7-1.5-2-1.9-4L156 23l-15.4 64.4c-.5 2-1.1 3.3-1.9 4-.8.7-2.2 1-4 1h-8.6zm128.5 2.7c-5.2 0-10.4-.6-15.4-1.8-5-1.2-8.9-2.5-11.5-4-1.6-.9-2.7-1.9-3.1-2.8-.4-.9-.6-1.9-.6-2.8v-5.1c0-2.1.8-3.1 2.3-3.1.6 0 1.2.1 1.8.3.6.2 1.5.6 2.5 1 3.4 1.5 7.1 2.7 11 3.5 4 .8 7.9 1.2 11.9 1.2 6.3 0 11.2-1.1 14.6-3.3 3.4-2.2 5.2-5.4 5.2-9.5 0-2.8-.9-5.1-2.7-7-1.8-1.9-5.2-3.6-10.1-5.2L246 52c-7.3-2.3-12.7-5.7-16-10.2-3.3-4.4-5-9.3-5-14.5 0-4.2.9-7.9 2.7-11.1 1.8-3.2 4.2-6 7.2-8.2 3-2.3 6.4-4 10.4-5.2 4-1.2 8.2-1.7 12.6-1.7 2.2 0 4.5.1 6.7.4 2.3.3 4.4.7 6.5 1.1 2 .5 3.9 1 5.7 1.6 1.8.6 3.2 1.2 4.2 1.8 1.4.8 2.4 1.6 3 2.5.6.8.9 1.9.9 3.3v4.7c0 2.1-.8 3.2-2.3 3.2-.8 0-2.1-.4-3.8-1.2-5.7-2.6-12.1-3.9-19.2-3.9-5.7 0-10.2.9-13.3 2.8-3.1 1.9-4.7 4.8-4.7 8.9 0 2.8 1 5.2 3 7.1 2 1.9 5.7 3.8 11 5.5l14.2 4.5c7.2 2.3 12.4 5.5 15.5 9.6 3.1 4.1 4.6 8.8 4.6 14 0 4.3-.9 8.2-2.6 11.6-1.8 3.4-4.2 6.4-7.3 8.8-3.1 2.5-6.8 4.3-11.1 5.6-4.5 1.4-9.2 2.1-14.3 2.1z"
|
||||
/>
|
||||
<path
|
||||
d="M273.5 143.7c-32.9 24.3-80.7 37.2-121.8 37.2-57.6 0-109.5-21.3-148.7-56.7-3.1-2.8-.3-6.6 3.4-4.4 42.4 24.6 94.7 39.5 148.8 39.5 36.5 0 76.6-7.6 113.5-23.2 5.5-2.5 10.2 3.6 4.8 7.6z"
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
fill="#f90"
|
||||
/>
|
||||
<path
|
||||
d="M287.2 128.1c-4.2-5.4-27.8-2.6-38.5-1.3-3.2.4-3.7-2.4-.8-4.5 18.8-13.2 49.7-9.4 53.3-5 3.6 4.5-1 35.4-18.6 50.2-2.7 2.3-5.3 1.1-4.1-1.9 4-9.9 12.9-32.2 8.7-37.5z"
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
fill="#f90"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -20,6 +20,7 @@ export { AddIcon } from './Add';
|
||||
export { ApplicationsIcon } from './Applications';
|
||||
export { AuditLogIcon } from './AuditLog';
|
||||
export { AuthConnectorsIcon } from './AuthConnectors';
|
||||
export { AWSIcon } from './AWS';
|
||||
export { LockIcon } from './Lock';
|
||||
export { ChevronRightIcon } from './ChevronRight';
|
||||
export { DatabasesIcon } from './Databases';
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { MemoryRouter } from 'react-router';
|
||||
|
||||
import { createTeleportContext } from 'teleport/mocks/contexts';
|
||||
import { ContextProvider } from 'teleport';
|
||||
|
||||
import { AwsOidc } from './AwsOidc';
|
||||
|
||||
export default {
|
||||
title: 'Teleport/Integrations/Enroll/AwsOidc',
|
||||
};
|
||||
|
||||
export const Flow = () => (
|
||||
<Provider>
|
||||
<AwsOidc />
|
||||
</Provider>
|
||||
);
|
||||
|
||||
const Provider = props => {
|
||||
const ctx = createTeleportContext({ customAcl: props.customAcl });
|
||||
|
||||
return (
|
||||
<MemoryRouter>
|
||||
<ContextProvider ctx={ctx}>{props.children}</ContextProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,361 @@
|
||||
/**
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { SwitchTransition, Transition } from 'react-transition-group';
|
||||
|
||||
import { Header, HeaderSubtitle } from 'teleport/Discover/Shared';
|
||||
import { Browser } from 'teleport/Integrations/Enroll/AwsOidc/browser/Browser';
|
||||
import { IAMHomeScreen } from 'teleport/Integrations/Enroll/AwsOidc/IAM/IAMHomeScreen';
|
||||
import { Cursor } from 'teleport/Integrations/Enroll/AwsOidc/browser/Cursor';
|
||||
import { IAMIdentityProvidersScreen } from 'teleport/Integrations/Enroll/AwsOidc/IAM/IAMIdentityProvidersScreen';
|
||||
import { IAMNewProviderScreen } from 'teleport/Integrations/Enroll/AwsOidc/IAM/IAMNewProviderScreen';
|
||||
import { FirstStageInstructions } from 'teleport/Integrations/Enroll/AwsOidc/instructions/FirstStageInstructions';
|
||||
import { SecondStageInstructions } from 'teleport/Integrations/Enroll/AwsOidc/instructions/SecondStageInstructions';
|
||||
|
||||
import { ThirdStageInstructions } from 'teleport/Integrations/Enroll/AwsOidc/instructions/ThirdStageInstructions';
|
||||
import { IAMProvider } from 'teleport/Integrations/Enroll/AwsOidc/IAM/IAMProvider';
|
||||
|
||||
import { IAMCreateNewRole } from 'teleport/Integrations/Enroll/AwsOidc/IAM/IAMCreateNewRole';
|
||||
import { FourthStageInstructions } from 'teleport/Integrations/Enroll/AwsOidc/instructions/FourthStageInstructions';
|
||||
import { IAMCreateNewRolePermissions } from 'teleport/Integrations/Enroll/AwsOidc/IAM/IAMCreateNewRolePermissions';
|
||||
import { FifthStageInstructions } from 'teleport/Integrations/Enroll/AwsOidc/instructions/FifthStageInstructions';
|
||||
import { IAMCreateNewPolicy } from 'teleport/Integrations/Enroll/AwsOidc/IAM/IAMCreateNewPolicy';
|
||||
import { SixthStageInstructions } from 'teleport/Integrations/Enroll/AwsOidc/instructions/SixthStageInstructions';
|
||||
|
||||
import { SeventhStageInstructions } from 'teleport/Integrations/Enroll/AwsOidc/instructions/SeventhStageInstructions';
|
||||
import { IAMRoles } from 'teleport/Integrations/Enroll/AwsOidc/IAM/IAMRoles';
|
||||
import useTeleport from 'teleport/useTeleport';
|
||||
|
||||
import { Stage, STAGES } from './stages';
|
||||
|
||||
const Container = styled.div`
|
||||
padding-right: 40px;
|
||||
padding-top: 16px;
|
||||
`;
|
||||
|
||||
const InstructionsContainer = styled.div`
|
||||
display: flex;
|
||||
margin-top: 50px;
|
||||
`;
|
||||
|
||||
const BrowserContainer = styled.div`
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
const RestartAnimation = styled.div`
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
opacity: ${p => (p.visible ? 1 : 0)};
|
||||
transition: 0.2s ease-in-out opacity;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
border-radius: 5px;
|
||||
padding: 5px 10px;
|
||||
cursor: pointer;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
box-shadow: 0 0 15px rgba(0, 0, 0, 0.3);
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 0 15px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
`;
|
||||
|
||||
const defaultStyle = {
|
||||
transition: 'opacity 250ms, transform 250ms',
|
||||
opacity: 0,
|
||||
width: '100%',
|
||||
};
|
||||
|
||||
const horizontalTransitionStyles = {
|
||||
entering: { opacity: 0, transform: 'translateX(50px)' },
|
||||
entered: { opacity: 1, transform: 'translateX(0%)' },
|
||||
exiting: { opacity: 0, transform: 'translateX(-50px)' },
|
||||
exited: { opacity: 0, transform: 'translateX(-50px)' },
|
||||
};
|
||||
|
||||
enum InstructionStep {
|
||||
First,
|
||||
Second,
|
||||
Third,
|
||||
Fourth,
|
||||
Fifth,
|
||||
Sixth,
|
||||
Seventh,
|
||||
}
|
||||
|
||||
export function AwsOidc() {
|
||||
const ctx = useTeleport();
|
||||
let clusterPublicUri = ctx.storeUser.state.cluster.publicURL;
|
||||
|
||||
const [stage, setStage] = useState(Stage.Initial);
|
||||
const [showRestartAnimation, setShowRestartAnimation] = useState(false);
|
||||
|
||||
const currentStageIndex = STAGES.findIndex(s => s.kind === stage);
|
||||
const currentStage = STAGES[currentStageIndex];
|
||||
const currentStageConfig = getStageConfig(stage);
|
||||
|
||||
const restartAnimation = useCallback(() => {
|
||||
setStage(currentStageConfig.restartStage);
|
||||
setShowRestartAnimation(false);
|
||||
}, [currentStageConfig]);
|
||||
|
||||
useEffect(() => {
|
||||
if (currentStage.end) {
|
||||
setShowRestartAnimation(true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (showRestartAnimation) {
|
||||
setShowRestartAnimation(false);
|
||||
}
|
||||
|
||||
if (currentStage.duration && STAGES[currentStageIndex + 1]) {
|
||||
const id = window.setTimeout(
|
||||
() => setStage(STAGES[currentStageIndex + 1].kind),
|
||||
currentStage.duration
|
||||
);
|
||||
|
||||
return () => window.clearTimeout(id);
|
||||
}
|
||||
}, [currentStage, currentStageIndex, showRestartAnimation]);
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Header>Set up your AWS account</Header>
|
||||
|
||||
<HeaderSubtitle>
|
||||
Instead of storing long-lived static credentials, Teleport will become a
|
||||
trusted OIDC provider with AWS to be able to request short lived
|
||||
credentials when performing operations automatically.
|
||||
</HeaderSubtitle>
|
||||
|
||||
<InstructionsContainer>
|
||||
<SwitchTransition mode="out-in">
|
||||
<Transition
|
||||
key={currentStageConfig.instructionStep}
|
||||
timeout={250}
|
||||
mountOnEnter
|
||||
unmountOnExit
|
||||
>
|
||||
{state => (
|
||||
<div
|
||||
style={{
|
||||
...defaultStyle,
|
||||
...horizontalTransitionStyles[state],
|
||||
}}
|
||||
>
|
||||
{currentStageConfig.instructionStep ===
|
||||
InstructionStep.First && (
|
||||
<FirstStageInstructions
|
||||
onNext={() => {
|
||||
setStage(Stage.NewProviderFullScreen);
|
||||
}}
|
||||
clusterPublicUri={clusterPublicUri}
|
||||
/>
|
||||
)}
|
||||
{currentStageConfig.instructionStep ===
|
||||
InstructionStep.Second && (
|
||||
<SecondStageInstructions
|
||||
onNext={() => {
|
||||
setStage(Stage.AddProvider);
|
||||
}}
|
||||
clusterPublicUri={clusterPublicUri}
|
||||
/>
|
||||
)}
|
||||
{currentStageConfig.instructionStep ===
|
||||
InstructionStep.Third && (
|
||||
<ThirdStageInstructions
|
||||
onNext={() => {
|
||||
setStage(Stage.CreateNewRole);
|
||||
}}
|
||||
clusterPublicUri={clusterPublicUri}
|
||||
/>
|
||||
)}
|
||||
{currentStageConfig.instructionStep ===
|
||||
InstructionStep.Fourth && (
|
||||
<FourthStageInstructions
|
||||
onNext={() => {
|
||||
setStage(Stage.CreatePolicy);
|
||||
}}
|
||||
clusterPublicUri={clusterPublicUri}
|
||||
/>
|
||||
)}
|
||||
{currentStageConfig.instructionStep ===
|
||||
InstructionStep.Fifth && (
|
||||
<FifthStageInstructions
|
||||
onNext={() => {
|
||||
setStage(Stage.AssignPolicyToRole);
|
||||
}}
|
||||
clusterPublicUri={clusterPublicUri}
|
||||
/>
|
||||
)}
|
||||
{currentStageConfig.instructionStep ===
|
||||
InstructionStep.Sixth && (
|
||||
<SixthStageInstructions
|
||||
onNext={() => {
|
||||
setStage(Stage.ListRoles);
|
||||
}}
|
||||
clusterPublicUri={clusterPublicUri}
|
||||
/>
|
||||
)}
|
||||
{currentStageConfig.instructionStep ===
|
||||
InstructionStep.Seventh && <SeventhStageInstructions />}
|
||||
</div>
|
||||
)}
|
||||
</Transition>
|
||||
</SwitchTransition>
|
||||
|
||||
<BrowserContainer>
|
||||
<Browser stage={stage}>
|
||||
<Cursor
|
||||
top={currentStage.cursor.top}
|
||||
left={currentStage.cursor.left}
|
||||
click={currentStage.cursor.click}
|
||||
/>
|
||||
{getStageComponent(stage, clusterPublicUri)}
|
||||
</Browser>
|
||||
|
||||
<RestartAnimation
|
||||
visible={showRestartAnimation}
|
||||
onClick={() => restartAnimation()}
|
||||
>
|
||||
Restart animation
|
||||
</RestartAnimation>
|
||||
</BrowserContainer>
|
||||
</InstructionsContainer>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
function getStageComponent(stage: Stage, uri: string) {
|
||||
let clusterPublicUri = uri;
|
||||
// Truncate long URI's so it doesn't mess up the animation screens.
|
||||
if (clusterPublicUri.length > 30) {
|
||||
clusterPublicUri = `${clusterPublicUri.substring(0, 30)}...`;
|
||||
}
|
||||
const props = { stage, clusterPublicUri };
|
||||
|
||||
if (stage >= Stage.Initial && stage <= Stage.ClickIdentityProviders) {
|
||||
return <IAMHomeScreen />;
|
||||
}
|
||||
|
||||
if (stage >= Stage.IdentityProviders && stage <= Stage.ClickAddProvider) {
|
||||
return <IAMIdentityProvidersScreen {...props} />;
|
||||
}
|
||||
|
||||
if (stage >= Stage.NewProvider && stage <= Stage.AddProvider) {
|
||||
return <IAMNewProviderScreen {...props} />;
|
||||
}
|
||||
|
||||
if (stage >= Stage.ProviderAdded && stage <= Stage.SelectProvider) {
|
||||
return <IAMIdentityProvidersScreen {...props} />;
|
||||
}
|
||||
|
||||
if (stage >= Stage.ProviderView && stage <= Stage.ClickCreateNewRole) {
|
||||
return <IAMProvider {...props} />;
|
||||
}
|
||||
|
||||
if (stage >= Stage.CreateNewRole && stage <= Stage.ClickNextPermissions) {
|
||||
return <IAMCreateNewRole {...props} />;
|
||||
}
|
||||
|
||||
if (
|
||||
stage >= Stage.ConfigureRolePermissions &&
|
||||
stage <= Stage.ClickCreatePolicy
|
||||
) {
|
||||
return <IAMCreateNewRolePermissions {...props} />;
|
||||
}
|
||||
|
||||
if (stage >= Stage.CreatePolicy && stage <= Stage.ClickCreatePolicyButton) {
|
||||
return <IAMCreateNewPolicy {...props} />;
|
||||
}
|
||||
|
||||
if (
|
||||
stage >= Stage.AssignPolicyToRole &&
|
||||
stage <= Stage.ClickCreateRoleButton
|
||||
) {
|
||||
return <IAMCreateNewRolePermissions {...props} />;
|
||||
}
|
||||
|
||||
if (stage >= Stage.ListRoles) {
|
||||
return <IAMRoles {...props} />;
|
||||
}
|
||||
}
|
||||
|
||||
function getStageConfig(stage: Stage) {
|
||||
if (stage >= Stage.Initial && stage <= Stage.NewProvider) {
|
||||
return {
|
||||
instructionStep: InstructionStep.First,
|
||||
restartStage: Stage.Initial,
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
stage >= Stage.NewProviderFullScreen &&
|
||||
stage <= Stage.ThumbprintSelected
|
||||
) {
|
||||
return {
|
||||
instructionStep: InstructionStep.Second,
|
||||
restartStage: Stage.NewProviderFullScreen,
|
||||
};
|
||||
}
|
||||
|
||||
if (stage >= Stage.AddProvider && stage <= Stage.ClickCreateNewRole) {
|
||||
return {
|
||||
instructionStep: InstructionStep.Third,
|
||||
restartStage: Stage.AddProvider,
|
||||
};
|
||||
}
|
||||
|
||||
if (stage >= Stage.CreateNewRole && stage <= Stage.ClickCreatePolicy) {
|
||||
return {
|
||||
instructionStep: InstructionStep.Fourth,
|
||||
restartStage: Stage.CreateNewRole,
|
||||
};
|
||||
}
|
||||
|
||||
if (stage >= Stage.CreatePolicy && stage <= Stage.ClickCreatePolicyButton) {
|
||||
return {
|
||||
instructionStep: InstructionStep.Fifth,
|
||||
restartStage: Stage.CreatePolicy,
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
stage >= Stage.AssignPolicyToRole &&
|
||||
stage <= Stage.ClickCreateRoleButton
|
||||
) {
|
||||
return {
|
||||
instructionStep: InstructionStep.Sixth,
|
||||
restartStage: Stage.AssignPolicyToRole,
|
||||
};
|
||||
}
|
||||
|
||||
if (stage >= Stage.ListRoles) {
|
||||
return {
|
||||
instructionStep: InstructionStep.Seventh,
|
||||
restartStage: Stage.ListRoles,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { AddIcon } from 'design/SVGIcon';
|
||||
|
||||
import { NextButton } from './common';
|
||||
|
||||
const Modal = styled.div`
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
z-index: 9;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
const AssignRole = styled.div<{ active: boolean }>`
|
||||
border-radius: 4px;
|
||||
border: 1px solid #bdbdbd;
|
||||
overflow: hidden;
|
||||
width: 500px;
|
||||
`;
|
||||
|
||||
const AssignRoleClose = styled.div`
|
||||
transform: rotate(45deg);
|
||||
position: relative;
|
||||
top: 2px;
|
||||
`;
|
||||
|
||||
const AssignRoleHeader = styled.div`
|
||||
background: #eee;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 16px;
|
||||
padding: 10px 20px;
|
||||
border-bottom: 1px solid #dddddd;
|
||||
`;
|
||||
|
||||
const AssignRoleFooter = styled.div`
|
||||
background: #eee;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
font-size: 16px;
|
||||
padding: 10px 20px;
|
||||
border-top: 1px solid #dddddd;
|
||||
`;
|
||||
|
||||
const AssignRoleContent = styled.div`
|
||||
background: white;
|
||||
padding: 10px 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
`;
|
||||
|
||||
const AssignRoleOption = styled.div<{ active: boolean }>`
|
||||
display: flex;
|
||||
background: ${p => (p.active ? '#f0f9ff' : 'white')};
|
||||
border: 1px solid ${p => (p.active ? '#99c2ee' : '#e6e6e6')};
|
||||
border-radius: 4px;
|
||||
flex: 0 0 200px;
|
||||
padding: 8px 12px 10px;
|
||||
`;
|
||||
|
||||
const AssignRoleOptionBulletContainer = styled.div`
|
||||
flex: 0 0 25px;
|
||||
padding: 4px 0;
|
||||
`;
|
||||
|
||||
const AssignRoleOptionBullet = styled.div<{ active: boolean }>`
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
background: ${p => (p.active ? '#1066bb' : '#cccccc')};
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
&:after {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: ${p => (p.active ? 6 : 11)}px;
|
||||
height: ${p => (p.active ? 6 : 11)}px;
|
||||
content: '';
|
||||
background: white;
|
||||
border-radius: 50%;
|
||||
}
|
||||
`;
|
||||
|
||||
const CancelButton = styled.div`
|
||||
color: #1869bb;
|
||||
padding: 5px 15px;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
margin-right: 10px;
|
||||
`;
|
||||
|
||||
const AssignRoleOptionDescription = styled.div`
|
||||
font-size: 12px;
|
||||
line-height: 1.2;
|
||||
color: #999;
|
||||
`;
|
||||
|
||||
export function AssignRoleModal({
|
||||
clusterPublicUri,
|
||||
}: {
|
||||
clusterPublicUri: string;
|
||||
}) {
|
||||
return (
|
||||
<Modal>
|
||||
<div>
|
||||
<AssignRole>
|
||||
<AssignRoleHeader>
|
||||
Assign role for {clusterPublicUri}
|
||||
<AssignRoleClose>
|
||||
<AddIcon fill="#444444" size={16} />
|
||||
</AssignRoleClose>
|
||||
</AssignRoleHeader>
|
||||
<AssignRoleContent>
|
||||
<AssignRoleOption active={true}>
|
||||
<AssignRoleOptionBulletContainer>
|
||||
<AssignRoleOptionBullet active={true} />
|
||||
</AssignRoleOptionBulletContainer>
|
||||
<div>
|
||||
<div>Create a new role</div>
|
||||
<AssignRoleOptionDescription>
|
||||
Create a new role for this identity provider and add
|
||||
permissions to the new role.
|
||||
</AssignRoleOptionDescription>
|
||||
</div>
|
||||
</AssignRoleOption>
|
||||
<AssignRoleOption>
|
||||
<AssignRoleOptionBulletContainer>
|
||||
<AssignRoleOptionBullet active={false} />
|
||||
</AssignRoleOptionBulletContainer>
|
||||
<div>
|
||||
<div>Use an existing role</div>
|
||||
<AssignRoleOptionDescription>
|
||||
Associate an existing role by adding this IdP to the trusted
|
||||
entities of the role.
|
||||
</AssignRoleOptionDescription>
|
||||
</div>
|
||||
</AssignRoleOption>
|
||||
</AssignRoleContent>
|
||||
<AssignRoleFooter>
|
||||
<CancelButton>Cancel</CancelButton>
|
||||
<NextButton>Next</NextButton>
|
||||
</AssignRoleFooter>
|
||||
</AssignRole>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
/**
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import Flex from 'design/Flex';
|
||||
import Text from 'design/Text';
|
||||
|
||||
import { Stage } from '../stages';
|
||||
|
||||
import { AWSWrapper } from './SharedComponents';
|
||||
|
||||
import {
|
||||
Content,
|
||||
Footer,
|
||||
Header,
|
||||
NextButton,
|
||||
Page,
|
||||
RoleButton,
|
||||
Section,
|
||||
SectionContent,
|
||||
SectionDropdown,
|
||||
SectionDropdownSelected,
|
||||
SectionTitle,
|
||||
} from './common';
|
||||
|
||||
import type { CommonIAMProps } from './common';
|
||||
|
||||
const Tabs = styled.div`
|
||||
display: flex;
|
||||
border-bottom: 1px solid #ccc;
|
||||
`;
|
||||
|
||||
const Tab = styled.div<{ active: boolean }>`
|
||||
background: ${p => (p.active ? 'white' : '#eeeeee')};
|
||||
font-weight: bold;
|
||||
border: 1px solid #cccccc;
|
||||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
padding: 5px 15px;
|
||||
margin-right: 10px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-top-width: ${p => (p.active ? 0 : '1px')};
|
||||
border-bottom-color: ${p => (p.active ? 'white' : '#cccccc')};
|
||||
margin-bottom: -1px;
|
||||
|
||||
&:after {
|
||||
height: 3px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
background: #e07701;
|
||||
content: '';
|
||||
display: ${p => (p.active ? 'block' : 'none')};
|
||||
}
|
||||
`;
|
||||
|
||||
const JSONEditor = styled.div<{ selected: boolean }>`
|
||||
border: 1px solid #ccc;
|
||||
margin-top: 20px;
|
||||
padding: 0 25px;
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
|
||||
&:after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
content: '';
|
||||
background: #b5d5ff;
|
||||
z-index: 0;
|
||||
display: ${p => (p.selected ? 'block' : 'none')};
|
||||
}
|
||||
|
||||
pre {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
`;
|
||||
|
||||
export function IAMCreateNewPolicy(props: CommonIAMProps) {
|
||||
let jsonEditor;
|
||||
if (props.stage >= Stage.ShowJSONEditor) {
|
||||
let content = `{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": []
|
||||
}`;
|
||||
if (props.stage >= Stage.PolicyJSONPasted) {
|
||||
content = `{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "rds:DescribeDBInstances",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}`;
|
||||
}
|
||||
|
||||
jsonEditor = (
|
||||
<JSONEditor selected={props.stage === Stage.JSONContentsSelected}>
|
||||
<pre>{content}</pre>
|
||||
</JSONEditor>
|
||||
);
|
||||
}
|
||||
|
||||
let content;
|
||||
if (props.stage <= Stage.PolicyClickNextTags) {
|
||||
content = (
|
||||
<>
|
||||
<Tabs>
|
||||
<Tab active={props.stage <= Stage.ClickJSONTab}>Visual Editor</Tab>
|
||||
<Tab active={props.stage >= Stage.ShowJSONEditor}>JSON</Tab>
|
||||
</Tabs>
|
||||
|
||||
{jsonEditor}
|
||||
|
||||
<Footer>
|
||||
<NextButton>Next: Tags</NextButton>
|
||||
</Footer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
props.stage >= Stage.PolicyTags &&
|
||||
props.stage <= Stage.PolicyClickNextReview
|
||||
) {
|
||||
content = (
|
||||
<>
|
||||
<Text fontSize={4} bold>
|
||||
Add tags - <em>optional</em>
|
||||
</Text>
|
||||
|
||||
<Flex mt={4}>
|
||||
<RoleButton>Add tag</RoleButton>
|
||||
</Flex>
|
||||
|
||||
<Footer>
|
||||
<NextButton>Next: Review</NextButton>
|
||||
</Footer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (props.stage >= Stage.PolicyReview) {
|
||||
content = (
|
||||
<>
|
||||
<Text fontSize={4} mb={4}>
|
||||
Review policy
|
||||
</Text>
|
||||
|
||||
<Section>
|
||||
<SectionTitle>Name*</SectionTitle>
|
||||
|
||||
<SectionContent>
|
||||
<SectionDropdown>
|
||||
<SectionDropdownSelected>
|
||||
{props.stage >= Stage.PolicyHasName ? (
|
||||
'SomePolicyName'
|
||||
) : (
|
||||
<> </>
|
||||
)}
|
||||
</SectionDropdownSelected>
|
||||
</SectionDropdown>
|
||||
</SectionContent>
|
||||
</Section>
|
||||
|
||||
<Footer>
|
||||
<NextButton>Create policy</NextButton>
|
||||
</Footer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<AWSWrapper>
|
||||
<Page>
|
||||
<Content>
|
||||
<Header>Create policy</Header>
|
||||
|
||||
{content}
|
||||
</Content>
|
||||
</Page>
|
||||
</AWSWrapper>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/**
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { Stage } from '../stages';
|
||||
|
||||
import { AWSWrapper } from './SharedComponents';
|
||||
|
||||
import {
|
||||
Content,
|
||||
Footer,
|
||||
Header,
|
||||
NextButton,
|
||||
Page,
|
||||
Section,
|
||||
SectionContent,
|
||||
SectionDropdown,
|
||||
SectionDropdownSelected,
|
||||
SectionTitle,
|
||||
SubHeader,
|
||||
} from './common';
|
||||
|
||||
import type { CommonIAMProps } from './common';
|
||||
|
||||
const TrustedEntities = styled.div`
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
`;
|
||||
|
||||
const TrustedEntity = styled.div<{ active: boolean }>`
|
||||
background: ${p => (p.active ? '#e6f3ff' : 'white')};
|
||||
border: 2px solid ${p => (p.active ? '#1066bb' : '#cccccc')};
|
||||
flex: 0 0 180px;
|
||||
margin-right: 10px;
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
const TrustedEntityContent = styled.div`
|
||||
padding: 10px 15px;
|
||||
`;
|
||||
|
||||
const TrustedEntityTitle = styled.div`
|
||||
font-weight: bold;
|
||||
`;
|
||||
|
||||
const TrustedEntityDescription = styled.div`
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
line-height: 1.2;
|
||||
`;
|
||||
|
||||
const SectionDropdownItems = styled.div`
|
||||
background: #222222;
|
||||
position: absolute;
|
||||
top: 34px;
|
||||
color: white;
|
||||
width: 300px;
|
||||
border-radius: 4px;
|
||||
`;
|
||||
|
||||
const SectionDropdownItem = styled.div<{ hovered: boolean }>`
|
||||
padding: 5px 15px;
|
||||
color: ${p => (p.hovered ? '#d27106' : 'white')};
|
||||
`;
|
||||
|
||||
export function IAMCreateNewRole(props: CommonIAMProps) {
|
||||
return (
|
||||
<AWSWrapper>
|
||||
<Page>
|
||||
<Content>
|
||||
<Header>Create role</Header>
|
||||
|
||||
<SubHeader>Select type of trusted entity</SubHeader>
|
||||
|
||||
<TrustedEntities>
|
||||
<TrustedEntity>
|
||||
<TrustedEntityContent>
|
||||
<TrustedEntityTitle>AWS service</TrustedEntityTitle>
|
||||
|
||||
<TrustedEntityDescription>
|
||||
EC2, Lambda and others
|
||||
</TrustedEntityDescription>
|
||||
</TrustedEntityContent>
|
||||
</TrustedEntity>
|
||||
|
||||
<TrustedEntity active={true}>
|
||||
<TrustedEntityContent>
|
||||
<TrustedEntityTitle>Web identity</TrustedEntityTitle>
|
||||
|
||||
<TrustedEntityDescription>
|
||||
Cognito or any OpenID provider
|
||||
</TrustedEntityDescription>
|
||||
</TrustedEntityContent>
|
||||
</TrustedEntity>
|
||||
|
||||
<TrustedEntity>
|
||||
<TrustedEntityContent>
|
||||
<TrustedEntityTitle>SAML 2.0 federation</TrustedEntityTitle>
|
||||
|
||||
<TrustedEntityDescription>
|
||||
Your corporate directory
|
||||
</TrustedEntityDescription>
|
||||
</TrustedEntityContent>
|
||||
</TrustedEntity>
|
||||
</TrustedEntities>
|
||||
|
||||
<SubHeader>Choose a web identity provider</SubHeader>
|
||||
|
||||
<Section>
|
||||
<SectionTitle>Identity Provider</SectionTitle>
|
||||
|
||||
<SectionContent>
|
||||
<SectionDropdown>
|
||||
<SectionDropdownSelected>
|
||||
{props.clusterPublicUri}:aud
|
||||
</SectionDropdownSelected>
|
||||
</SectionDropdown>
|
||||
</SectionContent>
|
||||
</Section>
|
||||
|
||||
<Section>
|
||||
<SectionTitle>Audience*</SectionTitle>
|
||||
|
||||
<SectionContent>
|
||||
<SectionDropdown>
|
||||
<SectionDropdownSelected>
|
||||
{props.stage >= Stage.DiscoverAudienceSelected ? (
|
||||
'discover.teleport'
|
||||
) : (
|
||||
<> </>
|
||||
)}
|
||||
</SectionDropdownSelected>
|
||||
|
||||
{props.stage >= Stage.ShowAudienceDropdown &&
|
||||
props.stage < Stage.DiscoverAudienceSelected && (
|
||||
<SectionDropdownItems>
|
||||
<SectionDropdownItem>
|
||||
discover.teleport
|
||||
</SectionDropdownItem>
|
||||
</SectionDropdownItems>
|
||||
)}
|
||||
</SectionDropdown>
|
||||
</SectionContent>
|
||||
</Section>
|
||||
|
||||
<Footer>
|
||||
<NextButton>Next: Permissions</NextButton>
|
||||
</Footer>
|
||||
</Content>
|
||||
</Page>
|
||||
</AWSWrapper>
|
||||
);
|
||||
}
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import Text from 'design/Text';
|
||||
import Flex from 'design/Flex';
|
||||
|
||||
import { Stage } from '../stages';
|
||||
|
||||
import { AWSWrapper } from './SharedComponents';
|
||||
|
||||
import {
|
||||
Content,
|
||||
Footer,
|
||||
Header,
|
||||
NextButton,
|
||||
Page,
|
||||
RoleButton,
|
||||
Section,
|
||||
SectionContent,
|
||||
SectionDropdown,
|
||||
SectionDropdownSelected,
|
||||
SectionTitle,
|
||||
SubHeader,
|
||||
TableCheckBox,
|
||||
TableHeader,
|
||||
TableItem,
|
||||
TableSearch,
|
||||
TableTitle,
|
||||
} from './common';
|
||||
|
||||
import type { CommonIAMProps } from './common';
|
||||
|
||||
const CreatePolicyButton = styled.div`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
`;
|
||||
|
||||
const Policies = styled.div`
|
||||
margin-top: 20px;
|
||||
`;
|
||||
|
||||
export function IAMCreateNewRolePermissions(props: CommonIAMProps) {
|
||||
let content = (
|
||||
<>
|
||||
<SubHeader>Attach permissions policies</SubHeader>
|
||||
|
||||
<CreatePolicyButton>
|
||||
<RoleButton>Create policy</RoleButton>
|
||||
</CreatePolicyButton>
|
||||
|
||||
<Footer>
|
||||
<NextButton>Next: Tags</NextButton>
|
||||
</Footer>
|
||||
</>
|
||||
);
|
||||
|
||||
if (props.stage >= Stage.RoleReview) {
|
||||
content = (
|
||||
<>
|
||||
<Text fontSize={4} mb={4}>
|
||||
Review
|
||||
</Text>
|
||||
|
||||
<Section>
|
||||
<SectionTitle>Role Name*</SectionTitle>
|
||||
|
||||
<SectionContent>
|
||||
<SectionDropdown>
|
||||
<SectionDropdownSelected>
|
||||
{props.stage >= Stage.RoleHasName ? (
|
||||
'SomeRoleName'
|
||||
) : (
|
||||
<> </>
|
||||
)}
|
||||
</SectionDropdownSelected>
|
||||
</SectionDropdown>
|
||||
</SectionContent>
|
||||
</Section>
|
||||
|
||||
<Footer>
|
||||
<NextButton>Create role</NextButton>
|
||||
</Footer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
props.stage >= Stage.RoleTags &&
|
||||
props.stage <= Stage.RoleClickNextReview
|
||||
) {
|
||||
content = (
|
||||
<>
|
||||
<Text fontSize={4} bold>
|
||||
Add tags - <em>optional</em>
|
||||
</Text>
|
||||
|
||||
<Flex mt={4}>
|
||||
<RoleButton>Add tag</RoleButton>
|
||||
</Flex>
|
||||
|
||||
<Footer>
|
||||
<NextButton>Next: Review</NextButton>
|
||||
</Footer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
props.stage >= Stage.AssignPolicyToRole &&
|
||||
props.stage <= Stage.RoleClickNextTags
|
||||
) {
|
||||
content = (
|
||||
<>
|
||||
<SubHeader>Attach permissions policies</SubHeader>
|
||||
|
||||
<CreatePolicyButton>
|
||||
<RoleButton>Create policy</RoleButton>
|
||||
|
||||
<RoleButton>Refresh</RoleButton>
|
||||
</CreatePolicyButton>
|
||||
|
||||
<Policies>
|
||||
<TableTitle>
|
||||
<TableSearch>
|
||||
{props.stage >= Stage.SearchForPolicy
|
||||
? 'SomePolicyName'
|
||||
: 'Search'}
|
||||
</TableSearch>
|
||||
</TableTitle>
|
||||
{props.stage >= Stage.SearchForPolicy ? (
|
||||
<div>
|
||||
<TableHeader>Policy Name</TableHeader>
|
||||
<TableItem selected={props.stage >= Stage.PolicySelected}>
|
||||
<TableCheckBox />
|
||||
SomePolicyName
|
||||
</TableItem>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<TableHeader>Policy Name</TableHeader>
|
||||
<TableItem>
|
||||
<TableCheckBox />
|
||||
AWSDefaultPolicy
|
||||
</TableItem>
|
||||
<TableItem>
|
||||
<TableCheckBox />
|
||||
AWSDefaultPolicy2
|
||||
</TableItem>
|
||||
<TableItem>
|
||||
<TableCheckBox />
|
||||
AWSDefaultPolicy3
|
||||
</TableItem>
|
||||
<TableItem>
|
||||
<TableCheckBox />
|
||||
AWSDefaultPolicy4
|
||||
</TableItem>
|
||||
</div>
|
||||
)}
|
||||
</Policies>
|
||||
|
||||
<Footer>
|
||||
<NextButton>Next: Tags</NextButton>
|
||||
</Footer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<AWSWrapper>
|
||||
<Page>
|
||||
<Content>
|
||||
<Header>Create role</Header>
|
||||
|
||||
{content}
|
||||
</Content>
|
||||
</Page>
|
||||
</AWSWrapper>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import Text from 'design/Text';
|
||||
import Flex from 'design/Flex';
|
||||
import Box from 'design/Box';
|
||||
|
||||
import { AWSWrapper } from './SharedComponents';
|
||||
|
||||
import {
|
||||
Blur,
|
||||
Content,
|
||||
Page,
|
||||
Sidebar,
|
||||
SidebarLink,
|
||||
SidebarLinkActive,
|
||||
SidebarSectionTitle,
|
||||
SidebarTitle,
|
||||
Title,
|
||||
} from './common';
|
||||
|
||||
export function IAMHomeScreen() {
|
||||
return (
|
||||
<AWSWrapper>
|
||||
<Page>
|
||||
<Sidebar>
|
||||
<SidebarTitle>Identity and Access Management (IAM)</SidebarTitle>
|
||||
|
||||
<SidebarLinkActive>Dashboard</SidebarLinkActive>
|
||||
|
||||
<SidebarSectionTitle>Access Management</SidebarSectionTitle>
|
||||
|
||||
<SidebarLink>User groups</SidebarLink>
|
||||
<SidebarLink>Users</SidebarLink>
|
||||
<SidebarLink>Roles</SidebarLink>
|
||||
<SidebarLink style={{ background: 'white' }}>
|
||||
Identity providers
|
||||
</SidebarLink>
|
||||
<SidebarLink>Account settings</SidebarLink>
|
||||
|
||||
<SidebarSectionTitle>Access reports</SidebarSectionTitle>
|
||||
|
||||
<SidebarLink>Access analyzer</SidebarLink>
|
||||
<SidebarLink>Credential report</SidebarLink>
|
||||
<SidebarLink>Organization activity</SidebarLink>
|
||||
</Sidebar>
|
||||
<Content>
|
||||
<Title>IAM Dashboard</Title>
|
||||
|
||||
<Blur>
|
||||
<Text mb={3}>Some blurred text here. Some other text here.</Text>
|
||||
<Text mb={5}>Some blurred text here</Text>
|
||||
<Text>IAM Resources</Text>
|
||||
<Flex justifyContent="space-between" mt={3}>
|
||||
<Box>
|
||||
User groups
|
||||
<div style={{ marginTop: 10, fontSize: 28, color: '#1166bb' }}>
|
||||
10
|
||||
</div>
|
||||
</Box>
|
||||
<Box>
|
||||
Users
|
||||
<div style={{ marginTop: 10, fontSize: 28, color: '#1166bb' }}>
|
||||
145
|
||||
</div>
|
||||
</Box>
|
||||
<Box>
|
||||
Identity providers
|
||||
<div style={{ marginTop: 10, fontSize: 28, color: '#1166bb' }}>
|
||||
0
|
||||
</div>
|
||||
</Box>
|
||||
</Flex>
|
||||
</Blur>
|
||||
</Content>
|
||||
</Page>
|
||||
</AWSWrapper>
|
||||
);
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import Flex from 'design/Flex';
|
||||
|
||||
import { Stage } from '../stages';
|
||||
|
||||
import { AWSWrapper, BreadcrumbArrow } from './SharedComponents';
|
||||
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbItemActive,
|
||||
Content,
|
||||
NextButton,
|
||||
Page,
|
||||
Sidebar,
|
||||
SidebarLink,
|
||||
SidebarLinkActive,
|
||||
SidebarSectionTitle,
|
||||
SidebarTitle,
|
||||
Title,
|
||||
} from './common';
|
||||
|
||||
import type { CommonIAMProps } from './common';
|
||||
|
||||
const Providers = styled.div`
|
||||
display: flex;
|
||||
margin-top: 20px;
|
||||
width: calc(100% - 60px);
|
||||
`;
|
||||
|
||||
const ProviderSection = styled.div`
|
||||
flex: 1;
|
||||
`;
|
||||
|
||||
const ProviderSectionTitle = styled.div`
|
||||
background: linear-gradient(#eee, #e0e0e0);
|
||||
padding: 5px 10px;
|
||||
font-weight: 700;
|
||||
`;
|
||||
|
||||
const ProviderTitle = styled.div`
|
||||
padding: 5px 10px;
|
||||
color: #16b;
|
||||
`;
|
||||
|
||||
const ProviderSectionContent = styled.div`
|
||||
padding: 5px 10px;
|
||||
`;
|
||||
|
||||
export function IAMIdentityProvidersScreen(props: CommonIAMProps) {
|
||||
if (props.stage >= Stage.ProviderAdded) {
|
||||
return (
|
||||
<AWSWrapper>
|
||||
<Page>
|
||||
<Content>
|
||||
<Breadcrumb>
|
||||
<BreadcrumbItem>IAM</BreadcrumbItem>
|
||||
<BreadcrumbArrow />
|
||||
<BreadcrumbItemActive>Identity providers</BreadcrumbItemActive>
|
||||
</Breadcrumb>
|
||||
|
||||
<Flex mt={5} alignItems="center">
|
||||
<Title style={{ marginBottom: 0 }}>Identity Providers (1)</Title>
|
||||
</Flex>
|
||||
|
||||
<Providers>
|
||||
<ProviderSection>
|
||||
<ProviderSectionTitle>Provider</ProviderSectionTitle>
|
||||
|
||||
<ProviderTitle>{props.clusterPublicUri}</ProviderTitle>
|
||||
</ProviderSection>
|
||||
<ProviderSection>
|
||||
<ProviderSectionTitle>Type</ProviderSectionTitle>
|
||||
|
||||
<ProviderSectionContent>OpenID Connect</ProviderSectionContent>
|
||||
</ProviderSection>
|
||||
<ProviderSection>
|
||||
<ProviderSectionTitle>Creation time</ProviderSectionTitle>
|
||||
|
||||
<ProviderSectionContent>1 minute ago</ProviderSectionContent>
|
||||
</ProviderSection>
|
||||
</Providers>
|
||||
</Content>
|
||||
</Page>
|
||||
</AWSWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<AWSWrapper>
|
||||
<Page>
|
||||
<Sidebar>
|
||||
<SidebarTitle>Identity and Access Management (IAM)</SidebarTitle>
|
||||
|
||||
<SidebarLink>Dashboard</SidebarLink>
|
||||
|
||||
<SidebarSectionTitle>Access Management</SidebarSectionTitle>
|
||||
|
||||
<SidebarLink>User groups</SidebarLink>
|
||||
<SidebarLink>Users</SidebarLink>
|
||||
<SidebarLink>Roles</SidebarLink>
|
||||
<SidebarLinkActive>Identity providers</SidebarLinkActive>
|
||||
<SidebarLink>Account settings</SidebarLink>
|
||||
|
||||
<SidebarSectionTitle>Access reports</SidebarSectionTitle>
|
||||
|
||||
<SidebarLink>Access analyzer</SidebarLink>
|
||||
<SidebarLink>Credential report</SidebarLink>
|
||||
<SidebarLink>Organization activity</SidebarLink>
|
||||
</Sidebar>
|
||||
<Content>
|
||||
<Breadcrumb>
|
||||
<BreadcrumbItem>IAM</BreadcrumbItem>
|
||||
<BreadcrumbArrow />
|
||||
<BreadcrumbItemActive>Identity providers</BreadcrumbItemActive>
|
||||
</Breadcrumb>
|
||||
|
||||
<Flex mt={5} alignItems="center">
|
||||
<Title style={{ marginBottom: 0 }}>Identity Providers (0)</Title>
|
||||
|
||||
<NextButton>Add provider</NextButton>
|
||||
</Flex>
|
||||
</Content>
|
||||
</Page>
|
||||
</AWSWrapper>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
/**
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import Box from 'design/Box';
|
||||
import Flex from 'design/Flex';
|
||||
|
||||
import { Stage } from '../stages';
|
||||
|
||||
import { AWSWrapper, BreadcrumbArrow } from './SharedComponents';
|
||||
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbItemActive,
|
||||
Content,
|
||||
Page,
|
||||
Sidebar,
|
||||
SidebarLink,
|
||||
SidebarLinkActive,
|
||||
SidebarSectionTitle,
|
||||
SidebarTitle,
|
||||
Title,
|
||||
} from './common';
|
||||
|
||||
import { OpenIDForm } from './OpenIDForm';
|
||||
|
||||
import type { CommonIAMProps } from './common';
|
||||
|
||||
const ProviderType = styled.div<{ active: boolean }>`
|
||||
flex: 0 0 220px;
|
||||
border: 1px solid ${p => (p.active ? '#3388dd' : '#cccccc')};
|
||||
background: ${p => (p.active ? '#f0f9ff' : 'white')};
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
margin-right: 10px;
|
||||
`;
|
||||
|
||||
const ProviderTypeIconContainer = styled.div`
|
||||
flex: 0 0 30px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding-top: 12px;
|
||||
`;
|
||||
|
||||
const ProviderTypeIcon = styled.div`
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background: ${p => (p.active ? '#1066bb' : 'white')};
|
||||
border: 1px solid ${p => (p.active ? '#1066bb' : '#cccccc')};
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
visibility: ${p => (p.active ? 'visible' : 'hidden')};
|
||||
position: absolute;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
background: white;
|
||||
border-radius: 50%;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
`;
|
||||
|
||||
const ProviderTypeInfo = styled.div`
|
||||
padding: 10px 0;
|
||||
font-size: 10px;
|
||||
color: #999;
|
||||
line-height: 12px;
|
||||
`;
|
||||
|
||||
const ProviderTypeTitle = styled.div`
|
||||
font-size: 14px;
|
||||
line-height: 16px;
|
||||
margin-bottom: 10px;
|
||||
color: #444;
|
||||
`;
|
||||
|
||||
const SlideOutPage = styled(Page)`
|
||||
transition: 1s transform cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transform: translate3d(${p => (p.hidden ? -251 : 0)}px, 0, 0);
|
||||
`;
|
||||
|
||||
export function IAMNewProviderScreen(props: CommonIAMProps) {
|
||||
const sidebarHidden = props.stage !== Stage.NewProvider;
|
||||
const openIDSelected = props.stage >= Stage.OpenIDConnectSelected;
|
||||
|
||||
return (
|
||||
<AWSWrapper>
|
||||
<SlideOutPage hidden={sidebarHidden}>
|
||||
<Sidebar>
|
||||
<SidebarTitle>Identity and Access Management (IAM)</SidebarTitle>
|
||||
|
||||
<SidebarLink>Dashboard</SidebarLink>
|
||||
|
||||
<SidebarSectionTitle>Access Management</SidebarSectionTitle>
|
||||
|
||||
<SidebarLink>User groups</SidebarLink>
|
||||
<SidebarLink>Users</SidebarLink>
|
||||
<SidebarLink>Roles</SidebarLink>
|
||||
<SidebarLinkActive>Identity providers</SidebarLinkActive>
|
||||
<SidebarLink>Account settings</SidebarLink>
|
||||
|
||||
<SidebarSectionTitle>Access reports</SidebarSectionTitle>
|
||||
|
||||
<SidebarLink>Access analyzer</SidebarLink>
|
||||
<SidebarLink>Credential report</SidebarLink>
|
||||
<SidebarLink>Organization activity</SidebarLink>
|
||||
</Sidebar>
|
||||
<Content>
|
||||
<Breadcrumb>
|
||||
<BreadcrumbItem>IAM</BreadcrumbItem>
|
||||
<BreadcrumbArrow />
|
||||
<BreadcrumbItem>Identity providers</BreadcrumbItem>
|
||||
<BreadcrumbArrow />
|
||||
<BreadcrumbItemActive>Create</BreadcrumbItemActive>
|
||||
</Breadcrumb>
|
||||
|
||||
<Box mt={5}>
|
||||
<Title>Add an Identity Provider </Title>
|
||||
</Box>
|
||||
|
||||
<Flex>
|
||||
<ProviderType active={!openIDSelected}>
|
||||
<ProviderTypeIconContainer>
|
||||
<ProviderTypeIcon active={!openIDSelected} />
|
||||
</ProviderTypeIconContainer>
|
||||
|
||||
<ProviderTypeInfo>
|
||||
<ProviderTypeTitle>SAML</ProviderTypeTitle>
|
||||
Establish trust between your AWS account and a SAML 2.0
|
||||
compatible Identity Provider such as Shibboleth or Active
|
||||
Directory Federation Services.
|
||||
</ProviderTypeInfo>
|
||||
</ProviderType>
|
||||
|
||||
<ProviderType active={openIDSelected}>
|
||||
<ProviderTypeIconContainer>
|
||||
<ProviderTypeIcon active={openIDSelected} />
|
||||
</ProviderTypeIconContainer>
|
||||
|
||||
<ProviderTypeInfo>
|
||||
<ProviderTypeTitle>OpenID Connect</ProviderTypeTitle>
|
||||
Establish trust between your AWS account and Identity Provider
|
||||
services, such as Google or Salesforce.
|
||||
</ProviderTypeInfo>
|
||||
</ProviderType>
|
||||
</Flex>
|
||||
|
||||
{openIDSelected && (
|
||||
<OpenIDForm
|
||||
stage={props.stage}
|
||||
clusterPublicUri={props.clusterPublicUri}
|
||||
/>
|
||||
)}
|
||||
</Content>
|
||||
</SlideOutPage>
|
||||
</AWSWrapper>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import Flex from 'design/Flex';
|
||||
import Box from 'design/Box';
|
||||
|
||||
import { Stage } from '../stages';
|
||||
|
||||
import { AWSWrapper, BreadcrumbArrow } from './SharedComponents';
|
||||
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbItemActive,
|
||||
Content,
|
||||
Page,
|
||||
RoleButton,
|
||||
Title,
|
||||
} from './common';
|
||||
|
||||
import { AssignRoleModal } from './AssignRoleModal';
|
||||
|
||||
import type { CommonIAMProps } from './common';
|
||||
|
||||
const AWSHeader = styled.div`
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
const AWSHeaderCount = styled.div`
|
||||
font-weight: 400;
|
||||
opacity: 0.5;
|
||||
margin-left: 10px;
|
||||
`;
|
||||
|
||||
const SummaryContent = styled.div`
|
||||
display: flex;
|
||||
border-top: 1px solid #cccccc;
|
||||
padding-top: 10px;
|
||||
margin-bottom: 40px;
|
||||
`;
|
||||
|
||||
const SummaryItem = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 200px;
|
||||
border-right: 1px solid #ccc;
|
||||
padding-left: 10px;
|
||||
|
||||
&:first-of-type {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&:last-of-type {
|
||||
border-right: none;
|
||||
}
|
||||
`;
|
||||
|
||||
export function IAMProvider(props: CommonIAMProps) {
|
||||
const showAssignRoleModal = props.stage >= Stage.ShowAssignRoleModal;
|
||||
|
||||
return (
|
||||
<AWSWrapper>
|
||||
<Page>
|
||||
{showAssignRoleModal && (
|
||||
<AssignRoleModal clusterPublicUri={props.clusterPublicUri} />
|
||||
)}
|
||||
|
||||
<Content>
|
||||
<Breadcrumb>
|
||||
<BreadcrumbItem>IAM</BreadcrumbItem>
|
||||
<BreadcrumbArrow />
|
||||
<BreadcrumbItem>Identity providers</BreadcrumbItem>
|
||||
<BreadcrumbArrow />
|
||||
<BreadcrumbItemActive>
|
||||
{props.clusterPublicUri}
|
||||
</BreadcrumbItemActive>
|
||||
</Breadcrumb>
|
||||
|
||||
<Flex
|
||||
mt={5}
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
mb={5}
|
||||
>
|
||||
<Title style={{ marginBottom: 0 }}>{props.clusterPublicUri}</Title>
|
||||
|
||||
<RoleButton>Assign role</RoleButton>
|
||||
</Flex>
|
||||
|
||||
<AWSHeader>Summary</AWSHeader>
|
||||
|
||||
<SummaryContent>
|
||||
<SummaryItem>
|
||||
Provider
|
||||
<div style={{ marginTop: 10 }}>{props.clusterPublicUri}</div>
|
||||
</SummaryItem>
|
||||
<SummaryItem>
|
||||
Provider Type
|
||||
<div style={{ marginTop: 10 }}>OpenID Connect</div>
|
||||
</SummaryItem>
|
||||
<SummaryItem>
|
||||
Creation Time
|
||||
<div style={{ marginTop: 10 }}>1 minute ago</div>
|
||||
</SummaryItem>
|
||||
</SummaryContent>
|
||||
|
||||
<Flex justifyContent="space-between">
|
||||
<Box width="48%">
|
||||
<AWSHeader>
|
||||
Audiences <AWSHeaderCount>(1)</AWSHeaderCount>
|
||||
</AWSHeader>
|
||||
</Box>
|
||||
|
||||
<Box width="48%">
|
||||
<AWSHeader>
|
||||
Thumbprints <AWSHeaderCount>(1)</AWSHeaderCount>
|
||||
</AWSHeader>
|
||||
</Box>
|
||||
</Flex>
|
||||
</Content>
|
||||
</Page>
|
||||
</AWSWrapper>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { Stage } from '../stages';
|
||||
|
||||
import { AWSWrapper } from './SharedComponents';
|
||||
|
||||
import { Content, Page } from './common';
|
||||
|
||||
import type { CommonIAMProps } from './common';
|
||||
|
||||
const RolesSuccess = styled.div`
|
||||
background: #019934;
|
||||
color: white;
|
||||
padding: 10px 15px;
|
||||
font-size: 16px;
|
||||
`;
|
||||
|
||||
const RolesSuccessLink = styled.span`
|
||||
text-decoration: underline;
|
||||
`;
|
||||
|
||||
const RolesHeader = styled.div`
|
||||
background-image: linear-gradient(#fff, #eee);
|
||||
padding: 20px 20px;
|
||||
border-top: 1px solid #ccc;
|
||||
border-bottom: 1px solid #ccc;
|
||||
font-weight: bold;
|
||||
font-size: 22px;
|
||||
`;
|
||||
|
||||
const PoliciesTableHeader = styled.div`
|
||||
background-image: linear-gradient(#eee, #e0e0e0);
|
||||
padding: 10px 45px;
|
||||
font-weight: bold;
|
||||
border: 1px solid #ccc;
|
||||
border-top: none;
|
||||
`;
|
||||
|
||||
const PoliciesCheckBox = styled.div`
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin-right: 20px;
|
||||
border-radius: 3px;
|
||||
`;
|
||||
|
||||
const PoliciesTableItem = styled.div<{ selected?: boolean }>`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 15px;
|
||||
border-bottom: 1px solid #cccccc;
|
||||
background: ${p => (p.selected ? '#e6f3ff' : 'white')};
|
||||
|
||||
${PoliciesCheckBox} {
|
||||
background: ${p => (p.selected ? '#1066bb' : 'white')};
|
||||
border: 1px solid ${p => (p.selected ? '#1066bb' : '#cccccc')};
|
||||
}
|
||||
`;
|
||||
|
||||
const RoleName = styled.div`
|
||||
font-size: 24px;
|
||||
`;
|
||||
|
||||
const SummaryTitle = styled.div`
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
margin-top: 30px;
|
||||
`;
|
||||
|
||||
const Summary = styled.div`
|
||||
margin-top: 20px;
|
||||
border-top: 1px solid #ccc;
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
const SummarySection = styled.div`
|
||||
margin-top: 5px;
|
||||
flex: 1;
|
||||
padding-top: 10px;
|
||||
`;
|
||||
|
||||
const SectionLabel = styled.div`
|
||||
font-size: 14px;
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
`;
|
||||
|
||||
const SectionValue = styled.div`
|
||||
font-size: 16px;
|
||||
`;
|
||||
|
||||
export function IAMRoles(props: CommonIAMProps) {
|
||||
let content;
|
||||
if (props.stage >= Stage.ListRoles && props.stage <= Stage.ClickRole) {
|
||||
content = (
|
||||
<div>
|
||||
<RolesSuccess>
|
||||
The role <RolesSuccessLink>SomeRoleName</RolesSuccessLink> has been
|
||||
created
|
||||
</RolesSuccess>
|
||||
<Content>
|
||||
<div>
|
||||
<RolesHeader>Roles</RolesHeader>
|
||||
|
||||
<div>
|
||||
<PoliciesTableHeader>Role Name</PoliciesTableHeader>
|
||||
<PoliciesTableItem>
|
||||
<PoliciesCheckBox />
|
||||
AWSDefaultRole
|
||||
</PoliciesTableItem>
|
||||
<PoliciesTableItem>
|
||||
<PoliciesCheckBox />
|
||||
AWSDefaultRole2
|
||||
</PoliciesTableItem>
|
||||
<PoliciesTableItem>
|
||||
<PoliciesCheckBox />
|
||||
AWSDefaultRole3
|
||||
</PoliciesTableItem>
|
||||
<PoliciesTableItem>
|
||||
<PoliciesCheckBox />
|
||||
AWSDefaultRole4
|
||||
</PoliciesTableItem>
|
||||
</div>
|
||||
</div>
|
||||
</Content>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (props.stage >= Stage.ViewRole) {
|
||||
content = (
|
||||
<Content>
|
||||
<RoleName>SomeRoleName</RoleName>
|
||||
|
||||
<SummaryTitle>Summary</SummaryTitle>
|
||||
|
||||
<Summary>
|
||||
<SummarySection style={{ borderRight: '1px solid #ccc' }}>
|
||||
<SectionLabel>Creation Date</SectionLabel>
|
||||
<SectionValue>Just now</SectionValue>
|
||||
|
||||
<SectionLabel>Lst activity</SectionLabel>
|
||||
<SectionValue>None</SectionValue>
|
||||
</SummarySection>
|
||||
<SummarySection style={{ paddingLeft: '20px' }}>
|
||||
<SectionLabel>ARN</SectionLabel>
|
||||
<SectionValue>
|
||||
arn:aws:iam::123456789:role/SomeRoleName
|
||||
</SectionValue>
|
||||
</SummarySection>
|
||||
</Summary>
|
||||
</Content>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<AWSWrapper>
|
||||
<Page>{content}</Page>
|
||||
</AWSWrapper>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
/**
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { Stage } from '../stages';
|
||||
|
||||
import { NextButton } from './common';
|
||||
|
||||
import type { CommonIAMProps } from './common';
|
||||
|
||||
const Form = styled.div`
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
const InputContainer = styled.div`
|
||||
display: flex;
|
||||
margin-bottom: 20px;
|
||||
`;
|
||||
|
||||
const Button = styled.div`
|
||||
background: linear-gradient(#fff, #dedede);
|
||||
border: 1px solid #b8b8b8;
|
||||
color: #444;
|
||||
padding: 3px 7px;
|
||||
border-radius: 5px;
|
||||
font-weight: 700;
|
||||
height: 32px;
|
||||
box-sizing: border-box;
|
||||
`;
|
||||
|
||||
const Input = styled.div`
|
||||
font-size: 14px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #ccc;
|
||||
padding: 3px 7px;
|
||||
height: 32px;
|
||||
box-sizing: border-box;
|
||||
width: 300px;
|
||||
margin-right: 15px;
|
||||
background: ${p => (p.disabled ? '#eeeeee' : 'white')};
|
||||
`;
|
||||
|
||||
const ThumbprintContainer = styled.div`
|
||||
background: #eeeeee;
|
||||
border: 1px solid #cccccc;
|
||||
padding: 7px 10px;
|
||||
display: flex;
|
||||
width: 400px;
|
||||
margin-bottom: 20px;
|
||||
`;
|
||||
|
||||
const ThumbprintSection = styled.div`
|
||||
margin-right: 60px;
|
||||
`;
|
||||
|
||||
const Thumbprint = styled.span`
|
||||
background: ${p => (p.selected ? '#add0f7' : 'none')};
|
||||
`;
|
||||
|
||||
const Buttons = styled.div`
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: calc(100% - 120px);
|
||||
margin-top: 20px;
|
||||
`;
|
||||
|
||||
export function OpenIDForm(props: CommonIAMProps) {
|
||||
const providerURL =
|
||||
props.stage >= Stage.PastedProviderURL
|
||||
? `https://${props.clusterPublicUri}`
|
||||
: 'https://';
|
||||
const audience =
|
||||
props.stage >= Stage.PastedAudience ? 'discover.teleport' : '';
|
||||
|
||||
let providerDisabled = false;
|
||||
let buttonText = 'Get thumbprint';
|
||||
if (props.stage >= Stage.ThumbprintLoading) {
|
||||
buttonText = 'Loading...';
|
||||
}
|
||||
if (props.stage >= Stage.ThumbprintResult) {
|
||||
buttonText = 'Edit URL';
|
||||
providerDisabled = true;
|
||||
}
|
||||
|
||||
let fingerprintResult;
|
||||
if (props.stage >= Stage.ThumbprintResult) {
|
||||
fingerprintResult = (
|
||||
<div>
|
||||
<strong>Verify thumbprint</strong>
|
||||
|
||||
<ThumbprintContainer>
|
||||
<ThumbprintSection>
|
||||
<div>Thumbprint</div>
|
||||
|
||||
<Thumbprint selected={props.stage === Stage.ThumbprintSelected}>
|
||||
examplevaluehere
|
||||
</Thumbprint>
|
||||
</ThumbprintSection>
|
||||
<ThumbprintSection>
|
||||
<div>Issued By</div>
|
||||
Example Inc
|
||||
</ThumbprintSection>
|
||||
</ThumbprintContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Form>
|
||||
<div>Provider URL</div>
|
||||
|
||||
<InputContainer>
|
||||
<Input disabled={providerDisabled}>{providerURL}</Input>
|
||||
|
||||
<Button>{buttonText}</Button>
|
||||
</InputContainer>
|
||||
|
||||
{fingerprintResult}
|
||||
|
||||
<div>Audience</div>
|
||||
|
||||
<InputContainer>
|
||||
<Input>{audience}</Input>
|
||||
</InputContainer>
|
||||
|
||||
<Buttons>
|
||||
<NextButton>Add provider</NextButton>
|
||||
</Buttons>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { AWSIcon, ChevronRightIcon } from 'design/SVGIcon';
|
||||
|
||||
import useTeleport from 'teleport/useTeleport';
|
||||
|
||||
import { BreadcrumbIconContainer } from './common';
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
width: 630px;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
const Header = styled.div`
|
||||
background: #232e3e;
|
||||
height: 32px;
|
||||
padding: 0 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
`;
|
||||
|
||||
const HeaderLogo = styled.div`
|
||||
height: 25px;
|
||||
`;
|
||||
|
||||
const HeaderUsername = styled.div`
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
`;
|
||||
|
||||
export function BreadcrumbArrow() {
|
||||
return (
|
||||
<BreadcrumbIconContainer>
|
||||
<ChevronRightIcon fill={'rgba(0, 0, 0, 0.8)'} size={10} />
|
||||
</BreadcrumbIconContainer>
|
||||
);
|
||||
}
|
||||
|
||||
export function AWSWrapper(props: React.PropsWithChildren<unknown>) {
|
||||
const ctx = useTeleport();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Header>
|
||||
<HeaderLogo>
|
||||
<AWSIcon size={24} />
|
||||
</HeaderLogo>
|
||||
|
||||
<HeaderUsername>{ctx.storeUser.state.username}</HeaderUsername>
|
||||
</Header>
|
||||
|
||||
{props.children}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
/**
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { Stage } from '../stages';
|
||||
|
||||
export interface CommonIAMProps {
|
||||
stage: Stage;
|
||||
clusterPublicUri: string;
|
||||
}
|
||||
|
||||
export const Header = styled.div`
|
||||
font-size: 22px;
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
export const Footer = styled.div`
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 46px;
|
||||
border-top: 1px solid #dcdcdc;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
padding-right: 30px;
|
||||
`;
|
||||
|
||||
export const Page = styled.div`
|
||||
display: flex;
|
||||
flex: 1;
|
||||
`;
|
||||
|
||||
export const Sidebar = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-right: 1px solid rgba(0, 0, 0, 0.1);
|
||||
flex: 0 0 250px;
|
||||
width: 250px;
|
||||
height: inherit;
|
||||
`;
|
||||
|
||||
export const SidebarSectionTitle = styled.div`
|
||||
font-weight: 700;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
padding: 0 20px;
|
||||
margin-top: 15px;
|
||||
margin-bottom: 5px;
|
||||
`;
|
||||
|
||||
export const SidebarTitle = styled.div`
|
||||
font-size: 18px;
|
||||
padding: 20px 20px;
|
||||
font-weight: bold;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.07);
|
||||
margin-bottom: 20px;
|
||||
`;
|
||||
|
||||
export const SidebarLink = styled.div`
|
||||
padding: 0 20px;
|
||||
margin-bottom: 5px;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
`;
|
||||
|
||||
export const SidebarLinkActive = styled(SidebarLink)`
|
||||
color: #0073bb;
|
||||
font-weight: 500;
|
||||
`;
|
||||
|
||||
export const Content = styled.div`
|
||||
padding: 30px;
|
||||
box-sizing: border-box;
|
||||
flex: 0 0 630px;
|
||||
width: 630px;
|
||||
`;
|
||||
|
||||
export const Title = styled.div`
|
||||
font-size: 18px;
|
||||
margin-bottom: 30px;
|
||||
`;
|
||||
|
||||
export const Blur = styled.div`
|
||||
filter: blur(5px);
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const Breadcrumb = styled.div`
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
export const BreadcrumbItem = styled.div`
|
||||
color: #0073bb;
|
||||
`;
|
||||
|
||||
export const BreadcrumbItemActive = styled(BreadcrumbItem)`
|
||||
font-weight: 700;
|
||||
color: #687078;
|
||||
`;
|
||||
|
||||
export const BreadcrumbIconContainer = styled.div`
|
||||
margin: 0 10px;
|
||||
`;
|
||||
|
||||
export const NextButton = styled.div`
|
||||
background: linear-gradient(#2c8bea, #1267bc);
|
||||
color: white;
|
||||
padding: 5px 15px;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #1d67b3;
|
||||
margin-left: 12px;
|
||||
`;
|
||||
|
||||
export const RoleButton = styled.div`
|
||||
background: linear-gradient(#fff, #dedede);
|
||||
color: #444;
|
||||
padding: 5px 10px;
|
||||
font-weight: 700;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #b8b8b8;
|
||||
width: 100px;
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
export const Section = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
`;
|
||||
|
||||
export const SectionTitle = styled.div`
|
||||
font-weight: bold;
|
||||
width: 100px;
|
||||
text-align: right;
|
||||
padding-right: 15px;
|
||||
`;
|
||||
|
||||
export const SectionContent = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 300px;
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
export const SectionDropdown = styled.div`
|
||||
position: relative;
|
||||
width: 300px;
|
||||
`;
|
||||
|
||||
export const SectionDropdownSelected = styled.div`
|
||||
border: 1px solid #ccc;
|
||||
padding: 5px 10px;
|
||||
border-radius: 4px;
|
||||
`;
|
||||
|
||||
export const SubHeader = styled.div`
|
||||
font-size: 20px;
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding-bottom: 5px;
|
||||
`;
|
||||
|
||||
export const TableTitle = styled.div`
|
||||
background: #e3e3e3;
|
||||
border: 1px solid #cccccc;
|
||||
padding: 13px 10px;
|
||||
`;
|
||||
|
||||
export const TableSearch = styled.div`
|
||||
background: white;
|
||||
border: 1px solid #cccccc;
|
||||
border-radius: 3px;
|
||||
width: 200px;
|
||||
padding: 3px 10px;
|
||||
`;
|
||||
|
||||
export const TableHeader = styled.div`
|
||||
background-image: linear-gradient(#eee, #e0e0e0);
|
||||
padding: 10px 45px;
|
||||
font-weight: bold;
|
||||
border: 1px solid #ccc;
|
||||
border-top: none;
|
||||
`;
|
||||
|
||||
export const TableCheckBox = styled.div`
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin-right: 20px;
|
||||
border-radius: 3px;
|
||||
`;
|
||||
|
||||
export const TableItem = styled.div<{ selected?: boolean }>`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 15px;
|
||||
border-bottom: 1px solid #cccccc;
|
||||
background: ${p => (p.selected ? '#e6f3ff' : 'white')};
|
||||
|
||||
${TableCheckBox} {
|
||||
background: ${p => (p.selected ? '#1066bb' : 'white')};
|
||||
border: 1px solid ${p => (p.selected ? '#1066bb' : '#ccc')};
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { AddIcon, LockIcon } from 'design/SVGIcon';
|
||||
|
||||
import {
|
||||
BrowserContainer,
|
||||
BrowserContentContainer,
|
||||
BrowserTab,
|
||||
BrowserTabClose,
|
||||
BrowserTabFavicon,
|
||||
BrowserTabs,
|
||||
BrowserTabTitle,
|
||||
BrowserTitleBarButton,
|
||||
BrowserTitleBarButtons,
|
||||
BrowserTitleBarContainer,
|
||||
BrowserURL,
|
||||
BrowserURLContainer,
|
||||
BrowserURLIcon,
|
||||
} from 'teleport/Integrations/Enroll/AwsOidc/browser/BrowserComponents';
|
||||
|
||||
import { Stage } from 'teleport/Integrations/Enroll/AwsOidc/stages';
|
||||
|
||||
interface BrowserProps {
|
||||
stage: Stage;
|
||||
}
|
||||
|
||||
export function Browser(props: React.PropsWithChildren<BrowserProps>) {
|
||||
const tabs = [];
|
||||
if (props.stage >= Stage.CreateNewRole) {
|
||||
tabs.push(
|
||||
<BrowserTab
|
||||
active={
|
||||
props.stage <= Stage.ClickCreatePolicy ||
|
||||
props.stage >= Stage.AssignPolicyToRole
|
||||
}
|
||||
key="create-role"
|
||||
>
|
||||
<BrowserTabFavicon />
|
||||
<BrowserTabTitle>
|
||||
{props.stage >= Stage.ListRoles ? 'Roles' : 'Create role'}
|
||||
</BrowserTabTitle>
|
||||
<BrowserTabClose>
|
||||
<AddIcon size={14} />
|
||||
</BrowserTabClose>
|
||||
</BrowserTab>
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
props.stage >= Stage.CreatePolicy &&
|
||||
props.stage <= Stage.ClickCreatePolicyButton
|
||||
) {
|
||||
tabs.push(
|
||||
<BrowserTab active={true} key="create-policy">
|
||||
<BrowserTabFavicon />
|
||||
<BrowserTabTitle>Create policy</BrowserTabTitle>
|
||||
<BrowserTabClose>
|
||||
<AddIcon size={14} />
|
||||
</BrowserTabClose>
|
||||
</BrowserTab>
|
||||
);
|
||||
}
|
||||
|
||||
//<BrowserTab active={true}>
|
||||
// <BrowserTabFavicon />
|
||||
// <BrowserTabTitle>Create policy</BrowserTabTitle>
|
||||
// <BrowserTabClose>
|
||||
// <AddIcon size={14} />
|
||||
// </BrowserTabClose>
|
||||
// </BrowserTab>
|
||||
|
||||
return (
|
||||
<BrowserContainer>
|
||||
<BrowserTitleBarContainer>
|
||||
<BrowserTitleBarButtons>
|
||||
<BrowserTitleBarButton style={{ backgroundColor: '#f95e57' }} />
|
||||
<BrowserTitleBarButton style={{ backgroundColor: '#fbbe2e' }} />
|
||||
<BrowserTitleBarButton style={{ backgroundColor: '#31c842' }} />
|
||||
</BrowserTitleBarButtons>
|
||||
|
||||
<BrowserURLContainer>
|
||||
<BrowserURL>
|
||||
<BrowserURLIcon>
|
||||
<LockIcon size={12} />
|
||||
</BrowserURLIcon>
|
||||
console.aws.amazon.com
|
||||
</BrowserURL>
|
||||
</BrowserURLContainer>
|
||||
</BrowserTitleBarContainer>
|
||||
|
||||
<BrowserTabs>
|
||||
<BrowserTab>
|
||||
<BrowserTabFavicon />
|
||||
<BrowserTabTitle>IAM Management Console</BrowserTabTitle>
|
||||
<BrowserTabClose>
|
||||
<AddIcon size={14} />
|
||||
</BrowserTabClose>
|
||||
</BrowserTab>
|
||||
{tabs}
|
||||
</BrowserTabs>
|
||||
|
||||
<BrowserContentContainer>{props.children}</BrowserContentContainer>
|
||||
</BrowserContainer>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import styled from 'styled-components';
|
||||
|
||||
import favicon from './favicon.png';
|
||||
|
||||
export const BrowserContainer = styled.div`
|
||||
position: relative;
|
||||
border-radius: 5px;
|
||||
width: 100%;
|
||||
box-shadow: 0px 0px 20px 0px rgba(0, 0, 0, 0.43);
|
||||
`;
|
||||
|
||||
export const BrowserTitleBarContainer = styled.div`
|
||||
background: #040b1d;
|
||||
height: 60px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
`;
|
||||
|
||||
export const BrowserTitleBarButtons = styled.div`
|
||||
display: flex;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 10px;
|
||||
transform: translate(0, -50%);
|
||||
`;
|
||||
|
||||
export const BrowserTitleBarButton = styled.div`
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
margin-right: 5px;
|
||||
`;
|
||||
|
||||
export const BrowserContentContainer = styled.div`
|
||||
background: white;
|
||||
color: black;
|
||||
height: var(--content-height, 660px);
|
||||
overflow-y: auto;
|
||||
border-bottom-left-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
`;
|
||||
|
||||
export const BrowserCode = styled.div`
|
||||
font-size: 12px;
|
||||
font-family: Menlo, DejaVu Sans Mono, Consolas, Lucida Console, monospace;
|
||||
line-height: 20px;
|
||||
white-space: pre-wrap;
|
||||
`;
|
||||
|
||||
export const BrowserURLContainer = styled.div`
|
||||
padding: 4px 10px;
|
||||
width: 300px;
|
||||
text-align: center;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
export const BrowserURL = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
export const BrowserURLIcon = styled.div`
|
||||
margin-right: 5px;
|
||||
top: 2px;
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
export const BrowserTabs = styled.div`
|
||||
display: flex;
|
||||
background: #060b1d;
|
||||
height: 36px;
|
||||
`;
|
||||
|
||||
export const BrowserTabFavicon = styled.div`
|
||||
margin-right: 10px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: url(${favicon}) no-repeat;
|
||||
`;
|
||||
|
||||
export const BrowserTabClose = styled.div`
|
||||
margin-left: 10px;
|
||||
svg {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
`;
|
||||
|
||||
export const BrowserTab = styled.div<{ active: boolean }>`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 10px;
|
||||
background: ${p => (p.active ? 'rgba(255, 255, 255, 0.1)' : 'none')};
|
||||
border-top-left-radius: ${p => (p.active ? '7px' : '0')};
|
||||
border-top-right-radius: ${p => (p.active ? '7px' : '0')};
|
||||
margin-right: 10px;
|
||||
|
||||
${BrowserTabClose} {
|
||||
display: ${p => (p.active ? 'block' : 'none')};
|
||||
}
|
||||
`;
|
||||
export const BrowserTabTitle = styled.div`
|
||||
font-size: 13px;
|
||||
`;
|
||||
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import styled, { keyframes } from 'styled-components';
|
||||
|
||||
import { SVGIconProps } from 'design/SVGIcon/common';
|
||||
|
||||
const Container = styled.div`
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 10;
|
||||
pointer-events: none;
|
||||
`;
|
||||
|
||||
const CursorContainer = styled.div`
|
||||
position: absolute;
|
||||
transition: 0.3s linear;
|
||||
`;
|
||||
|
||||
interface CursorProps {
|
||||
top: number;
|
||||
left: number;
|
||||
click: boolean;
|
||||
}
|
||||
|
||||
const pulse = keyframes`
|
||||
0% {
|
||||
transform: scale(0.1, 0.1);
|
||||
opacity: 0;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1.6, 1.6);
|
||||
opacity: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const fadeIn = keyframes`
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
20% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
80% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
export const Pulse = styled.div`
|
||||
background: #abc6e4;
|
||||
opacity: 0.5;
|
||||
border-radius: 50%;
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
top: 3px;
|
||||
z-index: 2;
|
||||
animation: ${fadeIn} 2s ease-in forwards;
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
border-radius: 50%;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
opacity: 0;
|
||||
box-shadow: 0 0 1px 2px #abc6e4;
|
||||
position: absolute;
|
||||
margin: -13px 0 0 -13px;
|
||||
animation: ${pulse} 2s ease-out infinite;
|
||||
}
|
||||
`;
|
||||
|
||||
export function Cursor(props: CursorProps) {
|
||||
const [showPulse, setShowPulse] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!props.click) {
|
||||
return;
|
||||
}
|
||||
|
||||
const id = window.setTimeout(() => setShowPulse(true), 1000);
|
||||
const id2 = window.setTimeout(() => setShowPulse(false), 3000);
|
||||
|
||||
return () => {
|
||||
clearTimeout(id);
|
||||
clearTimeout(id2);
|
||||
};
|
||||
}, [props.top, props.left, props.click]);
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<CursorContainer style={{ top: props.top, left: props.left }}>
|
||||
<div style={{ position: 'relative', zIndex: 3 }}>
|
||||
<CursorIcon />
|
||||
</div>
|
||||
|
||||
{showPulse && props.click && <Pulse />}
|
||||
</CursorContainer>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
function CursorIcon({ size = 40, fill = 'white' }: SVGIconProps) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 48 48"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={size}
|
||||
height={size}
|
||||
fill={fill}
|
||||
>
|
||||
<path d="M27.8,39.7c-0.1,0-0.2,0-0.4-0.1c-0.2-0.1-0.4-0.3-0.6-0.5l-3.7-8.6l-4.5,4.2C18.5,34.9,18.3,35,18,35c-0.1,0-0.3,0-0.4-0.1C17.3,34.8,17,34.4,17,34l0-22c0-0.4,0.2-0.8,0.6-0.9C17.7,11,17.9,11,18,11c0.2,0,0.5,0.1,0.7,0.3l16,15c0.3,0.3,0.4,0.7,0.3,1.1c-0.1,0.4-0.5,0.6-0.9,0.7l-6.3,0.6l3.9,8.5c0.1,0.2,0.1,0.5,0,0.8c-0.1,0.2-0.3,0.5-0.5,0.6l-2.9,1.3C28.1,39.7,27.9,39.7,27.8,39.7z" />
|
||||
<path
|
||||
fill="#212121"
|
||||
d="M18,12l16,15l-7.7,0.7l4.5,9.8l-2.9,1.3l-4.3-9.9L18,34L18,12 M18,10c-0.3,0-0.5,0.1-0.8,0.2c-0.7,0.3-1.2,1-1.2,1.8l0,22c0,0.8,0.5,1.5,1.2,1.8C17.5,36,17.8,36,18,36c0.5,0,1-0.2,1.4-0.5l3.4-3.2l3.1,7.3c0.2,0.5,0.6,0.9,1.1,1.1c0.2,0.1,0.5,0.1,0.7,0.1c0.3,0,0.5-0.1,0.8-0.2l2.9-1.3c0.5-0.2,0.9-0.6,1.1-1.1c0.2-0.5,0.2-1.1,0-1.5l-3.3-7.2l4.9-0.4c0.8-0.1,1.5-0.6,1.7-1.3c0.3-0.7,0.1-1.6-0.5-2.1l-16-15C19,10.2,18.5,10,18,10L18,10z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// export as default for use with React.lazy
|
||||
export { AwsOidc as default } from './AwsOidc';
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import Text from 'design/Text';
|
||||
import { ButtonPrimary } from 'design';
|
||||
import Box from 'design/Box';
|
||||
|
||||
import { TextSelectCopyMulti } from 'teleport/components/TextSelectCopy';
|
||||
|
||||
import { InstructionsContainer } from './common';
|
||||
|
||||
import type { CommonInstructionsProps } from './common';
|
||||
|
||||
export function FifthStageInstructions(props: CommonInstructionsProps) {
|
||||
const policy = `{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "rds:DescribeDBInstances",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}`;
|
||||
|
||||
return (
|
||||
<InstructionsContainer>
|
||||
<Text>
|
||||
Select the <strong>JSON</strong> tab
|
||||
</Text>
|
||||
|
||||
<Text mt={5}>
|
||||
Replace the JSON with the following
|
||||
<TextSelectCopyMulti lines={[{ text: policy }]} bash={false} />
|
||||
</Text>
|
||||
|
||||
<Text mt={5}>
|
||||
Click <strong>Next: Tags</strong> and then <strong>Next: Review</strong>
|
||||
</Text>
|
||||
|
||||
<Text mt={5}>
|
||||
Give the policy a name and then click <strong>Create policy</strong>
|
||||
</Text>
|
||||
|
||||
<Box mt={5}>
|
||||
<ButtonPrimary onClick={() => props.onNext()}>Next</ButtonPrimary>
|
||||
</Box>
|
||||
</InstructionsContainer>
|
||||
);
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import Box from 'design/Box';
|
||||
|
||||
import { ButtonPrimary } from 'design';
|
||||
|
||||
import { InstructionsContainer } from './common';
|
||||
|
||||
import type { CommonInstructionsProps } from './common';
|
||||
|
||||
const InstructionBlock = styled.div`
|
||||
margin-bottom: 30px;
|
||||
`;
|
||||
|
||||
export function FirstStageInstructions(props: CommonInstructionsProps) {
|
||||
return (
|
||||
<InstructionsContainer>
|
||||
<InstructionBlock>
|
||||
To connect Teleport to AWS as an identity provider, go to the{' '}
|
||||
<strong>AWS Management Console</strong>
|
||||
</InstructionBlock>
|
||||
<InstructionBlock>
|
||||
Search for <strong>IAM</strong>, and then click on{' '}
|
||||
<strong>Identity providers</strong>
|
||||
</InstructionBlock>
|
||||
<InstructionBlock>
|
||||
After that, click on <strong>Add Provider</strong>
|
||||
</InstructionBlock>
|
||||
|
||||
<Box mt={5}>
|
||||
<ButtonPrimary onClick={() => props.onNext()}>Next</ButtonPrimary>
|
||||
</Box>
|
||||
</InstructionsContainer>
|
||||
);
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import Text from 'design/Text';
|
||||
import Box from 'design/Box';
|
||||
|
||||
import { ButtonPrimary } from 'design';
|
||||
|
||||
import { InstructionsContainer } from './common';
|
||||
|
||||
import type { CommonInstructionsProps } from './common';
|
||||
|
||||
export function FourthStageInstructions(props: CommonInstructionsProps) {
|
||||
return (
|
||||
<InstructionsContainer>
|
||||
<Text>
|
||||
Select <strong>discover.teleport</strong> as the audience for the role
|
||||
</Text>
|
||||
|
||||
<Text mt={5}>
|
||||
Then click on <strong>Next: Permissions</strong>
|
||||
</Text>
|
||||
|
||||
<Text mt={5}>
|
||||
From the permissions page, click on <strong>Create policy</strong>
|
||||
</Text>
|
||||
|
||||
<Box mt={5}>
|
||||
<ButtonPrimary onClick={() => props.onNext()}>Next</ButtonPrimary>
|
||||
</Box>
|
||||
</InstructionsContainer>
|
||||
);
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { MemoryRouter } from 'react-router';
|
||||
|
||||
import { FirstStageInstructions } from './FirstStageInstructions';
|
||||
import { SecondStageInstructions } from './SecondStageInstructions';
|
||||
import { ThirdStageInstructions } from './ThirdStageInstructions';
|
||||
import { FourthStageInstructions } from './FourthStageInstructions';
|
||||
import { FifthStageInstructions } from './FifthStageInstructions';
|
||||
import { SixthStageInstructions } from './SixthStageInstructions';
|
||||
import {
|
||||
SeventhStageInstructions,
|
||||
SuccessfullyAddedIntegrationDialog,
|
||||
} from './SeventhStageInstructions';
|
||||
|
||||
export default {
|
||||
title: 'Teleport/Integrations/Enroll/AwsOidc/Instructions',
|
||||
};
|
||||
|
||||
export const Step1 = () => <FirstStageInstructions {...props} />;
|
||||
export const Step2 = () => <SecondStageInstructions {...props} />;
|
||||
export const Step3 = () => <ThirdStageInstructions {...props} />;
|
||||
export const Step4 = () => <FourthStageInstructions {...props} />;
|
||||
export const Step5 = () => <FifthStageInstructions {...props} />;
|
||||
export const Step6 = () => <SixthStageInstructions {...props} />;
|
||||
export const Step7 = () => (
|
||||
<MemoryRouter>
|
||||
<SeventhStageInstructions />
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
export const ConfirmDialog = () => (
|
||||
<MemoryRouter>
|
||||
<SuccessfullyAddedIntegrationDialog
|
||||
discoverEventId=""
|
||||
integrationName="some-integration-name"
|
||||
/>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
export const ConfirmDialogFromDiscover = () => (
|
||||
<MemoryRouter>
|
||||
<SuccessfullyAddedIntegrationDialog
|
||||
discoverEventId="some-event-id"
|
||||
integrationName="some-integration-name"
|
||||
/>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
const props = {
|
||||
onNext: () => null,
|
||||
clusterPublicUri: 'gravitationalwashington.cloud.gravitional.io:4444',
|
||||
};
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import Text from 'design/Text';
|
||||
import Box from 'design/Box';
|
||||
|
||||
import { ButtonPrimary } from 'design';
|
||||
|
||||
import FieldInput from 'shared/components/FieldInput';
|
||||
import Validation, { Validator } from 'shared/components/Validation';
|
||||
|
||||
import { requiredField } from 'shared/components/Validation/rules';
|
||||
|
||||
import { TextSelectCopyMulti } from 'teleport/components/TextSelectCopy';
|
||||
|
||||
import { InstructionsContainer } from './common';
|
||||
|
||||
import type { CommonInstructionsProps } from './common';
|
||||
|
||||
export function SecondStageInstructions(props: CommonInstructionsProps) {
|
||||
const [thumbprint, setThumbprint] = useState('');
|
||||
|
||||
function handleSubmit(validator: Validator) {
|
||||
if (!validator.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(lisa): validate thumbprint with the back.
|
||||
// This is a nice to have, so not a blocker.
|
||||
props.onNext();
|
||||
}
|
||||
|
||||
return (
|
||||
<InstructionsContainer>
|
||||
<Text>
|
||||
Now select <strong>OpenID Connect</strong>
|
||||
</Text>
|
||||
|
||||
<Text mt={5}>
|
||||
Copy the following into <strong>Provider URL</strong>
|
||||
</Text>
|
||||
|
||||
<Box mt={5}>
|
||||
<TextSelectCopyMulti
|
||||
bash={false}
|
||||
lines={[
|
||||
{
|
||||
text: `https://${props.clusterPublicUri}`,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Text mt={5}>
|
||||
Copy the following into <strong>Audience</strong>
|
||||
</Text>
|
||||
|
||||
<Box mt={5}>
|
||||
<TextSelectCopyMulti
|
||||
bash={false}
|
||||
lines={[
|
||||
{
|
||||
text: 'discover.teleport',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Text mt={5}>
|
||||
Then, click <strong>Get thumbprint</strong>
|
||||
</Text>
|
||||
|
||||
<Text mt={5}>Paste the thumbprint below for verification</Text>
|
||||
|
||||
<Validation>
|
||||
{({ validator }) => (
|
||||
<>
|
||||
<Box mt={2}>
|
||||
<FieldInput
|
||||
label="thumbprint"
|
||||
onChange={e => setThumbprint(e.target.value)}
|
||||
value={thumbprint}
|
||||
placeholder="Paste the thumbprint here"
|
||||
rule={requiredField('Thumbprint is required')}
|
||||
/>
|
||||
</Box>
|
||||
<Box mt={5}>
|
||||
<ButtonPrimary onClick={() => handleSubmit(validator)}>
|
||||
Next
|
||||
</ButtonPrimary>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</Validation>
|
||||
</InstructionsContainer>
|
||||
);
|
||||
}
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
/**
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { useLocation } from 'react-router';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Danger } from 'design/Alert';
|
||||
import { CircleCheck } from 'design/Icon';
|
||||
import { ButtonPrimary, ButtonSecondary, Text, Flex, Box } from 'design';
|
||||
import Dialog, {
|
||||
DialogHeader,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
} from 'design/Dialog';
|
||||
|
||||
import FieldInput from 'shared/components/FieldInput';
|
||||
import Validation, { Validator } from 'shared/components/Validation';
|
||||
import useAttempt from 'shared/hooks/useAttemptNext';
|
||||
|
||||
import { requiredField } from 'shared/components/Validation/rules';
|
||||
|
||||
import {
|
||||
IntegrationKind,
|
||||
integrationService,
|
||||
} from 'teleport/services/integrations';
|
||||
import cfg from 'teleport/config';
|
||||
|
||||
import { InstructionsContainer } from './common';
|
||||
|
||||
export function SeventhStageInstructions() {
|
||||
const location = useLocation<{ discoverEventId: string }>();
|
||||
|
||||
const { attempt, setAttempt } = useAttempt('');
|
||||
const [showConfirmBox, setShowConfirmBox] = useState(false);
|
||||
const [roleArn, setRoleArn] = useState('');
|
||||
const [name, setName] = useState('');
|
||||
|
||||
function handleSubmit(validator: Validator) {
|
||||
if (!validator.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
setAttempt({ status: 'processing' });
|
||||
integrationService
|
||||
.createIntegration({
|
||||
name,
|
||||
subKind: IntegrationKind.AwsOidc,
|
||||
awsoidc: { roleArn },
|
||||
})
|
||||
.then(() => setShowConfirmBox(true))
|
||||
.catch((err: Error) =>
|
||||
setAttempt({ status: 'failed', statusText: err.message })
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<InstructionsContainer>
|
||||
{attempt.status === 'failed' && (
|
||||
<Danger mb={5}>{attempt.statusText}</Danger>
|
||||
)}
|
||||
<Text>From the list of roles, select the role you just created</Text>
|
||||
|
||||
<Validation>
|
||||
{({ validator }) => (
|
||||
<>
|
||||
<Text mt={5}>Copy the role ARN and paste it below</Text>
|
||||
<Box mt={2}>
|
||||
<FieldInput
|
||||
label="Role ARN"
|
||||
onChange={e => setRoleArn(e.target.value)}
|
||||
value={roleArn}
|
||||
placeholder="Role ARN"
|
||||
rule={requiredField('Role ARN is required')}
|
||||
/>
|
||||
</Box>
|
||||
<Text mt={5}>Give this AWS integration a name</Text>
|
||||
<Box mt={2}>
|
||||
<FieldInput
|
||||
label="Name"
|
||||
onChange={e => setName(e.target.value)}
|
||||
value={name}
|
||||
placeholder="Integration name"
|
||||
rule={requiredField('Name is required')}
|
||||
/>
|
||||
</Box>
|
||||
<Box mt={5}>
|
||||
<ButtonPrimary
|
||||
onClick={() => handleSubmit(validator)}
|
||||
disabled={attempt.status === 'processing'}
|
||||
>
|
||||
Next
|
||||
</ButtonPrimary>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</Validation>
|
||||
{showConfirmBox && (
|
||||
<SuccessfullyAddedIntegrationDialog
|
||||
discoverEventId={location.state?.discoverEventId}
|
||||
integrationName={name}
|
||||
/>
|
||||
)}
|
||||
</InstructionsContainer>
|
||||
);
|
||||
}
|
||||
|
||||
export function SuccessfullyAddedIntegrationDialog({
|
||||
discoverEventId,
|
||||
integrationName,
|
||||
}: {
|
||||
discoverEventId: string;
|
||||
integrationName: string;
|
||||
}) {
|
||||
return (
|
||||
<Dialog
|
||||
dialogCss={() => ({ maxWidth: '500px', width: '100%' })}
|
||||
disableEscapeKeyDown={true}
|
||||
onClose={close}
|
||||
open={true}
|
||||
>
|
||||
<DialogHeader css={{ margin: '0 auto' }}>
|
||||
<CircleCheck mb={4} fontSize={60} color="success" />
|
||||
</DialogHeader>
|
||||
<DialogContent>
|
||||
<Text textAlign="center">
|
||||
AWS integration "{integrationName}" successfully added
|
||||
</Text>
|
||||
</DialogContent>
|
||||
<DialogFooter css={{ margin: '0 auto' }}>
|
||||
{discoverEventId ? (
|
||||
<ButtonPrimary
|
||||
size="large"
|
||||
as={Link}
|
||||
to={{
|
||||
pathname: cfg.routes.discover,
|
||||
state: {
|
||||
integrationSubKind: IntegrationKind.AwsOidc,
|
||||
integrationName,
|
||||
discoverEventId,
|
||||
},
|
||||
}}
|
||||
>
|
||||
Begin RDS Enrollment
|
||||
</ButtonPrimary>
|
||||
) : (
|
||||
<Flex gap="3">
|
||||
<ButtonPrimary as={Link} to={cfg.routes.integrations} size="large">
|
||||
Go to Integration List
|
||||
</ButtonPrimary>
|
||||
<ButtonSecondary
|
||||
as={Link}
|
||||
to={cfg.getIntegrationEnrollRoute(null)}
|
||||
size="large"
|
||||
>
|
||||
Add Another Integration
|
||||
</ButtonSecondary>
|
||||
</Flex>
|
||||
)}
|
||||
</DialogFooter>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import Box from 'design/Box';
|
||||
import Text from 'design/Text';
|
||||
|
||||
import { ButtonPrimary } from 'design';
|
||||
|
||||
import { InstructionsContainer } from './common';
|
||||
|
||||
import type { CommonInstructionsProps } from './common';
|
||||
|
||||
export function SixthStageInstructions(props: CommonInstructionsProps) {
|
||||
return (
|
||||
<InstructionsContainer>
|
||||
<Text>Close the "Create policy tab"</Text>
|
||||
|
||||
<Text mt={5}>
|
||||
Refresh the list of policies and select the policy you just created
|
||||
</Text>
|
||||
|
||||
<Text mt={5}>Search for the policy you just created and select it</Text>
|
||||
|
||||
<Text mt={5}>
|
||||
Click <strong>Next: Tags</strong> and then <strong>Next: Review</strong>
|
||||
</Text>
|
||||
|
||||
<Text mt={5}>
|
||||
Give the role a name and then click <strong>Create role</strong>
|
||||
</Text>
|
||||
|
||||
<Box mt={5}>
|
||||
<ButtonPrimary onClick={() => props.onNext()}>Next</ButtonPrimary>
|
||||
</Box>
|
||||
</InstructionsContainer>
|
||||
);
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import Text from 'design/Text';
|
||||
import Box from 'design/Box';
|
||||
|
||||
import { ButtonPrimary } from 'design';
|
||||
|
||||
import { InstructionsContainer } from './common';
|
||||
|
||||
import type { CommonInstructionsProps } from './common';
|
||||
|
||||
export function ThirdStageInstructions(props: CommonInstructionsProps) {
|
||||
return (
|
||||
<InstructionsContainer>
|
||||
<Text>
|
||||
Now click <strong>Add Provider</strong>
|
||||
</Text>
|
||||
|
||||
<Text mt={5}>
|
||||
Select the Identity provider that you just created{' '}
|
||||
<strong>({props.clusterPublicUri})</strong>
|
||||
</Text>
|
||||
|
||||
<Text mt={5}>
|
||||
Select <strong>Assign role</strong> and create a new role
|
||||
</Text>
|
||||
|
||||
<Box mt={5}>
|
||||
<ButtonPrimary onClick={() => props.onNext()}>Next</ButtonPrimary>
|
||||
</Box>
|
||||
</InstructionsContainer>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import styled from 'styled-components';
|
||||
|
||||
export interface CommonInstructionsProps {
|
||||
onNext: () => void;
|
||||
clusterPublicUri: string;
|
||||
}
|
||||
|
||||
export const InstructionsContainer = styled.div`
|
||||
flex: 0 0 600px;
|
||||
padding-right: 100px;
|
||||
`;
|
||||
@@ -0,0 +1,621 @@
|
||||
/**
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export enum Stage {
|
||||
Initial,
|
||||
ClickIdentityProviders,
|
||||
IdentityProviders,
|
||||
ClickAddProvider,
|
||||
NewProvider,
|
||||
NewProviderFullScreen,
|
||||
SelectOpenIDConnect,
|
||||
OpenIDConnectSelected,
|
||||
ClickProviderURL,
|
||||
PastedProviderURL,
|
||||
ClickAudience,
|
||||
PastedAudience,
|
||||
ClickGetThumbprint,
|
||||
ThumbprintLoading,
|
||||
ThumbprintResult,
|
||||
SelectThumbprint,
|
||||
ThumbprintSelected,
|
||||
AddProvider,
|
||||
ProviderAdded,
|
||||
SelectProvider,
|
||||
ProviderView,
|
||||
ClickAssignRole,
|
||||
ShowAssignRoleModal,
|
||||
ClickCreateNewRole,
|
||||
CreateNewRole,
|
||||
SelectAudienceDropdown,
|
||||
ShowAudienceDropdown,
|
||||
ClickDiscoverAudience,
|
||||
DiscoverAudienceSelected,
|
||||
ClickNextPermissions,
|
||||
ConfigureRolePermissions,
|
||||
ClickCreatePolicy,
|
||||
CreatePolicy,
|
||||
ClickJSONTab,
|
||||
ShowJSONEditor,
|
||||
SelectJSONContents,
|
||||
JSONContentsSelected,
|
||||
PolicyJSONPasted,
|
||||
PolicyClickNextTags,
|
||||
PolicyTags,
|
||||
PolicyClickNextReview,
|
||||
PolicyReview,
|
||||
ClickPolicyName,
|
||||
PolicyHasName,
|
||||
ClickCreatePolicyButton,
|
||||
AssignPolicyToRole,
|
||||
ClickRefreshButton,
|
||||
PoliciesLoaded,
|
||||
ClickSearchBox,
|
||||
SearchForPolicy,
|
||||
SelectPolicy,
|
||||
PolicySelected,
|
||||
RoleClickNextTags,
|
||||
RoleTags,
|
||||
RoleClickNextReview,
|
||||
RoleReview,
|
||||
ClickRoleName,
|
||||
RoleHasName,
|
||||
ClickCreateRoleButton,
|
||||
ListRoles,
|
||||
ClickRole,
|
||||
ViewRole,
|
||||
}
|
||||
|
||||
interface StageItem {
|
||||
kind: Stage;
|
||||
cursor: {
|
||||
top: number;
|
||||
left: number;
|
||||
click?: boolean;
|
||||
};
|
||||
duration?: number;
|
||||
end?: boolean;
|
||||
}
|
||||
|
||||
export const STAGES: StageItem[] = [
|
||||
{
|
||||
kind: Stage.Initial,
|
||||
cursor: {
|
||||
top: 236,
|
||||
left: 200,
|
||||
click: false,
|
||||
},
|
||||
duration: 2000,
|
||||
},
|
||||
{
|
||||
kind: Stage.ClickIdentityProviders,
|
||||
cursor: {
|
||||
top: 401,
|
||||
left: 60,
|
||||
click: true,
|
||||
},
|
||||
duration: 2500,
|
||||
},
|
||||
{
|
||||
kind: Stage.IdentityProviders,
|
||||
cursor: {
|
||||
top: 401,
|
||||
left: 200,
|
||||
},
|
||||
duration: 2000,
|
||||
},
|
||||
{
|
||||
kind: Stage.ClickAddProvider,
|
||||
cursor: {
|
||||
top: 221,
|
||||
left: 530,
|
||||
click: true,
|
||||
},
|
||||
duration: 1500,
|
||||
},
|
||||
{
|
||||
kind: Stage.NewProvider,
|
||||
cursor: {
|
||||
top: 221,
|
||||
left: 530,
|
||||
},
|
||||
end: true,
|
||||
},
|
||||
{
|
||||
kind: Stage.NewProviderFullScreen,
|
||||
cursor: {
|
||||
top: 221,
|
||||
left: 530,
|
||||
},
|
||||
duration: 1500,
|
||||
},
|
||||
{
|
||||
kind: Stage.SelectOpenIDConnect,
|
||||
cursor: {
|
||||
top: 281,
|
||||
left: 350,
|
||||
click: true,
|
||||
},
|
||||
duration: 2000,
|
||||
},
|
||||
{
|
||||
kind: Stage.OpenIDConnectSelected,
|
||||
cursor: {
|
||||
top: 281,
|
||||
left: 350,
|
||||
},
|
||||
duration: 2000,
|
||||
},
|
||||
{
|
||||
kind: Stage.ClickProviderURL,
|
||||
cursor: {
|
||||
top: 416,
|
||||
left: 160,
|
||||
click: true,
|
||||
},
|
||||
duration: 1500,
|
||||
},
|
||||
{
|
||||
kind: Stage.PastedProviderURL,
|
||||
cursor: {
|
||||
top: 416,
|
||||
left: 160,
|
||||
},
|
||||
duration: 1500,
|
||||
},
|
||||
{
|
||||
kind: Stage.ClickAudience,
|
||||
cursor: {
|
||||
top: 491,
|
||||
left: 160,
|
||||
click: true,
|
||||
},
|
||||
duration: 1500,
|
||||
},
|
||||
{
|
||||
kind: Stage.PastedAudience,
|
||||
cursor: {
|
||||
top: 491,
|
||||
left: 160,
|
||||
click: true,
|
||||
},
|
||||
duration: 2500,
|
||||
},
|
||||
{
|
||||
kind: Stage.ClickGetThumbprint,
|
||||
cursor: {
|
||||
top: 416,
|
||||
left: 400,
|
||||
click: true,
|
||||
},
|
||||
duration: 1000,
|
||||
},
|
||||
{
|
||||
kind: Stage.ThumbprintLoading,
|
||||
cursor: {
|
||||
top: 416,
|
||||
left: 400,
|
||||
},
|
||||
duration: 1000,
|
||||
},
|
||||
{
|
||||
kind: Stage.ThumbprintResult,
|
||||
cursor: {
|
||||
top: 416,
|
||||
left: 400,
|
||||
},
|
||||
duration: 1500,
|
||||
},
|
||||
{
|
||||
kind: Stage.SelectThumbprint,
|
||||
cursor: {
|
||||
top: 516,
|
||||
left: 100,
|
||||
click: true,
|
||||
},
|
||||
duration: 1500,
|
||||
},
|
||||
{
|
||||
kind: Stage.ThumbprintSelected,
|
||||
cursor: {
|
||||
top: 516,
|
||||
left: 100,
|
||||
},
|
||||
end: true,
|
||||
},
|
||||
{
|
||||
kind: Stage.AddProvider,
|
||||
cursor: {
|
||||
top: 666,
|
||||
left: 420,
|
||||
click: true,
|
||||
},
|
||||
duration: 2000,
|
||||
},
|
||||
{
|
||||
kind: Stage.ProviderAdded,
|
||||
cursor: {
|
||||
top: 516,
|
||||
left: 300,
|
||||
},
|
||||
duration: 2000,
|
||||
},
|
||||
{
|
||||
kind: Stage.SelectProvider,
|
||||
cursor: {
|
||||
top: 301,
|
||||
left: 70,
|
||||
click: true,
|
||||
},
|
||||
duration: 1500,
|
||||
},
|
||||
{
|
||||
kind: Stage.ProviderView,
|
||||
cursor: {
|
||||
top: 301,
|
||||
left: 70,
|
||||
},
|
||||
duration: 1500,
|
||||
},
|
||||
{
|
||||
kind: Stage.ClickAssignRole,
|
||||
cursor: {
|
||||
top: 221,
|
||||
left: 535,
|
||||
click: true,
|
||||
},
|
||||
duration: 2500,
|
||||
},
|
||||
{
|
||||
kind: Stage.ShowAssignRoleModal,
|
||||
cursor: {
|
||||
top: 221,
|
||||
left: 535,
|
||||
},
|
||||
duration: 2500,
|
||||
},
|
||||
{
|
||||
kind: Stage.ClickCreateNewRole,
|
||||
cursor: {
|
||||
top: 446,
|
||||
left: 500,
|
||||
click: true,
|
||||
},
|
||||
end: true,
|
||||
},
|
||||
{
|
||||
kind: Stage.CreateNewRole,
|
||||
cursor: {
|
||||
top: 446,
|
||||
left: 500,
|
||||
},
|
||||
duration: 2000,
|
||||
},
|
||||
{
|
||||
kind: Stage.SelectAudienceDropdown,
|
||||
cursor: {
|
||||
top: 477,
|
||||
left: 300,
|
||||
click: true,
|
||||
},
|
||||
duration: 2000,
|
||||
},
|
||||
{
|
||||
kind: Stage.ShowAudienceDropdown,
|
||||
cursor: {
|
||||
top: 477,
|
||||
left: 300,
|
||||
},
|
||||
duration: 1000,
|
||||
},
|
||||
{
|
||||
kind: Stage.ClickDiscoverAudience,
|
||||
cursor: {
|
||||
top: 512,
|
||||
left: 300,
|
||||
click: true,
|
||||
},
|
||||
duration: 2000,
|
||||
},
|
||||
{
|
||||
kind: Stage.DiscoverAudienceSelected,
|
||||
cursor: {
|
||||
top: 512,
|
||||
left: 300,
|
||||
},
|
||||
duration: 1000,
|
||||
},
|
||||
{
|
||||
kind: Stage.ClickNextPermissions,
|
||||
cursor: {
|
||||
top: 722,
|
||||
left: 500,
|
||||
click: true,
|
||||
},
|
||||
duration: 2500,
|
||||
},
|
||||
{
|
||||
kind: Stage.ConfigureRolePermissions,
|
||||
cursor: {
|
||||
top: 422,
|
||||
left: 500,
|
||||
},
|
||||
duration: 2500,
|
||||
},
|
||||
{
|
||||
kind: Stage.ClickCreatePolicy,
|
||||
cursor: {
|
||||
top: 256,
|
||||
left: 75,
|
||||
click: true,
|
||||
},
|
||||
end: true,
|
||||
},
|
||||
{
|
||||
kind: Stage.CreatePolicy,
|
||||
cursor: {
|
||||
top: 256,
|
||||
left: 75,
|
||||
},
|
||||
duration: 2500,
|
||||
},
|
||||
{
|
||||
kind: Stage.ClickJSONTab,
|
||||
cursor: {
|
||||
top: 210,
|
||||
left: 180,
|
||||
click: true,
|
||||
},
|
||||
duration: 2500,
|
||||
},
|
||||
{
|
||||
kind: Stage.ShowJSONEditor,
|
||||
cursor: {
|
||||
top: 210,
|
||||
left: 180,
|
||||
},
|
||||
duration: 2500,
|
||||
},
|
||||
{
|
||||
kind: Stage.SelectJSONContents,
|
||||
cursor: {
|
||||
top: 320,
|
||||
left: 140,
|
||||
click: true,
|
||||
},
|
||||
duration: 2500,
|
||||
},
|
||||
{
|
||||
kind: Stage.JSONContentsSelected,
|
||||
cursor: {
|
||||
top: 320,
|
||||
left: 140,
|
||||
},
|
||||
duration: 2500,
|
||||
},
|
||||
{
|
||||
kind: Stage.PolicyJSONPasted,
|
||||
cursor: {
|
||||
top: 320,
|
||||
left: 140,
|
||||
},
|
||||
duration: 2500,
|
||||
},
|
||||
{
|
||||
kind: Stage.PolicyClickNextTags,
|
||||
cursor: {
|
||||
top: 722,
|
||||
left: 530,
|
||||
click: true,
|
||||
},
|
||||
duration: 2500,
|
||||
},
|
||||
{
|
||||
kind: Stage.PolicyTags,
|
||||
cursor: {
|
||||
top: 722,
|
||||
left: 530,
|
||||
},
|
||||
duration: 2500,
|
||||
},
|
||||
{
|
||||
kind: Stage.PolicyClickNextReview,
|
||||
cursor: {
|
||||
top: 722,
|
||||
left: 530,
|
||||
click: true,
|
||||
},
|
||||
duration: 2500,
|
||||
},
|
||||
{
|
||||
kind: Stage.PolicyReview,
|
||||
cursor: {
|
||||
top: 722,
|
||||
left: 530,
|
||||
},
|
||||
duration: 2500,
|
||||
},
|
||||
{
|
||||
kind: Stage.ClickPolicyName,
|
||||
cursor: {
|
||||
top: 262,
|
||||
left: 230,
|
||||
click: true,
|
||||
},
|
||||
duration: 2500,
|
||||
},
|
||||
{
|
||||
kind: Stage.PolicyHasName,
|
||||
cursor: {
|
||||
top: 262,
|
||||
left: 230,
|
||||
},
|
||||
duration: 2500,
|
||||
},
|
||||
{
|
||||
kind: Stage.ClickCreatePolicyButton,
|
||||
cursor: {
|
||||
top: 722,
|
||||
left: 530,
|
||||
click: true,
|
||||
},
|
||||
end: true,
|
||||
},
|
||||
{
|
||||
kind: Stage.AssignPolicyToRole,
|
||||
cursor: {
|
||||
top: 260,
|
||||
left: 550,
|
||||
},
|
||||
duration: 1000,
|
||||
},
|
||||
{
|
||||
kind: Stage.ClickRefreshButton,
|
||||
cursor: {
|
||||
top: 260,
|
||||
left: 550,
|
||||
click: true,
|
||||
},
|
||||
duration: 2500,
|
||||
},
|
||||
{
|
||||
kind: Stage.PoliciesLoaded,
|
||||
cursor: {
|
||||
top: 260,
|
||||
left: 550,
|
||||
},
|
||||
duration: 1500,
|
||||
},
|
||||
{
|
||||
kind: Stage.ClickSearchBox,
|
||||
cursor: {
|
||||
top: 325,
|
||||
left: 120,
|
||||
click: true,
|
||||
},
|
||||
duration: 2500,
|
||||
},
|
||||
{
|
||||
kind: Stage.SearchForPolicy,
|
||||
cursor: {
|
||||
top: 325,
|
||||
left: 120,
|
||||
},
|
||||
duration: 1500,
|
||||
},
|
||||
{
|
||||
kind: Stage.SelectPolicy,
|
||||
cursor: {
|
||||
top: 420,
|
||||
left: 36,
|
||||
click: true,
|
||||
},
|
||||
duration: 2500,
|
||||
},
|
||||
{
|
||||
kind: Stage.PolicySelected,
|
||||
cursor: {
|
||||
top: 440,
|
||||
left: 50,
|
||||
},
|
||||
duration: 1500,
|
||||
},
|
||||
{
|
||||
kind: Stage.RoleClickNextTags,
|
||||
cursor: {
|
||||
top: 722,
|
||||
left: 530,
|
||||
click: true,
|
||||
},
|
||||
duration: 2500,
|
||||
},
|
||||
{
|
||||
kind: Stage.RoleTags,
|
||||
cursor: {
|
||||
top: 722,
|
||||
left: 530,
|
||||
},
|
||||
duration: 2500,
|
||||
},
|
||||
{
|
||||
kind: Stage.RoleClickNextReview,
|
||||
cursor: {
|
||||
top: 722,
|
||||
left: 530,
|
||||
click: true,
|
||||
},
|
||||
duration: 2500,
|
||||
},
|
||||
{
|
||||
kind: Stage.RoleReview,
|
||||
cursor: {
|
||||
top: 722,
|
||||
left: 530,
|
||||
},
|
||||
duration: 2500,
|
||||
},
|
||||
{
|
||||
kind: Stage.ClickRoleName,
|
||||
cursor: {
|
||||
top: 262,
|
||||
left: 230,
|
||||
click: true,
|
||||
},
|
||||
duration: 2500,
|
||||
},
|
||||
{
|
||||
kind: Stage.RoleHasName,
|
||||
cursor: {
|
||||
top: 262,
|
||||
left: 230,
|
||||
},
|
||||
duration: 2500,
|
||||
},
|
||||
{
|
||||
kind: Stage.ClickCreateRoleButton,
|
||||
cursor: {
|
||||
top: 722,
|
||||
left: 530,
|
||||
click: true,
|
||||
},
|
||||
end: true,
|
||||
},
|
||||
{
|
||||
kind: Stage.ListRoles,
|
||||
cursor: {
|
||||
top: 722,
|
||||
left: 530,
|
||||
},
|
||||
duration: 2000,
|
||||
},
|
||||
{
|
||||
kind: Stage.ClickRole,
|
||||
cursor: {
|
||||
top: 142,
|
||||
left: 120,
|
||||
click: true,
|
||||
},
|
||||
duration: 2500,
|
||||
},
|
||||
{
|
||||
kind: Stage.ViewRole,
|
||||
cursor: {
|
||||
top: 182,
|
||||
left: 150,
|
||||
},
|
||||
end: true,
|
||||
},
|
||||
];
|
||||
+5
-3
@@ -17,14 +17,16 @@
|
||||
import React from 'react';
|
||||
import { MemoryRouter } from 'react-router';
|
||||
|
||||
import cfg from 'teleport/config';
|
||||
|
||||
import { IntegrationEnroll } from './IntegrationEnroll';
|
||||
|
||||
export default {
|
||||
title: 'Teleport/Integrations',
|
||||
title: 'Teleport/Integrations/Enroll',
|
||||
};
|
||||
|
||||
export const Enroll = () => (
|
||||
<MemoryRouter>
|
||||
export const Picker = () => (
|
||||
<MemoryRouter initialEntries={[cfg.routes.integrationEnroll]}>
|
||||
<IntegrationEnroll />
|
||||
</MemoryRouter>
|
||||
);
|
||||
+18
-1
@@ -22,13 +22,30 @@ import {
|
||||
FeatureHeader,
|
||||
FeatureHeaderTitle,
|
||||
} from 'teleport/components/Layout';
|
||||
import cfg from 'teleport/config';
|
||||
import { Route, Switch } from 'teleport/components/Router';
|
||||
|
||||
import { IntegrationTiles } from './IntegrationTiles';
|
||||
import { NoCodeIntegrationDescription } from './common';
|
||||
import { getRoutesToEnrollIntegrations } from './IntegrationRoute';
|
||||
|
||||
export function IntegrationEnroll() {
|
||||
return (
|
||||
<FeatureBox>
|
||||
<Switch>
|
||||
{getRoutesToEnrollIntegrations()}
|
||||
<Route
|
||||
path={cfg.routes.integrationEnroll}
|
||||
component={IntegrationPicker}
|
||||
/>
|
||||
</Switch>
|
||||
</FeatureBox>
|
||||
);
|
||||
}
|
||||
|
||||
export function IntegrationPicker() {
|
||||
return (
|
||||
<>
|
||||
<FeatureHeader>
|
||||
<FeatureHeaderTitle>Select Integration Type</FeatureHeaderTitle>
|
||||
</FeatureHeader>
|
||||
@@ -36,6 +53,6 @@ export function IntegrationEnroll() {
|
||||
<NoCodeIntegrationDescription />
|
||||
<IntegrationTiles />
|
||||
</Box>
|
||||
</FeatureBox>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { lazy } from 'react';
|
||||
|
||||
import cfg from 'teleport/config';
|
||||
import { Route } from 'teleport/components/Router';
|
||||
import { IntegrationKind } from 'teleport/services/integrations';
|
||||
|
||||
const EnrollAwsOidc = lazy(
|
||||
() => import(/* webpackChunkName: "enroll-aws-oidc" */ './AwsOidc')
|
||||
);
|
||||
|
||||
export function getRoutesToEnrollIntegrations() {
|
||||
return [
|
||||
<Route
|
||||
key={IntegrationKind.AwsOidc}
|
||||
exact
|
||||
path={cfg.getIntegrationEnrollRoute(IntegrationKind.AwsOidc)}
|
||||
component={EnrollAwsOidc}
|
||||
/>,
|
||||
];
|
||||
}
|
||||
+6
-1
@@ -21,6 +21,7 @@ import awsIcon from 'design/assets/images/icons/aws.svg';
|
||||
|
||||
import cfg from 'teleport/config';
|
||||
import { ToolTipNoPermBadge } from 'teleport/components/ToolTipNoPermBadge';
|
||||
import { IntegrationKind } from 'teleport/services/integrations';
|
||||
|
||||
import { IntegrationTile } from './common';
|
||||
|
||||
@@ -35,7 +36,11 @@ export function IntegrationTiles({
|
||||
<IntegrationTile
|
||||
disabled={!hasAccess}
|
||||
as={hasAccess ? Link : null}
|
||||
to={hasAccess ? cfg.getIntegrationEnrollRoute('aws-oidc') : null}
|
||||
to={
|
||||
hasAccess
|
||||
? cfg.getIntegrationEnrollRoute(IntegrationKind.AwsOidc)
|
||||
: null
|
||||
}
|
||||
>
|
||||
<Image mt={3} mb={2} src={awsIcon} width="80px" height="80px" />
|
||||
<Text>
|
||||
-2
@@ -18,8 +18,6 @@ import React from 'react';
|
||||
import { Flex, Text, Box } from 'design';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export type IntegrationTypes = 'aws-oidc';
|
||||
|
||||
export const IntegrationTile = styled(Flex).attrs({
|
||||
'data-testid': 'tile',
|
||||
})`
|
||||
+2
-1
@@ -16,6 +16,7 @@
|
||||
|
||||
// export as default for use with React.lazy
|
||||
export { IntegrationEnroll as default } from './IntegrationEnroll';
|
||||
export { IntegrationTiles } from './IntegrationTiles';
|
||||
|
||||
export { IntegrationTiles } from './IntegrationTiles';
|
||||
export { getRoutesToEnrollIntegrations } from './IntegrationRoute';
|
||||
export * from './common';
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
getStatusCodeTitle,
|
||||
Integration,
|
||||
IntegrationStatusCode,
|
||||
IntegrationKind,
|
||||
Plugin,
|
||||
} from 'teleport/services/integrations';
|
||||
|
||||
@@ -168,7 +169,7 @@ const IconCell = ({ item }: { item: IntegrationLike }) => {
|
||||
} else {
|
||||
// Default is integration.
|
||||
switch (item.kind) {
|
||||
case 'aws-oidc':
|
||||
case IntegrationKind.AwsOidc:
|
||||
formattedText = 'Amazon Web Services (OIDC)';
|
||||
icon = <IconContainer src={awsIcon} />;
|
||||
break;
|
||||
|
||||
@@ -25,7 +25,6 @@ import {
|
||||
} from 'teleport/components/Layout';
|
||||
import useTeleport from 'teleport/useTeleport';
|
||||
import { integrationService } from 'teleport/services/integrations';
|
||||
import cfg from 'teleport/config';
|
||||
|
||||
import { IntegrationsAddButton } from './IntegrationsAddButton';
|
||||
import { IntegrationList } from './IntegrationList';
|
||||
@@ -40,9 +39,7 @@ export function Integrations() {
|
||||
const canCreateIntegrations = ctx.storeUser.getIntegrationsAccess().create;
|
||||
|
||||
useEffect(() => {
|
||||
run(() =>
|
||||
integrationService.fetchIntegrations(cfg.proxyCluster).then(setItems)
|
||||
);
|
||||
run(() => integrationService.fetchIntegrations().then(setItems));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
||||
@@ -615,10 +615,12 @@ const cfg = {
|
||||
});
|
||||
},
|
||||
|
||||
getIntegrationsUrl(clusterId: string, name?: string) {
|
||||
getIntegrationsUrl(integrationName?: string) {
|
||||
// Currently you can only create integrations at the root cluster.
|
||||
const clusterId = cfg.proxyCluster;
|
||||
return generateResourcePath(cfg.api.integrationsPath, {
|
||||
clusterId,
|
||||
name,
|
||||
name: integrationName,
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -105,7 +105,9 @@ const Integrations = React.lazy(
|
||||
);
|
||||
const IntegrationEnroll = React.lazy(
|
||||
() =>
|
||||
import(/* webpackChunkName: "integration-enroll" */ './IntegrationEnroll')
|
||||
import(
|
||||
/* webpackChunkName: "integration-enroll" */ '@gravitational/teleport/src/Integrations/Enroll'
|
||||
)
|
||||
);
|
||||
|
||||
// ****************************
|
||||
|
||||
@@ -84,7 +84,8 @@ const baseContext = {
|
||||
lastConnected: '2020-09-26T17:30:23.512876876Z',
|
||||
status: 'online',
|
||||
nodeCount: 1,
|
||||
publicURL: 'localhost',
|
||||
publicURL:
|
||||
'some-long-cluster-public-url-name.cloud.teleport.gravitational.io:1234',
|
||||
authVersion: '4.4.0-dev',
|
||||
proxyVersion: '4.4.0-dev',
|
||||
},
|
||||
|
||||
@@ -17,17 +17,31 @@
|
||||
import api from 'teleport/services/api';
|
||||
import cfg from 'teleport/config';
|
||||
|
||||
import { Integration, IntegrationStatusCode } from './types';
|
||||
import {
|
||||
Integration,
|
||||
IntegrationCreateRequest,
|
||||
IntegrationStatusCode,
|
||||
} from './types';
|
||||
|
||||
export const integrationService = {
|
||||
fetchIntegration(clusterId: string, name: string): Promise<Integration> {
|
||||
return api
|
||||
.get(cfg.getIntegrationsUrl(clusterId, name))
|
||||
.then(makeIntegration);
|
||||
fetchIntegration(name: string): Promise<Integration> {
|
||||
return api.get(cfg.getIntegrationsUrl(name)).then(makeIntegration);
|
||||
},
|
||||
|
||||
fetchIntegrations(clusterId: string): Promise<Integration[]> {
|
||||
return api.get(cfg.getIntegrationsUrl(clusterId)).then(makeIntegrations);
|
||||
fetchIntegrations(): Promise<Integration[]> {
|
||||
return api.get(cfg.getIntegrationsUrl()).then(makeIntegrations);
|
||||
},
|
||||
|
||||
createIntegration(req: IntegrationCreateRequest): Promise<void> {
|
||||
return api.post(cfg.getIntegrationsUrl(), req);
|
||||
},
|
||||
|
||||
updateIntegration(name: string): Promise<void> {
|
||||
return api.put(cfg.getIntegrationsUrl(name));
|
||||
},
|
||||
|
||||
deleteIntegration(name: string): Promise<void> {
|
||||
return api.delete(cfg.getIntegrationsUrl(name));
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -40,7 +40,12 @@ export type Integration<
|
||||
details?: string;
|
||||
statusCode: IntegrationStatusCode;
|
||||
};
|
||||
export type IntegrationKind = 'aws-oidc';
|
||||
// IntegrationKind string values should be in sync
|
||||
// with the backend value for defining the integration
|
||||
// resource's subKind field.
|
||||
export enum IntegrationKind {
|
||||
AwsOidc = 'aws-oidc',
|
||||
}
|
||||
export type IntegrationSpecAwsOidc = {
|
||||
roleArn: string;
|
||||
};
|
||||
@@ -86,3 +91,9 @@ export function getStatusCodeDescription(
|
||||
export type Plugin = Integration<'plugin', PluginKind, PluginSpec>;
|
||||
export type PluginSpec = Record<string, never>; // currently no 'spec' fields exposed to the frontend
|
||||
export type PluginKind = 'slack';
|
||||
|
||||
export type IntegrationCreateRequest = {
|
||||
name: string;
|
||||
subKind: IntegrationKind;
|
||||
awsoidc?: IntegrationSpecAwsOidc;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user