Closes #805 - Unable to signin on self-hosted installations with non-www subdomain

This commit is contained in:
Tom Moor
2018-11-18 12:07:11 -08:00
parent 3c563e3001
commit 3718a9609d
4 changed files with 39 additions and 9 deletions

View File

@ -8,6 +8,7 @@ import UsersStore from 'stores/UsersStore';
import CollectionsStore from 'stores/CollectionsStore'; import CollectionsStore from 'stores/CollectionsStore';
import IntegrationsStore from 'stores/IntegrationsStore'; import IntegrationsStore from 'stores/IntegrationsStore';
import LoadingIndicator from 'components/LoadingIndicator'; import LoadingIndicator from 'components/LoadingIndicator';
import { isCustomSubdomain } from 'shared/utils/domains';
type Props = { type Props = {
auth: AuthStore, auth: AuthStore,
@ -26,13 +27,12 @@ const Auth = observer(({ auth, children }: Props) => {
} }
// If we're authenticated but viewing a subdomain that doesn't match the // If we're authenticated but viewing a subdomain that doesn't match the
// authenticated team then kick the user to the teams subdomain. // currently authenticated team then kick the user to the teams subdomain.
// www is a special case, as always
if ( if (
process.env.SUBDOMAINS_ENABLED && process.env.SUBDOMAINS_ENABLED &&
team.subdomain && team.subdomain &&
!hostname.startsWith(`${team.subdomain}.`) && isCustomSubdomain(hostname) &&
!hostname.startsWith('www.') !hostname.startsWith(`${team.subdomain}.`)
) { ) {
window.location.href = `${team.url}${window.location.pathname}`; window.location.href = `${team.url}${window.location.pathname}`;
return <LoadingIndicator />; return <LoadingIndicator />;

View File

@ -8,6 +8,7 @@ import serve from 'koa-static';
import parseDomain from 'parse-domain'; import parseDomain from 'parse-domain';
import apexRedirect from './middlewares/apexRedirect'; import apexRedirect from './middlewares/apexRedirect';
import renderpage from './utils/renderpage'; import renderpage from './utils/renderpage';
import { isCustomSubdomain } from '../shared/utils/domains';
import { robotsResponse } from './utils/robots'; import { robotsResponse } from './utils/robots';
import { NotFoundError } from './errors'; import { NotFoundError } from './errors';
import { Team } from './models'; import { Team } from './models';
@ -67,8 +68,6 @@ router.get('/changelog', async ctx => {
router.get('/', async ctx => { router.get('/', async ctx => {
const lastSignedIn = ctx.cookies.get('lastSignedIn'); const lastSignedIn = ctx.cookies.get('lastSignedIn');
const accessToken = ctx.cookies.get('accessToken'); const accessToken = ctx.cookies.get('accessToken');
const domain = parseDomain(ctx.request.hostname);
const subdomain = domain ? domain.subdomain : undefined;
// Because we render both the signed in and signed out views depending // Because we render both the signed in and signed out views depending
// on a cookie it's important that the browser does not render from cache. // on a cookie it's important that the browser does not render from cache.
@ -82,11 +81,16 @@ router.get('/', async ctx => {
// If we're on a custom subdomain then we display a slightly different signed // If we're on a custom subdomain then we display a slightly different signed
// out view that includes the teams basic information. // out view that includes the teams basic information.
if (subdomain && subdomain !== 'www') { if (
process.env.SUBDOMAINS_ENABLED === 'true' &&
isCustomSubdomain(ctx.request.hostname)
) {
const domain = parseDomain(ctx.request.hostname);
const subdomain = domain ? domain.subdomain : undefined;
const team = await Team.find({ const team = await Team.find({
where: { subdomain }, where: { subdomain },
}); });
if (team && process.env.SUBDOMAINS_ENABLED === 'true') { if (team) {
return renderpage( return renderpage(
ctx, ctx,
<SubdomainSignin <SubdomainSignin

View File

@ -9,6 +9,13 @@ export function stripSubdomain(hostname: string) {
return parsed.domain; return parsed.domain;
} }
export function isCustomSubdomain(hostname: string) {
const parsed = parseDomain(hostname);
if (!parsed) return false;
if (!parsed.subdomain || parsed.subdomain === 'www') return false;
return true;
}
export const RESERVED_SUBDOMAINS = [ export const RESERVED_SUBDOMAINS = [
'about', 'about',
'account', 'account',

View File

@ -1,5 +1,5 @@
/* eslint-disable flowtype/require-valid-file-annotation */ /* eslint-disable flowtype/require-valid-file-annotation */
import { stripSubdomain } from './domains'; import { stripSubdomain, isCustomSubdomain } from './domains';
describe('#stripSubdomain', () => { describe('#stripSubdomain', () => {
test('to work with localhost', () => { test('to work with localhost', () => {
@ -15,3 +15,22 @@ describe('#stripSubdomain', () => {
expect(stripSubdomain('test.example.com:3000')).toBe('example.com'); expect(stripSubdomain('test.example.com:3000')).toBe('example.com');
}); });
}); });
describe('#isCustomSubdomain', () => {
test('to work with localhost', () => {
expect(isCustomSubdomain('localhost')).toBe(false);
});
test('to return false for domains without a subdomain', () => {
expect(isCustomSubdomain('example')).toBe(false);
expect(isCustomSubdomain('example.com')).toBe(false);
expect(isCustomSubdomain('example.org:3000')).toBe(false);
});
test('to return false for www', () => {
expect(isCustomSubdomain('www.example.com')).toBe(false);
expect(isCustomSubdomain('www.example.com:3000')).toBe(false);
});
test('to return true for subdomains', () => {
expect(isCustomSubdomain('test.example.com')).toBe(true);
expect(isCustomSubdomain('test.example.com:3000')).toBe(true);
});
});