fix: expected origin boundary works now with initial props

Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
Tilman Vatteroth 2023-04-04 20:29:59 +02:00
parent 8977100830
commit 9771ffcf00
3 changed files with 64 additions and 15 deletions

View file

@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file) * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
@ -14,14 +14,16 @@ import { UiNotificationBoundary } from '../components/notifications/ui-notificat
import { StoreProvider } from '../redux/store-provider' import { StoreProvider } from '../redux/store-provider'
import { BaseUrlFromEnvExtractor } from '../utils/base-url-from-env-extractor' import { BaseUrlFromEnvExtractor } from '../utils/base-url-from-env-extractor'
import { configureLuxon } from '../utils/configure-luxon' import { configureLuxon } from '../utils/configure-luxon'
import { ExpectedOriginBoundary } from '../utils/uri-origin-boundary' import { determineCurrentOrigin } from '../utils/determine-current-origin'
import type { AppInitialProps, AppProps } from 'next/app' import { ExpectedOriginBoundary } from '../utils/expected-origin-boundary'
import type { AppContext, AppInitialProps, AppProps } from 'next/app'
import React from 'react' import React from 'react'
configureLuxon() configureLuxon()
interface AppPageProps { interface AppPageProps {
baseUrls: BaseUrls | undefined baseUrls: BaseUrls | undefined
currentOrigin: string | undefined
} }
/** /**
@ -31,8 +33,8 @@ interface AppPageProps {
function HedgeDocApp({ Component, pageProps }: AppProps<AppPageProps>) { function HedgeDocApp({ Component, pageProps }: AppProps<AppPageProps>) {
return ( return (
<BaseUrlContextProvider baseUrls={pageProps.baseUrls}> <BaseUrlContextProvider baseUrls={pageProps.baseUrls}>
<ExpectedOriginBoundary currentOrigin={pageProps.currentOrigin}>
<StoreProvider> <StoreProvider>
<ExpectedOriginBoundary>
<BaseHead /> <BaseHead />
<ApplicationLoader> <ApplicationLoader>
<ErrorBoundary> <ErrorBoundary>
@ -41,20 +43,22 @@ function HedgeDocApp({ Component, pageProps }: AppProps<AppPageProps>) {
</UiNotificationBoundary> </UiNotificationBoundary>
</ErrorBoundary> </ErrorBoundary>
</ApplicationLoader> </ApplicationLoader>
</ExpectedOriginBoundary>
</StoreProvider> </StoreProvider>
</ExpectedOriginBoundary>
</BaseUrlContextProvider> </BaseUrlContextProvider>
) )
} }
const baseUrlFromEnvExtractor: BaseUrlFromEnvExtractor = new BaseUrlFromEnvExtractor() const baseUrlFromEnvExtractor = new BaseUrlFromEnvExtractor()
HedgeDocApp.getInitialProps = (): AppInitialProps<AppPageProps> => { HedgeDocApp.getInitialProps = ({ ctx }: AppContext): AppInitialProps<AppPageProps> => {
const baseUrls = baseUrlFromEnvExtractor.extractBaseUrls().orElse(undefined) const baseUrls = baseUrlFromEnvExtractor.extractBaseUrls().orElse(undefined)
const currentOrigin = determineCurrentOrigin(ctx)
return { return {
pageProps: { pageProps: {
baseUrls baseUrls,
currentOrigin
} }
} }
} }

View file

@ -0,0 +1,33 @@
/*
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { isClientSideRendering } from './is-client-side-rendering'
import type { NextPageContext } from 'next'
/**
* Determines the location origin of the current request.
* Client side rendering will use the browsers window location.
* Server side rendering will use the http request.
*
* @param context The next page context that contains the http headers
* @return the determined request origin. Will be undefined if no origin could be determined.
*/
export const determineCurrentOrigin = (context: NextPageContext): string | undefined => {
if (isClientSideRendering()) {
return window.location.origin
}
const headers = context.req?.headers
if (headers === undefined) {
return undefined
}
const protocol = headers['x-forwarded-proto'] ?? 'http'
const host = headers['x-forwarded-host'] ?? headers['host']
if (host === undefined) {
return undefined
}
return `${protocol as string}://${host as string}`
}

View file

@ -1,25 +1,37 @@
/* /*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file) * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { useBaseUrl } from '../hooks/common/use-base-url' import { useBaseUrl } from '../hooks/common/use-base-url'
import { isClientSideRendering } from './is-client-side-rendering'
import React, { Fragment, useMemo } from 'react' import React, { Fragment, useMemo } from 'react'
import type { PropsWithChildren } from 'react' import type { PropsWithChildren } from 'react'
export interface ExpectedOriginBoundaryProps {
currentOrigin?: string
}
/** /**
* Checks if the url of the current browser window matches the expected origin. * Checks if the url of the current browser window matches the expected origin.
* This is necessary to ensure that the render endpoint is only opened from the rendering origin. * This is necessary to ensure that the render endpoint is only opened from the rendering origin.
* *
* @param children The children react element that should be rendered if the origin is correct * @param children The children react element that should be rendered if the origin is correct
* @param currentOrigin the current origin from client or server side rendering context
*/ */
export const ExpectedOriginBoundary: React.FC<PropsWithChildren> = ({ children }) => { export const ExpectedOriginBoundary: React.FC<PropsWithChildren<ExpectedOriginBoundaryProps>> = ({
children,
currentOrigin
}) => {
const baseUrl = useBaseUrl() const baseUrl = useBaseUrl()
const expectedOrigin = useMemo(() => new URL(baseUrl).origin, [baseUrl]) const expectedOrigin = useMemo(() => new URL(baseUrl).origin, [baseUrl])
if (isClientSideRendering() && window.location.origin !== expectedOrigin) { if (currentOrigin !== expectedOrigin) {
return <span>{`You can't open this page using this URL. For this endpoint "${expectedOrigin}" is expected.`}</span> return (
<span
className={
'text-white bg-dark'
}>{`You can't open this page using this URL. For this endpoint "${expectedOrigin}" is expected.`}</span>
)
} else { } else {
return <Fragment>{children}</Fragment> return <Fragment>{children}</Fragment>
} }