From 5e6b679fef49fca7ebf230d467bb5429406cbcf3 Mon Sep 17 00:00:00 2001 From: Gao Sun Date: Mon, 6 Feb 2023 18:44:34 +0800 Subject: [PATCH] refactor(core): support localhost disabling --- packages/core/src/app/init.ts | 23 ++++++----- packages/core/src/env-set/GlobalValues.ts | 22 +---------- packages/core/src/env-set/UrlSet.ts | 48 +++++++++++++++++++++++ packages/core/src/index.ts | 3 +- packages/core/src/oidc/adapter.ts | 5 +-- 5 files changed, 66 insertions(+), 35 deletions(-) create mode 100644 packages/core/src/env-set/UrlSet.ts diff --git a/packages/core/src/app/init.ts b/packages/core/src/app/init.ts index 5f7fb5b84..5276cd58d 100644 --- a/packages/core/src/app/init.ts +++ b/packages/core/src/app/init.ts @@ -53,25 +53,30 @@ export default async function initApp(app: Koa): Promise { ); const coreServer = await createHttp2Server(); - const adminServer = await createHttp2Server(); - coreServer.listen(urlSet.port, () => { logListening(); }); - adminServer.listen(adminUrlSet.port, () => { - logListening('admin'); - }); + // Create another server if admin localhost enabled + if (!adminUrlSet.isLocalhostDisabled) { + const adminServer = await createHttp2Server(); + adminServer.listen(adminUrlSet.port, () => { + logListening('admin'); + }); + } return; } - // Chrome doesn't allow insecure http/2 servers + // Chrome doesn't allow insecure HTTP/2 servers, stick with HTTP for localhost. app.listen(urlSet.port, () => { logListening(); }); - app.listen(adminUrlSet.port, () => { - logListening('admin'); - }); + // Create another server if admin localhost enabled + if (!adminUrlSet.isLocalhostDisabled) { + app.listen(adminUrlSet.port, () => { + logListening('admin'); + }); + } } diff --git a/packages/core/src/env-set/GlobalValues.ts b/packages/core/src/env-set/GlobalValues.ts index 48796b820..8a1e1ec96 100644 --- a/packages/core/src/env-set/GlobalValues.ts +++ b/packages/core/src/env-set/GlobalValues.ts @@ -1,8 +1,9 @@ import net from 'net'; import { tryThat } from '@logto/shared'; -import { assertEnv, deduplicate, getEnv, getEnvAsStringArray } from '@silverhand/essentials'; +import { assertEnv, getEnv, getEnvAsStringArray } from '@silverhand/essentials'; +import UrlSet from './UrlSet.js'; import { isTrue } from './parameters.js'; import { throwErrorWithDsnMessage } from './throw-errors.js'; @@ -11,25 +12,6 @@ const developmentTenantIdKey = 'DEVELOPMENT_TENANT_ID'; type MultiTenancyMode = 'domain' | 'env'; -export class UrlSet { - public readonly port = Number(getEnv(this.envPrefix + 'PORT') || this.defaultPort); - public readonly localhostUrl = `${this.isHttpsEnabled ? 'https' : 'http'}://localhost:${ - this.port - }`; - - public readonly endpoint = getEnv(this.envPrefix + 'ENDPOINT', this.localhostUrl); - - constructor( - public readonly isHttpsEnabled: boolean, - protected readonly defaultPort: number, - protected readonly envPrefix: string = '' - ) {} - - public deduplicated(): string[] { - return deduplicate([this.localhostUrl, this.endpoint]); - } -} - export default class GlobalValues { public readonly isProduction = getEnv('NODE_ENV') === 'production'; public readonly isTest = getEnv('NODE_ENV') === 'test'; diff --git a/packages/core/src/env-set/UrlSet.ts b/packages/core/src/env-set/UrlSet.ts new file mode 100644 index 000000000..e0be46fc6 --- /dev/null +++ b/packages/core/src/env-set/UrlSet.ts @@ -0,0 +1,48 @@ +import { deduplicate, getEnv, trySafe } from '@silverhand/essentials'; + +import { isTrue } from './parameters.js'; + +const localhostDisabledMessage = 'Localhost has been disabled in this URL Set.'; + +export default class UrlSet { + readonly #port = Number(getEnv(this.envPrefix + 'PORT') || this.defaultPort); + readonly #endpoint = getEnv(this.envPrefix + 'ENDPOINT'); + + public readonly isLocalhostDisabled = isTrue(getEnv(this.envPrefix + 'DISABLE_LOCALHOST')); + + constructor( + public readonly isHttpsEnabled: boolean, + protected readonly defaultPort: number, + protected readonly envPrefix: string = '' + ) {} + + public deduplicated(): string[] { + return deduplicate( + [trySafe(() => this.localhostUrl), trySafe(() => this.endpoint)].filter( + (value): value is string => typeof value === 'string' + ) + ); + } + + public get port() { + if (this.isLocalhostDisabled) { + throw new Error(localhostDisabledMessage); + } + + return this.#port; + } + + public get localhostUrl() { + return `${this.isHttpsEnabled ? 'https' : 'http'}://localhost:${this.port}`; + } + + public get endpoint() { + const value = this.#endpoint || this.localhostUrl; + + if (this.isLocalhostDisabled && new URL(value).hostname === 'localhost') { + throw new Error(localhostDisabledMessage); + } + + return value; + } +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 799184004..d42e2a169 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -5,8 +5,7 @@ import Koa from 'koa'; dotenv.config({ path: await findUp('.env', {}) }); -// Import after env has configured - +// Import after env has been configured const { loadConnectorFactories } = await import('./utils/connectors/factories.js'); const { EnvSet } = await import('./env-set/index.js'); const { default: initI18n } = await import('./i18n/init.js'); diff --git a/packages/core/src/oidc/adapter.ts b/packages/core/src/oidc/adapter.ts index 4512fed41..9c0bc5541 100644 --- a/packages/core/src/oidc/adapter.ts +++ b/packages/core/src/oidc/adapter.ts @@ -30,10 +30,7 @@ const buildDemoAppUris = ( oidcClientMetadata: OidcClientMetadata ): Pick => { const { urlSet } = EnvSet.values; - const urls = [ - appendPath(urlSet.localhostUrl, MountedApps.DemoApp).toString(), - appendPath(urlSet.endpoint, MountedApps.DemoApp).toString(), - ]; + const urls = urlSet.deduplicated().map((url) => appendPath(url, MountedApps.DemoApp).toString()); const data = { redirectUris: deduplicate([...urls, ...oidcClientMetadata.redirectUris]),