Skip to content

Commit

Permalink
[v17] aws oidc user tasks (#52262)
Browse files Browse the repository at this point in the history
* Add navigation to individual aws resource rule tables (#49855)

* Add rules tables for aws resources

* review feedback

* Show user tasks for aws oidc integration (#50100)

* User task design updates (#52280)

* Set featurebox left padding

* Set side panel padding

* Stop modifying table contents

* Deep link open sidepanel

* PR feedback

* User task design updates (#52318)

* fix rebase imports
  • Loading branch information
michellescripts authored Feb 20, 2025
1 parent 8278065 commit c14de3d
Show file tree
Hide file tree
Showing 39 changed files with 3,847 additions and 254 deletions.
663 changes: 653 additions & 10 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions web/__mocks__/react-markdown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Manually mocks react-markdown for testing due to ES modules
// https://jestjs.io/docs/manual-mocks
function ReactMarkdown({ children }) {
return <>{children}</>;
}

export default ReactMarkdown;
57 changes: 57 additions & 0 deletions web/packages/design/src/Tabs/Tabs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* Teleport
* Copyright (C) 2024 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import { NavLink } from 'react-router-dom';
import styled from 'styled-components';

export const TabsContainer = styled.div`
position: relative;
display: flex;
gap: ${p => p.theme.space[5]}px;
align-items: center;
padding: 0 ${p => p.theme.space[5]}px;
border-bottom: 1px solid ${p => p.theme.colors.spotBackground[0]};
`;

export const TabContainer = styled(NavLink)<{ selected?: boolean }>`
padding: ${p => p.theme.space[1] + p.theme.space[2]}px
${p => p.theme.space[2]}px;
position: relative;
cursor: pointer;
z-index: 2;
opacity: ${p => (p.selected ? 1 : 0.5)};
transition: opacity 0.3s linear;
color: ${p => p.theme.colors.text.main};
font-weight: 300;
font-size: 22px;
line-height: ${p => p.theme.space[5]}px;
white-space: nowrap;
text-decoration: none;
&:hover {
opacity: 1;
}
`;

export const TabBorder = styled.div`
position: absolute;
bottom: -1px;
background: ${p => p.theme.colors.brand};
height: 2px;
transition: all 0.3s cubic-bezier(0.19, 1, 0.22, 1);
`;
3 changes: 2 additions & 1 deletion web/packages/teleport/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
"@xterm/addon-webgl": "^0.18.0",
"@xterm/xterm": "^5.5.0",
"create-react-class": "^15.6.3",
"events": "3.3.0"
"events": "3.3.0",
"react-markdown": "^9.0.3"
},
"devDependencies": {
"@gravitational/build": "workspace:*",
Expand Down
2 changes: 1 addition & 1 deletion web/packages/teleport/src/Integrations/IntegrationList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export function IntegrationList(props: Props) {
}

function getRowStyle(row: IntegrationLike): React.CSSProperties {
if (row.kind !== 'okta') return;
if (row.kind !== 'okta' && row.kind !== IntegrationKind.AwsOidc) return;
return { cursor: 'pointer' };
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,38 +16,61 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import { addHours } from 'date-fns';
import React from 'react';
import {
makeErrorAttempt,
makeProcessingAttempt,
makeSuccessAttempt,
} from 'shared/hooks/useAsync';

import cfg from 'teleport/config';
import { AwsOidcDashboard } from 'teleport/Integrations/status/AwsOidc/AwsOidcDashboard';
import { MockAwsOidcStatusProvider } from 'teleport/Integrations/status/AwsOidc/testHelpers/mockAwsOidcStatusProvider';
import { AwsOidcStatusContextState } from 'teleport/Integrations/status/AwsOidc/useAwsOidcStatus';
import {
IntegrationKind,
ResourceTypeSummary,
} from 'teleport/services/integrations';
import { IntegrationKind } from 'teleport/services/integrations';

import { makeAwsOidcStatusContextState } from './testHelpers/makeAwsOidcStatusContextState';

export default {
title: 'Teleport/Integrations/AwsOidc',
};

const setup = {
path: cfg.routes.integrationStatus,
initialEntries: [
cfg.getIntegrationStatusRoute(IntegrationKind.AwsOidc, 'oidc-int'),
],
};

// Loaded dashboard with data for each aws resource and a navigation header
export function Dashboard() {
return (
<MockAwsOidcStatusProvider value={makeAwsOidcStatusContextState()}>
<MockAwsOidcStatusProvider
{...setup}
value={makeAwsOidcStatusContextState()}
>
<AwsOidcDashboard />
</MockAwsOidcStatusProvider>
);
}

// Loaded dashboard with missing data for each aws resource and a navigation header
export function DashboardMissingData() {
const state = makeAwsOidcStatusContextState();
state.statsAttempt.data = undefined;
return (
<MockAwsOidcStatusProvider {...setup} value={state}>
<AwsOidcDashboard />
</MockAwsOidcStatusProvider>
);
}

// Loaded dashboard with missing data for each aws resource and a navigation header
export function DashboardMissingSummary() {
const state = makeAwsOidcStatusContextState();
state.statsAttempt.data.awseks = undefined;
state.statsAttempt.data.awsrds = undefined;
state.statsAttempt.data.awsec2 = undefined;
return (
<MockAwsOidcStatusProvider value={state}>
<MockAwsOidcStatusProvider {...setup} value={state}>
<AwsOidcDashboard />
</MockAwsOidcStatusProvider>
);
Expand All @@ -56,10 +79,10 @@ export function DashboardMissingData() {
// Loading screen
export function StatsProcessing() {
const props = makeAwsOidcStatusContextState({
statsAttempt: { status: 'processing', data: null, statusText: '' },
statsAttempt: makeProcessingAttempt(),
});
return (
<MockAwsOidcStatusProvider value={props}>
<MockAwsOidcStatusProvider {...setup} value={props}>
<AwsOidcDashboard />
</MockAwsOidcStatusProvider>
);
Expand All @@ -68,14 +91,10 @@ export function StatsProcessing() {
// No header, no loading indicator
export function IntegrationProcessing() {
const props = makeAwsOidcStatusContextState({
integrationAttempt: {
status: 'processing',
data: null,
statusText: '',
},
integrationAttempt: makeProcessingAttempt(),
});
return (
<MockAwsOidcStatusProvider value={props}>
<MockAwsOidcStatusProvider {...setup} value={props}>
<AwsOidcDashboard />
</MockAwsOidcStatusProvider>
);
Expand All @@ -84,32 +103,39 @@ export function IntegrationProcessing() {
// Loaded error message
export function StatsFailed() {
const props = makeAwsOidcStatusContextState({
statsAttempt: {
status: 'error',
data: null,
statusText: 'failed to get stats',
error: {},
},
statsAttempt: makeErrorAttempt(new Error('failed to get stats')),
});
return (
<MockAwsOidcStatusProvider value={props}>
<MockAwsOidcStatusProvider {...setup} value={props}>
<AwsOidcDashboard />
</MockAwsOidcStatusProvider>
);
}

// Loaded dashboard with data for each aws resource but no navigation header
// Loaded error message
export function IntegrationFailed() {
const props = makeAwsOidcStatusContextState({
integrationAttempt: {
status: 'error',
data: null,
statusText: 'failed to get integration',
error: {},
},
integrationAttempt: makeErrorAttempt(
new Error('failed to get integration')
),
});
return (
<MockAwsOidcStatusProvider value={props}>
<MockAwsOidcStatusProvider {...setup} value={props}>
<AwsOidcDashboard />
</MockAwsOidcStatusProvider>
);
}

// Loaded error message
export function BothFailed() {
const props = makeAwsOidcStatusContextState({
statsAttempt: makeErrorAttempt(new Error('failed to get stats')),
integrationAttempt: makeErrorAttempt(
new Error('failed to get integration')
),
});
return (
<MockAwsOidcStatusProvider {...setup} value={props}>
<AwsOidcDashboard />
</MockAwsOidcStatusProvider>
);
Expand All @@ -118,10 +144,10 @@ export function IntegrationFailed() {
// Blank screen
export function StatsNoData() {
const props = makeAwsOidcStatusContextState({
statsAttempt: { status: 'success', data: null, statusText: '' },
statsAttempt: makeSuccessAttempt(null),
});
return (
<MockAwsOidcStatusProvider value={props}>
<MockAwsOidcStatusProvider {...setup} value={props}>
<AwsOidcDashboard />
</MockAwsOidcStatusProvider>
);
Expand All @@ -130,71 +156,11 @@ export function StatsNoData() {
// No header, no loading indicator
export function IntegrationNoData() {
const props = makeAwsOidcStatusContextState({
integrationAttempt: {
status: 'success',
data: null,
statusText: '',
},
integrationAttempt: makeSuccessAttempt(null),
});
return (
<MockAwsOidcStatusProvider value={props}>
<MockAwsOidcStatusProvider {...setup} value={props}>
<AwsOidcDashboard />
</MockAwsOidcStatusProvider>
);
}

function makeAwsOidcStatusContextState(
overrides: Partial<AwsOidcStatusContextState> = {}
): AwsOidcStatusContextState {
return Object.assign(
{
integrationAttempt: {
status: 'success',
statusText: '',
data: {
resourceType: 'integration',
name: 'integration-one',
kind: IntegrationKind.AwsOidc,
spec: {
roleArn: 'arn:aws:iam::111456789011:role/bar',
},
statusCode: 1,
},
},
statsAttempt: {
status: 'success',
statusText: '',
data: {
name: 'integration-one',
subKind: IntegrationKind.AwsOidc,
awsoidc: {
roleArn: 'arn:aws:iam::111456789011:role/bar',
},
awsec2: makeResourceTypeSummary(),
awsrds: makeResourceTypeSummary(),
awseks: makeResourceTypeSummary(),
},
},
},
overrides
);
}

function makeResourceTypeSummary(
overrides: Partial<ResourceTypeSummary> = {}
): ResourceTypeSummary {
return Object.assign(
{
rulesCount: Math.floor(Math.random() * 100),
resourcesFound: Math.floor(Math.random() * 100),
resourcesEnrollmentFailed: Math.floor(Math.random() * 100),
resourcesEnrollmentSuccess: Math.floor(Math.random() * 100),
discoverLastSync: addHours(
new Date().getTime(),
-Math.floor(Math.random() * 100)
),
ecsDatabaseServiceCount: Math.floor(Math.random() * 100),
},
overrides
);
}
Loading

0 comments on commit c14de3d

Please sign in to comment.