From 66c790fbf1f50120bf7bea6f07d1bc8a002c9c46 Mon Sep 17 00:00:00 2001 From: Tymoteusz Czech <2625371+Tymek@users.noreply.github.com> Date: Thu, 14 Sep 2023 10:17:33 +0200 Subject: [PATCH] fix: Integrations quality updates (#4677) Fix issues uncovered when reviewing integrations list and form. - YouTube CSP - Text content and formatting - Margins - Update old integration icons - Fix headers in dark theme --- frontend/src/assets/icons/datadog.svg | 6 +- frontend/src/assets/icons/jira.svg | 22 +++--- frontend/src/assets/icons/sdks/Logo-swift.svg | 5 ++ frontend/src/assets/icons/slack.svg | 19 +++++- frontend/src/assets/icons/teams.svg | 67 ++++++++++++------- frontend/src/assets/icons/webhooks.svg | 17 ++++- .../IntegrationDelete/IntegrationDelete.tsx | 4 +- .../IntegrationForm/IntegrationForm.tsx | 4 +- .../AvailableIntegrations.tsx | 4 -- .../AvailableIntegrations/SDKs.ts | 6 +- .../IntegrationCard/IntegrationCard.tsx | 1 + .../IntegrationCardMenu.tsx | 53 +++++++++++---- .../IntegrationIcon/IntegrationIcon.tsx | 4 +- .../IntegrationList.styles.tsx | 2 +- .../__snapshots__/create-config.test.ts.snap | 3 + src/lib/create-config.ts | 10 +++ src/lib/middleware/secure-headers.ts | 18 +++++ src/lib/types/option.ts | 6 ++ .../reference/deploy/configuring-unleash.md | 17 +++-- 19 files changed, 197 insertions(+), 71 deletions(-) create mode 100644 frontend/src/assets/icons/sdks/Logo-swift.svg diff --git a/frontend/src/assets/icons/datadog.svg b/frontend/src/assets/icons/datadog.svg index 49e6f8473f..bcdfd15bbb 100644 --- a/frontend/src/assets/icons/datadog.svg +++ b/frontend/src/assets/icons/datadog.svg @@ -1 +1,5 @@ - \ No newline at end of file + + + + diff --git a/frontend/src/assets/icons/jira.svg b/frontend/src/assets/icons/jira.svg index b29b8db9af..f345e9b409 100644 --- a/frontend/src/assets/icons/jira.svg +++ b/frontend/src/assets/icons/jira.svg @@ -1,15 +1,17 @@ - - - - + + + + + - - - + + + - - - + + + diff --git a/frontend/src/assets/icons/sdks/Logo-swift.svg b/frontend/src/assets/icons/sdks/Logo-swift.svg new file mode 100644 index 0000000000..4a6514c109 --- /dev/null +++ b/frontend/src/assets/icons/sdks/Logo-swift.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/icons/slack.svg b/frontend/src/assets/icons/slack.svg index 69a4eb6a21..21b4269642 100644 --- a/frontend/src/assets/icons/slack.svg +++ b/frontend/src/assets/icons/slack.svg @@ -1 +1,18 @@ - \ No newline at end of file + + + + + + + + + + + + + + diff --git a/frontend/src/assets/icons/teams.svg b/frontend/src/assets/icons/teams.svg index d005badd9f..20e99c5315 100644 --- a/frontend/src/assets/icons/teams.svg +++ b/frontend/src/assets/icons/teams.svg @@ -1,26 +1,45 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/icons/webhooks.svg b/frontend/src/assets/icons/webhooks.svg index ec5cddf369..64d45d03bb 100644 --- a/frontend/src/assets/icons/webhooks.svg +++ b/frontend/src/assets/icons/webhooks.svg @@ -1 +1,16 @@ - \ No newline at end of file + + + + + + + + + + + + + diff --git a/frontend/src/component/integrations/IntegrationForm/IntegrationDelete/IntegrationDelete.tsx b/frontend/src/component/integrations/IntegrationForm/IntegrationDelete/IntegrationDelete.tsx index 22ddff6d5d..ca14b8d45b 100644 --- a/frontend/src/component/integrations/IntegrationForm/IntegrationDelete/IntegrationDelete.tsx +++ b/frontend/src/component/integrations/IntegrationForm/IntegrationDelete/IntegrationDelete.tsx @@ -46,8 +46,8 @@ export const IntegrationDelete: VFC = ({ id }) => { <> Delete integration - Deleting an integration it will delete the entire configuration - and it will automatically disable the integration + Deleting an integration will delete the entire configuration and + will automatically disable the integration = ({ show={() => ( {alerts?.map(({ type, text }) => ( - {text} + + {text} + ))} )} diff --git a/frontend/src/component/integrations/IntegrationList/AvailableIntegrations/AvailableIntegrations.tsx b/frontend/src/component/integrations/IntegrationList/AvailableIntegrations/AvailableIntegrations.tsx index 29bae1c51e..c3f71d26dd 100644 --- a/frontend/src/component/integrations/IntegrationList/AvailableIntegrations/AvailableIntegrations.tsx +++ b/frontend/src/component/integrations/IntegrationList/AvailableIntegrations/AvailableIntegrations.tsx @@ -1,7 +1,6 @@ import { type VFC } from 'react'; import { Box, Typography, styled } from '@mui/material'; import type { AddonTypeSchema } from 'openapi'; -import useLoading from 'hooks/useLoading'; import { PageContent } from 'component/common/PageContent/PageContent'; import { PageHeader } from 'component/common/PageHeader/PageHeader'; import { IntegrationCard } from '../IntegrationCard/IntegrationCard'; @@ -60,13 +59,10 @@ export const AvailableIntegrations: VFC = ({ const serverSdks = OFFICIAL_SDKS.filter(sdk => sdk.type === 'server'); const clientSdks = OFFICIAL_SDKS.filter(sdk => sdk.type === 'client'); - const ref = useLoading(loading || false); - return ( } isLoading={loading} - ref={ref} > diff --git a/frontend/src/component/integrations/IntegrationList/AvailableIntegrations/SDKs.ts b/frontend/src/component/integrations/IntegrationList/AvailableIntegrations/SDKs.ts index 85a2c99add..d4e7e78f24 100644 --- a/frontend/src/component/integrations/IntegrationList/AvailableIntegrations/SDKs.ts +++ b/frontend/src/component/integrations/IntegrationList/AvailableIntegrations/SDKs.ts @@ -38,7 +38,7 @@ export const OFFICIAL_SDKS: Sdk[] = [ { name: 'python', displayName: 'Python SDK', - description: 'Officially Unleash Client for Python', + description: 'Official Unleash Client for Python', documentationUrl: 'https://docs.getunleash.io/reference/sdks/python', type: 'server', }, @@ -79,8 +79,8 @@ export const OFFICIAL_SDKS: Sdk[] = [ type: 'client', }, { - name: 'ios', - displayName: 'iOS Proxy SDK', + name: 'swift', + displayName: 'Swift Proxy SDK', description: 'Officially Unleash Client for iOS', documentationUrl: 'https://docs.getunleash.io/reference/sdks/ios-proxy', type: 'client', diff --git a/frontend/src/component/integrations/IntegrationList/IntegrationCard/IntegrationCard.tsx b/frontend/src/component/integrations/IntegrationList/IntegrationCard/IntegrationCard.tsx index 1c2e7c3af8..9e10205683 100644 --- a/frontend/src/component/integrations/IntegrationList/IntegrationCard/IntegrationCard.tsx +++ b/frontend/src/component/integrations/IntegrationList/IntegrationCard/IntegrationCard.tsx @@ -36,6 +36,7 @@ const StyledLink = styled(Link)(({ theme }) => ({ backgroundColor: theme.palette.action.hover, }, })); + const StyledAnchor = styled('a')(({ theme }) => ({ display: 'flex', flexDirection: 'column', diff --git a/frontend/src/component/integrations/IntegrationList/IntegrationCard/IntegrationCardMenu/IntegrationCardMenu.tsx b/frontend/src/component/integrations/IntegrationList/IntegrationCard/IntegrationCardMenu/IntegrationCardMenu.tsx index 3e8e634bf8..b3c809dca2 100644 --- a/frontend/src/component/integrations/IntegrationList/IntegrationCard/IntegrationCardMenu/IntegrationCardMenu.tsx +++ b/frontend/src/component/integrations/IntegrationList/IntegrationCard/IntegrationCardMenu/IntegrationCardMenu.tsx @@ -21,6 +21,8 @@ import type { AddonSchema } from 'openapi'; import useAddons from 'hooks/api/getters/useAddons/useAddons'; import useToast from 'hooks/useToast'; import { formatUnknownError } from 'utils/formatUnknownError'; +import { Dialogue } from 'component/common/Dialogue/Dialogue'; +import { event } from 'cypress/types/jquery'; interface IIntegrationCardMenuProps { addon: AddonSchema; @@ -35,10 +37,19 @@ const StyledMenu = styled('div')(({ theme }) => ({ alignItems: 'center', })); +const preventPropagation = + (fn: () => void) => (event: React.SyntheticEvent) => { + event.preventDefault(); + event.stopPropagation(); + fn(); + }; + export const IntegrationCardMenu: VFC = ({ addon, }) => { - const [open, setOpen] = useState(false); + const [isMenuOpen, setIsMenuOpen] = useState(false); + const [isDeleteOpen, setIsDeleteOpen] = useState(false); + const [isToggleOpen, setIsToggleOpen] = useState(false); const [anchorEl, setAnchorEl] = useState(null); const { updateAddon, removeAddon } = useAddonsApi(); const { refetchAddons } = useAddons(); @@ -46,12 +57,12 @@ export const IntegrationCardMenu: VFC = ({ const handleMenuClick = (event: React.SyntheticEvent) => { event.preventDefault(); - if (open) { - setOpen(false); + if (isMenuOpen) { + setIsMenuOpen(false); setAnchorEl(null); } else { setAnchorEl(event.currentTarget); - setOpen(true); + setIsMenuOpen(true); } }; const updateAccess = useHasRootAccess(UPDATE_ADDON); @@ -93,9 +104,9 @@ export const IntegrationCardMenu: VFC = ({ @@ -119,10 +130,7 @@ export const IntegrationCardMenu: VFC = ({ onClose={handleMenuClick} > { - e.preventDefault(); - toggleIntegration(); - }} + onClick={preventPropagation(() => setIsToggleOpen(true))} disabled={!updateAccess} > @@ -134,10 +142,7 @@ export const IntegrationCardMenu: VFC = ({ {' '} { - e.preventDefault(); - deleteIntegration(); - }} + onClick={preventPropagation(() => setIsDeleteOpen(true))} > @@ -145,6 +150,26 @@ export const IntegrationCardMenu: VFC = ({ Delete + + setIsToggleOpen(false))} + title="Confirm deletion" + > + + Are you sure you want to{' '} + {addon.enabled ? 'disable' : 'enable'} this Integration? + + + setIsDeleteOpen(false))} + title="Confirm deletion" + > + Are you sure you want to delete this Integration? + ); }; diff --git a/frontend/src/component/integrations/IntegrationList/IntegrationIcon/IntegrationIcon.tsx b/frontend/src/component/integrations/IntegrationList/IntegrationIcon/IntegrationIcon.tsx index d4f3c50292..a8b86b903a 100644 --- a/frontend/src/component/integrations/IntegrationList/IntegrationIcon/IntegrationIcon.tsx +++ b/frontend/src/component/integrations/IntegrationList/IntegrationIcon/IntegrationIcon.tsx @@ -15,7 +15,7 @@ import android from 'assets/icons/sdks/Logo-android.svg'; import dotnet from 'assets/icons/sdks/Logo-net.svg'; import flutter from 'assets/icons/sdks/Logo-flutter.svg'; import go from 'assets/icons/sdks/Logo-go.svg'; -import ios from 'assets/icons/sdks/Logo-ios.svg'; +import swift from 'assets/icons/sdks/Logo-swift.svg'; import java from 'assets/icons/sdks/Logo-java.svg'; import javascript from 'assets/icons/sdks/Logo-javascript.svg'; import node from 'assets/icons/sdks/Logo-node.svg'; @@ -58,7 +58,7 @@ const integrations: Record< dotnet: { title: 'Dotnet', icon: dotnet }, flutter: { title: 'Flutter', icon: flutter }, go: { title: 'Go', icon: go }, - ios: { title: 'iOS', icon: ios }, + swift: { title: 'Swift', icon: swift }, java: { title: 'Java', icon: java }, javascript: { title: 'Javascript', icon: javascript }, node: { title: 'Node', icon: node }, diff --git a/frontend/src/component/integrations/IntegrationList/IntegrationList.styles.tsx b/frontend/src/component/integrations/IntegrationList/IntegrationList.styles.tsx index bd47a49f8f..1204d1cdfd 100644 --- a/frontend/src/component/integrations/IntegrationList/IntegrationList.styles.tsx +++ b/frontend/src/component/integrations/IntegrationList/IntegrationList.styles.tsx @@ -2,7 +2,7 @@ import { styled } from '@mui/material'; export const StyledCardsGrid = styled('div')<{ small?: boolean }>( ({ theme, small = false }) => ({ - gridTemplateColumns: `repeat(auto-fit, minmax(${ + gridTemplateColumns: `repeat(auto-fill, minmax(${ small ? '250px' : '350px' }, 1fr))`, gridAutoRows: '1fr', diff --git a/src/lib/__snapshots__/create-config.test.ts.snap b/src/lib/__snapshots__/create-config.test.ts.snap index c2bd0150a9..622373b754 100644 --- a/src/lib/__snapshots__/create-config.test.ts.snap +++ b/src/lib/__snapshots__/create-config.test.ts.snap @@ -7,7 +7,10 @@ exports[`should create default config 1`] = ` "connectSrc": [], "defaultSrc": [], "fontSrc": [], + "frameSrc": [], "imgSrc": [], + "mediaSrc": [], + "objectSrc": [], "scriptSrc": [], "styleSrc": [], }, diff --git a/src/lib/create-config.ts b/src/lib/create-config.ts index f7ab231924..a18015f686 100644 --- a/src/lib/create-config.ts +++ b/src/lib/create-config.ts @@ -316,6 +316,9 @@ const parseCspConfig = ( imgSrc: cspConfig.imgSrc || [], styleSrc: cspConfig.styleSrc || [], connectSrc: cspConfig.connectSrc || [], + mediaSrc: cspConfig.mediaSrc || [], + objectSrc: cspConfig.objectSrc || [], + frameSrc: cspConfig.frameSrc || [], }; }; @@ -326,6 +329,10 @@ const parseCspEnvironmentVariables = (): ICspDomainConfig => { const scriptSrc = process.env.CSP_ALLOWED_SCRIPT?.split(',') || []; const imgSrc = process.env.CSP_ALLOWED_IMG?.split(',') || []; const connectSrc = process.env.CSP_ALLOWED_CONNECT?.split(',') || []; + const mediaSrc = process.env.CSP_ALLOWED_MEDIA?.split(',') || []; + const objectSrc = process.env.CSP_ALLOWED_OBJECT?.split(',') || []; + const frameSrc = process.env.CSP_ALLOWED_FRAME?.split(',') || []; + return { defaultSrc, fontSrc, @@ -333,6 +340,9 @@ const parseCspEnvironmentVariables = (): ICspDomainConfig => { scriptSrc, imgSrc, connectSrc, + mediaSrc, + objectSrc, + frameSrc, }; }; diff --git a/src/lib/middleware/secure-headers.ts b/src/lib/middleware/secure-headers.ts index 3a1812bae7..efa6245498 100644 --- a/src/lib/middleware/secure-headers.ts +++ b/src/lib/middleware/secure-headers.ts @@ -55,6 +55,24 @@ const secureHeaders: (config: IUnleashConfig) => RequestHandler = (config) => { 'europe-west3-metrics-304612.cloudfunctions.net', ...config.additionalCspAllowedDomains.connectSrc, ], + mediaSrc: [ + '*.youtube.com', + '*.youtube-nocookie.com', + ...config.additionalCspAllowedDomains.mediaSrc, + ], + objectSrc: [ + '*.youtube.com', + '*.youtube-nocookie.com', + ...config.additionalCspAllowedDomains.objectSrc, + ], + frameSrc: [ + "'self'", + 'cdn.getunleash.io', + 'gravatar.com', + '*.youtube.com', + '*.youtube-nocookie.com', + ...config.additionalCspAllowedDomains.frameSrc, + ], }, }, crossOriginEmbedderPolicy: false, diff --git a/src/lib/types/option.ts b/src/lib/types/option.ts index 533df47352..d6d85358ad 100644 --- a/src/lib/types/option.ts +++ b/src/lib/types/option.ts @@ -162,6 +162,9 @@ export interface ICspDomainOptions { scriptSrc?: string[]; imgSrc?: string[]; connectSrc?: string[]; + frameSrc?: string[]; + objectSrc?: string[]; + mediaSrc?: string[]; } export interface ICspDomainConfig { @@ -171,6 +174,9 @@ export interface ICspDomainConfig { scriptSrc: string[]; imgSrc: string[]; connectSrc: string[]; + frameSrc: string[]; + objectSrc: string[]; + mediaSrc: string[]; } interface IFrontendApi { diff --git a/website/docs/reference/deploy/configuring-unleash.md b/website/docs/reference/deploy/configuring-unleash.md index 2f55af6702..c26bd2adbc 100644 --- a/website/docs/reference/deploy/configuring-unleash.md +++ b/website/docs/reference/deploy/configuring-unleash.md @@ -102,13 +102,16 @@ unleash.start(unleashOptions); - **preHook** (function) - this is a hook if you need to provide any middlewares to express before `unleash` adds any. Express app instance is injected as first argument. - **preRouterHook** (function) - use this to register custom express middlewares before the `unleash` specific routers are added. - **secureHeaders** (boolean) - use this to enable security headers (HSTS, CSP, etc) when serving Unleash from HTTPS. Can also be configured through the environment variable `SECURE_HEADERS`. -- **additionalCspAllowedDomains** (CspAllowedDomains) - use this when you want to enable security headers but have additional domains you need to allow traffic to - - You can set the environment variable CSP_ALLOWED_DEFAULT to allow new defaultSrc (comma separated list) - - You can set the environment variable CSP_ALLOWED_FONT to allow new fontSrc (comma separated list) - - You can set the environment variable CSP_ALLOWED_STYLE to allow new styleSrc (comma separated list) - - You can set the environment variable CSP_ALLOWED_SCRIPT to allow new scriptSrc (comma separated list) - - You can set the environment variable CSP_ALLOWED_IMG to allow new imgSrc (comma separated list) - - You can set the environment variable CSP_ALLOWED_CONNECT to allow new connectSrc (comma separated list) +- **additionalCspAllowedDomains** (CspAllowedDomains) - use this when you want to enable security headers but have additional domains you need to allow traffic to you can set the following environment variables: + - `CSP_ALLOWED_DEFAULT` to allow new defaultSrc (comma separated list) + - `CSP_ALLOWED_FONT` to allow new fontSrc (comma separated list) + - `CSP_ALLOWED_STYLE` to allow new styleSrc (comma separated list) + - `CSP_ALLOWED_SCRIPT` to allow new scriptSrc (comma separated list) + - `CSP_ALLOWED_IMG` to allow new imgSrc (comma separated list) + - `CSP_ALLOWED_CONNECT` to allow new connectSrc (comma separated list) + - `CSP_ALLOWED_FRAME` to allow new frameSrc (comma separated listed) + - `CSP_ALLOWED_MEDIA` to allow new mediaSrc (comma separated list) + - `CSP_ALLOWED_OBJECT` to allow new objectSrc (comma separated list) - **server** - The server config object taking the following properties - _port_ - which port the unleash-server should bind to. If port is omitted or is 0, the operating system will assign an arbitrary unused port. Will be ignored if pipe is specified. This value may also be set via the `HTTP_PORT` environment variable - _host_ - which host the unleash-server should bind to. If host is omitted, the server will accept connections on the unspecified IPv6 address (::) when IPv6 is available, or the unspecified IPv4 address (0.0.0.0) otherwise. This value may also be set via the `HTTP_HOST` environment variable