test: Hotfix/Orgs rewrite causing 404 with 3 or more dots domains (#9803)

* Fix RegExp

* Used ORGANIZATIONS_ENABLED to opt-out of orgs behaviour

* Add comments

* Add a new testcase

* Add response headers for easier debugging and Checkly tests in production
pull/9728/head^2
Hariom Balhara 2023-06-28 00:31:30 +05:30 committed by GitHub
parent 66d47974cb
commit 8384c3f9ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 131 additions and 42 deletions

View File

@ -0,0 +1,15 @@
const getDefaultSubdomain = (url) => {
if (!url.startsWith("http:") && !url.startsWith("https:")) {
// Make it a valid URL. Mabe we can simply return null and opt-out from orgs support till the use a URL scheme.
url = `https://${url}`;
}
const _url = new URL(url);
const regex = new RegExp(/^([a-z]+\:\/{2})?((?<subdomain>[\w-.]+)\.[\w-]+\.\w+)$/);
//console.log(_url.hostname, _url.hostname.match(regex));
return _url.hostname.match(regex)?.groups?.subdomain || null;
};
exports.getSubdomainRegExp = (url) => {
const defaultSubdomain = getDefaultSubdomain(url);
const subdomain = defaultSubdomain ? `(?!${defaultSubdomain})[^.]+` : "[^.]+";
return subdomain;
};

View File

@ -5,6 +5,7 @@ const englishTranslation = require("./public/static/locales/en/common.json");
const { withAxiom } = require("next-axiom");
const { i18n } = require("./next-i18next.config");
const { pages } = require("./pages");
const { getSubdomainRegExp } = require("./getSubdomainRegExp");
if (!process.env.NEXTAUTH_SECRET) throw new Error("Please set NEXTAUTH_SECRET");
if (!process.env.CALENDSO_ENCRYPTION_KEY) throw new Error("Please set CALENDSO_ENCRYPTION_KEY");
@ -70,14 +71,6 @@ const informAboutDuplicateTranslations = () => {
};
informAboutDuplicateTranslations();
const getSubdomain = () => {
const _url = new URL(process.env.NEXT_PUBLIC_WEBAPP_URL);
const regex = new RegExp(/^([a-z]+\:\/{2})?((?<subdomain>[\w-]+)\.[\w-]+\.\w+)$/);
//console.log(_url.hostname, _url.hostname.match(regex));
return _url.hostname.match(regex)?.groups?.subdomain || null;
};
const plugins = [];
if (process.env.ANALYZE === "true") {
// only load dependency if env `ANALYZE` was set
@ -102,6 +95,39 @@ const teamTypeRouteRegExp = "/team/:slug/:type((?!book$)[^/]+)";
const privateLinkRouteRegExp = "/d/:link/:slug((?!book$)[^/]+)";
const embedUserTypeRouteRegExp = `/:user((?!${pages.join("/|")})[^/]*)/:type/embed`;
const embedTeamTypeRouteRegExp = "/team/:slug/:type/embed";
const subdomainRegExp = getSubdomainRegExp(process.env.NEXT_PUBLIC_WEBAPP_URL);
// Important Note: Do update the RegExp in apps/web/test/lib/next-config.test.ts when changing it.
const orgHostRegExp = `^(?<orgSlug>${subdomainRegExp})\\..*`;
const matcherConfigRootPath = {
has: [
{
type: "host",
value: orgHostRegExp,
},
],
source: "/",
};
const matcherConfigOrgMemberPath = {
has: [
{
type: "host",
value: orgHostRegExp,
},
],
source: `/:user((?!${pages.join("|")}|_next|public)[a-zA-Z0-9\-_]+)`,
};
const matcherConfigUserPath = {
has: [
{
type: "host",
value: `^(?<orgSlug>${subdomainRegExp}[^.]+)\\..*`,
},
],
source: `/:user((?!${pages.join("|")}|_next|public))/:path*`,
};
/** @type {import("next").NextConfig} */
const nextConfig = {
@ -193,40 +219,23 @@ const nextConfig = {
return config;
},
async rewrites() {
const defaultSubdomain = getSubdomain();
const subdomain = defaultSubdomain ? `(?!${defaultSubdomain})[^.]+` : "[^.]+";
const beforeFiles = [
{
has: [
{
type: "host",
value: `^(?<orgSlug>${subdomain})\\..*`,
},
],
source: "/",
destination: "/team/:orgSlug",
},
{
has: [
{
type: "host",
value: `^(?<orgSlug>${subdomain})\\..*`,
},
],
source: `/:user((?!${pages.join("|")}|_next|public)[a-zA-Z0-9\-_]+)`,
destination: "/org/:orgSlug/:user",
},
{
has: [
{
type: "host",
value: `^(?<orgSlug>${subdomain}[^.]+)\\..*`,
},
],
source: `/:user((?!${pages.join("|")}|_next|public))/:path*`,
destination: "/:user/:path*",
},
...(process.env.ORGANIZATIONS_ENABLED
? [
{
...matcherConfigRootPath,
destination: "/team/:orgSlug",
},
{
...matcherConfigOrgMemberPath,
destination: "/org/:orgSlug/:user",
},
{
...matcherConfigUserPath,
destination: "/:user/:path*",
},
]
: []),
];
let afterFiles = [
@ -381,6 +390,35 @@ const nextConfig = {
},
],
},
...[
{
...matcherConfigRootPath,
headers: [
{
key: "X-Cal-Org-path",
value: "/team/:orgSlug",
},
],
},
{
...matcherConfigOrgMemberPath,
headers: [
{
key: "X-Cal-Org-path",
value: "/org/:orgSlug/:user",
},
],
},
{
...matcherConfigUserPath,
headers: [
{
key: "X-Cal-Org-path",
value: "/:user/:path",
},
],
},
],
];
},
async redirects() {

View File

@ -1,5 +1,7 @@
import { it, expect, describe, beforeAll, afterAll } from "vitest";
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { getSubdomainRegExp } = require("../../getSubdomainRegExp");
let userTypeRouteRegExp: RegExp;
let teamTypeRouteRegExp:RegExp;
let privateLinkRouteRegExp:RegExp
@ -161,3 +163,36 @@ describe('next.config.js - RegExp', ()=>{
})
})
describe('next.config.js - Org Rewrite', ()=> {
// RegExp copied from next.config.js
const orgHostRegExp = (subdomainRegExp:string)=> new RegExp(`^(?<orgSlug>${subdomainRegExp})\\..*`)
describe('SubDomain Retrieval from NEXT_PUBLIC_WEBAPP_URL', ()=>{
it('https://app.cal.com', ()=>{
const subdomainRegExp = getSubdomainRegExp('https://app.cal.com');
expect(orgHostRegExp(subdomainRegExp).exec('app.cal.com')).toEqual(null)
expect(orgHostRegExp(subdomainRegExp).exec('company.app.cal.com')?.groups?.orgSlug).toEqual('company')
expect(orgHostRegExp(subdomainRegExp).exec('org.cal.com')?.groups?.orgSlug).toEqual('org')
})
it('app.cal.com', ()=>{
const subdomainRegExp = getSubdomainRegExp('app.cal.com');
expect(orgHostRegExp(subdomainRegExp).exec('app.cal.com')).toEqual(null)
expect(orgHostRegExp(subdomainRegExp).exec('company.app.cal.com')?.groups?.orgSlug).toEqual('company')
})
it('https://calcom.app.company.com', ()=>{
const subdomainRegExp = getSubdomainRegExp('https://calcom.app.company.com');
expect(orgHostRegExp(subdomainRegExp).exec('calcom.app.company.com')).toEqual(null)
expect(orgHostRegExp(subdomainRegExp).exec('acme.calcom.app.company.com')?.groups?.orgSlug).toEqual('acme')
})
it('https://calcom.example.com', ()=>{
const subdomainRegExp = getSubdomainRegExp('https://calcom.example.com');
expect(orgHostRegExp(subdomainRegExp).exec('calcom.example.com')).toEqual(null)
expect(orgHostRegExp(subdomainRegExp).exec('acme.calcom.example.com')?.groups?.orgSlug).toEqual('acme')
// The following also matches which causes anything other than the domain in NEXT_PUBLIC_WEBAPP_URL to give 404
expect(orgHostRegExp(subdomainRegExp).exec('some-other.company.com')?.groups?.orgSlug).toEqual('some-other')
})
})
})

View File

@ -22,7 +22,8 @@ export function defaultCookies(useSecureCookies: boolean): CookiesOptions {
const defaultOptions: CookieOption["options"] = {
domain: isENVDev
? process.env.ORGANIZATIONS_ENABLED
? ".cal.local"
? //FIXME: This is causing login to not work if someone uses anything other .cal.local for testing
".cal.local"
: undefined
: NEXTAUTH_COOKIE_DOMAIN,
// To enable cookies on widgets,