From baee47a276001b3f3dd4a42136308bdc0683ccf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?= =?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?= =?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?= Date: Fri, 22 Dec 2023 15:19:50 +0100 Subject: [PATCH] refactor(core): Move all base URLs to UrlService (no-changelog) (#8141) This change kept coming up in #6713, #7773, and #8135. So this PR moves the existing code without actually changing anything, to help get rid of some of the circular dependencies. ## Review / Merge checklist - [x] PR title and summary are descriptive. --- packages/cli/src/GenericHelpers.ts | 16 -------- packages/cli/src/PublicApi/index.ts | 7 ++-- .../UserManagement/UserManagementHelper.ts | 17 +------- packages/cli/src/WebhookHelpers.ts | 12 ------ .../cli/src/WorkflowExecuteAdditionalData.ts | 6 +-- packages/cli/src/commands/start.ts | 6 +-- .../oauth/abstractOAuth.controller.ts | 5 ++- .../controllers/passwordReset.controller.ts | 5 ++- ...romentHelpers.ts => environmentHelpers.ts} | 0 .../variables/variables.service.ee.ts | 2 +- packages/cli/src/services/frontend.service.ts | 16 ++++---- packages/cli/src/services/url.service.ts | 40 +++++++++++++++++++ packages/cli/src/services/user.service.ts | 13 +++--- .../src/sso/saml/routes/saml.controller.ee.ts | 11 +++-- packages/cli/src/sso/saml/saml.service.ee.ts | 13 +++--- .../cli/src/sso/saml/serviceProvider.ee.ts | 9 +++-- 16 files changed, 93 insertions(+), 85 deletions(-) rename packages/cli/src/environments/variables/{enviromentHelpers.ts => environmentHelpers.ts} (100%) create mode 100644 packages/cli/src/services/url.service.ts diff --git a/packages/cli/src/GenericHelpers.ts b/packages/cli/src/GenericHelpers.ts index 4645535332..b37d395882 100644 --- a/packages/cli/src/GenericHelpers.ts +++ b/packages/cli/src/GenericHelpers.ts @@ -1,6 +1,5 @@ import type express from 'express'; import { validate } from 'class-validator'; -import config from '@/config'; import type { WorkflowEntity } from '@db/entities/WorkflowEntity'; import type { CredentialsEntity } from '@db/entities/CredentialsEntity'; import type { TagEntity } from '@db/entities/TagEntity'; @@ -8,21 +7,6 @@ import type { User } from '@db/entities/User'; import type { UserUpdatePayload } from '@/requests'; import { BadRequestError } from './errors/response-errors/bad-request.error'; -/** - * Returns the base URL n8n is reachable from - */ -export function getBaseUrl(): string { - const protocol = config.getEnv('protocol'); - const host = config.getEnv('host'); - const port = config.getEnv('port'); - const path = config.getEnv('path'); - - if ((protocol === 'http' && port === 80) || (protocol === 'https' && port === 443)) { - return `${protocol}://${host}${path}`; - } - return `${protocol}://${host}:${port}${path}`; -} - /** * Returns the session id if one is set */ diff --git a/packages/cli/src/PublicApi/index.ts b/packages/cli/src/PublicApi/index.ts index 8a88a51e07..d4cb53b26a 100644 --- a/packages/cli/src/PublicApi/index.ts +++ b/packages/cli/src/PublicApi/index.ts @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/naming-convention */ +import { Container } from 'typedi'; import type { Router } from 'express'; import express from 'express'; import fs from 'fs/promises'; @@ -11,11 +12,11 @@ import type { OpenAPIV3 } from 'openapi-types'; import type { JsonObject } from 'swagger-ui-express'; import config from '@/config'; -import { getInstanceBaseUrl } from '@/UserManagement/UserManagementHelper'; -import { Container } from 'typedi'; + import { InternalHooks } from '@/InternalHooks'; import { License } from '@/License'; import { UserRepository } from '@db/repositories/user.repository'; +import { UrlService } from '@/services/url.service'; async function createApiRouter( version: string, @@ -29,7 +30,7 @@ async function createApiRouter( // from the Swagger UI swaggerDocument.server = [ { - url: `${getInstanceBaseUrl()}/${publicApiEndpoint}/${version}}`, + url: `${Container.get(UrlService).getInstanceBaseUrl()}/${publicApiEndpoint}/${version}}`, }, ]; const apiController = express.Router(); diff --git a/packages/cli/src/UserManagement/UserManagementHelper.ts b/packages/cli/src/UserManagement/UserManagementHelper.ts index 2a1cdd38fd..548719fdc0 100644 --- a/packages/cli/src/UserManagement/UserManagementHelper.ts +++ b/packages/cli/src/UserManagement/UserManagementHelper.ts @@ -1,30 +1,15 @@ import { In } from 'typeorm'; import { Container } from 'typedi'; +import type { Scope } from '@n8n/permissions'; import type { WhereClause } from '@/Interfaces'; import type { User } from '@db/entities/User'; -import config from '@/config'; import { License } from '@/License'; -import { getWebhookBaseUrl } from '@/WebhookHelpers'; -import type { Scope } from '@n8n/permissions'; export function isSharingEnabled(): boolean { return Container.get(License).isSharingEnabled(); } -/** - * Return the n8n instance base URL without trailing slash. - */ -export function getInstanceBaseUrl(): string { - const n8nBaseUrl = config.getEnv('editorBaseUrl') || getWebhookBaseUrl(); - - return n8nBaseUrl.endsWith('/') ? n8nBaseUrl.slice(0, n8nBaseUrl.length - 1) : n8nBaseUrl; -} - -export function generateUserInviteUrl(inviterId: string, inviteeId: string): string { - return `${getInstanceBaseUrl()}/signup?inviterId=${inviterId}&inviteeId=${inviteeId}`; -} - // return the difference between two arrays export function rightDiff( [arr1, keyExtractor1]: [T1[], (item: T1) => string], diff --git a/packages/cli/src/WebhookHelpers.ts b/packages/cli/src/WebhookHelpers.ts index f218ec11c1..f86b90621e 100644 --- a/packages/cli/src/WebhookHelpers.ts +++ b/packages/cli/src/WebhookHelpers.ts @@ -49,7 +49,6 @@ import type { WebhookCORSRequest, WebhookRequest, } from '@/Interfaces'; -import * as GenericHelpers from '@/GenericHelpers'; import * as ResponseHelper from '@/ResponseHelper'; import * as WorkflowHelpers from '@/WorkflowHelpers'; import { WorkflowRunner } from '@/WorkflowRunner'; @@ -820,14 +819,3 @@ export async function executeWebhook( return; } } - -/** - * Returns the base URL of the webhooks - */ -export function getWebhookBaseUrl() { - let urlBaseWebhook = process.env.WEBHOOK_URL ?? GenericHelpers.getBaseUrl(); - if (!urlBaseWebhook.endsWith('/')) { - urlBaseWebhook += '/'; - } - return urlBaseWebhook; -} diff --git a/packages/cli/src/WorkflowExecuteAdditionalData.ts b/packages/cli/src/WorkflowExecuteAdditionalData.ts index e81d970982..cdb809a83c 100644 --- a/packages/cli/src/WorkflowExecuteAdditionalData.ts +++ b/packages/cli/src/WorkflowExecuteAdditionalData.ts @@ -48,7 +48,6 @@ import type { } from '@/Interfaces'; import { NodeTypes } from '@/NodeTypes'; import { Push } from '@/push'; -import * as WebhookHelpers from '@/WebhookHelpers'; import * as WorkflowHelpers from '@/WorkflowHelpers'; import { findSubworkflowStart, isWorkflowIdValid } from '@/utils'; import { PermissionChecker } from './UserManagement/PermissionChecker'; @@ -68,6 +67,7 @@ import { Logger } from './Logger'; import { saveExecutionProgress } from './executionLifecycleHooks/saveExecutionProgress'; import { WorkflowStaticDataService } from './workflows/workflowStaticData.service'; import { WorkflowRepository } from './databases/repositories/workflow.repository'; +import { UrlService } from './services/url.service'; const ERROR_TRIGGER_TYPE = config.getEnv('nodes.errorTriggerType'); @@ -133,7 +133,7 @@ export function executeErrorWorkflow( // Check if there was an error and if so if an errorWorkflow or a trigger is set let pastExecutionUrl: string | undefined; if (executionId !== undefined) { - pastExecutionUrl = `${WebhookHelpers.getWebhookBaseUrl()}workflow/${ + pastExecutionUrl = `${Container.get(UrlService).getWebhookBaseUrl()}workflow/${ workflowData.id }/executions/${executionId}`; } @@ -965,7 +965,7 @@ export async function getBase( currentNodeParameters?: INodeParameters, executionTimeoutTimestamp?: number, ): Promise { - const urlBaseWebhook = WebhookHelpers.getWebhookBaseUrl(); + const urlBaseWebhook = Container.get(UrlService).getWebhookBaseUrl(); const formWaitingBaseUrl = urlBaseWebhook + config.getEnv('endpoints.formWaiting'); diff --git a/packages/cli/src/commands/start.ts b/packages/cli/src/commands/start.ts index be9039fd6b..a2d674d9ad 100644 --- a/packages/cli/src/commands/start.ts +++ b/packages/cli/src/commands/start.ts @@ -15,7 +15,6 @@ import config from '@/config'; import { ActiveExecutions } from '@/ActiveExecutions'; import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner'; -import * as GenericHelpers from '@/GenericHelpers'; import { Server } from '@/Server'; import { EDITOR_UI_DIST_DIR, LICENSE_FEATURES } from '@/constants'; import { eventBus } from '@/eventbus'; @@ -27,6 +26,7 @@ import { SingleMainSetup } from '@/services/orchestration/main/SingleMainSetup'; import { OrchestrationHandlerMainService } from '@/services/orchestration/main/orchestration.handler.main.service'; import { PruningService } from '@/services/pruning.service'; import { MultiMainSetup } from '@/services/orchestration/main/MultiMainSetup.ee'; +import { UrlService } from '@/services/url.service'; import { SettingsRepository } from '@db/repositories/settings.repository'; import { ExecutionRepository } from '@db/repositories/execution.repository'; import { FeatureNotLicensedError } from '@/errors/feature-not-licensed.error'; @@ -77,7 +77,7 @@ export class Start extends BaseCommand { * Opens the UI in browser */ private openBrowser() { - const editorUrl = GenericHelpers.getBaseUrl(); + const editorUrl = Container.get(UrlService).baseUrl; // eslint-disable-next-line @typescript-eslint/no-unused-vars open(editorUrl, { wait: true }).catch((error: Error) => { @@ -321,7 +321,7 @@ export class Start extends BaseCommand { // Start to get active workflows and run their triggers await this.activeWorkflowRunner.init(); - const editorUrl = GenericHelpers.getBaseUrl(); + const editorUrl = Container.get(UrlService).baseUrl; this.log(`\nEditor is now accessible via:\n${editorUrl}`); // Allow to open n8n editor by pressing "o" diff --git a/packages/cli/src/controllers/oauth/abstractOAuth.controller.ts b/packages/cli/src/controllers/oauth/abstractOAuth.controller.ts index 9229c3aaa4..185a9afa01 100644 --- a/packages/cli/src/controllers/oauth/abstractOAuth.controller.ts +++ b/packages/cli/src/controllers/oauth/abstractOAuth.controller.ts @@ -7,13 +7,13 @@ import type { User } from '@db/entities/User'; import { CredentialsRepository } from '@db/repositories/credentials.repository'; import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository'; import type { ICredentialsDb } from '@/Interfaces'; -import { getInstanceBaseUrl } from '@/UserManagement/UserManagementHelper'; import type { OAuthRequest } from '@/requests'; import { RESPONSE_ERROR_MESSAGES } from '@/constants'; import { CredentialsHelper } from '@/CredentialsHelper'; import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData'; import { Logger } from '@/Logger'; import { ExternalHooks } from '@/ExternalHooks'; +import { UrlService } from '@/services/url.service'; import { BadRequestError } from '@/errors/response-errors/bad-request.error'; import { NotFoundError } from '@/errors/response-errors/not-found.error'; @@ -27,10 +27,11 @@ export abstract class AbstractOAuthController { private readonly credentialsHelper: CredentialsHelper, private readonly credentialsRepository: CredentialsRepository, private readonly sharedCredentialsRepository: SharedCredentialsRepository, + private readonly urlService: UrlService, ) {} get baseUrl() { - const restUrl = `${getInstanceBaseUrl()}/${config.getEnv('endpoints.rest')}`; + const restUrl = `${this.urlService.getInstanceBaseUrl()}/${config.getEnv('endpoints.rest')}`; return `${restUrl}/oauth${this.oauthVersion}-credential`; } diff --git a/packages/cli/src/controllers/passwordReset.controller.ts b/packages/cli/src/controllers/passwordReset.controller.ts index 06b1dd250c..dce0338e2e 100644 --- a/packages/cli/src/controllers/passwordReset.controller.ts +++ b/packages/cli/src/controllers/passwordReset.controller.ts @@ -5,7 +5,6 @@ import { IsNull, Not } from 'typeorm'; import validator from 'validator'; import { Get, Post, RestController } from '@/decorators'; -import { getInstanceBaseUrl } from '@/UserManagement/UserManagementHelper'; import { PasswordUtility } from '@/services/password.utility'; import { UserManagementMailer } from '@/UserManagement/email'; import { PasswordResetRequest } from '@/requests'; @@ -19,6 +18,7 @@ import { MfaService } from '@/Mfa/mfa.service'; import { Logger } from '@/Logger'; import { ExternalHooks } from '@/ExternalHooks'; import { InternalHooks } from '@/InternalHooks'; +import { UrlService } from '@/services/url.service'; import { InternalServerError } from '@/errors/response-errors/internal-server.error'; import { BadRequestError } from '@/errors/response-errors/bad-request.error'; import { UnauthorizedError } from '@/errors/response-errors/unauthorized.error'; @@ -41,6 +41,7 @@ export class PasswordResetController { private readonly mailer: UserManagementMailer, private readonly userService: UserService, private readonly mfaService: MfaService, + private readonly urlService: UrlService, private readonly license: License, private readonly passwordUtility: PasswordUtility, ) {} @@ -130,7 +131,7 @@ export class PasswordResetController { firstName, lastName, passwordResetUrl: url, - domain: getInstanceBaseUrl(), + domain: this.urlService.getInstanceBaseUrl(), }); } catch (error) { void this.internalHooks.onEmailFailed({ diff --git a/packages/cli/src/environments/variables/enviromentHelpers.ts b/packages/cli/src/environments/variables/environmentHelpers.ts similarity index 100% rename from packages/cli/src/environments/variables/enviromentHelpers.ts rename to packages/cli/src/environments/variables/environmentHelpers.ts diff --git a/packages/cli/src/environments/variables/variables.service.ee.ts b/packages/cli/src/environments/variables/variables.service.ee.ts index a962896de4..96060e7a4a 100644 --- a/packages/cli/src/environments/variables/variables.service.ee.ts +++ b/packages/cli/src/environments/variables/variables.service.ee.ts @@ -2,7 +2,7 @@ import { Container, Service } from 'typedi'; import type { Variables } from '@db/entities/Variables'; import { InternalHooks } from '@/InternalHooks'; import { generateNanoId } from '@db/utils/generators'; -import { canCreateNewVariable } from './enviromentHelpers'; +import { canCreateNewVariable } from './environmentHelpers'; import { CacheService } from '@/services/cache.service'; import { VariablesRepository } from '@db/repositories/variables.repository'; import type { DeepPartial } from 'typeorm'; diff --git a/packages/cli/src/services/frontend.service.ts b/packages/cli/src/services/frontend.service.ts index fe7d6141ba..cac41c7fd7 100644 --- a/packages/cli/src/services/frontend.service.ts +++ b/packages/cli/src/services/frontend.service.ts @@ -12,18 +12,16 @@ import type { } from 'n8n-workflow'; import { InstanceSettings } from 'n8n-core'; +import config from '@/config'; import { LICENSE_FEATURES } from '@/constants'; import { CredentialsOverwrites } from '@/CredentialsOverwrites'; import { CredentialTypes } from '@/CredentialTypes'; import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials'; import { License } from '@/License'; -import { getInstanceBaseUrl } from '@/UserManagement/UserManagementHelper'; -import * as WebhookHelpers from '@/WebhookHelpers'; -import config from '@/config'; import { getCurrentAuthenticationMethod } from '@/sso/ssoHelpers'; import { getLdapLoginLabel } from '@/Ldap/helpers'; import { getSamlLoginLabel } from '@/sso/saml/samlHelpers'; -import { getVariablesLimit } from '@/environments/variables/enviromentHelpers'; +import { getVariablesLimit } from '@/environments/variables/environmentHelpers'; import { getWorkflowHistoryLicensePruneTime, getWorkflowHistoryPruneTime, @@ -31,6 +29,7 @@ import { import { UserManagementMailer } from '@/UserManagement/email'; import type { CommunityPackagesService } from '@/services/communityPackages.service'; import { Logger } from '@/Logger'; +import { UrlService } from './url.service'; @Service() export class FrontendService { @@ -46,6 +45,7 @@ export class FrontendService { private readonly license: License, private readonly mailer: UserManagementMailer, private readonly instanceSettings: InstanceSettings, + private readonly urlService: UrlService, ) { loadNodesAndCredentials.addPostProcessor(async () => this.generateTypes()); void this.generateTypes(); @@ -61,7 +61,7 @@ export class FrontendService { } private initSettings() { - const instanceBaseUrl = getInstanceBaseUrl(); + const instanceBaseUrl = this.urlService.getInstanceBaseUrl(); const restEndpoint = config.getEnv('endpoints.rest'); const telemetrySettings: ITelemetrySettings = { @@ -93,7 +93,7 @@ export class FrontendService { maxExecutionTimeout: config.getEnv('executions.maxTimeout'), workflowCallerPolicyDefaultOption: config.getEnv('workflows.callerPolicyDefaultOption'), timezone: config.getEnv('generic.timezone'), - urlBaseWebhook: WebhookHelpers.getWebhookBaseUrl(), + urlBaseWebhook: this.urlService.getWebhookBaseUrl(), urlBaseEditor: instanceBaseUrl, versionCli: '', releaseChannel: config.getEnv('generic.releaseChannel'), @@ -222,8 +222,8 @@ export class FrontendService { const restEndpoint = config.getEnv('endpoints.rest'); // Update all urls, in case `WEBHOOK_URL` was updated by `--tunnel` - const instanceBaseUrl = getInstanceBaseUrl(); - this.settings.urlBaseWebhook = WebhookHelpers.getWebhookBaseUrl(); + const instanceBaseUrl = this.urlService.getInstanceBaseUrl(); + this.settings.urlBaseWebhook = this.urlService.getWebhookBaseUrl(); this.settings.urlBaseEditor = instanceBaseUrl; this.settings.oauthCallbackUrls = { oauth1: `${instanceBaseUrl}/${restEndpoint}/oauth1-credential/callback`, diff --git a/packages/cli/src/services/url.service.ts b/packages/cli/src/services/url.service.ts new file mode 100644 index 0000000000..b1ef737c2f --- /dev/null +++ b/packages/cli/src/services/url.service.ts @@ -0,0 +1,40 @@ +import { Service } from 'typedi'; +import config from '@/config'; + +@Service() +export class UrlService { + /** Returns the base URL n8n is reachable from */ + readonly baseUrl: string; + + constructor() { + this.baseUrl = this.generateBaseUrl(); + } + + /** Returns the base URL of the webhooks */ + getWebhookBaseUrl() { + let urlBaseWebhook = process.env.WEBHOOK_URL ?? this.baseUrl; + if (!urlBaseWebhook.endsWith('/')) { + urlBaseWebhook += '/'; + } + return urlBaseWebhook; + } + + /** Return the n8n instance base URL without trailing slash */ + getInstanceBaseUrl(): string { + const n8nBaseUrl = config.getEnv('editorBaseUrl') || this.getWebhookBaseUrl(); + + return n8nBaseUrl.endsWith('/') ? n8nBaseUrl.slice(0, n8nBaseUrl.length - 1) : n8nBaseUrl; + } + + private generateBaseUrl(): string { + const protocol = config.getEnv('protocol'); + const host = config.getEnv('host'); + const port = config.getEnv('port'); + const path = config.getEnv('path'); + + if ((protocol === 'http' && port === 80) || (protocol === 'https' && port === 443)) { + return `${protocol}://${host}${path}`; + } + return `${protocol}://${host}:${port}${path}`; + } +} diff --git a/packages/cli/src/services/user.service.ts b/packages/cli/src/services/user.service.ts index 41037f26c2..cae8c40c3c 100644 --- a/packages/cli/src/services/user.service.ts +++ b/packages/cli/src/services/user.service.ts @@ -1,10 +1,9 @@ -import Container, { Service } from 'typedi'; +import { Container, Service } from 'typedi'; import type { EntityManager, FindManyOptions, FindOneOptions, FindOptionsWhere } from 'typeorm'; import { In } from 'typeorm'; import { User } from '@db/entities/User'; import type { IUserSettings } from 'n8n-workflow'; import { UserRepository } from '@db/repositories/user.repository'; -import { generateUserInviteUrl, getInstanceBaseUrl } from '@/UserManagement/UserManagementHelper'; import type { PublicUser } from '@/Interfaces'; import type { PostHogClient } from '@/posthog'; import { type JwtPayload, JwtService } from './jwt.service'; @@ -14,6 +13,7 @@ import { createPasswordSha } from '@/auth/jwt'; import { UserManagementMailer } from '@/UserManagement/email'; import { InternalHooks } from '@/InternalHooks'; import { RoleService } from '@/services/role.service'; +import { UrlService } from '@/services/url.service'; import { ApplicationError, ErrorReporterProxy as ErrorReporter } from 'n8n-workflow'; import type { UserRequest } from '@/requests'; import { InternalServerError } from '@/errors/response-errors/internal-server.error'; @@ -26,6 +26,7 @@ export class UserService { private readonly jwtService: JwtService, private readonly mailer: UserManagementMailer, private readonly roleService: RoleService, + private readonly urlService: UrlService, ) {} async findOne(options: FindOneOptions) { @@ -78,7 +79,7 @@ export class UserService { } generatePasswordResetUrl(user: User) { - const instanceBaseUrl = getInstanceBaseUrl(); + const instanceBaseUrl = this.urlService.getInstanceBaseUrl(); const url = new URL(`${instanceBaseUrl}/change-password`); url.searchParams.append('token', this.generatePasswordResetToken(user)); @@ -161,7 +162,7 @@ export class UserService { } private addInviteUrl(inviterId: string, invitee: PublicUser) { - const url = new URL(getInstanceBaseUrl()); + const url = new URL(this.urlService.getInstanceBaseUrl()); url.pathname = '/signup'; url.searchParams.set('inviterId', inviterId); url.searchParams.set('inviteeId', invitee.id); @@ -193,11 +194,11 @@ export class UserService { toInviteUsers: { [key: string]: string }, role: 'member' | 'admin', ) { - const domain = getInstanceBaseUrl(); + const domain = this.urlService.getInstanceBaseUrl(); return Promise.all( Object.entries(toInviteUsers).map(async ([email, id]) => { - const inviteAcceptUrl = generateUserInviteUrl(owner.id, id); + const inviteAcceptUrl = `${domain}/signup?inviterId=${owner.id}&inviteeId=${id}`; const invitedUser: UserRequest.InviteResponse = { user: { id, diff --git a/packages/cli/src/sso/saml/routes/saml.controller.ee.ts b/packages/cli/src/sso/saml/routes/saml.controller.ee.ts index 85958e83a6..eeaf74bf14 100644 --- a/packages/cli/src/sso/saml/routes/saml.controller.ee.ts +++ b/packages/cli/src/sso/saml/routes/saml.controller.ee.ts @@ -1,6 +1,5 @@ import express from 'express'; import { Container, Service } from 'typedi'; -import { getInstanceBaseUrl } from '@/UserManagement/UserManagementHelper'; import { Authorized, Get, @@ -35,12 +34,16 @@ import url from 'url'; import querystring from 'querystring'; import { BadRequestError } from '@/errors/response-errors/bad-request.error'; import { AuthError } from '@/errors/response-errors/auth.error'; +import { UrlService } from '@/services/url.service'; @Service() @Authorized() @RestController('/sso/saml') export class SamlController { - constructor(private samlService: SamlService) {} + constructor( + private readonly samlService: SamlService, + private readonly urlService: UrlService, + ) {} @NoAuthRequired() @Get(SamlUrls.metadata) @@ -147,10 +150,10 @@ export class SamlController { if (isSamlLicensedAndEnabled()) { await issueCookie(res, loginResult.authenticatedUser); if (loginResult.onboardingRequired) { - return res.redirect(getInstanceBaseUrl() + SamlUrls.samlOnboarding); + return res.redirect(this.urlService.getInstanceBaseUrl() + SamlUrls.samlOnboarding); } else { const redirectUrl = req.body?.RelayState ?? SamlUrls.defaultRedirect; - return res.redirect(getInstanceBaseUrl() + redirectUrl); + return res.redirect(this.urlService.getInstanceBaseUrl() + redirectUrl); } } else { return res.status(202).send(loginResult.attributes); diff --git a/packages/cli/src/sso/saml/saml.service.ee.ts b/packages/cli/src/sso/saml/saml.service.ee.ts index 20ce62749e..2a20c25c7d 100644 --- a/packages/cli/src/sso/saml/saml.service.ee.ts +++ b/packages/cli/src/sso/saml/saml.service.ee.ts @@ -24,12 +24,12 @@ import axios from 'axios'; import https from 'https'; import type { SamlLoginBinding } from './types'; import { validateMetadata, validateResponse } from './samlValidator'; -import { getInstanceBaseUrl } from '@/UserManagement/UserManagementHelper'; import { Logger } from '@/Logger'; import { UserRepository } from '@db/repositories/user.repository'; import { SettingsRepository } from '@db/repositories/settings.repository'; import { BadRequestError } from '@/errors/response-errors/bad-request.error'; import { AuthError } from '@/errors/response-errors/auth.error'; +import { UrlService } from '@/services/url.service'; @Service() export class SamlService { @@ -55,7 +55,7 @@ export class SamlService { loginLabel: 'SAML', wantAssertionsSigned: true, wantMessageSigned: true, - relayState: getInstanceBaseUrl(), + relayState: this.urlService.getInstanceBaseUrl(), signatureConfig: { prefix: 'ds', location: { @@ -73,7 +73,10 @@ export class SamlService { }; } - constructor(private readonly logger: Logger) {} + constructor( + private readonly logger: Logger, + private readonly urlService: UrlService, + ) {} async init(): Promise { // load preferences first but do not apply so as to not load samlify unnecessarily @@ -143,14 +146,14 @@ export class SamlService { private getRedirectLoginRequestUrl(relayState?: string): BindingContext { const sp = this.getServiceProviderInstance(); - sp.entitySetting.relayState = relayState ?? getInstanceBaseUrl(); + sp.entitySetting.relayState = relayState ?? this.urlService.getInstanceBaseUrl(); const loginRequest = sp.createLoginRequest(this.getIdentityProviderInstance(), 'redirect'); return loginRequest; } private getPostLoginRequestUrl(relayState?: string): PostBindingContext { const sp = this.getServiceProviderInstance(); - sp.entitySetting.relayState = relayState ?? getInstanceBaseUrl(); + sp.entitySetting.relayState = relayState ?? this.urlService.getInstanceBaseUrl(); const loginRequest = sp.createLoginRequest( this.getIdentityProviderInstance(), 'post', diff --git a/packages/cli/src/sso/saml/serviceProvider.ee.ts b/packages/cli/src/sso/saml/serviceProvider.ee.ts index fa1fde6283..372d277b30 100644 --- a/packages/cli/src/sso/saml/serviceProvider.ee.ts +++ b/packages/cli/src/sso/saml/serviceProvider.ee.ts @@ -1,21 +1,22 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import { getInstanceBaseUrl } from '@/UserManagement/UserManagementHelper'; +import { Container } from 'typedi'; import type { ServiceProviderInstance } from 'samlify'; +import { UrlService } from '@/services/url.service'; import { SamlUrls } from './constants'; import type { SamlPreferences } from './types/samlPreferences'; let serviceProviderInstance: ServiceProviderInstance | undefined; export function getServiceProviderEntityId(): string { - return getInstanceBaseUrl() + SamlUrls.restMetadata; + return Container.get(UrlService).getInstanceBaseUrl() + SamlUrls.restMetadata; } export function getServiceProviderReturnUrl(): string { - return getInstanceBaseUrl() + SamlUrls.restAcs; + return Container.get(UrlService).getInstanceBaseUrl() + SamlUrls.restAcs; } export function getServiceProviderConfigTestReturnUrl(): string { - return getInstanceBaseUrl() + SamlUrls.configTestReturn; + return Container.get(UrlService).getInstanceBaseUrl() + SamlUrls.configTestReturn; } // TODO:SAML: make these configurable for the end user