[hotfix] Invitation URLs and SSO redirection URLs are not subpath compatible (#7155)

* Added subpath compatible code to the invitation URL generation

* fix: sharable app link
- launch app url

* Added PR changes
This commit is contained in:
Muhsin Shah C P 2023-08-08 19:26:55 +05:30 committed by GitHub
parent 36efa1be11
commit 4f9f555187
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 61 additions and 30 deletions

View file

@ -14,7 +14,9 @@ export default function GoogleSSOLoginButton(props) {
e.preventDefault();
const { client_id } = props.configs;
const authUrl = buildURLWithQuery('https://accounts.google.com/o/oauth2/auth', {
redirect_uri: `${window.public_config?.TOOLJET_HOST}/sso/google${props.configId ? `/${props.configId}` : ''}`,
redirect_uri: `${window.public_config?.TOOLJET_HOST}${window.public_config?.SUB_PATH ?? '/'}sso/google${
props.configId ? `/${props.configId}` : ''
}`,
response_type: 'id_token',
scope: 'email profile',
client_id,

View file

@ -9,6 +9,7 @@ import Textarea from '@/_ui/Textarea';
import { withTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
import { getPrivateRoute } from '@/_helpers/routes';
import { getSubpath } from '@/_helpers/utils';
class ManageAppUsersComponent extends React.Component {
constructor(props) {
@ -133,7 +134,7 @@ class ManageAppUsersComponent extends React.Component {
render() {
const { isLoading, app, slugError, isSlugVerificationInProgress } = this.state;
const appId = app.id;
const appLink = `${window.public_config?.TOOLJET_HOST}/applications/`;
const appLink = `${window.public_config?.TOOLJET_HOST}${getSubpath() ? getSubpath() : ''}/applications/`;
const shareableLink = appLink + (this.props.slug || appId);
const slugButtonClass = isSlugVerificationInProgress ? '' : slugError !== null ? 'is-invalid' : 'is-valid';
const embeddableLink = `<iframe width="560" height="315" src="${appLink}${this.props.slug}" title="Tooljet app - ${this.props.slug}" frameborder="0" allowfullscreen></iframe>`;

View file

@ -12,6 +12,7 @@ import SolidIcon from '@/_ui/Icon/SolidIcons';
import BulkIcon from '@/_ui/Icon/BulkIcons';
import { getPrivateRoute } from '@/_helpers/routes';
import { getSubpath } from '@/_helpers/utils';
const { defaultIcon } = configs;
export default function AppCard({
@ -149,7 +150,9 @@ export default function AppCard({
)}
onClick={() => {
if (app?.current_version_id) {
window.open(urlJoin(window.public_config?.TOOLJET_HOST, `/applications/${app.slug}`));
window.open(
urlJoin(window.public_config?.TOOLJET_HOST, getSubpath() ?? '', `/applications/${app.slug}`)
);
} else {
navigate(app?.current_version_id ? `/applications/${app.slug}` : '');
}

View file

@ -205,11 +205,13 @@ class ManageOrgUsersComponent extends React.Component {
if (user.account_setup_token) {
return urlJoin(
window.public_config?.TOOLJET_HOST,
window.public_config?.SUB_PATH ?? '',
`/invitations/${user.account_setup_token}/workspaces/${user.invitation_token}?oid=${authenticationService?.currentSessionValue.current_organization_id}`
);
}
return urlJoin(
window.public_config?.TOOLJET_HOST,
window.public_config?.SUB_PATH ?? '',
`/organization-invitations/${user.invitation_token}?oid=${authenticationService?.currentSessionValue.current_organization_id}`
);
};

View file

@ -166,7 +166,9 @@ export function GeneralSettings({ settings, updateData, instanceSettings, darkMo
<div className="d-flex justify-content-between form-control align-items-center">
<p id="login-url" data-cy="workspace-login-url">
{`${window.public_config?.TOOLJET_HOST}/login/${authenticationService?.currentSessionValue?.current_organization_id}`}
{`${window.public_config?.TOOLJET_HOST}${
window.public_config?.SUB_PATH ? window.public_config?.SUB_PATH : '/'
}login/${authenticationService?.currentSessionValue?.current_organization_id}`}
</p>
<SolidIcon name="copy" width="16" onClick={() => copyFunction('login-url')} />
</div>

View file

@ -152,10 +152,9 @@ export function Git({ settings, updateData }) {
{t('header.organization.menus.manageSSO.github.redirectUrl', 'Redirect URL')}
</label>
<div className="d-flex justify-content-between form-control align-items-center">
<p
data-cy="redirect-url"
id="redirect-url"
>{`${window.public_config?.TOOLJET_HOST}/sso/git/${configId}`}</p>
<p data-cy="redirect-url" id="redirect-url">{`${window.public_config?.TOOLJET_HOST}${
window.public_config?.SUB_PATH ? window.public_config?.SUB_PATH : '/'
}sso/git/${configId}`}</p>
<SolidIcon name="copy" width="16" onClick={() => copyFunction('redirect-url')} />
</div>
</div>

View file

@ -113,10 +113,9 @@ export function Google({ settings, updateData }) {
{t('header.organization.menus.manageSSO.google.redirectUrl', 'Redirect URL')}
</label>
<div className="d-flex justify-content-between form-control align-items-center">
<p
data-cy="redirect-url"
id="redirect-url"
>{`${window.public_config?.TOOLJET_HOST}/sso/google/${configId}`}</p>
<p data-cy="redirect-url" id="redirect-url">{`${window.public_config?.TOOLJET_HOST}${
window.public_config?.SUB_PATH ? window.public_config?.SUB_PATH : '/'
}sso/google/${configId}`}</p>
<SolidIcon name="copy" width="16" onClick={() => copyFunction('redirect-url')} />
</div>
</div>

View file

@ -17,7 +17,7 @@ import {
URL_SSO_SOURCE,
WORKSPACE_USER_STATUS,
} from 'src/helpers/user_lifecycle';
import { dbTransactionWrap, generateNextName } from 'src/helpers/utils.helper';
import { dbTransactionWrap, generateInviteURL, generateNextName } from 'src/helpers/utils.helper';
import { DeepPartial, EntityManager } from 'typeorm';
import { GitOAuthService } from './git_oauth.service';
import { GoogleOAuthService } from './google_oauth.service';
@ -316,9 +316,12 @@ export class OauthService {
)?.invitationToken;
return decamelizeKeys({
redirectUrl: `${this.configService.get<string>('TOOLJET_HOST')}/invitations/${
userDetails.invitationToken
}/workspaces/${organizationToken}?oid=${organization.id}&source=${URL_SSO_SOURCE}`,
redirectUrl: generateInviteURL(
userDetails.invitationToken,
organizationToken,
organization.id,
URL_SSO_SOURCE
),
});
}
}
@ -331,9 +334,7 @@ export class OauthService {
manager
);
return decamelizeKeys({
redirectUrl: `${this.configService.get<string>('TOOLJET_HOST')}/invitations/${
userDetails.invitationToken
}?source=${URL_SSO_SOURCE}`,
redirectUrl: generateInviteURL(userDetails.invitationToken, null, null, URL_SSO_SOURCE),
});
}
return await this.authService.generateLoginResultPayload(

View file

@ -144,3 +144,25 @@ export const truncateAndReplace = (name) => {
}
return name + secondsSinceEpoch;
};
export const generateInviteURL = (
invitationToken: string,
organizationToken?: string,
organizationId?: string,
source?: string
) => {
const host = process.env.TOOLJET_HOST;
const subpath = process.env.SUB_PATH;
return `${host}${subpath ? subpath : '/'}invitations/${invitationToken}${
organizationToken ? `/workspaces/${organizationToken}${organizationId ? `?oid=${organizationId}` : ''}` : ''
}${source ? `${organizationId ? '&' : '?'}source=${source}` : ''}`;
};
export const generateOrgInviteURL = (organizationToken: string, organizationId?: string) => {
const host = process.env.TOOLJET_HOST;
const subpath = process.env.SUB_PATH;
return `${host}${subpath ? subpath : '/'}organization-invitations/${organizationToken}${
organizationId ? `?oid=${organizationId}` : ''
}`;
};

View file

@ -21,7 +21,7 @@ import { DeepPartial, EntityManager, Repository } from 'typeorm';
import { OrganizationUser } from 'src/entities/organization_user.entity';
import { CreateAdminDto, CreateUserDto } from '@dto/user.dto';
import { AcceptInviteDto } from '@dto/accept-organization-invite.dto';
import { dbTransactionWrap, generateNextName } from 'src/helpers/utils.helper';
import { dbTransactionWrap, generateInviteURL, generateNextName, generateOrgInviteURL } from 'src/helpers/utils.helper';
import {
getUserErrorMessages,
getUserStatusAndSource,
@ -458,7 +458,8 @@ export class AuthService {
user.email,
`${user.firstName} ${user.lastName} ?? ''`,
user.invitationToken,
`${organizationUser.invitationToken}?oid=${organizationUser.organizationId}`
`${organizationUser.invitationToken}`,
organizationUser.organizationId
)
.catch((err) => console.error('Error while sending welcome mail', err));
throw new UnauthorizedException(
@ -484,13 +485,11 @@ export class AuthService {
if (!user && organizationUser) {
return {
redirect_url: `${this.configService.get<string>(
'TOOLJET_HOST'
)}/organization-invitations/${organizationToken}?oid=${organizationUser.organizationId}`,
redirect_url: generateOrgInviteURL(organizationToken, organizationUser.organizationId),
};
} else if (user && !organizationUser) {
return {
redirect_url: `${this.configService.get<string>('TOOLJET_HOST')}/invitations/${token}`,
redirect_url: generateInviteURL(token),
};
}
}

View file

@ -1,5 +1,6 @@
import { Injectable } from '@nestjs/common';
import handlebars from 'handlebars';
import { generateInviteURL, generateOrgInviteURL } from 'src/helpers/utils.helper';
const path = require('path');
const fs = require('fs');
const nodemailer = require('nodemailer');
@ -70,13 +71,12 @@ export class EmailService {
name: string,
invitationtoken: string,
organizationInvitationToken?: string,
organizationId?: string,
organizationName?: string,
sender?: string
) {
const subject = 'Welcome to ToolJet';
const inviteUrl = `${this.TOOLJET_HOST}/invitations/${invitationtoken}${
organizationInvitationToken ? `/workspaces/${organizationInvitationToken}` : ''
}`;
const inviteUrl = generateInviteURL(invitationtoken, organizationInvitationToken, organizationId);
const html = `
<!DOCTYPE html>
<html>
@ -117,7 +117,7 @@ export class EmailService {
organizationName: string
) {
const subject = 'Welcome to ToolJet';
const inviteUrl = `${this.TOOLJET_HOST}/organization-invitations/${invitationtoken}`;
const inviteUrl = generateOrgInviteURL(invitationtoken);
const html = `
<!DOCTYPE html>
<html>

View file

@ -598,7 +598,8 @@ export class OrganizationsService {
user.email,
user.firstName,
user.invitationToken,
`${organizationUser.invitationToken}?oid=${organizationUser.organizationId}`,
organizationUser.invitationToken,
organizationUser.organizationId,
currentOrganization.name,
`${currentUser.firstName} ${currentUser.lastName ?? ''}`
)