From dd889ed2984c4c8a19a8b3cdb7ab7287a1258ea5 Mon Sep 17 00:00:00 2001 From: Rafael Tapia <rafael.tapia@rocket.chat> Date: Mon, 10 Feb 2025 16:20:13 -0300 Subject: [PATCH] fix: url not being properly mounted (#35115) --- .changeset/olive-stingrays-share.md | 5 ++ packages/cas-validate/jest.config.ts | 12 +++++ packages/cas-validate/package.json | 6 ++- packages/cas-validate/src/validate.spec.ts | 53 ++++++++++++++++++++++ packages/cas-validate/src/validate.ts | 19 ++++++-- yarn.lock | 1 + 6 files changed, 90 insertions(+), 6 deletions(-) create mode 100644 .changeset/olive-stingrays-share.md create mode 100644 packages/cas-validate/jest.config.ts create mode 100644 packages/cas-validate/src/validate.spec.ts diff --git a/.changeset/olive-stingrays-share.md b/.changeset/olive-stingrays-share.md new file mode 100644 index 00000000000..af0d6972172 --- /dev/null +++ b/.changeset/olive-stingrays-share.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/cas-validate": patch +--- + +Fixes a problem with CAS when URL connection ended with `/` diff --git a/packages/cas-validate/jest.config.ts b/packages/cas-validate/jest.config.ts new file mode 100644 index 00000000000..90df717c769 --- /dev/null +++ b/packages/cas-validate/jest.config.ts @@ -0,0 +1,12 @@ +import server from '@rocket.chat/jest-presets/server'; +import type { Config } from 'jest'; + +export default { + projects: [ + { + displayName: 'server', + preset: server.preset, + testMatch: ['<rootDir>/src/**/*.spec.[jt]s?(x)'], + }, + ], +} satisfies Config; diff --git a/packages/cas-validate/package.json b/packages/cas-validate/package.json index 5c21ecfa41d..0a005a4d8cb 100644 --- a/packages/cas-validate/package.json +++ b/packages/cas-validate/package.json @@ -11,7 +11,8 @@ "lint": "eslint --ext .js,.jsx,.ts,.tsx .", "lint:fix": "eslint --ext .js,.jsx,.ts,.tsx . --fix", "build": "rm -rf dist && tsc -p tsconfig.json", - "dev": "tsc -p tsconfig.json --watch --preserveWatchOutput" + "dev": "tsc -p tsconfig.json --watch --preserveWatchOutput", + "testunit": "jest" }, "main": "./dist/index.js", "typings": "./dist/index.d.ts", @@ -19,6 +20,7 @@ "/dist" ], "dependencies": { - "cheerio": "1.0.0" + "cheerio": "1.0.0", + "jest": "^29.7.0" } } diff --git a/packages/cas-validate/src/validate.spec.ts b/packages/cas-validate/src/validate.spec.ts new file mode 100644 index 00000000000..5fab0576153 --- /dev/null +++ b/packages/cas-validate/src/validate.spec.ts @@ -0,0 +1,53 @@ +import { getQueryPath } from './validate'; + +describe('getQueryPath', () => { + it('should generate the correct query path without trailing slash', () => { + const partialPathname = '/cas'; + const validatePath = 'validate'; + const query = { ticket: 'ST-12345', service: 'https://myapp.com' }; + + const result = getQueryPath(partialPathname, validatePath, query); + + expect(result).toBe('/cas/validate?ticket=ST-12345&service=https%3A%2F%2Fmyapp.com'); + }); + + it('should generate the correct query path with trailing slash', () => { + const partialPathname = '/cas/'; + const validatePath = 'validate'; + const query = { ticket: 'ST-12345', service: 'https://myapp.com' }; + + const result = getQueryPath(partialPathname, validatePath, query); + + expect(result).toBe('/cas/validate?ticket=ST-12345&service=https%3A%2F%2Fmyapp.com'); + }); + + it('should generate the correct query path with `/` partialPathname', () => { + const partialPathname = '/'; + const validatePath = 'validate'; + const query = { ticket: 'ST-12345', service: 'https://myapp.com' }; + + const result = getQueryPath(partialPathname, validatePath, query); + + expect(result).toBe('/validate?ticket=ST-12345&service=https%3A%2F%2Fmyapp.com'); + }); + + it('should generate the correct query path with empty partialPathname', () => { + const partialPathname = ''; + const validatePath = 'validate'; + const query = { ticket: 'ST-12345', service: 'https://myapp.com' }; + + const result = getQueryPath(partialPathname, validatePath, query); + + expect(result).toBe('/validate?ticket=ST-12345&service=https%3A%2F%2Fmyapp.com'); + }); + + it('should generate the correct query path with empty query', () => { + const partialPathname = '/cas'; + const validatePath = 'validate'; + const query = {}; + + const result = getQueryPath(partialPathname, validatePath, query); + + expect(result).toBe('/cas/validate'); + }); +}); diff --git a/packages/cas-validate/src/validate.ts b/packages/cas-validate/src/validate.ts index cef47a50a23..44cb8667667 100644 --- a/packages/cas-validate/src/validate.ts +++ b/packages/cas-validate/src/validate.ts @@ -1,5 +1,6 @@ import type { IncomingMessage } from 'http'; import https from 'https'; +import type { ParsedUrlQueryInput } from 'querystring'; import url from 'url'; import type { Cheerio, CheerioAPI } from 'cheerio'; @@ -149,6 +150,19 @@ function parseAttributes(elemSuccess: Cheerio<any>, cheerio: CheerioAPI): Record return attributes; } +export function getQueryPath( + partialPathname: string, + validatePath: string, + query: string | ParsedUrlQueryInput | null | undefined, +): string { + const pathname = partialPathname?.endsWith('/') ? partialPathname + validatePath : `${partialPathname}/${validatePath}`; + + return url.format({ + pathname, + query, + }); +} + export function validate(options: CasOptions, ticket: string, callback: CasCallback, renew = false): void { if (!options.base_url) { throw new Error('Required CAS option `base_url` missing.'); @@ -176,10 +190,7 @@ export function validate(options: CasOptions, ticket: string, callback: CasCallb ...(renew ? { renew: 1 } : {}), }; - const queryPath = url.format({ - pathname: `${pathname}/${validatePath}`, - query, - }); + const queryPath = getQueryPath(pathname ?? '/', validatePath, query); const req = https.get( { diff --git a/yarn.lock b/yarn.lock index 40b92d6b52b..3596ddbc6cb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7566,6 +7566,7 @@ __metadata: dependencies: cheerio: "npm:1.0.0" eslint: "npm:~8.45.0" + jest: "npm:^29.7.0" typescript: "npm:~5.7.2" languageName: unknown linkType: soft -- GitLab