diff --git a/src/identity/IdentityProviderHttpHandler.ts b/src/identity/IdentityProviderHttpHandler.ts index 2d9034134..efec23b7b 100644 --- a/src/identity/IdentityProviderHttpHandler.ts +++ b/src/identity/IdentityProviderHttpHandler.ts @@ -1,4 +1,3 @@ -import urljoin from 'url-join'; import type { ErrorHandler } from '../ldp/http/ErrorHandler'; import type { RequestParser } from '../ldp/http/RequestParser'; import { OkResponseDescription } from '../ldp/http/response/OkResponseDescription'; @@ -18,7 +17,7 @@ import { APPLICATION_JSON } from '../util/ContentTypes'; import { BadRequestHttpError } from '../util/errors/BadRequestHttpError'; import { assertError, createErrorMessage } from '../util/errors/ErrorUtil'; import { InternalServerError } from '../util/errors/InternalServerError'; -import { trimTrailingSlashes } from '../util/PathUtil'; +import { joinUrl, trimTrailingSlashes } from '../util/PathUtil'; import { addTemplateMetadata } from '../util/ResourceUtil'; import type { ProviderFactory } from './configuration/ProviderFactory'; import type { @@ -113,7 +112,7 @@ export class IdentityProviderHttpHandler extends HttpHandler { ) { super(); // Trimming trailing slashes so the relative URL starts with a slash after slicing this off - this.baseUrl = trimTrailingSlashes(urljoin(baseUrl, idpPath)); + this.baseUrl = trimTrailingSlashes(joinUrl(baseUrl, idpPath)); this.requestParser = requestParser; this.providerFactory = providerFactory; this.interactionRoutes = interactionRoutes; diff --git a/src/identity/configuration/IdentityProviderFactory.ts b/src/identity/configuration/IdentityProviderFactory.ts index 0f972df5f..0f00c5f13 100644 --- a/src/identity/configuration/IdentityProviderFactory.ts +++ b/src/identity/configuration/IdentityProviderFactory.ts @@ -12,11 +12,10 @@ import type { AnyObject, Account, ErrorOut, Adapter } from 'oidc-provider'; import { Provider } from 'oidc-provider'; -import urljoin from 'url-join'; import type { ErrorHandler } from '../../ldp/http/ErrorHandler'; import type { ResponseWriter } from '../../ldp/http/ResponseWriter'; import type { KeyValueStorage } from '../../storage/keyvalue/KeyValueStorage'; -import { ensureTrailingSlash } from '../../util/PathUtil'; +import { ensureTrailingSlash, joinUrl } from '../../util/PathUtil'; import type { AdapterFactory } from '../storage/AdapterFactory'; import type { ProviderFactory } from './ProviderFactory'; @@ -213,7 +212,7 @@ export class IdentityProviderFactory implements ProviderFactory { * this would result in `/foo/idp/device/auth`. */ private createRoute(relative: string): string { - return new URL(urljoin(this.baseUrl, this.idpPath, relative)).pathname; + return new URL(joinUrl(this.baseUrl, this.idpPath, relative)).pathname; } /** diff --git a/src/identity/interaction/email-password/handler/ForgotPasswordHandler.ts b/src/identity/interaction/email-password/handler/ForgotPasswordHandler.ts index dd1b7df68..3f6e5d82e 100644 --- a/src/identity/interaction/email-password/handler/ForgotPasswordHandler.ts +++ b/src/identity/interaction/email-password/handler/ForgotPasswordHandler.ts @@ -1,7 +1,6 @@ import assert from 'assert'; -import urljoin from 'url-join'; import { getLoggerFor } from '../../../../logging/LogUtil'; -import { ensureTrailingSlash } from '../../../../util/PathUtil'; +import { ensureTrailingSlash, joinUrl } from '../../../../util/PathUtil'; import type { TemplateEngine } from '../../../../util/templates/TemplateEngine'; import type { EmailSender } from '../../util/EmailSender'; import { getFormDataRequestBody } from '../../util/FormDataUtil'; @@ -74,7 +73,7 @@ export class ForgotPasswordHandler extends InteractionHandler { */ private async sendResetMail(recordId: string, email: string): Promise { this.logger.info(`Sending password reset to ${email}`); - const resetLink = urljoin(this.baseUrl, this.idpPath, `resetpassword/${recordId}`); + const resetLink = joinUrl(this.baseUrl, this.idpPath, `resetpassword/${recordId}`); const renderedEmail = await this.templateEngine.render({ resetLink }); await this.emailSender.handleSafe({ recipient: email, diff --git a/src/identity/interaction/email-password/handler/RegistrationHandler.ts b/src/identity/interaction/email-password/handler/RegistrationHandler.ts index fb24e0cd0..ed6eeee17 100644 --- a/src/identity/interaction/email-password/handler/RegistrationHandler.ts +++ b/src/identity/interaction/email-password/handler/RegistrationHandler.ts @@ -1,11 +1,11 @@ import assert from 'assert'; -import urljoin from 'url-join'; import type { Operation } from '../../../../ldp/operations/Operation'; import type { ResourceIdentifier } from '../../../../ldp/representation/ResourceIdentifier'; import { getLoggerFor } from '../../../../logging/LogUtil'; import type { IdentifierGenerator } from '../../../../pods/generate/IdentifierGenerator'; import type { PodManager } from '../../../../pods/PodManager'; import type { PodSettings } from '../../../../pods/settings/PodSettings'; +import { joinUrl } from '../../../../util/PathUtil'; import type { OwnershipValidator } from '../../../ownership/OwnershipValidator'; import { getFormDataRequestBody } from '../../util/FormDataUtil'; import { assertPassword, throwIdpInteractionError } from '../EmailPasswordUtil'; @@ -129,7 +129,7 @@ export class RegistrationHandler extends InteractionHandler { // Create or verify the WebID if (result.createWebId) { - result.webId = urljoin(podBaseUrl!.path, this.webIdSuffix); + result.webId = joinUrl(podBaseUrl!.path, this.webIdSuffix); } else { await this.ownershipValidator.handleSafe({ webId: result.webId! }); } diff --git a/src/util/PathUtil.ts b/src/util/PathUtil.ts index 894396539..cbcbad745 100644 --- a/src/util/PathUtil.ts +++ b/src/util/PathUtil.ts @@ -1,4 +1,5 @@ import { posix, win32 } from 'path'; +import urljoin from 'url-join'; import type { ResourceIdentifier } from '../ldp/representation/ResourceIdentifier'; /** @@ -190,3 +191,9 @@ export function resolveAssetPath(path: string = modulePathPlaceholder): string { } return absoluteFilePath(path); } + +/** + * Concatenates all the given strings into a normalized URL. + * Will place slashes between input strings if necessary. + */ +export const joinUrl = urljoin; diff --git a/test/integration/Identity.test.ts b/test/integration/Identity.test.ts index 3e6da6405..140822e17 100644 --- a/test/integration/Identity.test.ts +++ b/test/integration/Identity.test.ts @@ -3,9 +3,9 @@ import { URL } from 'url'; import { load } from 'cheerio'; import type { Response } from 'cross-fetch'; import { fetch } from 'cross-fetch'; -import urljoin from 'url-join'; import type { App } from '../../src/init/App'; import { APPLICATION_X_WWW_FORM_URLENCODED } from '../../src/util/ContentTypes'; +import { joinUrl } from '../../src/util/PathUtil'; import { getPort } from '../util/Util'; import { getDefaultVariables, getTestConfigPath, instantiateFromConfig } from './Config'; import { IdentityTestState } from './IdentityTestState'; @@ -51,7 +51,7 @@ describe('A Solid server with IDP', (): void => { let app: App; const redirectUrl = 'http://mockedredirect/'; const oidcIssuer = baseUrl; - const card = urljoin(baseUrl, 'profile/card'); + const card = joinUrl(baseUrl, 'profile/card'); const webId = `${card}#me`; const email = 'test@test.com'; const password = 'password!'; diff --git a/test/unit/identity/IdentityProviderHttpHandler.test.ts b/test/unit/identity/IdentityProviderHttpHandler.test.ts index 6be3b140a..fbf056b4c 100644 --- a/test/unit/identity/IdentityProviderHttpHandler.test.ts +++ b/test/unit/identity/IdentityProviderHttpHandler.test.ts @@ -1,5 +1,4 @@ import type { Provider } from 'oidc-provider'; -import urljoin from 'url-join'; import type { ProviderFactory } from '../../../src/identity/configuration/ProviderFactory'; import { InteractionRoute, IdentityProviderHttpHandler } from '../../../src/identity/IdentityProviderHttpHandler'; import type { InteractionHandler } from '../../../src/identity/interaction/email-password/handler/InteractionHandler'; @@ -19,6 +18,7 @@ import type { } from '../../../src/storage/conversion/RepresentationConverter'; import { BadRequestHttpError } from '../../../src/util/errors/BadRequestHttpError'; import { InternalServerError } from '../../../src/util/errors/InternalServerError'; +import { joinUrl } from '../../../src/util/PathUtil'; import { readableToString } from '../../../src/util/StreamUtil'; import { SOLID_HTTP, SOLID_META } from '../../../src/util/Vocabularies'; @@ -43,7 +43,7 @@ describe('An IdentityProviderHttpHandler', (): void => { requestParser = { handleSafe: jest.fn(async(req: HttpRequest): Promise => ({ - target: { path: urljoin(baseUrl, req.url!) }, + target: { path: joinUrl(baseUrl, req.url!) }, method: req.method!, body: new BasicRepresentation('', req.headers['content-type'] ?? 'text/plain'), preferences: { type: { 'text/html': 1 }}, diff --git a/test/unit/identity/interaction/email-password/handler/RegistrationHandler.test.ts b/test/unit/identity/interaction/email-password/handler/RegistrationHandler.test.ts index caf959ce2..bc594af09 100644 --- a/test/unit/identity/interaction/email-password/handler/RegistrationHandler.test.ts +++ b/test/unit/identity/interaction/email-password/handler/RegistrationHandler.test.ts @@ -1,4 +1,3 @@ -import urljoin from 'url-join'; import { RegistrationHandler, } from '../../../../../../src/identity/interaction/email-password/handler/RegistrationHandler'; @@ -10,6 +9,7 @@ import type { ResourceIdentifier } from '../../../../../../src/ldp/representatio import type { IdentifierGenerator } from '../../../../../../src/pods/generate/IdentifierGenerator'; import type { PodManager } from '../../../../../../src/pods/PodManager'; import type { PodSettings } from '../../../../../../src/pods/settings/PodSettings'; +import { joinUrl } from '../../../../../../src/util/PathUtil'; import { createPostFormOperation } from './Util'; describe('A RegistrationHandler', (): void => { @@ -248,7 +248,7 @@ describe('A RegistrationHandler', (): void => { it('can create a WebID with an account and pod.', async(): Promise => { const params = { email, password, confirmPassword, podName, createWebId, register, createPod }; - const generatedWebID = urljoin(baseUrl, podName, webIdSuffix); + const generatedWebID = joinUrl(baseUrl, podName, webIdSuffix); podSettings.webId = generatedWebID; podSettings.oidcIssuer = baseUrl;